@ctrliq/quantic-components 1.61.4 → 1.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,6 +31,7 @@ export declare class Combobox extends Combobox_base {
31
31
  required: boolean;
32
32
  invalid: boolean;
33
33
  emptyLabel: string;
34
+ allowCustomValue: boolean;
34
35
  filterFn?: (option: ComboboxOption, inputValue: string) => boolean;
35
36
  private _open;
36
37
  private _inputValue;
@@ -45,6 +46,7 @@ export declare class Combobox extends Combobox_base {
45
46
  private nextNavigableIndex;
46
47
  private openList;
47
48
  private closeList;
49
+ private commitCustomValue;
48
50
  private positionListbox;
49
51
  private selectOption;
50
52
  private handleInput;
@@ -35,6 +35,7 @@ let Combobox = class Combobox extends A11yAttributesMixin(QuanticElement) {
35
35
  this.required = false;
36
36
  this.invalid = false;
37
37
  this.emptyLabel = 'No results';
38
+ this.allowCustomValue = false;
38
39
  this._open = false;
39
40
  this._inputValue = '';
40
41
  this._activeIndex = -1;
@@ -43,11 +44,17 @@ let Combobox = class Combobox extends A11yAttributesMixin(QuanticElement) {
43
44
  this.internals = this.attachInternals();
44
45
  }
45
46
  updated(changedProperties) {
46
- if (changedProperties.has('value') || changedProperties.has('options')) {
47
+ const valueChanged = changedProperties.has('value');
48
+ const optionsChanged = changedProperties.has('options');
49
+ const customValueChanged = changedProperties.has('allowCustomValue');
50
+ if (valueChanged || customValueChanged || (optionsChanged && !this._open)) {
47
51
  const selected = this.options.find((o) => o.value === this.value);
48
52
  if (selected) {
49
53
  this._inputValue = selected.label;
50
54
  }
55
+ else if (this.allowCustomValue) {
56
+ this._inputValue = this.value;
57
+ }
51
58
  else {
52
59
  this._inputValue = '';
53
60
  }
@@ -102,15 +109,34 @@ let Combobox = class Combobox extends A11yAttributesMixin(QuanticElement) {
102
109
  await this.updateComplete;
103
110
  this.positionListbox();
104
111
  }
105
- closeList() {
112
+ closeList(commit = false) {
106
113
  if (!this._open)
107
114
  return;
108
115
  this._open = false;
109
116
  this._activeIndex = -1;
110
117
  this.cleanup?.();
111
118
  this.cleanup = undefined;
112
- const selected = this.options.find((o) => o.value === this.value);
113
- this._inputValue = selected?.label ?? '';
119
+ if (this.allowCustomValue && commit) {
120
+ this.commitCustomValue();
121
+ }
122
+ else {
123
+ const selected = this.options.find((o) => o.value === this.value);
124
+ this._inputValue =
125
+ selected?.label ?? (this.allowCustomValue ? this.value : '');
126
+ }
127
+ }
128
+ commitCustomValue() {
129
+ const trimmed = this._inputValue.trim();
130
+ this._inputValue = trimmed;
131
+ if (trimmed === this.value)
132
+ return;
133
+ this.value = trimmed;
134
+ this.internals.setFormValue(trimmed);
135
+ this.dispatchEvent(new CustomEvent('value-changed', {
136
+ detail: { value: trimmed },
137
+ bubbles: true,
138
+ composed: true,
139
+ }));
114
140
  }
115
141
  positionListbox() {
116
142
  const listbox = this._listboxRef.value;
@@ -197,18 +223,23 @@ let Combobox = class Combobox extends A11yAttributesMixin(QuanticElement) {
197
223
  this.selectOption(opt);
198
224
  }
199
225
  }
226
+ else if (this.allowCustomValue && this._inputValue.trim()) {
227
+ e.preventDefault();
228
+ this.closeList(true);
229
+ this.commitCustomValue();
230
+ }
200
231
  break;
201
232
  case 'Escape':
202
233
  e.preventDefault();
203
234
  this.closeList();
204
235
  break;
205
236
  case 'Tab':
206
- this.closeList();
237
+ this.closeList(true);
207
238
  break;
208
239
  }
209
240
  }
210
241
  handleBlur() {
211
- this.closeList();
242
+ this.closeList(true);
212
243
  this.dispatchEvent(new Event('blur', { bubbles: true, composed: true }));
213
244
  }
214
245
  handleFocus() {
@@ -233,7 +264,7 @@ let Combobox = class Combobox extends A11yAttributesMixin(QuanticElement) {
233
264
  this.value = state;
234
265
  this.internals.setFormValue(state);
235
266
  const selected = this.options.find((o) => o.value === state);
236
- this._inputValue = selected?.label ?? '';
267
+ this._inputValue = selected?.label ?? (this.allowCustomValue ? state : '');
237
268
  }
238
269
  formDisabledCallback(disabled) {
239
270
  this.disabled = disabled;
@@ -700,6 +731,15 @@ __decorate([
700
731
  description: 'Text shown in the listbox when no options match the input.',
701
732
  })
702
733
  ], Combobox.prototype, "emptyLabel", void 0);
734
+ __decorate([
735
+ prop({
736
+ type: 'boolean',
737
+ default: 'false',
738
+ description: 'When true, the user can type a value that does not match any option. ' +
739
+ 'The typed text is preserved on blur/Enter and surfaced through the value-changed event. ' +
740
+ 'When false (default), only values from the options list are accepted.',
741
+ })
742
+ ], Combobox.prototype, "allowCustomValue", void 0);
703
743
  __decorate([
704
744
  prop({
705
745
  type: '(option: ComboboxOption, inputValue: string) => boolean',
@@ -717,7 +757,7 @@ __decorate([
717
757
  ], Combobox.prototype, "_activeIndex", void 0);
718
758
  Combobox = __decorate([
719
759
  event('value-changed', {
720
- description: 'Fired when the user selects an option from the list.',
760
+ description: 'Fired when the user selects an option from the list, or commits a custom value when allowCustomValue is enabled.',
721
761
  detail: '{ value: string }',
722
762
  }),
723
763
  event('input-changed', {
@@ -43,5 +43,8 @@ export declare const EmptyState: import("../shared/story").StoryObject<object>;
43
43
  export declare const WithDisabledOptions: import("../shared/story").StoryObject<object>;
44
44
  export declare const WithCustomFilter: import("../shared/story").StoryObject<object>;
45
45
  export declare const WithLongOptionLabels: import("../shared/story").StoryObject<object>;
46
+ export declare const AllowCustomValue: import("../shared/story").StoryObject<object>;
47
+ export declare const AllowCustomValueWithPreselected: import("../shared/story").StoryObject<object>;
48
+ export declare const AllowCustomValueInAField: import("../shared/story").StoryObject<object>;
46
49
  export declare const InAField: import("../shared/story").StoryObject<object>;
47
50
  export declare const InAFieldWithValidation: import("../shared/story").StoryObject<object>;
@@ -107,6 +107,43 @@ export const WithLongOptionLabels = story().render(({ html }) => html `
107
107
  ></quantic-combobox>
108
108
  </div>
109
109
  `);
110
+ export const AllowCustomValue = story().render(({ html, action }) => html `
111
+ <div style="max-width: 320px">
112
+ <quantic-combobox
113
+ placeholder="Type or pick a framework…"
114
+ allowCustomValue
115
+ .options=${frameworks}
116
+ @value-changed=${action('value-changed')}
117
+ ></quantic-combobox>
118
+ </div>
119
+ `);
120
+ export const AllowCustomValueWithPreselected = story().render(({ html, action }) => html `
121
+ <div style="max-width: 320px">
122
+ <quantic-combobox
123
+ placeholder="Type or pick a framework…"
124
+ value="react"
125
+ allowCustomValue
126
+ .options=${frameworks}
127
+ @value-changed=${action('value-changed')}
128
+ ></quantic-combobox>
129
+ </div>
130
+ `);
131
+ export const AllowCustomValueInAField = story().render(({ html, action }) => html `
132
+ <div style="max-width: 320px">
133
+ <quantic-combobox-field
134
+ label="Volume name"
135
+ hint="Pick an existing volume or type a new name."
136
+ placeholder="e.g. my-volume"
137
+ allowCustomValue
138
+ .options=${[
139
+ { label: 'vol-data-01', value: 'vol-data-01' },
140
+ { label: 'vol-scratch', value: 'vol-scratch' },
141
+ { label: 'vol-models-shared', value: 'vol-models-shared' },
142
+ ]}
143
+ @value-changed=${action('value-changed')}
144
+ ></quantic-combobox-field>
145
+ </div>
146
+ `);
110
147
  export const InAField = story().render(({ html, action }) => html `
111
148
  <div style="max-width: 320px">
112
149
  <quantic-combobox-field
@@ -10,6 +10,7 @@ export default renderAsField('combobox', Combobox, function (a11yContext) {
10
10
  .options=${this.options}
11
11
  .size=${this.size}
12
12
  .emptyLabel=${this.emptyLabel}
13
+ .allowCustomValue=${this.allowCustomValue}
13
14
  .filterFn=${this.filterFn}
14
15
  ?disabled=${this.disabled}
15
16
  ?required=${this.required}