@instawork/design-system
Version:
The design system for Instawork's web apps
346 lines • 12.4 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
privateMap.set(receiver, value);
return value;
};
var __disabled;
import * as $ from 'jquery';
import { CustomInputComponent } from '../custom-input';
import './nested-input.component.scss';
const DATA_ATTR_DISABLED_PLACEHOLDER = 'disabled-placeholder';
const ATTR_SHOW_DISABLED_VALUE = 'show-disabled-value';
const DATA_CURRENT_VALUE = 'current-value';
const DATA_PLACEHOLDER = 'placeholder';
const DATA_ORIGINAL_VALUE = 'original-value';
const DATA_TABINDEX = 'tab-index';
// keys that need to have their property values synced with the value element's attributes
const ATTR_PROPS = [
'max',
'maxLength',
'min',
'minLength',
];
/**
* A custom component that supports adding extra content inside a .form-control styled input.
*
* Optional attributes:
* data-disabled-placeholder: If set, hides the input value and uses the specified placeholder when the input is
* disabled.
*
* Usage:
* <iw-nested-input [data-disabled-placeholder]="-">
* <input type="text" name="..." />
* <span>Extra content here</span>
* </iw-nested-input>
*/
export class NestedInputComponent extends CustomInputComponent {
constructor($el) {
var _a, _b;
super($el);
__disabled.set(this, void 0);
this.$display = this.getDisplayInput();
this.$displayPlaceholder = this.getDisplayPlaceholder();
this.$value = this.getValueInput();
this.valueEl = this.$value[0];
this.required = this.$value.prop('required') || typeof this.$el.attr('required') !== 'undefined';
this.disabled = this.$value.prop('disabled') || typeof this.$el.attr('disabled') !== 'undefined';
this.placeholder = this.$display.prop('placeholder') || this.$el.attr('placeholder');
this.$el.data(DATA_PLACEHOLDER, this.placeholder);
this.init();
// define these property setters/getters so that they can be set via jQuery
Object.defineProperties(this.el, Object.assign({ disabled: {
get: () => this.disabled,
set: (disabled) => this.disabled = !!disabled,
}, showDisabledValue: {
get: () => this.showDisabledValue,
set: (showDisabledValue) => this.showDisabledValue = showDisabledValue,
}, placeholder: {
get: () => this.placeholder,
set: (placeholder) => this.placeholder = placeholder,
}, value: {
get: () => this.value,
set: (value) => this.value = value,
}, validity: {
get: () => { var _a; return (_a = this.valueEl) === null || _a === void 0 ? void 0 : _a.validity; },
}, checkValidity: {
value: (_a = this.valueEl) === null || _a === void 0 ? void 0 : _a.checkValidity.bind(this.valueEl),
}, reportValidity: {
value: (_b = this.valueEl) === null || _b === void 0 ? void 0 : _b.reportValidity.bind(this.valueEl),
}, required: {
get: () => this.required,
set: (value) => this.required = value,
} }, ATTR_PROPS.reduce((result, key) => {
result[key] = {
get: () => this[key],
set: (value) => this[key] = value,
};
return result;
}, {})));
}
static loadPlugin() {
super.loadPlugin();
if (!$.fn.iwNestedInput) {
$.fn.iwNestedInput = this.jQueryPlugin('NestedInputComponent');
}
}
get disabled() {
return __classPrivateFieldGet(this, __disabled);
}
set disabled(disabled) {
const isChange = (!!__classPrivateFieldGet(this, __disabled)) !== (!!disabled);
__classPrivateFieldSet(this, __disabled, !!disabled);
if (isChange) {
this.onDisabledChange(__classPrivateFieldGet(this, __disabled));
}
}
get placeholder() {
return this._placeholder;
}
set placeholder(placeholder) {
this._placeholder = placeholder;
if (this.disabled && this.disabledPlaceholder) {
return;
}
if (typeof placeholder === 'undefined') {
this.$display.removeProp('placeholder');
}
else {
this.$display.prop('placeholder', placeholder);
}
}
get disabledPlaceholder() {
return this.$el.data(DATA_ATTR_DISABLED_PLACEHOLDER);
}
get value() {
return this.$value.val();
}
set value(value) {
if (value !== this.value) {
this.$el.attr('value', value); // so the attribute updates in the DOM
this.setDisplay(value);
this.setValue(value);
this.$el.trigger('change');
}
}
get max() {
return this.getAttrProp('max');
}
set max(value) {
this.setAttrProp('max', value);
}
get maxLength() {
return this.getAttrProp('maxLength');
}
set maxLength(value) {
this.setAttrProp('maxLength', value);
}
get min() {
return this.getAttrProp('min');
}
set min(value) {
this.setAttrProp('min', value);
}
get minLength() {
return this.getAttrProp('minLength');
}
set minLength(value) {
this.setAttrProp('minLength', value);
}
get required() {
return this.getAttrProp('required');
}
set required(value) {
this.setAttrProp('required', value);
}
get showDisabledValue() {
return this.$el.attr(ATTR_SHOW_DISABLED_VALUE) !== undefined;
}
set showDisabledValue(value) {
if (value) {
this.$el.attr(ATTR_SHOW_DISABLED_VALUE);
}
else {
this.$el.removeAttr(ATTR_SHOW_DISABLED_VALUE);
}
}
/**
* Returns the value set at the time of initialization, if any
*/
get originalValue() {
return this.$el.data(DATA_ORIGINAL_VALUE);
}
/**
* Gets the current value, regardless of disabled state.
*/
get currentValue() {
return this.$el.data(DATA_CURRENT_VALUE);
}
get isModified() {
var _a, _b;
return ((_a = this.originalValue) === null || _a === void 0 ? void 0 : _a.valueOf()) !== ((_b = this.currentValue) === null || _b === void 0 ? void 0 : _b.valueOf());
}
/**
* Returns a {@link JQuery} object representing the input element(s) that the user will be able see and interact with
*/
getDisplayInput() {
return this.getValueInput();
}
/**
* Returns a {@link JQuery} object representing the input element(s) that the will be used to display the placeholder
* value. This may be the same as {@link getDisplayInput}.
*/
getDisplayPlaceholder() {
return this.getDisplayInput();
}
/**
* Returns a {@link JQuery} object representing the input element(s) that the will be used to represent the value of
* the input component. This may be the same as {@link getDisplayInput}.
*/
getValueInput() {
return this.$el.find(':input');
}
/**
* Encapsulates all initialization logic that can be customized by subclasses
*/
init() {
// TODO: move all of this except initValue to CustomComponent
this.initDomRefs();
this.validateHostAttributes();
this.initDom();
this.initValue();
this.initAttrProps();
this.initEvents();
}
/**
* Hook for subclasses to find and initialize references to child elements created by the template
*/
initDomRefs() { }
/**
* Copies the "value" attribute from the host element to the value element, saves the original value in a data
* property
*/
initValue() {
const value = this.findOriginalValue() || '';
const importedValue = this.importValue(value);
this.setValue(this.formatValue(importedValue));
this.$el
.data(DATA_ORIGINAL_VALUE, value)
.data(DATA_CURRENT_VALUE, value);
}
/**
* Hook for subclasses to make any additional updates to the DOM
*/
initDom() { }
/**
* Syncs attribute values from the host element to corresponding attributes on the value element
*/
initAttrProps() {
ATTR_PROPS.forEach(name => this.setAttrProp(name, this.$el.attr(name)));
}
/**
* Hook for subclasses to validate parsed values from attributes
*/
validateHostAttributes() { }
/**
* Set up event handlers
*/
initEvents() {
// focus the first input when clicking directly on the element itself or any non-input child element
this.$el.on('click', (e) => {
if ($(e.target).is(':not(:input)')) {
this.$display.first().focus();
}
});
this.$el.on('change', () => {
this.$el.data(DATA_CURRENT_VALUE, this.value);
});
}
/**
* Returns the string representation of the original value as defined in the initial rendering of the document
*/
findOriginalValue() {
return this.$el.attr('value') || this.$value.val();
}
/**
* Sets the representation of the value that is shown in the UI
*/
setDisplay(value) {
this.$display.val(value);
}
/**
* Optionally convert the string representation of a value to a parsed value
*/
importValue(value) {
// TODO: extract more of TemporalComponent's importValue / syncValue / onValue pattern to standardize the value
// processing pipeline for "nested" components
return value;
}
/**
* Updates the value element's value property with a new value
*/
setValue(value) {
this.$value.val(value);
}
/**
* Convert a parsed value to its string representation for storing in the value element's value property
*/
formatValue(value) {
if (typeof value === 'undefined') {
return '';
}
return value.toString();
}
onDisabledChange(disabled) {
this.$display.prop('disabled', disabled);
if (disabled) {
this.$el
.attr('disabled', '')
// remove the tabindex attribute so that the element cannot be given focus when disabled
.data(DATA_TABINDEX, this.$el.attr('tabindex'))
.removeAttr('tabindex');
}
else {
this.$el
.removeAttr('disabled')
// restore the previously set tabindex
.attr('tabindex', this.$el.data(DATA_TABINDEX));
}
this.updatePlaceholderState(disabled);
}
syncPlaceholder(disabled) {
const enabledPlaceholder = this.placeholder || '';
const disabledPlaceholder = this.disabledPlaceholder || enabledPlaceholder;
this.$displayPlaceholder.prop('placeholder', disabled ? disabledPlaceholder : enabledPlaceholder);
}
updatePlaceholderState(disabled) {
if (disabled) {
if (this.disabledPlaceholder && !this.showDisabledValue) {
this.setDisplay('');
}
}
else {
this.setDisplay(this.currentValue);
}
const nonInputContentVisibility = disabled && !!this.disabledPlaceholder && !this.showDisabledValue ? 'hidden' : 'visible';
this.$el.find(':not(:input)').css('visibility', nonInputContentVisibility);
this.syncPlaceholder(disabled);
}
// TODO: add mapped types for supported attribute-based properties
getAttrProp(name) {
return this.$value.prop(name);
}
setAttrProp(name, value) {
this.$value.prop(name, value);
}
}
__disabled = new WeakMap();
NestedInputComponent.COMPONENT_SELECTOR = 'iw-nested-input';
//# sourceMappingURL=nested-input.component.js.map