From 16db8a846e8b3ca6e1cd5a68a895c042c0f4ca8d Mon Sep 17 00:00:00 2001 From: Carlos Lopez Jr Date: Tue, 30 Jul 2024 22:36:55 -0400 Subject: [PATCH] fix(input): support disabled and readonly with [multiple] --- components/Input.js | 98 ++++++++++++++++++++++++++--------------- components/InputChip.js | 23 +++++----- 2 files changed, 76 insertions(+), 45 deletions(-) diff --git a/components/Input.js b/components/Input.js index c4d1765d..985a4baf 100644 --- a/components/Input.js +++ b/components/Input.js @@ -375,45 +375,55 @@ export default CustomElement this.changeSuggestion({ value: this._listboxValue }); } }, - populateInputFromListbox() { - if (this.multiple) { - const { _values } = this; - /** @type {InstanceType} */ - let element = this.refs.chips.firstElementChild; - - for (let i = 0; i < _values.length; i++) { - const currentValue = _values[i]; - let foundOption; - if (this.listbox) { - for (const option of this.listbox.options) { - if (option.value === currentValue) { - foundOption = option; - break; - } - } - } + refreshMultiple() { + const { _values, multiple } = this; + if (!multiple) { + this.refs.chips.replaceChildren(); + return; + } + /** @type {InstanceType} */ + let element = this.refs.chips.firstElementChild; - element ??= this.refs.chips.appendChild(document.createElement('mdw-input-chip')); - element.closeButton = true; - element.textContent = foundOption?.label || currentValue; - // eslint-disable-next-line unicorn/prefer-add-event-listener - element.onclose ??= this.onChipClose.bind(this); - element = element.nextElementSibling; - } - while (element) { - const prev = element; - element = element.nextElementSibling; - prev.remove(); - } - this._chipSelected = false; - this._input.value = ''; - this._draftInput = ''; - this._listboxValue = ''; + for (let i = 0; i < _values.length; i++) { + const currentValue = _values[i]; + let foundOption; if (this.listbox) { for (const option of this.listbox.options) { - option.selected = _values.includes(option.value); + if (option.value === currentValue) { + foundOption = option; + break; + } } } + + element ??= this.refs.chips.appendChild(document.createElement('mdw-input-chip')); + element.closeButton = true; + element.textContent = foundOption?.label || currentValue; + element.textContent = foundOption?.label || currentValue; + element.disabled = this.disabled; + element.readOnly = this.readOnly; + // eslint-disable-next-line unicorn/prefer-add-event-listener + element.onclose ??= this.onChipClose.bind(this); + element = element.nextElementSibling; + } + while (element) { + const prev = element; + element = element.nextElementSibling; + prev.remove(); + } + this._chipSelected = false; + this._input.value = ''; + this._draftInput = ''; + this._listboxValue = ''; + if (this.listbox) { + for (const option of this.listbox.options) { + option.selected = _values.includes(option.value); + } + } + }, + populateInputFromListbox() { + if (this.multiple) { + this.refreshMultiple(); return; } if (!this._isSelect) return; @@ -495,6 +505,8 @@ export default CustomElement break; case 'ArrowDown': case 'Down': + if (this.disabled) return; + if (this.readOnly) return; this._chipSelected = false; if (this.readOnly) return; if (event.altKey) { @@ -506,6 +518,8 @@ export default CustomElement break; case 'ArrowUp': case 'Up': + if (this.disabled) return; + if (this.readOnly) return; if (event.altKey) { this.toggleListbox(); break; @@ -526,6 +540,8 @@ export default CustomElement this.closeListbox(); break; case 'Space': + if (this.disabled) return; + if (this.readOnly) return; if (!this._isSelect) return; if (!this._listbox) return; if (this._expanded) { @@ -538,6 +554,8 @@ export default CustomElement } break; case 'Backspace': + if (this.disabled) return; + if (this.readOnly) return; if (!this.multiple) return; if (this._isSelect) return; if (!this._input.value) { @@ -738,7 +756,7 @@ export default CustomElement _valuesChanged(previous, current) { if (this.multiple && current) { this._value = current.join(','); - this.populateInputFromListbox(); + this.refreshMultiple(); } }, _chipSelectedChanged(previous, current) { @@ -771,6 +789,16 @@ export default CustomElement this._onSetValue(this._input.value); } }, + disabledStateChanged() { + this.refreshMultiple(); + this._chipSelected = false; + this.closeListbox(); + }, + readOnlyChanged() { + this.refreshMultiple(); + this._chipSelected = false; + this.closeListbox(); + }, }) .html`
diff --git a/components/InputChip.js b/components/InputChip.js index 5a5e6ee9..c4460d5b 100644 --- a/components/InputChip.js +++ b/components/InputChip.js @@ -1,6 +1,5 @@ import { EVENT_HANDLER_TYPE } from '../core/customTypes.js'; import DelegatesFocusMixin from '../mixins/DelegatesFocusMixin.js'; -import FormAssociatedMixin from '../mixins/FormAssociatedMixin.js'; import HyperlinkMixin from '../mixins/HyperlinkMixin.js'; import ShapeMixin from '../mixins/ShapeMixin.js'; @@ -12,7 +11,6 @@ import './IconButton.js'; export default Box .extend() .mixin(ShapeMixin) - .mixin(FormAssociatedMixin) // Tap into FormAssociated for disabledState .mixin(DelegatesFocusMixin) .mixin(HyperlinkMixin) .observe({ @@ -20,6 +18,8 @@ export default Box closeButton: 'boolean', closeIcon: { empty: 'close' }, closeInk: { empty: 'inherit' }, + readOnly: { attr: 'readonly', type: 'boolean' }, + disabled: 'boolean', icon: 'string', iconInk: 'string', src: 'string', @@ -36,6 +36,9 @@ export default Box hasIcon({ icon, svg, src, svgPath } = this) { return icon ?? svg ?? src ?? svgPath; }, + showCloseIcon({ disabled, readOnly, closeButton }) { + return closeButton && !disabled && !readOnly; + }, }) .html` - + - Close + Close ` .css` /* https://m3.material.io/components/chips/specs */ @@ -80,7 +83,7 @@ export default Box padding-inline-start: calc(4px + (var(--mdw-density) * 2px)); } - :host(:where([close-button])) { + :host(:where([close-button]:not([disabled]):not([readonly]))) { padding-inline-end: calc(8px + 18px + 8px + (var(--mdw-density) * 2px)); } @@ -117,13 +120,12 @@ export default Box --mdw-ink: rgb(var(--mdw-color__on-surface-variant)); } - #outline:is([ink],[color]) { + #outline:is([ink],[color],[disabled]) { /* stylelint-disable-next-line rule-selector-property-disallowed-list */ --mdw-ink: inherit; } - #slot[disabled], - #icon[disabled] { + :host([disabled]) { color: rgba(var(--mdw-color__on-surface), 0.38); } @@ -148,11 +150,12 @@ export default Box .recompose(({ refs: { anchor, slot, icon, outline } }) => { icon.after(slot); anchor.remove(); - slot.setAttribute('disabled', '{disabledState}'); + slot.setAttribute('disabled', '{disabled}'); slot.removeAttribute('ink'); slot.removeAttribute('color'); outline.removeAttribute('mdw-if'); outline.setAttribute('ink', '{ink}'); outline.setAttribute('color', '{color}'); + outline.setAttribute('disabled', '{disabled}'); }) .autoRegister('mdw-input-chip');