gd-bs
Version:
Bootstrap JavaScript, TypeScript and Web Components library.
838 lines (837 loc) • 36.6 kB
JavaScript
import { CheckboxGroup, CheckboxGroupTypes } from "../checkboxGroup";
import { CustomControls } from "./custom";
import { Dropdown } from "../dropdown";
import { InputGroup, InputGroupTypes } from "../inputGroup";
import { ListBox } from "../listBox";
import { FormControlTypes, FormValidationTypes } from ".";
/**
* Form Control
*/
export class FormControl {
// Constructor
constructor(props, formProps, elLabel) {
this._cb = null;
this._custom = null;
this._el = null;
this._elDesc = null;
this._elLabel = null;
this._formProps = null;
this._ddl = null;
this._isRendered = false;
this._lb = null;
this._tb = null;
// Save the parameters
this._formProps = formProps;
this._props = props;
this._elLabel = elLabel;
// See if there is a rendering event
if (typeof (this._props.onControlRendering) === "function") {
// Call the event and see if a promise is returned
let returnVal = this._props.onControlRendering(Object.assign({}, this._props));
if (returnVal && typeof (returnVal["then"]) === "function") {
// Wait for it to complete
returnVal["then"](newProps => {
// Update the properties
this._props = newProps || this._props;
// Create the control
this.create();
});
}
else {
// Create the control
this.create();
}
}
else {
// Create the control
this.create();
}
}
// Configure the control
configure() {
// Ensure a control was created
if (this.control) {
// Set the element
this._el = this.control.el;
// See if an error message exists
if (this._props.errorMessage) {
// Get the group
let elGroup = this._el.querySelector(".input-group") || this._el.querySelector(".form-check:last-child");
if (elGroup) {
// Add the error message
let elErrorMessage = document.createElement("div");
elErrorMessage.className = "invalid-feedback";
elErrorMessage.innerHTML = this._props.errorMessage;
elGroup.appendChild(elErrorMessage);
}
}
// See if an element was defined to render to
if (this._props.el) {
// Append the control to the element
this._props.el.appendChild(this._el);
}
// See if the label is set
if (this._elLabel && this._formProps.isFloating && this._el.id) {
// Set the attributes
this._elLabel.setAttribute("for", this._el.id);
}
}
}
// Creates the control
create() {
// Parse the custom classes to add
let className = this._props.controlClassName || "";
// Set the value
let formValue = this._formProps.value ? this._formProps.value[this._props.name] : null;
let value = typeof (this._props.value) === "undefined" ? formValue : this._props.value;
// Render the control based on the type
switch (this._props.type) {
// Checkbox
case FormControlTypes.Checkbox:
let cbProps = this._props;
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: cbProps.colSize,
hideLabel: true,
isInline: cbProps.isInline,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
items: cbProps.items,
onChange: cbProps.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Checkbox,
value
});
break;
// Color Picker
case FormControlTypes.ColorPicker:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.ColorPicker,
value
});
break;
// Datalist
case FormControlTypes.Datalist:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: true,
id: this._props.id,
isDatalist: true,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
onChange: this._props.onChange,
required: this._props.required,
title: this._props.title,
value
});
break;
// Dropdown
case FormControlTypes.Dropdown:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: true,
id: this._props.id,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
required: this._props.required,
title: this._props.title,
value
});
break;
// Dropdown
case FormControlTypes.DropdownButton:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: false,
id: this._props.id,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
label: this.props.placeholder,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
placement: this._props.placement,
required: this._props.required,
title: this._props.title,
value
});
break;
// Dropdown
case FormControlTypes.DropdownCheckbox:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: false,
id: this._props.id,
isCheckbox: true,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
label: this.props.placeholder,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
placement: this._props.placement,
required: this._props.required,
title: this._props.title,
value
});
break;
// Email
case FormControlTypes.Email:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.Email,
value
});
break;
// File
case FormControlTypes.File:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.File,
value
});
break;
// List Box
case FormControlTypes.ListBox:
// Add the list box
this._lb = ListBox({
id: this._props.name,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
required: this._props.required,
value
});
break;
// Multi-Checkbox
case FormControlTypes.MultiCheckbox:
let cbMultiProps = this._props;
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: cbMultiProps.colSize,
hideLabel: true,
isDisabled: this._props.isDisabled,
isInline: cbMultiProps.isInline,
isReadonly: this._props.isReadonly,
items: cbMultiProps.items,
multi: true,
onChange: cbMultiProps.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Checkbox,
value
});
break;
// Multi-Dropdown
case FormControlTypes.MultiDropdown:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: true,
id: this._props.id,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
multi: true,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
required: this._props.required,
title: this._props.title,
value
});
break;
// Multi-Dropdown Button
case FormControlTypes.MultiDropdownButton:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: false,
id: this._props.id,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
label: this._props.placeholder,
multi: true,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
placement: this._props.placement,
required: this._props.required,
title: this._props.title,
value
});
break;
// Multi-Dropdown Checkbox
case FormControlTypes.MultiDropdownCheckbox:
// Add the dropdown
this._ddl = Dropdown({
className,
formFl: false,
id: this._props.id,
isCheckbox: true,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
label: this._props.placeholder,
multi: true,
onChange: this._props.onChange,
onMenuRendering: this._props.onMenuRendering,
placement: this._props.placement,
required: this._props.required,
title: this._props.title,
value
});
break;
// Multi-List Box
case FormControlTypes.MultiListBox:
// Add the list box
this._lb = ListBox({
id: this._props.name,
isReadonly: this._props.isReadonly || this._props.isDisabled,
items: this._props.items,
multi: true,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
required: this._props.required,
value
});
break;
// Multi-Radio
case FormControlTypes.MultiRadio:
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: this._props.colSize,
hideLabel: true,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
items: this._props.items,
multi: true,
onChange: this._props.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Radio,
value
});
break;
// Multi-Switch
case FormControlTypes.MultiSwitch:
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: this._props.colSize,
hideLabel: true,
isDisabled: this._props.isDisabled,
isInline: this._props.isInline,
isReadonly: this._props.isReadonly,
items: this._props.items,
multi: true,
onChange: this._props.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Switch,
value
});
break;
// Password
case FormControlTypes.Password:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.Password,
value
});
break;
// Radio
case FormControlTypes.Radio:
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: this._props.colSize,
hideLabel: true,
isDisabled: this._props.isDisabled,
isInline: this._props.isInline,
isReadonly: this._props.isReadonly,
items: this._props.items,
onChange: this._props.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Radio,
value
});
break;
// Range
case FormControlTypes.Range:
// Add the input
this._tb = InputGroup({
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
min: this._props.min || 0,
max: this._props.max || 100,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
required: this._props.required,
step: this._props.step,
title: this._props.title,
type: InputGroupTypes.Range,
value
});
break;
// Read Only
case FormControlTypes.Readonly:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: true,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.TextField,
value
});
break;
// Switch
case FormControlTypes.Switch:
// Add the checkbox group
this._cb = CheckboxGroup({
className,
colSize: this._props.colSize,
hideLabel: true,
isDisabled: this._props.isDisabled,
isInline: this._props.isInline,
isReadonly: this._props.isReadonly,
items: this._props.items,
onChange: this._props.onChange,
required: this._props.required,
title: this._props.title,
type: CheckboxGroupTypes.Switch,
value
});
break;
// Text Area
case FormControlTypes.TextArea:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
rows: this._props.rows,
title: this._props.title,
type: InputGroupTypes.TextArea,
value
});
break;
// Text Field
case FormControlTypes.TextField:
// Add the input
this._tb = InputGroup({
appendedDropdown: this.props.appendedDropdown,
appendedLabel: this.props.appendedLabel,
className,
id: this._props.id,
isDisabled: this._props.isDisabled,
isReadonly: this._props.isReadonly,
onChange: this._props.onChange,
placeholder: this._props.placeholder,
prependedDropdown: this.props.prependedDropdown,
prependedLabel: this.props.prependedLabel,
required: this._props.required,
title: this._props.title,
type: InputGroupTypes.TextField,
value
});
break;
// Custom Type
default:
// Create the default element
this._el = document.createElement("div");
this._el.className = className;
// See if there is a custom type
let custom = CustomControls.getByType(this._props.type);
if (custom && typeof (custom) === "function") {
// Set the default value
this._props.value = this._props.value || value;
// Execute the event
this._custom = custom(this._props, this._formProps);
}
break;
}
// See if a checkbox was rendered and an id was set
if (this.control && this._props.id) {
// Set the id
this.control.el.id = this._props.id;
}
// Configure the control
this.configure();
// Wait before executing the rendered event, otherwise the controls will be null
setTimeout(() => {
// Execute the events
this._props.onControlRendered ? this._props.onControlRendered(this) : null;
this._formProps.onControlRendered ? this._formProps.onControlRendered(this) : null;
// Set the flag
this._isRendered = true;
}, 10);
}
/**
* Public Interface
*/
get el() { return this._el; }
// The checkbox control
get checkbox() { return this._cb; }
// The dropdown control
get dropdown() { return this._ddl; }
// The textbox control
get control() { return this._cb || this._ddl || this._lb || this._tb || this._custom; }
// The control label
get label() { return this._elLabel; }
// The listbox control
get listbox() { return this._lb; }
// The textbox control
get textbox() { return this._tb; }
// Method to get the form control value
getValue() {
// See if there is an override event
if (this._props.onGetValue) {
return this._props.onGetValue(this._props);
}
// See if this is a checkbox
if (this._cb) {
// See if this is a multi-checkbox
if (this._props.type == FormControlTypes.MultiCheckbox || this._props.type == FormControlTypes.MultiRadio || this._props.type == FormControlTypes.MultiSwitch) {
// Return the selected items
return this._cb.getValue().selectedItems;
}
// Return a boolean
return this._cb.getValue().selectedItems ? true : false;
}
// See if this is a dropdown
if (this._ddl) {
// Return the value
return this._ddl.getValue();
}
// See if this is a list box
if (this._lb) {
// Return the value
return this._lb.getValue();
}
// See if this is a textbox
if (this._tb) {
// See if this is a file
if (this._props.type == FormControlTypes.File) {
// Return the file information
return this._tb.getFileInfo();
}
// Return the value
return this._tb.getValue();
}
}
// Hides the control
hide() {
// Ensure an element exists
if (this._el) {
// See if this is a row
if (this._el.parentElement && this._el.parentElement.parentElement && this._el.parentElement.parentElement.classList.contains("row")) {
// See if there are other controls in this row
if (this._el.parentElement.parentElement.querySelectorAll(".col").length > 1) {
// Hide the column element
this._el.parentElement.classList.add("d-none");
}
else {
// Hide the row element
this._el.parentElement.parentElement.classList.add("d-none");
}
}
// Else, ensure the parent element exists
else if (this._el.parentElement) {
// Hide the group element
this._el.parentElement.classList.add("d-none");
}
}
}
// Is loaded
isLoaded() {
// Return a promise
return new Promise(resolve => {
// Wait for the control to be created
let id = setInterval(() => {
// See if the control has been rendered
if (this.isRendered) {
// Stop the loop
clearInterval(id);
// Resolve the promise
resolve();
}
}, 10);
});
}
// Flag indicating the control is loaded
get isRendered() { return this._isRendered; }
// Validates the control
get isValid() {
let validation = { isValid: true };
// Get the element and value
let elControl = (this._cb || this._ddl || this._lb || this._tb) ? (this._cb || this._ddl || this._lb || this._tb).el : this._el;
let value = this.getValue();
// See if this control is required
if (this._props.required) {
// See if a value doesn't exists
if (value == null) {
// Set the flag
validation.isValid = false;
}
// Else, see if the value is an array
else if (typeof (value.length) === "number") {
// Set the flag
validation.isValid = value.length > 0;
}
// Else, see if this is a single checkbox
else if (this._cb && typeof (value) === "boolean") {
// Set the flag
validation.isValid = value;
}
// Else, see if this is a dropdown and ensure it has a value
else if (this._ddl && !this._ddl.isMulti && value) {
// Set the flag to ensure a text/value exists
validation.isValid = value.text || value.value ? true : false;
}
}
// See if an event exists
if (this._props.onValidate) {
// Call the event
let returnValue = this._props.onValidate(this._props, { value });
if (typeof (returnValue) === "boolean") {
// Set the flag
validation.isValid = returnValue;
}
// Else, ensure it exists
else if (returnValue) {
// Set the validation
validation = Object.assign(Object.assign({}, validation), returnValue);
}
}
// Update the validation
this.updateValidation(elControl, validation);
// Return the flag
return validation.isValid;
}
// The form control properties
get props() { return this._props; }
// Sets the description of the control
setDescription(value) {
var _a, _b;
// Get the element
let elDesc = (_b = (_a = this._el) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.querySelector(".form-text");
if (elDesc) {
// Update the description
elDesc.innerHTML = value || "";
}
}
// Sets the form control label
setLabel(value) {
// Update the label
this._elLabel ? this._elLabel.innerHTML = value || "" : null;
}
// Sets the custom control
setControl(control) {
// Set the custom control
this._custom = control;
}
// Sets the form control value
setValue(value) {
// Set the value
this.control ? this.control.setValue(value) : null;
}
// Shows the control
show() {
// Ensure an element exists
if (this._el) {
// See if this is a row
if (this._el.parentElement && this._el.parentElement.parentElement && this._el.parentElement.parentElement.classList.contains("row")) {
// See if there are other controls in this row
if (this._el.parentElement.parentElement.querySelectorAll(".col").length > 1) {
// Show the column element
this._el.parentElement.classList.remove("d-none");
}
else {
// Show the row element
this._el.parentElement.parentElement.classList.remove("d-none");
}
}
// Else, ensure the parent element exists
else if (this._el.parentElement) {
// Show the group element
this._el.parentElement.classList.remove("d-none");
}
}
}
// Updates the control validation
updateValidation(elControl, validation) {
// See if this is a checkbox/switch
let isCheckbox = elControl.querySelectorAll(".form-check").length > 0;
// Get the form controls
let elFormControls = isCheckbox ? [elControl] : elControl.querySelectorAll(".form-control");
elFormControls = elFormControls.length == 0 ? elControl.querySelectorAll(".form-select") : elFormControls;
// Parse the form controls
for (let i = 0; i < elFormControls.length; i++) {
// Ensure the control exists
let elFormControl = elFormControls[i];
if (!isCheckbox) {
// Clear the invalid/valid classes
elFormControl.classList.remove("is-invalid");
elFormControl.classList.remove("is-valid");
// Set the class
elFormControl.classList.add(validation.isValid ? "is-valid" : "is-invalid");
}
else {
let validateControls = (controls) => {
// Parse the controls
for (let i = 0; i < controls.length; i++) {
let control = controls[i];
// Clear the invalid/valid classes
control.classList.remove("is-invalid");
control.classList.remove("is-valid");
// Set the class
control.classList.add(validation.isValid ? "is-valid" : "is-invalid");
}
};
// Get the checkboxes
let elCheckboxes = elControl.querySelectorAll(".form-check-input");
if (elCheckboxes.length > 0) {
// Validate the controls
validateControls(elCheckboxes);
// Set the form control
elFormControl = elCheckboxes.length > 0 ? elCheckboxes[elCheckboxes.length - 1] : elFormControl;
}
// Get the custom controls
let elCustomControls = elControl.querySelectorAll(".custom-control-input");
if (elCustomControls.length > 0) {
// Validate the controls
validateControls(elCustomControls);
// Set the form control
elFormControl = elCustomControls.length > 0 ? elCustomControls[elCustomControls.length - 1] : elFormControl;
}
}
// Ensure the form control exists
if (elFormControl) {
let useTooltip = this._formProps.validationType == FormValidationTypes.Tooltip;
// Clear the old valid message if it exists
let validClassName = useTooltip ? "valid-tooltip" : "valid-feedback";
let elMessage = elFormControl.parentNode.querySelector("." + validClassName);
if (elMessage) {
// Clear the message
elMessage.innerHTML = "";
elMessage.style.display = "";
}
// Clear the old valid message if it exists
let invalidClassName = useTooltip ? "invalid-tooltip" : "invalid-feedback";
elMessage = elFormControl.parentNode.querySelector("." + invalidClassName);
if (elMessage) {
// Clear the message
elMessage.innerHTML = "";
elMessage.style.display = "";
}
// See if there is invalid feedback
if (validation.invalidMessage || this._props.errorMessage) {
// Get the element
let invalidClassName = useTooltip ? "invalid-tooltip" : "invalid-feedback";
elMessage = elFormControl.parentNode.querySelector("." + invalidClassName);
if (elMessage == null) {
// Create the element
elMessage = document.createElement("div");
elMessage.className = invalidClassName;
elFormControl.parentNode.appendChild(elMessage);
}
// Set the message
elMessage.innerHTML = validation.invalidMessage || this._props.errorMessage;
// Update the display
elMessage.style.display = validation.isValid ? "" : "block";
}
// See if there is valid feedback
if (validation.validMessage) {
// Get the element
let validClassName = useTooltip ? "valid-tooltip" : "valid-feedback";
elMessage = elFormControl.parentNode.querySelector("." + validClassName);
if (elMessage == null) {
// Create the element
elMessage = document.createElement("div");
elMessage.className = validClassName;
elFormControl.parentNode.appendChild(elMessage);
}
// Set the message
elMessage.innerHTML = validation.validMessage;
// Update the display
elMessage.style.display = validation.isValid ? "block" : "";
}
}
}
}
}