gd-bs
Version:
Bootstrap JavaScript, TypeScript and Web Components library.
377 lines (323 loc) • 14.2 kB
text/typescript
import { IInputGroup, IInputGroupFileValue, IInputGroupProps } from "./types";
import { Base } from "../base";
import { Button } from "../button";
import { Dropdown } from "../dropdown";
import { IDropdown, IDropdownItem } from "../dropdown/types";
import { HTML } from "./templates";
/**
* Input Group Types
*/
export enum InputGroupTypes {
ColorPicker = 1,
Email = 2,
File = 3,
Password = 4,
Range = 5,
Search = 6,
TextArea = 7,
TextField = 8
}
/**
* Input Group
* @param props The input group properties.
*/
class _InputGroup extends Base<IInputGroupProps> implements IInputGroup {
private _ddlAppended: IDropdown = null;
private _ddlPrepended: IDropdown = null;
private _fileValue: IInputGroupFileValue = null;
private _initFl: boolean = false;
// Constructor
constructor(props: IInputGroupProps, template: string = HTML) {
super(template, props);
// Configure the collapse
this.configure();
// Configure the textbox
this.configureTextbox();
// Configure the events
this.configureEvents();
// Configure the parent
this.configureParent();
// Set the flag
this._initFl = true;
}
// Configure the card group
private configure() {
let elInput = this.el.querySelector("input");
if (elInput) {
// Set the class names
this.props.isLarge ? this.el.classList.add("input-group-lg") : null;
this.props.isSmall ? this.el.classList.add("input-group-sm") : null;
// Update the label
let label = this.el.querySelector("label");
if (label) {
this.props.id ? label.setAttribute("for", this.props.id) : null;
// Set the label if it exists
if (this.props.label) { label.innerHTML = this.props.label; }
// Else, remove it
else { this.el.removeChild(label); }
}
// See if the label exists
if (this.props.prependedLabel) {
// Add the label
let label = document.createElement("span");
label.classList.add("input-group-text");
label.innerHTML = this.props.prependedLabel;
this.el.insertBefore(label, elInput);
}
// Parse the buttons
let buttons = this.props.prependedButtons || [];
for (let i = 0; i < buttons.length; i++) {
// Add the button
this.el.insertBefore(Button(buttons[i]).el, elInput);
}
// See if there is a dropdown
if (this.props.prependedDropdown) {
// Add the dropdown
this._ddlPrepended = Dropdown(this.props.prependedDropdown);
this.el.insertBefore(this._ddlPrepended.el, elInput);
}
// Default the appended buttons
let appendedButtons = this.props.appendedButtons || [];
if (this.props.type == InputGroupTypes.Range) {
// Add the button
appendedButtons.push({
id: "range-value",
text: this.props.value == null ? "" : this.props.value
});
}
// See if the label exists
if (this.props.appendedLabel) {
// Add the label
let label = document.createElement("span");
label.classList.add("input-group-text");
label.innerHTML = this.props.appendedLabel;
this.el.appendChild(label);
}
// Parse the buttons
for (let i = 0; i < appendedButtons.length; i++) {
// Add the button
this.el.appendChild(Button(appendedButtons[i]).el);
}
// See if there is a dropdown
if (this.props.appendedDropdown) {
// Add the dropdown
this._ddlAppended = Dropdown(this.props.appendedDropdown);
this.el.appendChild(this._ddlAppended.el);
}
}
}
// Configure the events
private configureEvents() {
let isMultiLine = this.props.type == InputGroupTypes.TextArea;
let elInput = this.el.querySelector("input") || this.el.querySelector("textarea");
if (elInput) {
// See if a change event exists
let callbackValue = null;
if (this.props.onChange) {
// Add an input event
elInput.addEventListener("input", ev => {
// See if we have already executed the change event
if (callbackValue != elInput.value) {
// Set the value
callbackValue = elInput.value;
// Call the change event
this.props.onChange(this.getValue(), ev);
}
});
}
// See if this is a range
if (this.props.type == InputGroupTypes.Range) {
// Add a change event
elInput.addEventListener("input", () => {
// Get the button
let btn = this.el.querySelector("#range-value");
if (btn) {
// Update the value
btn.innerHTML = elInput.value;
}
})
}
// See if this is not a multi-line
if (!isMultiLine) {
// Add a mouse up event to detect the clear event
elInput.addEventListener("mouseup", ev => {
// Get the current value
let el = ev.currentTarget as HTMLInputElement;
let oldValue = el.value;
// Wait for the user to stop updating the value
setTimeout(() => {
// Get the current value
let currentValue = el.value;
// See if the values have changed
if (currentValue != oldValue) {
// See if we have already executed the change event
if (callbackValue != currentValue) {
// Set the value
callbackValue = currentValue;
// Call the events
this.props.onChange ? this.props.onChange(this.getValue(), ev) : null;
this.props.onClear && callbackValue == "" ? this.props.onClear() : null;
}
}
}, 1);
});
}
// See if this is a file
if (this.props.type == InputGroupTypes.File) {
// Set the change event
(elInput as HTMLInputElement).addEventListener("change", (ev) => {
// Get the source file
let srcFile = ev.target["files"][0];
if (srcFile) {
let reader = new FileReader();
// Set the file loaded event
reader.onloadend = (ev) => {
this._fileValue = {
data: ev.target.result as any,
name: srcFile.name
};
}
// Set the error
reader.onerror = (ev: any) => {
// Log
console.log("Error reading the file", srcFile, ev.target.error);
}
// Read the file
reader.readAsArrayBuffer(srcFile);
}
});
}
}
}
// Configures the text box
private configureTextbox() {
let isTextArea = this.props.type == InputGroupTypes.TextArea;
let input = this.el.querySelector("input");
let textarea = this.el.querySelector("textarea");
// See if this is a text area
if (isTextArea) {
// Remove the input
input ? this.el.removeChild(input) : null;
// Ensure the textarea exists
if (textarea) {
// Update the textbox
this.props.id ? textarea.id = this.props.id : null;
this.props.placeholder ? textarea.placeholder = this.props.placeholder : null;
textarea.disabled = this.props.isDisabled ? true : false;
textarea.readOnly = this.props.isReadonly ? true : false;
textarea.required = this.props.required ? true : false;
textarea.rows = this.props.rows;
this.props.title ? textarea.title = this.props.title : null;
}
} else {
// Remove the textarea
textarea ? this.el.removeChild(textarea) : null;
// Ensure the input exists
if (input) {
// Update the textbox
this.props.id ? input.id = this.props.id : null;
this.props.placeholder ? input.placeholder = this.props.placeholder : null;
input.disabled = this.props.isDisabled ? true : false;
input.readOnly = this.props.isReadonly ? true : false;
input.required = this.props.required ? true : false;
this.props.title ? input.title = this.props.title : null;
typeof (this.props.min) === "number" ? input.min = this.props.min + "" : null;
typeof (this.props.max) === "number" ? input.max = this.props.max + "" : null;
typeof (this.props.step) === "number" ? input.step = this.props.step + "" : null;
// Update the type
switch (this.props.type) {
// Color Picker
case InputGroupTypes.ColorPicker:
input.classList.add("form-control-color");
input.type = "color";
break;
// Email
case InputGroupTypes.Email:
input.classList.add("form-email");
input.type = "email";
break;
// File
case InputGroupTypes.File:
input.type = "file";
break;
// Password
case InputGroupTypes.Password:
input.classList.add("form-password");
input.type = "password";
break;
// Range
case InputGroupTypes.Range:
input.classList.add("form-range");
input.type = "range";
break;
// Search
case InputGroupTypes.Search:
input.classList.add("form-search");
input.type = "search";
input.setAttribute("aria-label", "Search");
break;
}
}
}
// Set the default value
this.setValue(this.props.value);
}
/**
* Public Interface
*/
get appendedDropdown() { return this._ddlAppended; }
disable() { this.elTextbox.disabled = true; }
enable() { this.elTextbox.disabled = false; }
getFileInfo() { return this._fileValue; }
getValue() {
let value = "";
// See if a prepended dropdown exist
if (this._ddlPrepended) {
// See if this is a multi item
if (this.props.prependedDropdown.multi) {
// Set the value
let items = this._ddlPrepended.getValue() as IDropdownItem[];
for (let i = 0; i < items.length; i++) {
// Add the value
value += items[i].value;
}
} else {
// Set the value
value += (this._ddlPrepended.getValue() as IDropdownItem)?.value;
}
}
// Append the input value
value += this.elTextbox.value;
// See if a appended dropdown exist
if (this._ddlAppended) {
// See if this is a multi item
if (this.props.appendedDropdown.multi) {
// Set the value
let items = this._ddlAppended.getValue() as IDropdownItem[];
for (let i = 0; i < items.length; i++) {
// Add the value
value += items[i].value;
}
} else {
// Set the value
value += (this._ddlAppended.getValue() as IDropdownItem)?.value;
}
}
// Return the value
return value;
}
get prependedDropdown() { return this._ddlPrepended; }
// Sets the textbox value
setValue(value: string = "") {
// Set the textbox value
this.elTextbox.value = value;
// See if a change event exists
if (this._initFl && this.props.onChange) {
// Execute the change event
this.props.onChange(value);
}
}
// Returns the textbox
get elTextbox(): HTMLInputElement | HTMLTextAreaElement { return this.el.querySelector("input") || this.el.querySelector("textarea"); }
}
export const InputGroup = (props: IInputGroupProps, template?: string): IInputGroup => { return new _InputGroup(props, template); }