@destyler/combobox 0.2.6-beta.1 → 0.2.7

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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import * as _destyler_anatomy0 from "@destyler/anatomy";
2
1
  import { CollectionItem, CollectionItem as CollectionItem$1, CollectionOptions, CollectionOptions as CollectionOptions$1, ListCollection } from "@destyler/collection";
3
2
  import * as _destyler_xstate0 from "@destyler/xstate";
4
3
  import { AnyEventObject, Machine, XSend, XState } from "@destyler/xstate";
@@ -7,7 +6,7 @@ import { FocusOutsideEvent, InteractOutsideEvent, InteractOutsideHandlers, Point
7
6
  import { CommonProperties, DirectionProperty, NormalizeProps, PropTypes, RequiredBy } from "@destyler/types";
8
7
 
9
8
  //#region src/anatomy.d.ts
10
- declare const anatomy: _destyler_anatomy0.AnatomyInstance<"root" | "clearTrigger" | "content" | "control" | "input" | "item" | "itemGroup" | "itemGroupLabel" | "itemIndicator" | "itemText" | "label" | "list" | "positioner" | "trigger">;
9
+ declare const anatomy: any;
11
10
  //#endregion
12
11
  //#region src/collection.d.ts
13
12
  declare function collection<T extends CollectionItem$1>(options: CollectionOptions$1<T>): ListCollection<T>;
@@ -59,56 +58,67 @@ interface PublicContext<T extends CollectionItem = CollectionItem> extends Direc
59
58
  /**
60
59
  * Whether the combobox is open
61
60
  */
62
- 'open'?: boolean | undefined;
61
+ open?: boolean | undefined;
63
62
  /**
64
- * Whether the combobox open state is controlled by the user
63
+ * The initial open state of the combobox when it is not controlled.
64
+ * Prefer this over seeding with `open` for uncontrolled usage.
65
65
  */
66
- 'open.controlled'?: boolean | undefined;
66
+ defaultOpen?: boolean | undefined;
67
67
  /**
68
68
  * The ids of the elements in the combobox. Useful for composition.
69
69
  */
70
- 'ids'?: ElementIds | undefined;
70
+ ids?: ElementIds | undefined;
71
71
  /**
72
- * The current value of the combobox's input
72
+ * The controlled value of the combobox's input
73
73
  */
74
- 'inputValue': string;
74
+ inputValue?: string | undefined;
75
+ /**
76
+ * The initial value of the combobox's input when it is not controlled.
77
+ * Prefer this over seeding with `inputValue` for uncontrolled usage.
78
+ */
79
+ defaultInputValue?: string | undefined;
75
80
  /**
76
81
  * The `name` attribute of the combobox's input. Useful for form submission
77
82
  */
78
- 'name'?: string | undefined;
83
+ name?: string | undefined;
79
84
  /**
80
85
  * The associate form of the combobox.
81
86
  */
82
- 'form'?: string | undefined;
87
+ form?: string | undefined;
83
88
  /**
84
89
  * Whether the combobox is disabled
85
90
  */
86
- 'disabled'?: boolean | undefined;
91
+ disabled?: boolean | undefined;
87
92
  /**
88
93
  * Whether the combobox is readonly. This puts the combobox in a "non-editable" mode
89
94
  * but the user can still interact with it
90
95
  */
91
- 'readOnly'?: boolean | undefined;
96
+ readOnly?: boolean | undefined;
92
97
  /**
93
98
  * Whether the combobox is invalid
94
99
  */
95
- 'invalid'?: boolean | undefined;
100
+ invalid?: boolean | undefined;
96
101
  /**
97
102
  * Whether the combobox is required
98
103
  */
99
- 'required'?: boolean | undefined;
104
+ required?: boolean | undefined;
100
105
  /**
101
106
  * The placeholder text of the combobox's input
102
107
  */
103
- 'placeholder'?: string | undefined;
108
+ placeholder?: string | undefined;
104
109
  /**
105
110
  * The active item's id. Used to set the `aria-activedescendant` attribute
106
111
  */
107
- 'highlightedValue': string | null;
112
+ highlightedValue: string | null;
113
+ /**
114
+ * The controlled keys of the selected items
115
+ */
116
+ value?: string[] | undefined;
108
117
  /**
109
- * The keys of the selected items
118
+ * The initial keys of the selected items when it is not controlled.
119
+ * Prefer this over seeding with `value` for uncontrolled usage.
110
120
  */
111
- 'value': string[];
121
+ defaultValue?: string[] | undefined;
112
122
  /**
113
123
  * Defines the auto-completion behavior of the combobox.
114
124
  *
@@ -117,7 +127,7 @@ interface PublicContext<T extends CollectionItem = CollectionItem> extends Direc
117
127
  *
118
128
  * @default "none"
119
129
  */
120
- 'inputBehavior': 'autohighlight' | 'autocomplete' | 'none';
130
+ inputBehavior: 'autohighlight' | 'autocomplete' | 'none';
121
131
  /**
122
132
  * The behavior of the combobox input when an item is selected
123
133
  *
@@ -127,92 +137,92 @@ interface PublicContext<T extends CollectionItem = CollectionItem> extends Direc
127
137
  *
128
138
  * @default "replace"
129
139
  */
130
- 'selectionBehavior': 'clear' | 'replace' | 'preserve';
140
+ selectionBehavior: 'clear' | 'replace' | 'preserve';
131
141
  /**
132
142
  * Whether to autofocus the input on mount
133
143
  */
134
- 'autoFocus'?: boolean | undefined;
144
+ autoFocus?: boolean | undefined;
135
145
  /**
136
146
  * Whether to open the combobox popup on initial click on the input
137
147
  * @default false
138
148
  */
139
- 'openOnClick'?: boolean | undefined;
149
+ openOnClick?: boolean | undefined;
140
150
  /**
141
151
  * Whether to show the combobox when the input value changes
142
152
  * @default true
143
153
  */
144
- 'openOnChange'?: boolean | ((details: InputValueChangeDetails) => boolean) | undefined;
154
+ openOnChange?: boolean | ((details: InputValueChangeDetails) => boolean) | undefined;
145
155
  /**
146
156
  * Whether to allow typing custom values in the input
147
157
  */
148
- 'allowCustomValue'?: boolean | undefined;
158
+ allowCustomValue?: boolean | undefined;
149
159
  /**
150
160
  * Whether to loop the keyboard navigation through the items
151
161
  * @default true
152
162
  */
153
- 'loopFocus'?: boolean | undefined;
163
+ loopFocus?: boolean | undefined;
154
164
  /**
155
165
  * The positioning options to dynamically position the menu
156
166
  */
157
- 'positioning': PositioningOptions;
167
+ positioning: PositioningOptions;
158
168
  /**
159
169
  * Function called when the input's value changes
160
170
  */
161
- 'onInputValueChange'?: ((details: InputValueChangeDetails) => void) | undefined;
171
+ onInputValueChange?: ((details: InputValueChangeDetails) => void) | undefined;
162
172
  /**
163
173
  * Function called when a new item is selected
164
174
  */
165
- 'onValueChange'?: ((details: ValueChangeDetails<T>) => void) | undefined;
175
+ onValueChange?: ((details: ValueChangeDetails<T>) => void) | undefined;
166
176
  /**
167
177
  * Function called when an item is highlighted using the pointer
168
178
  * or keyboard navigation.
169
179
  */
170
- 'onHighlightChange'?: ((details: HighlightChangeDetails<T>) => void) | undefined;
180
+ onHighlightChange?: ((details: HighlightChangeDetails<T>) => void) | undefined;
171
181
  /**
172
182
  * Function called when the popup is opened
173
183
  */
174
- 'onOpenChange'?: ((details: OpenChangeDetails) => void) | undefined;
184
+ onOpenChange?: ((details: OpenChangeDetails) => void) | undefined;
175
185
  /**
176
186
  * Specifies the localized strings that identifies the accessibility elements and their states
177
187
  */
178
- 'translations': IntlTranslations;
188
+ translations: IntlTranslations;
179
189
  /**
180
190
  * The collection of items
181
191
  */
182
- 'collection': ListCollection<any>;
192
+ collection: ListCollection<any>;
183
193
  /**
184
194
  * Whether to allow multiple selection.
185
195
  *
186
196
  * **Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`.
187
197
  * It is recommended to render the selected items in a separate container.
188
198
  */
189
- 'multiple'?: boolean | undefined;
199
+ multiple?: boolean | undefined;
190
200
  /**
191
201
  * Whether to close the combobox when an item is selected.
192
202
  */
193
- 'closeOnSelect'?: boolean | undefined;
203
+ closeOnSelect?: boolean | undefined;
194
204
  /**
195
205
  * Whether to open the combobox on arrow key press
196
206
  * @default true
197
207
  */
198
- 'openOnKeyPress': boolean;
208
+ openOnKeyPress: boolean;
199
209
  /**
200
210
  * Function to scroll to a specific index
201
211
  */
202
- 'scrollToIndexFn'?: ((details: ScrollToIndexDetails) => void) | undefined;
212
+ scrollToIndexFn?: ((details: ScrollToIndexDetails) => void) | undefined;
203
213
  /**
204
214
  * Whether the combobox is a composed with other composite widgets like tabs
205
215
  * @default true
206
216
  */
207
- 'composite': boolean;
217
+ composite: boolean;
208
218
  /**
209
219
  * Whether to disable registering this a dismissable layer
210
220
  */
211
- 'disableLayer'?: boolean | undefined;
221
+ disableLayer?: boolean | undefined;
212
222
  /**
213
223
  * Function to navigate to the selected item
214
224
  */
215
- 'navigate': (details: NavigateDetails) => void;
225
+ navigate: (details: NavigateDetails) => void;
216
226
  }
217
227
  type UserDefinedContext<T extends CollectionItem = CollectionItem> = RequiredBy<PublicContext<T>, 'id' | 'collection'>;
218
228
  type ComputedContext = Readonly<{
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { ariaAttr, clickIfLink, createScope, dataAttr, getEventKey, isAnchorElem
5
5
  import { getPlacement, getPlacementStyles } from "@destyler/popper";
6
6
  import { ariaHidden } from "@destyler/aria-hidden";
7
7
  import { trackDismissableElement } from "@destyler/dismissable";
8
- import { addOrRemove, compact, isArray, isBoolean, isEqual, match } from "@destyler/utils";
8
+ import { addOrRemove, compact, isArray, isBoolean, isControlled, isEqual, isPropUserProvided, match, resolveControllableOpen, resolveControllableProp, withControllableProvided } from "@destyler/utils";
9
9
 
10
10
  //#region src/anatomy.ts
11
11
  const anatomy = createAnatomy("combobox").parts("root", "clearTrigger", "content", "control", "input", "item", "itemGroup", "itemGroupLabel", "itemIndicator", "itemText", "label", "list", "positioner", "trigger");
@@ -548,17 +548,32 @@ const invoke = {
548
548
  ctx.onInputValueChange?.({ inputValue: ctx.inputValue });
549
549
  }
550
550
  };
551
+ function proposeValueChange(ctx, value) {
552
+ const prevSelectedItems = ctx.selectedItems;
553
+ const items = value.map((v) => {
554
+ const foundItem = prevSelectedItems.find((item) => ctx.collection.getItemValue(item) === v);
555
+ if (foundItem) return foundItem;
556
+ return ctx.collection.find(v);
557
+ });
558
+ ctx.onValueChange?.({
559
+ value: Array.from(value),
560
+ items: Array.from(items)
561
+ });
562
+ }
551
563
  const set = {
552
564
  value: (ctx, value, force = false) => {
553
565
  if (isEqual(ctx.value, value)) return;
554
566
  if (value == null && !force) return;
555
- if (value == null && force) {
556
- ctx.value = [];
557
- invoke.valueChange(ctx);
567
+ let next;
568
+ if (value == null) next = [];
569
+ else if (isArray(value)) next = value;
570
+ else next = ctx.multiple ? addOrRemove(ctx.value, value) : [value];
571
+ if (isEqual(ctx.value, next)) return;
572
+ if (isControlled(ctx, "value")) {
573
+ proposeValueChange(ctx, next);
558
574
  return;
559
575
  }
560
- if (isArray(value)) ctx.value = value;
561
- else if (value != null) ctx.value = ctx.multiple ? addOrRemove(ctx.value, value) : [value];
576
+ ctx.value = next;
562
577
  invoke.valueChange(ctx);
563
578
  },
564
579
  highlightedValue: (ctx, value, force = false) => {
@@ -569,15 +584,36 @@ const set = {
569
584
  },
570
585
  inputValue: (ctx, value) => {
571
586
  if (isEqual(ctx.inputValue, value)) return;
587
+ if (isControlled(ctx, "inputValue")) {
588
+ ctx.onInputValueChange?.({ inputValue: value });
589
+ return;
590
+ }
572
591
  ctx.inputValue = value;
573
592
  invoke.inputChange(ctx);
574
593
  }
575
594
  };
576
595
  function machine(userContext) {
577
- const ctx = compact(userContext);
596
+ const ctx = compact(withControllableProvided(userContext, [
597
+ "open",
598
+ "value",
599
+ "inputValue"
600
+ ]));
601
+ const { initialOpen } = resolveControllableOpen(ctx);
602
+ const { initial: initialValue } = resolveControllableProp({
603
+ value: ctx.value,
604
+ defaultValue: ctx.defaultValue,
605
+ valueProvided: isPropUserProvided(ctx, "value"),
606
+ fallback: []
607
+ });
608
+ const { initial: initialInputValue } = resolveControllableProp({
609
+ value: ctx.inputValue,
610
+ defaultValue: ctx.defaultInputValue,
611
+ valueProvided: isPropUserProvided(ctx, "inputValue"),
612
+ fallback: ""
613
+ });
578
614
  return createMachine({
579
615
  id: "combobox",
580
- initial: ctx.open ? "suggesting" : "idle",
616
+ initial: initialOpen ? "suggesting" : "idle",
581
617
  context: {
582
618
  loopFocus: true,
583
619
  openOnClick: false,
@@ -597,6 +633,8 @@ function machine(userContext) {
597
633
  clickIfLink(node);
598
634
  },
599
635
  ...ctx,
636
+ value: initialValue,
637
+ inputValue: initialInputValue,
600
638
  highlightedItem: null,
601
639
  selectedItems: [],
602
640
  valueAsString: "",
@@ -1091,7 +1129,7 @@ function machine(userContext) {
1091
1129
  allowCustomValue: (ctx$1) => !!ctx$1.allowCustomValue,
1092
1130
  hasHighlightedItem: (ctx$1) => ctx$1.highlightedValue != null,
1093
1131
  closeOnSelect: (ctx$1) => !!ctx$1.closeOnSelect,
1094
- isOpenControlled: (ctx$1) => !!ctx$1["open.controlled"],
1132
+ isOpenControlled: (ctx$1) => isControlled(ctx$1, "open"),
1095
1133
  openOnChange: (ctx$1, evt) => {
1096
1134
  if (isBoolean(ctx$1.openOnChange)) return ctx$1.openOnChange;
1097
1135
  return !!ctx$1.openOnChange?.({ inputValue: evt.value });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@destyler/combobox",
3
3
  "type": "module",
4
- "version": "0.2.6-beta.1",
4
+ "version": "0.2.7",
5
5
  "description": "autocomplete input and command palette with a list of suggestions.",
6
6
  "author": "Elone Hoo <elonehoo@gmail.com>",
7
7
  "license": "MIT",
@@ -22,15 +22,15 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@destyler/anatomy": "0.2.6-beta.1",
26
- "@destyler/aria-hidden": "0.2.6-beta.1",
27
- "@destyler/collection": "0.2.6-beta.1",
28
- "@destyler/dismissable": "0.2.6-beta.1",
29
- "@destyler/types": "0.2.6-beta.1",
30
- "@destyler/utils": "0.2.6-beta.1",
31
- "@destyler/xstate": "0.2.6-beta.1",
32
- "@destyler/dom": "0.2.6-beta.1",
33
- "@destyler/popper": "0.2.6-beta.1"
25
+ "@destyler/anatomy": "0.2.7",
26
+ "@destyler/aria-hidden": "0.2.7",
27
+ "@destyler/collection": "0.2.7",
28
+ "@destyler/dom": "0.2.7",
29
+ "@destyler/dismissable": "0.2.7",
30
+ "@destyler/popper": "0.2.7",
31
+ "@destyler/types": "0.2.7",
32
+ "@destyler/xstate": "0.2.7",
33
+ "@destyler/utils": "0.2.7"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"