Skip to content

Commit

Permalink
feat(input): partial support for input-chips
Browse files Browse the repository at this point in the history
  • Loading branch information
clshortfuse committed Jul 15, 2024
1 parent eba6ce9 commit 64c0960
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 38 deletions.
94 changes: 79 additions & 15 deletions components/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,17 @@ export default CustomElement
_focusedPosInSet: { value: -1 },
_listboxSize: { value: -1 },
_draftInput: { type: 'string', nullable: false },
_suggestedText: { type: 'string', nullable: false },
_suggestedValue: { type: 'string', nullable: false },
_hasSuggestion: 'boolean',
_listboxValue: 'string',
_lastComputedListboxValue: { type: 'string', nullable: false },
_values: { value: [] },
})
.observe({
_hasListbox({ _listbox }) {
return !!_listbox;
},
_isSelect({ type }) {
return type.toLowerCase() === 'select-one';
return type.toLowerCase() === 'select-one' || type.toLocaleLowerCase() === 'select-multiple';
},
})
.define({
Expand All @@ -78,6 +79,10 @@ export default CustomElement
_onListboxClickListener: null,
/** @type {EventListener} */
_onPopupFocusoutListener: null,
_suggestionText: '',
_suggestionValue: '',
/** @type {HTMLOptionElement} */
_suggestionOption: null,
})
.define({
stateTargetElement() { return this.refs.control; },
Expand Down Expand Up @@ -106,8 +111,9 @@ export default CustomElement
});

const { label: suggestionText, value: suggestionValue } = option;
this._suggestedText = suggestionText;
this._suggestedValue = suggestionValue;
this._suggestionText = suggestionText;
this._suggestionValue = suggestionValue;
this._suggestionOption = option;
this._hasSuggestion = true;
this.acceptSuggestion(true);
this.closeListbox();
Expand Down Expand Up @@ -153,7 +159,7 @@ export default CustomElement
popup.replaceChildren(_listbox);
popup.showPopup(shape, false);
popup.addEventListener('focusout', this._onPopupFocusoutListener);
_listbox.value = this.value;
_listbox.value = this._listboxValue;
const [option] = _listbox.selectedOptions;
if (option) {
option.scrollIntoView({
Expand Down Expand Up @@ -200,8 +206,9 @@ export default CustomElement
} = this;
const { label: suggestionText, value: suggestionValue } = option;

this._suggestedText = suggestionText;
this._suggestedValue = suggestionValue;
this._suggestionText = suggestionText;
this._suggestionValue = suggestionValue;
this._suggestionOption = option;
this._hasSuggestion = true;
if (autoSelect) {
this.acceptSuggestion(true);
Expand Down Expand Up @@ -234,11 +241,27 @@ export default CustomElement
acceptSuggestion(emitChange = false) {
if (!this._hasSuggestion) return;
if (this.readOnly) return;
const { _suggestedText, _suggestedValue, _input } = this;
this.value = _suggestedValue;
_input.value = _suggestedText;
this._draftInput = _suggestedText;
this._listbox.value = _suggestedValue;
const { _suggestionText, _suggestionValue, _input, multiple, _listbox, _values } = this;
if (multiple) {
const newArray = [..._values, _suggestionText];
this._values = [..._values, _suggestionText];
const newChip = document.createElement('mdw-chip');
newChip.label = _suggestionText;
newChip.value = _suggestionValue;
newChip.textContent = _suggestionText;
newChip.icon = this._suggestionOption?.icon;
this.refs.chips.appendChild(newChip);
this._value = newArray.join(',');
_input.value = '';
this._draftInput = '';
_listbox.value = '';
this._listboxValue = '';
} else {
this.value = _suggestionValue;
_input.value = _suggestionText;
this._draftInput = _suggestionText;
_listbox.value = _suggestionValue;
}
if (emitChange) {
this.dispatchEvent(new Event('change', { bubbles: true }));
}
Expand Down Expand Up @@ -345,7 +368,7 @@ export default CustomElement
if (this._isSelect) {
this.changeSuggestion({ label: this._draftInput });
} else {
this.changeSuggestion({ value: this.value });
this.changeSuggestion({ value: this._listboxValue });
}
},
})
Expand Down Expand Up @@ -582,8 +605,29 @@ export default CustomElement
shape.setAttribute('trailing-icon', '{computedTrailingIcon}');
labelText.setAttribute('trailing-icon', '{computedTrailingIcon}');
})
.overrides({
_onSetValue(value) {
if (this._isSelect) {
this._listbox.value = value;
const [option] = this._listbox.selectedOptions;
if (option) {
this._input.value = option.label;
}
this._value = value;
} else {
// Apply user value to input and read back result to apply control to parse
this._input.value = value;
this._value = this._input.value;
}
},
})
.on({
valueChanged(previous, current) {
_valueChanged(previous, current) {
if (!this.multiple) {
this._listboxValue = current;
}
},
_listboxValueChanged(previous, current) {
if (!this._hasListbox) return;
this._listbox.value = current;
this._draftInput = current;
Expand All @@ -599,6 +643,7 @@ export default CustomElement
},
})
.html`
<div id=chips mdw-if={multiple}></div>
<slot id=slot></slot>
<div id=aria-listbox role=listbox mdw-if={_hasListbox}>
<div id=aria-active role=option aria-hidden=false aria-label={selectedOption.label}
Expand All @@ -621,7 +666,26 @@ export default CustomElement
#control:where([type="button"], [is-select]) {
cursor: pointer;
}
#chips {
order:-1;
display: flex;
align-items: center;
gap: 8px;
padding-inline-end: 8px;
transition: 100ms;
}
#chips:empty {
padding-inline-end: 0px;
}
`
.recompose(({ refs: { inline, chips } }) => {
inline.prepend(chips);
})
.extend((Base) => class Input extends Base {
/** @type {InstanceType<ReturnType<RippleMixin>>['addRipple']} */
addRipple(...args) {
Expand Down
58 changes: 35 additions & 23 deletions mixins/TextFieldMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ export default function TextFieldMixin(Base) {
<div id=shape role=none filled={filled} icon={icon} trailing-icon={trailingIcon}
populated={populatedState} focused={focusedState} label={label} outlined={outlined}
shape-top={_shapeShapeTop}>
<mdw-icon mdw-if={icon} id=icon aria-hidden=true disabled={disabledState} icon={icon} variation={computedIconVariation} ></mdw-icon>
<span mdw-if={inputPrefix} class=inline id=prefix aria-hidden=true focused={focusedState} populated={populatedState}>{inputPrefix}</span>
<span mdw-if={inputSuffix} class=inline id=suffix aria-hidden=true focused={focusedState} populated={populatedState}>{inputSuffix}</span>
<mdw-icon mdw-if={icon} id=icon aria-hidden=true disabled={disabledState} icon={icon} variation={computedIconVariation}></mdw-icon>
<div id=inline role=none filled={filled} icon={icon} trailing-icon={trailingIcon}
populated={populatedState} focused={focusedState} label={label} outlined={outlined}>
<span mdw-if={inputPrefix} class=inline id=prefix aria-hidden=true focused={focusedState} populated={populatedState}>{inputPrefix}</span>
<span mdw-if={inputSuffix} class=inline id=suffix aria-hidden=true focused={focusedState} populated={populatedState}>{inputSuffix}</span>
</div>
<mdw-icon-button tabindex=-1 disabled={disabledState} mdw-if={trailingIcon} id=trailing-icon ink={trailingIconInk} disabled={disabledState} icon={trailingIcon}>{trailingIconLabel}</mdw-icon-button>
<div mdw-if={filled} id=indicator aria-hidden=true focused={focusedState} hovered={hoveredState} errored={erroredState} disabled={disabledState}></div>
</div>
Expand All @@ -86,7 +89,7 @@ export default function TextFieldMixin(Base) {
</div>
`
.recompose(({ html, refs: { control, outline, state, shape } }) => {
.recompose(({ html, refs: { control, outline, state, shape, inline } }) => {
control.setAttribute('placeholder', '{computePlaceholder}');
control.setAttribute('aria-label', '{label}');
control.setAttribute('input-suffix', '{inputSuffix}');
Expand All @@ -106,9 +109,8 @@ export default function TextFieldMixin(Base) {
`);
outline.setAttribute('label', '{label}');
outline.setAttribute('errored', '{erroredState}');
shape.prepend(outline, state, control);

outline.after(state, control);
shape.prepend(outline, state);
inline.prepend(control);
})
.on({
sizeChanged(oldValue, newValue) {
Expand Down Expand Up @@ -190,9 +192,6 @@ export default function TextFieldMixin(Base) {
display: flex;
align-items: center;
overflow: visible;
cursor: inherit;
z-index: 0;
Expand All @@ -213,10 +212,27 @@ export default function TextFieldMixin(Base) {
transition: none 200ms cubic-bezier(0.0, 0.0, 0.2, 1);
}
#shape:where([filled],[outlined]) {
padding-inline: 16px;
#inline {
display: flex;
align-items: center;
overflow: visible;
flex: 1;
cursor: inherit;
font-weight: inherit;
font-size: inherit;
line-height: inherit;
font-family: inherit;
letter-spacing: inherit;
transition: none 200ms cubic-bezier(0.0, 0.0, 0.2, 1);
}
#shape:where([filled],[color]) {
background-color: rgb(var(--mdw-bg));
color: rgb(var(--mdw-color__on-surface));
Expand All @@ -235,14 +251,6 @@ export default function TextFieldMixin(Base) {
color: rgb(var(--mdw-ink))
}
#shape:where([filled],[outlined])[icon] {
padding-inline-start: 12px;
}
#shape[trailing-icon] {
padding-inline-end: 0;
}
#shape[focused] {
transition: none 100ms cubic-bezier(0.4, 0.0, 1, 1);
}
Expand All @@ -252,6 +260,10 @@ export default function TextFieldMixin(Base) {
--mdw-shape__size__bottom-end-size: 0px;
}
#inline:where([filled],[outlined]) {
padding-inline: 16px;
}
#prefix,
#suffix,
#control::placeholder {
Expand Down Expand Up @@ -340,7 +352,7 @@ export default function TextFieldMixin(Base) {
#icon {
order: -2;
margin-inline-end: 16px;
margin-inline-start: 12px;
font-size: 24px;
}
Expand All @@ -357,7 +369,7 @@ export default function TextFieldMixin(Base) {
order: 2;
/* stylelint-disable-next-line declaration-property-value-disallowed-list */
margin-inline: 8px 4px;
margin-inline-end: 12px;
}
#indicator {
Expand Down Expand Up @@ -650,7 +662,7 @@ export default function TextFieldMixin(Base) {
padding: 0;
}
#shape[label][filled] {
#inline[label][filled] {
align-items: flex-start;
}
Expand Down

0 comments on commit 64c0960

Please sign in to comment.