mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
text input component
This commit is contained in:
128
sdkjs-plugins/content/icons/src/js/components/input/input.css
Normal file
128
sdkjs-plugins/content/icons/src/js/components/input/input.css
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
.input-field-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-main {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-element {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
&::placeholder {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-focused .input-field-element {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-disabled .input-field-element {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #c0c4cc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear button */
|
||||||
|
.input-field-clear {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #c0c4cc;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Counter */
|
||||||
|
.input-field-counter {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-counter-warning {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-counter-error {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validation */
|
||||||
|
.input-field-validation {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #f56c6c;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-invalid .input-field-element {
|
||||||
|
border-color: #f56c6c;
|
||||||
|
&:focus {
|
||||||
|
border-color: #f56c6c;
|
||||||
|
box-shadow: 0 0 0 2px rgba(245, 108, 108, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-valid .input-field-element {
|
||||||
|
border-color: #67c23a;
|
||||||
|
&:focus {
|
||||||
|
border-color: #67c23a;
|
||||||
|
box-shadow: 0 0 0 2px rgba(103, 194, 58, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Different input types */
|
||||||
|
.input-field-password .input-field-element {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-search .input-field-element {
|
||||||
|
padding-left: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sizes */
|
||||||
|
.input-field-sm .input-field-element {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field-lg .input-field-element {
|
||||||
|
padding: 14px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
341
sdkjs-plugins/content/icons/src/js/components/input/input.js
Normal file
341
sdkjs-plugins/content/icons/src/js/components/input/input.js
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
class InputField {
|
||||||
|
_container;
|
||||||
|
_options;
|
||||||
|
input;
|
||||||
|
#clearButton;
|
||||||
|
#counter;
|
||||||
|
#counterCurrent;
|
||||||
|
#counterMax;
|
||||||
|
#validationElement;
|
||||||
|
isFocused;
|
||||||
|
isValid;
|
||||||
|
_validationMessage;
|
||||||
|
|
||||||
|
constructor(container, options = {}) {
|
||||||
|
this._container =
|
||||||
|
typeof container === "string"
|
||||||
|
? document.querySelector(container)
|
||||||
|
: container;
|
||||||
|
|
||||||
|
this._options = {
|
||||||
|
type: options.type || "text",
|
||||||
|
placeholder: options.placeholder || "",
|
||||||
|
value: options.value || "",
|
||||||
|
disabled: options.disabled || false,
|
||||||
|
readonly: options.readonly || false,
|
||||||
|
required: options.required || false,
|
||||||
|
maxLength: options.maxLength || null,
|
||||||
|
minLength: options.minLength || null,
|
||||||
|
pattern: options.pattern || null,
|
||||||
|
showCounter: options.showCounter || false,
|
||||||
|
showClear: options.showClear || false,
|
||||||
|
validation: options.validation || null,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isFocused = false;
|
||||||
|
this.isValid = true;
|
||||||
|
this._validationMessage = "";
|
||||||
|
|
||||||
|
this.#init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#init() {
|
||||||
|
this._createDOM();
|
||||||
|
this.#bindEvents();
|
||||||
|
this.#updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDOM() {
|
||||||
|
this._container.innerHTML = "";
|
||||||
|
this._container.classList.add("input-field-container");
|
||||||
|
|
||||||
|
this._container.innerHTML = `
|
||||||
|
<div class="input-field ${
|
||||||
|
this._options.disabled ? "input-field-disabled" : ""
|
||||||
|
}">
|
||||||
|
<div class="input-field-main">
|
||||||
|
<input
|
||||||
|
class="input-field-element"
|
||||||
|
type="${this._options.type}"
|
||||||
|
placeholder="${this._options.placeholder}"
|
||||||
|
value="${this._options.value}"
|
||||||
|
${this._options.disabled ? "disabled" : ""}
|
||||||
|
${this._options.readonly ? "readonly" : ""}
|
||||||
|
${this._options.required ? "required" : ""}
|
||||||
|
${
|
||||||
|
this._options.maxLength
|
||||||
|
? `maxlength="${this._options.maxLength}"`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
this._options.pattern
|
||||||
|
? `pattern="${this._options.pattern}"`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
${
|
||||||
|
this._options.showClear
|
||||||
|
? `
|
||||||
|
<button type="button" class="input-field-clear" style="display: none;">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
${
|
||||||
|
this._options.showCounter
|
||||||
|
? `
|
||||||
|
<div class="input-field-counter">
|
||||||
|
<span class="input-field-counter-current">0</span>
|
||||||
|
/
|
||||||
|
<span class="input-field-counter-max">${
|
||||||
|
this._options.maxLength || "∞"
|
||||||
|
}</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
<div class="input-field-validation" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Links to elements
|
||||||
|
this.input = this._container.querySelector(".input-field-element");
|
||||||
|
this.#clearButton = this._container.querySelector(".input-field-clear");
|
||||||
|
this.#counter = this._container.querySelector(".input-field-counter");
|
||||||
|
this.#counterCurrent = this._container.querySelector(
|
||||||
|
".input-field-counter-current"
|
||||||
|
);
|
||||||
|
this.#counterMax = this._container.querySelector(
|
||||||
|
".input-field-counter-max"
|
||||||
|
);
|
||||||
|
this.#validationElement = this._container.querySelector(
|
||||||
|
".input-field-validation"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#bindEvents() {
|
||||||
|
this.input.addEventListener("focus", () => this.#handleFocus());
|
||||||
|
this.input.addEventListener("blur", () => this.#handleBlur());
|
||||||
|
|
||||||
|
this.input.addEventListener("input", (e) => this.#handleInput(e));
|
||||||
|
|
||||||
|
this.input.addEventListener("keydown", (e) => this.#handleKeydown(e));
|
||||||
|
|
||||||
|
if (this.#clearButton) {
|
||||||
|
this.#clearButton.addEventListener("click", () => this.clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input.addEventListener("change", () => this.validate());
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleFocus() {
|
||||||
|
this.isFocused = true;
|
||||||
|
this._container.classList.add("input-field-focused");
|
||||||
|
this.#updateClearButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleBlur() {
|
||||||
|
this.isFocused = false;
|
||||||
|
this._container.classList.remove("input-field-focused");
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleInput(e) {
|
||||||
|
this.#updateClearButton();
|
||||||
|
this.#updateCounter();
|
||||||
|
this.#triggerInputEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleKeydown(e) {
|
||||||
|
if (e.key === "Escape" && this._options.showClear) {
|
||||||
|
this.clear();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
this.#triggerSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateClearButton() {
|
||||||
|
if (this.#clearButton) {
|
||||||
|
const hasValue = this.input.value.length > 0;
|
||||||
|
this.#clearButton.style.display = hasValue ? "block" : "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateCounter() {
|
||||||
|
if (this.#counter && this._options.maxLength) {
|
||||||
|
const current = this.input.value.length;
|
||||||
|
const max = this._options.maxLength;
|
||||||
|
|
||||||
|
this.#counterCurrent.textContent = current;
|
||||||
|
this.#counterMax.textContent = max;
|
||||||
|
|
||||||
|
// Change color when near max
|
||||||
|
if (current > max * 0.9) {
|
||||||
|
this.#counter.classList.add("input-field-counter-warning");
|
||||||
|
} else {
|
||||||
|
this.#counter.classList.remove("input-field-counter-warning");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current > max) {
|
||||||
|
this.#counter.classList.add("input-field-counter-error");
|
||||||
|
} else {
|
||||||
|
this.#counter.classList.remove("input-field-counter-error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
if (!this._options.validation) {
|
||||||
|
this.isValid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = this.input.value;
|
||||||
|
let isValid = true;
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
// Basic validation HTML
|
||||||
|
if (this._options.required && !value.trim()) {
|
||||||
|
isValid = false;
|
||||||
|
message = "This field is required";
|
||||||
|
} else if (
|
||||||
|
this._options.minLength &&
|
||||||
|
value.length < this._options.minLength
|
||||||
|
) {
|
||||||
|
isValid = false;
|
||||||
|
message = `Minimum length is ${this._options.minLength} characters`;
|
||||||
|
} else if (
|
||||||
|
this._options.maxLength &&
|
||||||
|
value.length > this._options.maxLength
|
||||||
|
) {
|
||||||
|
isValid = false;
|
||||||
|
message = `Maximum length is ${this._options.maxLength} characters`;
|
||||||
|
} else if (
|
||||||
|
this._options.pattern &&
|
||||||
|
!new RegExp(this._options.pattern).test(value)
|
||||||
|
) {
|
||||||
|
isValid = false;
|
||||||
|
message = "Invalid format";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom validation
|
||||||
|
if (isValid && typeof this._options.validation === "function") {
|
||||||
|
const customValidation = this._options.validation(value);
|
||||||
|
if (customValidation && !customValidation.isValid) {
|
||||||
|
isValid = false;
|
||||||
|
message = customValidation.message || "Invalid value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isValid = isValid;
|
||||||
|
this._validationMessage = message;
|
||||||
|
this.updateValidationState();
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateValidationState() {
|
||||||
|
if (this.#validationElement) {
|
||||||
|
if (!this.isValid) {
|
||||||
|
this.#validationElement.textContent = this._validationMessage;
|
||||||
|
this.#validationElement.style.display = "block";
|
||||||
|
this._container.classList.add("input-field-invalid");
|
||||||
|
this._container.classList.remove("input-field-valid");
|
||||||
|
} else if (this.input.value.length > 0) {
|
||||||
|
this.#validationElement.style.display = "none";
|
||||||
|
this._container.classList.add("input-field-valid");
|
||||||
|
this._container.classList.remove("input-field-invalid");
|
||||||
|
} else {
|
||||||
|
this.#validationElement.style.display = "none";
|
||||||
|
this._container.classList.remove(
|
||||||
|
"input-field-valid",
|
||||||
|
"input-field-invalid"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateState() {
|
||||||
|
this.#updateClearButton();
|
||||||
|
this.#updateCounter();
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
getValue() {
|
||||||
|
return this.input.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value) {
|
||||||
|
this.input.value = value;
|
||||||
|
this.#updateState();
|
||||||
|
this.#triggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.setValue("");
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
blur() {
|
||||||
|
this.input.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
enable() {
|
||||||
|
this.input.disabled = false;
|
||||||
|
this._options.disabled = false;
|
||||||
|
this._container.classList.remove("input-field-disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
disable() {
|
||||||
|
this.input.disabled = true;
|
||||||
|
this._options.disabled = true;
|
||||||
|
this._container.classList.add("input-field-disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
#triggerInputEvent(e) {
|
||||||
|
const event = new CustomEvent("inputfield:input", {
|
||||||
|
detail: {
|
||||||
|
value: this.input.value,
|
||||||
|
originalEvent: e,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this._container.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#triggerChange() {
|
||||||
|
const event = new CustomEvent("inputfield:change", {
|
||||||
|
detail: {
|
||||||
|
value: this.input.value,
|
||||||
|
isValid: this.isValid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this._container.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#triggerSubmit() {
|
||||||
|
const event = new CustomEvent("inputfield:submit", {
|
||||||
|
detail: {
|
||||||
|
value: this.input.value,
|
||||||
|
isValid: this.isValid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this._container.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this._container.innerHTML = "";
|
||||||
|
this._container.classList.remove("input-field-container");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
class SearchInput extends InputField {
|
||||||
|
constructor(container, options = {}) {
|
||||||
|
super(container, {
|
||||||
|
type: "search",
|
||||||
|
showClear: true,
|
||||||
|
showSearchIcon: true,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDOM() {
|
||||||
|
super._createDOM();
|
||||||
|
this._container.classList.add("input-field-search");
|
||||||
|
|
||||||
|
if (this._options.showSearchIcon) {
|
||||||
|
const icon = document.createElement("span");
|
||||||
|
icon.className = "input-field-search-icon";
|
||||||
|
icon.innerHTML = "🔍";
|
||||||
|
icon.style.position = "absolute";
|
||||||
|
icon.style.left = "12px";
|
||||||
|
icon.style.top = "50%";
|
||||||
|
icon.style.transform = "translateY(-50%)";
|
||||||
|
icon.style.color = "#c0c4cc";
|
||||||
|
|
||||||
|
this._container.querySelector(".input-field-main").prepend(icon);
|
||||||
|
this.input.style.paddingLeft = "36px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user