vorma 0.86.0-pre.2 → 0.86.0-pre.3

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.
Files changed (104) hide show
  1. package/.dist/core/_index.d.ts +1 -0
  2. package/.dist/core/_index.d.ts.map +1 -1
  3. package/.dist/core/_index.js +6 -0
  4. package/.dist/core/_index.js.map +1 -1
  5. package/.dist/core/vorma_client_wasm_bg.wasm +0 -0
  6. package/.dist/ui/solid/solid.js +29 -39
  7. package/.dist/ui/solid/solid.js.map +1 -1
  8. package/core/client_wasm/vorma_client_wasm_bg.wasm +0 -0
  9. package/core/create_client_core.ts +9 -1
  10. package/package.json +1 -16
  11. package/ui/solid/solid.tsx +29 -10
  12. package/.dist/kit/lab/design/components/remix/remix.d.ts +0 -2309
  13. package/.dist/kit/lab/design/components/remix/remix.d.ts.map +0 -1
  14. package/.dist/kit/lab/design/components/remix/remix.js +0 -7022
  15. package/.dist/kit/lab/design/components/remix/remix.js.map +0 -1
  16. package/.dist/kit/lab/design/core/core.d.ts +0 -527
  17. package/.dist/kit/lab/design/core/core.d.ts.map +0 -1
  18. package/.dist/kit/lab/design/core/core.js +0 -668
  19. package/.dist/kit/lab/design/core/core.js.map +0 -1
  20. package/.dist/kit/matcher/matcher.d.ts +0 -78
  21. package/.dist/kit/matcher/matcher.d.ts.map +0 -1
  22. package/.dist/kit/matcher/matcher.js +0 -517
  23. package/.dist/kit/matcher/matcher.js.map +0 -1
  24. package/.dist/ui/remix/remix.d.ts +0 -73
  25. package/.dist/ui/remix/remix.d.ts.map +0 -1
  26. package/.dist/ui/remix/remix.js +0 -409
  27. package/.dist/ui/remix/remix.js.map +0 -1
  28. package/kit/lab/design/components/remix/accordion.ts +0 -474
  29. package/kit/lab/design/components/remix/alert-dialog.ts +0 -512
  30. package/kit/lab/design/components/remix/alert.ts +0 -213
  31. package/kit/lab/design/components/remix/aspect-ratio.ts +0 -111
  32. package/kit/lab/design/components/remix/badge.ts +0 -59
  33. package/kit/lab/design/components/remix/box.ts +0 -49
  34. package/kit/lab/design/components/remix/button.ts +0 -314
  35. package/kit/lab/design/components/remix/callout.ts +0 -127
  36. package/kit/lab/design/components/remix/checkable-state.ts +0 -187
  37. package/kit/lab/design/components/remix/checkbox-group.ts +0 -443
  38. package/kit/lab/design/components/remix/checkbox.ts +0 -215
  39. package/kit/lab/design/components/remix/chip.ts +0 -165
  40. package/kit/lab/design/components/remix/code-block.ts +0 -181
  41. package/kit/lab/design/components/remix/collapsible.ts +0 -325
  42. package/kit/lab/design/components/remix/component-state.ts +0 -55
  43. package/kit/lab/design/components/remix/component-style.ts +0 -364
  44. package/kit/lab/design/components/remix/composite-navigation.ts +0 -71
  45. package/kit/lab/design/components/remix/conditions.ts +0 -25
  46. package/kit/lab/design/components/remix/controllable-state.ts +0 -58
  47. package/kit/lab/design/components/remix/description-list.ts +0 -275
  48. package/kit/lab/design/components/remix/dialog.ts +0 -653
  49. package/kit/lab/design/components/remix/empty-state.ts +0 -146
  50. package/kit/lab/design/components/remix/field.ts +0 -418
  51. package/kit/lab/design/components/remix/fieldset.ts +0 -153
  52. package/kit/lab/design/components/remix/form-mirror.ts +0 -94
  53. package/kit/lab/design/components/remix/form-reset.ts +0 -85
  54. package/kit/lab/design/components/remix/form.ts +0 -109
  55. package/kit/lab/design/components/remix/grid.ts +0 -187
  56. package/kit/lab/design/components/remix/group-label.ts +0 -47
  57. package/kit/lab/design/components/remix/icon.ts +0 -176
  58. package/kit/lab/design/components/remix/input.ts +0 -163
  59. package/kit/lab/design/components/remix/kbd.ts +0 -58
  60. package/kit/lab/design/components/remix/list.ts +0 -120
  61. package/kit/lab/design/components/remix/listbox.ts +0 -1116
  62. package/kit/lab/design/components/remix/menu.ts +0 -1477
  63. package/kit/lab/design/components/remix/ordered-collection.ts +0 -95
  64. package/kit/lab/design/components/remix/popover-scroll-lock.ts +0 -89
  65. package/kit/lab/design/components/remix/popover.ts +0 -576
  66. package/kit/lab/design/components/remix/popup-behavior.ts +0 -96
  67. package/kit/lab/design/components/remix/progress.ts +0 -185
  68. package/kit/lab/design/components/remix/radio-group.ts +0 -420
  69. package/kit/lab/design/components/remix/recipe.ts +0 -70
  70. package/kit/lab/design/components/remix/remix.ts +0 -683
  71. package/kit/lab/design/components/remix/responsive.ts +0 -106
  72. package/kit/lab/design/components/remix/root.ts +0 -137
  73. package/kit/lab/design/components/remix/select.ts +0 -1546
  74. package/kit/lab/design/components/remix/separator.ts +0 -108
  75. package/kit/lab/design/components/remix/slider.ts +0 -308
  76. package/kit/lab/design/components/remix/spinner.ts +0 -114
  77. package/kit/lab/design/components/remix/stack.ts +0 -205
  78. package/kit/lab/design/components/remix/stat.ts +0 -127
  79. package/kit/lab/design/components/remix/surface.ts +0 -71
  80. package/kit/lab/design/components/remix/switch.ts +0 -219
  81. package/kit/lab/design/components/remix/table.ts +0 -395
  82. package/kit/lab/design/components/remix/tabs.ts +0 -404
  83. package/kit/lab/design/components/remix/text.ts +0 -169
  84. package/kit/lab/design/components/remix/textarea.ts +0 -164
  85. package/kit/lab/design/components/remix/toast.ts +0 -299
  86. package/kit/lab/design/components/remix/tooltip.ts +0 -330
  87. package/kit/lab/design/components/remix/typeahead.ts +0 -81
  88. package/kit/lab/design/components/remix/types.ts +0 -19
  89. package/kit/lab/design/components/remix/visually-hidden.ts +0 -100
  90. package/kit/lab/design/core/color.ts +0 -46
  91. package/kit/lab/design/core/core.ts +0 -111
  92. package/kit/lab/design/core/create_primitive.ts +0 -455
  93. package/kit/lab/design/core/create_system.ts +0 -303
  94. package/kit/lab/design/core/css.ts +0 -127
  95. package/kit/lab/design/core/define_system.ts +0 -508
  96. package/kit/lab/design/core/math.ts +0 -12
  97. package/kit/lab/design/core/object.ts +0 -31
  98. package/kit/lab/design/core/recipe/create_recipe.ts +0 -185
  99. package/kit/lab/design/core/recipe/recipe.ts +0 -26
  100. package/kit/lab/design/core/recipe/style.ts +0 -62
  101. package/kit/lab/design/core/recipe/types.ts +0 -160
  102. package/kit/lab/design/core/resolve_token.ts +0 -118
  103. package/kit/lab/design/core/types.ts +0 -636
  104. package/ui/remix/remix.tsx +0 -901
@@ -1,1546 +0,0 @@
1
- import {
2
- Fragment,
3
- createElement,
4
- on,
5
- ref,
6
- type Handle,
7
- type Props,
8
- type RemixNode,
9
- } from "remix/ui";
10
- import {
11
- createRecipe,
12
- type AnyGeneratedSystemMetadata,
13
- type RecipeVariantPropsFor,
14
- type RecipeVariantValue,
15
- type RecipeWithVariantGroups,
16
- } from "../../core/core.ts";
17
- import { checkableStateFromValue, type CheckableStateName } from "./checkable-state.ts";
18
- import {
19
- ariaBoolean,
20
- ariaTrue,
21
- componentDataAttribute,
22
- componentStateAttribute,
23
- dataFlag,
24
- openStateFromBoolean,
25
- } from "./component-state.ts";
26
- import {
27
- createComponentAnatomyAttrs,
28
- createComponentSlotProps,
29
- createComponentStyleTargets,
30
- } from "./component-style.ts";
31
- import {
32
- get_collection_navigation_item,
33
- get_first_collection_item,
34
- get_last_collection_item,
35
- } from "./composite-navigation.ts";
36
- import { create_controllable_state } from "./controllable-state.ts";
37
- import { createNativeSelectFormMirror } from "./form-mirror.ts";
38
- import {
39
- create_group_label_ref_mix,
40
- create_group_label_relationship,
41
- type GroupLabelRelationship,
42
- } from "./group-label.ts";
43
- import {
44
- create_ordered_collection,
45
- type OrderedCollectionItem,
46
- } from "./ordered-collection.ts";
47
- import { popoverScrollLockGuard } from "./popover-scroll-lock.ts";
48
- import {
49
- createPopupRefMix,
50
- createPopupRelationship,
51
- type PopupRelationship,
52
- } from "./popup-behavior.ts";
53
- import {
54
- mergeRecipeConditionSelectors,
55
- type RecipeConditionSelectorMap,
56
- } from "./recipe.ts";
57
- import { type BreakpointForStyleSystem, type ResponsiveProps } from "./responsive.ts";
58
- import { create_typeahead } from "./typeahead.ts";
59
- import type { ComponentStyle, ComponentStyleSystem, RemixComponent } from "./types.ts";
60
-
61
- const select_slot = {
62
- group: "group",
63
- groupLabel: "groupLabel",
64
- icon: "icon",
65
- list: "list",
66
- option: "option",
67
- optionIndicator: "optionIndicator",
68
- optionText: "optionText",
69
- popup: "popup",
70
- separator: "separator",
71
- trigger: "trigger",
72
- value: "value",
73
- } as const;
74
-
75
- export type SelectRecipeSlot = (typeof select_slot)[keyof typeof select_slot];
76
-
77
- export type SelectRecipeCondition =
78
- | "active"
79
- | "disabled"
80
- | "focusVisible"
81
- | "highlighted"
82
- | "hover"
83
- | "open"
84
- | "placeholder"
85
- | "reducedMotion"
86
- | "selected";
87
-
88
- export type SelectRecipeInput<
89
- TVariant extends string = string,
90
- TSize extends string = string,
91
- TPopupLayout extends string = string,
92
- > = RecipeWithVariantGroups<
93
- SelectRecipeSlot,
94
- string,
95
- ComponentStyle,
96
- {
97
- popupLayout: TPopupLayout;
98
- size: TSize;
99
- variant: TVariant;
100
- }
101
- >;
102
-
103
- export type SelectRecipeVariant<TRecipe extends SelectRecipeInput> = RecipeVariantValue<
104
- TRecipe,
105
- "variant"
106
- >;
107
- export type SelectRecipeSize<TRecipe extends SelectRecipeInput> = RecipeVariantValue<
108
- TRecipe,
109
- "size"
110
- >;
111
- export type SelectRecipePopupLayout<TRecipe extends SelectRecipeInput> =
112
- RecipeVariantValue<TRecipe, "popupLayout">;
113
- export type SelectRecipeSelection<TRecipe extends SelectRecipeInput> =
114
- RecipeVariantPropsFor<TRecipe, "popupLayout" | "size" | "variant">;
115
-
116
- export type SelectStyleSystem<
117
- TMode extends string = string,
118
- TRecipe extends SelectRecipeInput = SelectRecipeInput,
119
- TMetadata extends AnyGeneratedSystemMetadata = AnyGeneratedSystemMetadata,
120
- > = ComponentStyleSystem<TMode, { recipe: { select: TRecipe } }, TMetadata>;
121
-
122
- export const selectOpenChangeReason = {
123
- escape: "escape",
124
- interactOutside: "interactOutside",
125
- option: "option",
126
- tab: "tab",
127
- trigger: "trigger",
128
- } as const;
129
-
130
- export const selectValueChangeReason = {
131
- keyboard: "keyboard",
132
- pointer: "pointer",
133
- } as const;
134
-
135
- export const selectHighlightChangeReason = {
136
- keyboard: "keyboard",
137
- open: "open",
138
- pointer: "pointer",
139
- typeahead: "typeahead",
140
- } as const;
141
-
142
- export type SelectOpenChangeReason =
143
- (typeof selectOpenChangeReason)[keyof typeof selectOpenChangeReason];
144
-
145
- export type SelectValueChangeReason =
146
- (typeof selectValueChangeReason)[keyof typeof selectValueChangeReason];
147
-
148
- export type SelectHighlightChangeReason =
149
- (typeof selectHighlightChangeReason)[keyof typeof selectHighlightChangeReason];
150
-
151
- export type SelectOpenChangeDetails = {
152
- event?: Event;
153
- reason: SelectOpenChangeReason;
154
- };
155
-
156
- export type SelectValueChangeDetails = {
157
- event?: Event;
158
- reason: SelectValueChangeReason;
159
- };
160
-
161
- export type SelectHighlightChangeDetails = {
162
- event?: Event;
163
- reason: SelectHighlightChangeReason;
164
- };
165
-
166
- export type SelectValueChangeHandler<TValue extends string = string> = (
167
- value: TValue | null,
168
- details?: SelectValueChangeDetails,
169
- ) => void;
170
-
171
- export type SelectOpenChangeHandler = (
172
- open: boolean,
173
- details?: SelectOpenChangeDetails,
174
- ) => void;
175
-
176
- export type SelectHighlightChangeHandler<TValue extends string = string> = (
177
- value: TValue | null,
178
- details?: SelectHighlightChangeDetails,
179
- ) => void;
180
-
181
- export type SelectProps<TValue extends string = string> = {
182
- autoComplete?: string;
183
- children?: RemixNode;
184
- defaultOpen?: boolean;
185
- defaultHighlightedValue?: TValue | null;
186
- defaultValue?: TValue | null;
187
- disabled?: boolean;
188
- form?: string;
189
- highlightedValue?: TValue | null;
190
- invalid?: boolean;
191
- loopFocus?: boolean;
192
- name?: string;
193
- onHighlightChange?: SelectHighlightChangeHandler<TValue>;
194
- onOpenChange?: SelectOpenChangeHandler;
195
- onOpenChangeComplete?: SelectOpenChangeHandler;
196
- onValueChange?: SelectValueChangeHandler<TValue>;
197
- open?: boolean;
198
- placeholder?: string;
199
- readOnly?: boolean;
200
- required?: boolean;
201
- typeahead?: boolean;
202
- value?: TValue | null;
203
- };
204
-
205
- export type SelectTriggerStyleProps<
206
- TVariant extends string = string,
207
- TSize extends string = string,
208
- > = {
209
- size?: TSize;
210
- variant?: TVariant;
211
- };
212
-
213
- export type SelectTriggerProps<
214
- TVariant extends string = string,
215
- TSize extends string = string,
216
- TBreakpoint extends string = string,
217
- > = Omit<Props<"button">, "style"> &
218
- SelectTriggerStyleProps<TVariant, TSize> &
219
- ResponsiveProps<SelectTriggerStyleProps<TVariant, TSize>, TBreakpoint> & {
220
- style?: never;
221
- };
222
-
223
- export type SelectPopupStyleProps<TLayout extends string = string> = {
224
- layout?: TLayout;
225
- };
226
-
227
- export type SelectPopupProps<
228
- TLayout extends string = string,
229
- TBreakpoint extends string = string,
230
- > = Omit<Props<"div">, "style"> &
231
- SelectPopupStyleProps<TLayout> &
232
- ResponsiveProps<SelectPopupStyleProps<TLayout>, TBreakpoint> & {
233
- style?: never;
234
- };
235
-
236
- export type SelectListProps<
237
- TLayout extends string = string,
238
- TBreakpoint extends string = string,
239
- > = SelectPopupProps<TLayout, TBreakpoint>;
240
-
241
- export type SelectValueProps = Omit<Props<"span">, "style"> & {
242
- placeholder?: RemixNode;
243
- style?: never;
244
- };
245
-
246
- export type SelectIconProps = Omit<Props<"span">, "style"> & {
247
- style?: never;
248
- };
249
-
250
- export type SelectGroupProps = Omit<Props<"div">, "style"> & {
251
- style?: never;
252
- };
253
-
254
- export type SelectGroupLabelProps = Omit<Props<"div">, "style"> & {
255
- style?: never;
256
- };
257
-
258
- export type SelectSeparatorProps = Omit<Props<"div">, "style"> & {
259
- style?: never;
260
- };
261
-
262
- export type SelectOptionProps<TValue extends string = string> = Omit<
263
- Props<"div">,
264
- "style"
265
- > & {
266
- disabled?: boolean;
267
- style?: never;
268
- textValue?: string;
269
- value: TValue;
270
- };
271
-
272
- export type SelectOptionTextProps = Omit<Props<"span">, "style"> & {
273
- style?: never;
274
- };
275
-
276
- export type SelectOptionIndicatorProps = Omit<Props<"span">, "style"> & {
277
- style?: never;
278
- };
279
-
280
- export type SelectComponents<
281
- TVariant extends string = string,
282
- TSize extends string = string,
283
- TPopupLayout extends string = string,
284
- > = {
285
- Group: RemixComponent<SelectGroupProps, SelectGroupRuntimeContext>;
286
- GroupLabel: RemixComponent<SelectGroupLabelProps>;
287
- Icon: RemixComponent<SelectIconProps>;
288
- List: RemixComponent<SelectListProps<TPopupLayout>>;
289
- Option: RemixComponent<SelectOptionProps>;
290
- OptionIndicator: RemixComponent<SelectOptionIndicatorProps>;
291
- OptionText: RemixComponent<SelectOptionTextProps>;
292
- Popup: RemixComponent<SelectPopupProps<TPopupLayout>>;
293
- Root: RemixComponent<SelectProps, SelectRuntimeContext>;
294
- Separator: RemixComponent<SelectSeparatorProps>;
295
- Trigger: RemixComponent<SelectTriggerProps<TVariant, TSize>>;
296
- Value: RemixComponent<SelectValueProps>;
297
- };
298
-
299
- export type SelectOptions = {
300
- conditions?: Partial<
301
- Readonly<Record<SelectRecipeSlot, RecipeConditionSelectorMap<string>>>
302
- >;
303
- };
304
-
305
- type SelectRuntimeContext = {
306
- close: (details: SelectOpenChangeDetails) => void;
307
- get_disabled: () => boolean;
308
- get_highlighted_id: () => string | undefined;
309
- get_highlighted_value: () => string | null;
310
- get_invalid: () => boolean;
311
- get_list_id: () => string;
312
- get_open: () => boolean;
313
- get_placeholder: () => string | undefined;
314
- get_popup_relationship: () => PopupRelationship;
315
- get_popup_id: () => string;
316
- get_read_only: () => boolean;
317
- get_required: () => boolean;
318
- get_selected_text: () => string | undefined;
319
- get_trigger_id: () => string;
320
- get_value: () => string | null;
321
- handle_keydown: (event: KeyboardEvent) => void;
322
- highlight_by_offset: (offset: number, details: SelectHighlightChangeDetails) => void;
323
- highlight_first: () => void;
324
- highlight_last: () => void;
325
- highlight_value: (
326
- value: string | null,
327
- details: SelectHighlightChangeDetails,
328
- ) => void;
329
- open: (details: SelectOpenChangeDetails, highlight?: SelectOpenHighlight) => void;
330
- register_option: (option: RegisteredSelectOption) => void;
331
- register_trigger: (node: HTMLElement | null) => void;
332
- search: (text: string, event: KeyboardEvent) => void;
333
- select_value: (value: string | null, details: SelectValueChangeDetails) => void;
334
- sync_popup: () => void;
335
- toggle: (details: SelectOpenChangeDetails) => void;
336
- unregister_option: (id: string) => void;
337
- };
338
-
339
- type RegisteredSelectOption = OrderedCollectionItem<string>;
340
-
341
- type SelectOpenHighlight = "first" | "last" | "selected";
342
-
343
- type SelectGroupRuntimeContext = GroupLabelRelationship;
344
-
345
- const trigger_conditions = {
346
- active: "&:active",
347
- disabled: "&:disabled, &[aria-disabled='true']",
348
- focusVisible: "&:focus-visible",
349
- hover: "&:hover",
350
- open: "&[aria-expanded='true']",
351
- placeholder: `&[${componentDataAttribute.placeholder}]`,
352
- reducedMotion: "@media (prefers-reduced-motion: reduce)",
353
- } as const satisfies RecipeConditionSelectorMap<SelectRecipeCondition>;
354
-
355
- const option_conditions = {
356
- disabled: `&[aria-disabled='true'], &[${componentDataAttribute.disabled}]`,
357
- highlighted: `&[${componentDataAttribute.highlighted}]`,
358
- reducedMotion: "@media (prefers-reduced-motion: reduce)",
359
- selected: `&[aria-selected='true'], &[${componentDataAttribute.selected}]`,
360
- } as const satisfies RecipeConditionSelectorMap<SelectRecipeCondition>;
361
-
362
- const open_conditions = {
363
- open: `&:popover-open, &[${componentDataAttribute.open}]`,
364
- reducedMotion: "@media (prefers-reduced-motion: reduce)",
365
- } as const satisfies RecipeConditionSelectorMap<SelectRecipeCondition>;
366
-
367
- const value_conditions = {
368
- placeholder: `&[${componentDataAttribute.placeholder}]`,
369
- reducedMotion: "@media (prefers-reduced-motion: reduce)",
370
- } as const satisfies RecipeConditionSelectorMap<SelectRecipeCondition>;
371
-
372
- const select_scope = "select";
373
- const select_option_value_attribute = "data-vorma-select-value";
374
- const select_page_jump_size = 10;
375
- const select_typeahead_reset_ms = 700;
376
-
377
- function infer_text_value(children: RemixNode): string {
378
- if (typeof children === "string" || typeof children === "number") {
379
- return String(children);
380
- }
381
- return "";
382
- }
383
-
384
- function is_select_keyboard_selection(event: KeyboardEvent): boolean {
385
- return event.key === "Enter" || event.key === " " || event.key === "Spacebar";
386
- }
387
-
388
- function is_printable_key_event(event: KeyboardEvent): boolean {
389
- return event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey;
390
- }
391
-
392
- function coerce_select_value(value: string | null | undefined): string | null {
393
- return value ?? null;
394
- }
395
-
396
- function select_option_state(selected: boolean): CheckableStateName {
397
- return checkableStateFromValue(selected, false);
398
- }
399
-
400
- export function createSelect<
401
- TMode extends string,
402
- TRecipe extends SelectRecipeInput,
403
- TMetadata extends AnyGeneratedSystemMetadata,
404
- >(
405
- style_system: SelectStyleSystem<TMode, TRecipe, TMetadata>,
406
- options: SelectOptions = {},
407
- ): SelectComponents<
408
- SelectRecipeVariant<TRecipe>,
409
- SelectRecipeSize<TRecipe>,
410
- SelectRecipePopupLayout<TRecipe>
411
- > {
412
- type TVariant = SelectRecipeVariant<TRecipe>;
413
- type TSize = SelectRecipeSize<TRecipe>;
414
- type TPopupLayout = SelectRecipePopupLayout<TRecipe>;
415
- type TBreakpoint = BreakpointForStyleSystem<typeof style_system>;
416
-
417
- const select_recipe = createRecipe(style_system.token.recipe.select);
418
-
419
- function Root(
420
- handle: Handle<SelectProps, SelectRuntimeContext>,
421
- ): (props: SelectProps) => RemixNode {
422
- let local_open = handle.props.defaultOpen ?? false;
423
- let local_value = handle.props.defaultValue ?? null;
424
- let local_highlighted_value = handle.props.defaultHighlightedValue ?? null;
425
- let last_completed_open = handle.props.open ?? local_open;
426
- let last_open_details: SelectOpenChangeDetails = {
427
- reason: selectOpenChangeReason.trigger,
428
- };
429
- const popup_relationship = createPopupRelationship();
430
- const option_collection = create_ordered_collection<RegisteredSelectOption>();
431
- const option_typeahead = create_typeahead<RegisteredSelectOption>({
432
- timeoutMs: select_typeahead_reset_ms,
433
- });
434
- const open_state = create_controllable_state<boolean, SelectOpenChangeDetails>({
435
- getControlled: () => {
436
- return handle.props.open;
437
- },
438
- getLocal: () => {
439
- return local_open;
440
- },
441
- getOnChange: () => {
442
- return handle.props.onOpenChange;
443
- },
444
- setLocal: (value) => {
445
- local_open = value;
446
- },
447
- });
448
- const value_state = create_controllable_state<
449
- string | null,
450
- SelectValueChangeDetails
451
- >({
452
- getControlled: () => {
453
- if (handle.props.value === undefined) {
454
- return undefined;
455
- }
456
- return coerce_select_value(handle.props.value);
457
- },
458
- getLocal: () => {
459
- return local_value;
460
- },
461
- getOnChange: () => {
462
- return handle.props.onValueChange;
463
- },
464
- setLocal: (value) => {
465
- local_value = value;
466
- },
467
- });
468
- const highlighted_state = create_controllable_state<
469
- string | null,
470
- SelectHighlightChangeDetails
471
- >({
472
- getControlled: () => {
473
- if (handle.props.highlightedValue === undefined) {
474
- return undefined;
475
- }
476
- return coerce_select_value(handle.props.highlightedValue);
477
- },
478
- getLocal: () => {
479
- return local_highlighted_value;
480
- },
481
- getOnChange: () => {
482
- return handle.props.onHighlightChange;
483
- },
484
- setLocal: (value) => {
485
- local_highlighted_value = value;
486
- },
487
- });
488
-
489
- function get_open(): boolean {
490
- return open_state.get();
491
- }
492
-
493
- function get_value(): string | null {
494
- return value_state.get();
495
- }
496
-
497
- function get_highlighted_value(): string | null {
498
- return highlighted_state.get();
499
- }
500
-
501
- function get_options(): RegisteredSelectOption[] {
502
- return option_collection.getItems();
503
- }
504
-
505
- function get_enabled_options(): RegisteredSelectOption[] {
506
- return option_collection.getEnabledItems();
507
- }
508
-
509
- function find_option(value: string | null): RegisteredSelectOption | undefined {
510
- if (value === null) {
511
- return undefined;
512
- }
513
- return option_collection.findByValue(value);
514
- }
515
-
516
- function scroll_highlighted_option_into_view(): void {
517
- const highlighted_option = find_option(get_highlighted_value());
518
- if (
519
- highlighted_option &&
520
- typeof highlighted_option.node.scrollIntoView === "function"
521
- ) {
522
- highlighted_option.node.scrollIntoView({ block: "nearest" });
523
- }
524
- }
525
-
526
- function schedule_highlight_scroll(): void {
527
- handle.queueTask((signal) => {
528
- if (signal.aborted) {
529
- return;
530
- }
531
- scroll_highlighted_option_into_view();
532
- });
533
- }
534
-
535
- function request_open(open: boolean, details: SelectOpenChangeDetails): void {
536
- if (open && handle.props.disabled) {
537
- return;
538
- }
539
- const changed = open_state.set(open, details);
540
- if (!changed) {
541
- return;
542
- }
543
-
544
- last_open_details = details;
545
- void handle.update();
546
- }
547
-
548
- function set_highlighted_value(
549
- value: string | null,
550
- details: SelectHighlightChangeDetails,
551
- ): void {
552
- const changed = highlighted_state.set(value, details);
553
- if (!changed) {
554
- return;
555
- }
556
-
557
- schedule_highlight_scroll();
558
- void handle.update();
559
- }
560
-
561
- function highlight_option(
562
- option: RegisteredSelectOption | undefined,
563
- details: SelectHighlightChangeDetails,
564
- ): void {
565
- set_highlighted_value(option?.value ?? null, details);
566
- }
567
-
568
- function move_highlight(
569
- offset: number,
570
- details: SelectHighlightChangeDetails,
571
- clamp = false,
572
- ): void {
573
- const enabled_options = get_enabled_options();
574
- if (enabled_options.length === 0) {
575
- highlight_option(undefined, details);
576
- return;
577
- }
578
-
579
- const next_option = get_collection_navigation_item({
580
- clamp,
581
- currentValue: get_highlighted_value(),
582
- fallbackValue: get_value(),
583
- items: enabled_options,
584
- loop: handle.props.loopFocus,
585
- offset,
586
- });
587
- if (next_option === undefined) {
588
- return;
589
- }
590
- highlight_option(next_option, details);
591
- }
592
-
593
- function select_value(
594
- value: string | null,
595
- details: SelectValueChangeDetails,
596
- ): void {
597
- if (handle.props.disabled || handle.props.readOnly) {
598
- return;
599
- }
600
-
601
- const option = find_option(value);
602
- if (option?.disabled) {
603
- return;
604
- }
605
- value_state.set(value, details);
606
- if (handle.props.highlightedValue === undefined) {
607
- local_highlighted_value = value;
608
- }
609
- request_open(false, {
610
- event: details.event,
611
- reason:
612
- details.event instanceof KeyboardEvent && details.event.key === "Tab"
613
- ? selectOpenChangeReason.tab
614
- : selectOpenChangeReason.option,
615
- });
616
- void handle.update();
617
- }
618
-
619
- function search(text: string, event: KeyboardEvent): void {
620
- if (handle.props.typeahead === false) {
621
- return;
622
- }
623
-
624
- const match = option_typeahead.search({
625
- currentValue: get_highlighted_value(),
626
- items: get_enabled_options(),
627
- key: text,
628
- });
629
-
630
- highlight_option(match, {
631
- event,
632
- reason: selectHighlightChangeReason.typeahead,
633
- });
634
- }
635
-
636
- function apply_open_highlight(
637
- highlight: SelectOpenHighlight | undefined,
638
- details: SelectOpenChangeDetails,
639
- ): void {
640
- if (highlight === "first") {
641
- highlight_option(get_first_collection_item(get_enabled_options()), {
642
- event: details.event,
643
- reason: selectHighlightChangeReason.open,
644
- });
645
- return;
646
- }
647
- if (highlight === "last") {
648
- highlight_option(get_last_collection_item(get_enabled_options()), {
649
- event: details.event,
650
- reason: selectHighlightChangeReason.open,
651
- });
652
- return;
653
- }
654
- if (highlight === "selected" && get_highlighted_value() === null) {
655
- highlight_option(find_option(get_value()), {
656
- event: details.event,
657
- reason: selectHighlightChangeReason.open,
658
- });
659
- }
660
- }
661
-
662
- function create_hidden_select(props: SelectProps): RemixNode {
663
- return createNativeSelectFormMirror({
664
- autoComplete: props.autoComplete,
665
- disabled: props.disabled,
666
- form: props.form,
667
- name: props.name,
668
- options: get_options().map((option) => {
669
- return {
670
- disabled: option.disabled,
671
- text: option.text,
672
- value: option.value,
673
- };
674
- }),
675
- placeholder: props.placeholder,
676
- required: props.required,
677
- value: get_value(),
678
- });
679
- }
680
-
681
- function open_from_keyboard(
682
- event: KeyboardEvent,
683
- highlight?: SelectOpenHighlight,
684
- ): void {
685
- request_open(true, {
686
- event,
687
- reason: selectOpenChangeReason.trigger,
688
- });
689
- apply_open_highlight(highlight, {
690
- event,
691
- reason: selectOpenChangeReason.trigger,
692
- });
693
- }
694
-
695
- function handle_keyboard(event: KeyboardEvent): void {
696
- if (handle.props.disabled) {
697
- return;
698
- }
699
-
700
- const keyboard_highlight_details: SelectHighlightChangeDetails = {
701
- event,
702
- reason: selectHighlightChangeReason.keyboard,
703
- };
704
-
705
- if (event.altKey && event.key === "ArrowDown") {
706
- event.preventDefault();
707
- open_from_keyboard(event, "selected");
708
- return;
709
- }
710
-
711
- if (event.altKey && event.key === "ArrowUp" && get_open()) {
712
- event.preventDefault();
713
- select_value(get_highlighted_value(), {
714
- event,
715
- reason: selectValueChangeReason.keyboard,
716
- });
717
- return;
718
- }
719
-
720
- if (event.key === "ArrowDown") {
721
- event.preventDefault();
722
- if (get_open()) {
723
- move_highlight(1, keyboard_highlight_details);
724
- return;
725
- }
726
- open_from_keyboard(event, "selected");
727
- return;
728
- }
729
-
730
- if (event.key === "ArrowUp") {
731
- event.preventDefault();
732
- if (get_open()) {
733
- move_highlight(-1, keyboard_highlight_details);
734
- return;
735
- }
736
- open_from_keyboard(event, "last");
737
- return;
738
- }
739
-
740
- if (event.key === "Home") {
741
- event.preventDefault();
742
- if (!get_open()) {
743
- open_from_keyboard(event, "first");
744
- return;
745
- }
746
- highlight_option(
747
- get_first_collection_item(get_enabled_options()),
748
- keyboard_highlight_details,
749
- );
750
- return;
751
- }
752
-
753
- if (event.key === "End") {
754
- event.preventDefault();
755
- const enabled_options = get_enabled_options();
756
- if (!get_open()) {
757
- open_from_keyboard(event, "last");
758
- return;
759
- }
760
- highlight_option(
761
- get_last_collection_item(enabled_options),
762
- keyboard_highlight_details,
763
- );
764
- return;
765
- }
766
-
767
- if (event.key === "PageDown" && get_open()) {
768
- event.preventDefault();
769
- move_highlight(select_page_jump_size, keyboard_highlight_details, true);
770
- return;
771
- }
772
-
773
- if (event.key === "PageUp" && get_open()) {
774
- event.preventDefault();
775
- move_highlight(-select_page_jump_size, keyboard_highlight_details, true);
776
- return;
777
- }
778
-
779
- if (is_select_keyboard_selection(event)) {
780
- event.preventDefault();
781
- if (!get_open()) {
782
- open_from_keyboard(event, "selected");
783
- return;
784
- }
785
- select_value(get_highlighted_value(), {
786
- event,
787
- reason: selectValueChangeReason.keyboard,
788
- });
789
- return;
790
- }
791
-
792
- if (event.key === "Escape" && get_open()) {
793
- event.preventDefault();
794
- request_open(false, {
795
- event,
796
- reason: selectOpenChangeReason.escape,
797
- });
798
- return;
799
- }
800
-
801
- if (event.key === "Tab" && get_open()) {
802
- const highlighted_value = get_highlighted_value();
803
- if (highlighted_value === null) {
804
- request_open(false, {
805
- event,
806
- reason: selectOpenChangeReason.tab,
807
- });
808
- return;
809
- }
810
- select_value(highlighted_value, {
811
- event,
812
- reason: selectValueChangeReason.keyboard,
813
- });
814
- return;
815
- }
816
-
817
- if (is_printable_key_event(event)) {
818
- event.preventDefault();
819
- if (!get_open()) {
820
- request_open(true, {
821
- event,
822
- reason: selectOpenChangeReason.trigger,
823
- });
824
- }
825
- search(event.key, event);
826
- }
827
- }
828
-
829
- const context: SelectRuntimeContext = {
830
- close: (details) => {
831
- request_open(false, details);
832
- },
833
- get_disabled: () => {
834
- return handle.props.disabled === true;
835
- },
836
- get_highlighted_id: () => {
837
- return find_option(get_highlighted_value())?.id;
838
- },
839
- get_highlighted_value,
840
- get_invalid: () => {
841
- return handle.props.invalid === true;
842
- },
843
- get_list_id: () => {
844
- return `${handle.id}-list`;
845
- },
846
- get_open,
847
- get_placeholder: () => {
848
- return handle.props.placeholder;
849
- },
850
- get_popup_relationship: () => {
851
- return popup_relationship;
852
- },
853
- get_popup_id: () => {
854
- return `${handle.id}-popup`;
855
- },
856
- get_read_only: () => {
857
- return handle.props.readOnly === true;
858
- },
859
- get_required: () => {
860
- return handle.props.required === true;
861
- },
862
- get_selected_text: () => {
863
- return find_option(get_value())?.text;
864
- },
865
- get_trigger_id: () => {
866
- return `${handle.id}-trigger`;
867
- },
868
- get_value,
869
- handle_keydown: handle_keyboard,
870
- highlight_by_offset: (offset, details) => {
871
- move_highlight(offset, details);
872
- },
873
- highlight_first: () => {
874
- highlight_option(get_first_collection_item(get_enabled_options()), {
875
- reason: selectHighlightChangeReason.keyboard,
876
- });
877
- },
878
- highlight_last: () => {
879
- highlight_option(get_last_collection_item(get_enabled_options()), {
880
- reason: selectHighlightChangeReason.keyboard,
881
- });
882
- },
883
- highlight_value: (value, details) => {
884
- highlight_option(find_option(value), details);
885
- },
886
- open: (details, highlight = "selected") => {
887
- if (handle.props.disabled) {
888
- return;
889
- }
890
- apply_open_highlight(highlight, details);
891
- request_open(true, details);
892
- },
893
- register_option: (option) => {
894
- const changed = option_collection.register(option);
895
- if (changed) {
896
- void handle.update();
897
- }
898
- },
899
- register_trigger: (node) => {
900
- popup_relationship.registerTrigger(node);
901
- },
902
- search,
903
- select_value,
904
- sync_popup: () => {
905
- popup_relationship.syncPopup(get_open());
906
- schedule_highlight_scroll();
907
- },
908
- toggle: (details) => {
909
- if (get_open()) {
910
- request_open(false, details);
911
- return;
912
- }
913
- context.open(details);
914
- },
915
- unregister_option: (id) => {
916
- const option = option_collection.unregister(id);
917
- void handle.update();
918
- if (option?.value === get_highlighted_value()) {
919
- set_highlighted_value(null, {
920
- reason: selectHighlightChangeReason.open,
921
- });
922
- }
923
- },
924
- };
925
- handle.context.set(context);
926
-
927
- return (props: SelectProps): RemixNode => {
928
- const current_open = get_open();
929
- if (current_open !== last_completed_open) {
930
- const completed_open = current_open;
931
- const completed_details = last_open_details;
932
- last_completed_open = current_open;
933
- handle.queueTask((signal) => {
934
- if (signal.aborted) {
935
- return;
936
- }
937
- props.onOpenChangeComplete?.(completed_open, completed_details);
938
- });
939
- }
940
-
941
- return createElement(
942
- Fragment,
943
- {},
944
- props.children,
945
- create_hidden_select(props),
946
- );
947
- };
948
- }
949
-
950
- function create_resolve_slot<TProps extends object>(
951
- slot: SelectRecipeSlot,
952
- selection: (props: Partial<TProps>) => SelectRecipeSelection<TRecipe>,
953
- ): (
954
- props: Partial<TProps>,
955
- ) => ReturnType<typeof select_recipe.resolve>["slots"][typeof slot] {
956
- return (props) => {
957
- return select_recipe.resolve(selection(props)).slots[slot];
958
- };
959
- }
960
-
961
- function Trigger(
962
- handle: Handle<SelectTriggerProps<TVariant, TSize, TBreakpoint>>,
963
- ): (props: SelectTriggerProps<TVariant, TSize, TBreakpoint>) => RemixNode {
964
- const context = handle.context.get(Root);
965
- const resolve_slot = create_resolve_slot<
966
- SelectTriggerStyleProps<TVariant, TSize>
967
- >(select_slot.trigger, (props) => {
968
- return {
969
- size: props.size,
970
- variant: props.variant,
971
- } satisfies SelectRecipeSelection<TRecipe>;
972
- });
973
-
974
- return (props: SelectTriggerProps<TVariant, TSize, TBreakpoint>): RemixNode => {
975
- const {
976
- at,
977
- children,
978
- mix,
979
- size,
980
- type = "button",
981
- variant,
982
- ...trigger_props
983
- } = props;
984
- const is_open = context.get_open();
985
- const has_value = context.get_value() !== null;
986
- const highlighted_id = context.get_highlighted_id();
987
- const parts = createComponentStyleTargets({
988
- at,
989
- hostElements: {
990
- [select_slot.trigger]: "button",
991
- },
992
- targets: {
993
- [select_slot.trigger]: {
994
- host: select_slot.trigger,
995
- conditions: mergeRecipeConditionSelectors(
996
- trigger_conditions,
997
- options.conditions?.[select_slot.trigger],
998
- ),
999
- resolveSlot: resolve_slot,
1000
- },
1001
- },
1002
- props: { size, variant },
1003
- styleSystem: style_system,
1004
- });
1005
- const trigger_click_mix = on<HTMLButtonElement, "click">("click", (event) => {
1006
- context.toggle({
1007
- event,
1008
- reason: selectOpenChangeReason.trigger,
1009
- });
1010
- });
1011
- const trigger_keydown_mix = on<HTMLButtonElement, "keydown">(
1012
- "keydown",
1013
- (event) => {
1014
- context.handle_keydown(event);
1015
- },
1016
- );
1017
- const trigger_ref_mix = ref<HTMLButtonElement>((node, signal) => {
1018
- context.register_trigger(node);
1019
- signal.addEventListener("abort", () => {
1020
- context.register_trigger(null);
1021
- });
1022
- });
1023
-
1024
- return createElement(
1025
- "button",
1026
- createComponentSlotProps({
1027
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.trigger),
1028
- mix: [
1029
- trigger_ref_mix,
1030
- trigger_click_mix,
1031
- trigger_keydown_mix,
1032
- parts.hosts[select_slot.trigger].mix,
1033
- ],
1034
- props: {
1035
- ...trigger_props,
1036
- "aria-activedescendant": is_open ? highlighted_id : undefined,
1037
- "aria-controls": context.get_list_id(),
1038
- "aria-disabled": ariaTrue(context.get_disabled()),
1039
- "aria-expanded": ariaBoolean(is_open),
1040
- "aria-haspopup": "listbox",
1041
- "aria-invalid": ariaTrue(context.get_invalid()),
1042
- "aria-readonly": ariaTrue(context.get_read_only()),
1043
- "aria-required": ariaTrue(context.get_required()),
1044
- [componentDataAttribute.disabled]: dataFlag(
1045
- context.get_disabled(),
1046
- ),
1047
- [componentDataAttribute.invalid]: dataFlag(context.get_invalid()),
1048
- [componentDataAttribute.open]: dataFlag(is_open),
1049
- [componentDataAttribute.placeholder]: dataFlag(!has_value),
1050
- [componentDataAttribute.readOnly]: dataFlag(
1051
- context.get_read_only(),
1052
- ),
1053
- [componentDataAttribute.required]: dataFlag(
1054
- context.get_required(),
1055
- ),
1056
- [componentStateAttribute]: openStateFromBoolean(is_open),
1057
- disabled: context.get_disabled(),
1058
- id: context.get_trigger_id(),
1059
- mix,
1060
- role: "combobox",
1061
- type,
1062
- },
1063
- }),
1064
- children,
1065
- );
1066
- };
1067
- }
1068
-
1069
- function Popup(
1070
- handle: Handle<SelectPopupProps<TPopupLayout, TBreakpoint>>,
1071
- ): (props: SelectPopupProps<TPopupLayout, TBreakpoint>) => RemixNode {
1072
- const context = handle.context.get(Root);
1073
- const resolve_slot = create_resolve_slot<SelectPopupStyleProps<TPopupLayout>>(
1074
- select_slot.popup,
1075
- (props) => {
1076
- return {
1077
- popupLayout: props.layout,
1078
- } satisfies SelectRecipeSelection<TRecipe>;
1079
- },
1080
- );
1081
-
1082
- return (props: SelectPopupProps<TPopupLayout, TBreakpoint>): RemixNode => {
1083
- const { at, children, layout, mix, ...popup_props } = props;
1084
- const is_open = context.get_open();
1085
- const parts = createComponentStyleTargets({
1086
- at,
1087
- targets: {
1088
- [select_slot.popup]: {
1089
- host: select_slot.popup,
1090
- conditions: mergeRecipeConditionSelectors(
1091
- open_conditions,
1092
- options.conditions?.[select_slot.popup],
1093
- ),
1094
- resolveSlot: resolve_slot,
1095
- },
1096
- },
1097
- props: { layout },
1098
- styleSystem: style_system,
1099
- });
1100
- const popup_ref_mix = createPopupRefMix({
1101
- getOpen: context.get_open,
1102
- onInteractOutside: (event) => {
1103
- context.close({
1104
- event,
1105
- reason: selectOpenChangeReason.interactOutside,
1106
- });
1107
- },
1108
- relationship: context.get_popup_relationship(),
1109
- });
1110
-
1111
- handle.queueTask((signal) => {
1112
- if (signal.aborted) {
1113
- return;
1114
- }
1115
- context.sync_popup();
1116
- });
1117
-
1118
- return createElement(
1119
- "div",
1120
- createComponentSlotProps({
1121
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.popup),
1122
- mix: [
1123
- popoverScrollLockGuard(),
1124
- popup_ref_mix,
1125
- parts.hosts[select_slot.popup].mix,
1126
- ],
1127
- props: {
1128
- ...popup_props,
1129
- "aria-labelledby": context.get_trigger_id(),
1130
- [componentDataAttribute.open]: dataFlag(is_open),
1131
- hidden: !is_open,
1132
- id: context.get_popup_id(),
1133
- mix,
1134
- popover: "manual",
1135
- },
1136
- }),
1137
- children,
1138
- );
1139
- };
1140
- }
1141
-
1142
- function List(
1143
- handle: Handle<SelectListProps<TPopupLayout, TBreakpoint>>,
1144
- ): (props: SelectListProps<TPopupLayout, TBreakpoint>) => RemixNode {
1145
- const context = handle.context.get(Root);
1146
- const resolve_slot = create_resolve_slot<SelectPopupStyleProps<TPopupLayout>>(
1147
- select_slot.list,
1148
- (props) => {
1149
- return {
1150
- popupLayout: props.layout,
1151
- } satisfies SelectRecipeSelection<TRecipe>;
1152
- },
1153
- );
1154
-
1155
- return (props: SelectPopupProps<TPopupLayout, TBreakpoint>): RemixNode => {
1156
- const { at, children, layout, mix, ...list_props } = props;
1157
- const parts = createComponentStyleTargets({
1158
- at,
1159
- targets: {
1160
- [select_slot.list]: {
1161
- host: select_slot.list,
1162
- conditions: mergeRecipeConditionSelectors(
1163
- open_conditions,
1164
- options.conditions?.[select_slot.list],
1165
- ),
1166
- resolveSlot: resolve_slot,
1167
- },
1168
- },
1169
- props: { layout },
1170
- styleSystem: style_system,
1171
- });
1172
- const list_keydown_mix = on<HTMLElement, "keydown">("keydown", (event) => {
1173
- context.handle_keydown(event);
1174
- });
1175
-
1176
- return createElement(
1177
- "div",
1178
- createComponentSlotProps({
1179
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.list),
1180
- mix: [list_keydown_mix, parts.hosts[select_slot.list].mix],
1181
- props: {
1182
- ...list_props,
1183
- "aria-labelledby": context.get_trigger_id(),
1184
- id: context.get_list_id(),
1185
- mix,
1186
- role: "listbox",
1187
- },
1188
- }),
1189
- children,
1190
- );
1191
- };
1192
- }
1193
-
1194
- function Value(
1195
- handle: Handle<SelectValueProps>,
1196
- ): (props: SelectValueProps) => RemixNode {
1197
- const context = handle.context.get(Root);
1198
-
1199
- return (props: SelectValueProps): RemixNode => {
1200
- const { children, mix, placeholder, ...value_props } = props;
1201
- const selected_text = context.get_selected_text();
1202
- const placeholder_text = placeholder ?? context.get_placeholder();
1203
- const is_placeholder = selected_text === undefined;
1204
- const parts = createComponentStyleTargets({
1205
- targets: {
1206
- [select_slot.value]: {
1207
- host: select_slot.value,
1208
- conditions: mergeRecipeConditionSelectors(
1209
- value_conditions,
1210
- options.conditions?.[select_slot.value],
1211
- ),
1212
- resolveSlot: () => {
1213
- return select_recipe.resolve().slots.value;
1214
- },
1215
- },
1216
- },
1217
- props: {},
1218
- styleSystem: style_system,
1219
- });
1220
-
1221
- return createElement(
1222
- "span",
1223
- createComponentSlotProps({
1224
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.value),
1225
- mix: parts.hosts[select_slot.value].mix,
1226
- props: {
1227
- ...value_props,
1228
- [componentDataAttribute.placeholder]: dataFlag(is_placeholder),
1229
- mix,
1230
- },
1231
- }),
1232
- children ?? selected_text ?? placeholder_text,
1233
- );
1234
- };
1235
- }
1236
-
1237
- function create_static_span_component<TProps extends { mix?: unknown }>(
1238
- slot: SelectRecipeSlot,
1239
- ): RemixComponent<TProps> {
1240
- return () => {
1241
- return (props: TProps): RemixNode => {
1242
- const { children, mix, ...rest_props } = props as TProps & {
1243
- children?: RemixNode;
1244
- mix?: unknown;
1245
- };
1246
- const parts = createComponentStyleTargets({
1247
- targets: {
1248
- [slot]: {
1249
- host: slot,
1250
- resolveSlot: () => {
1251
- return select_recipe.resolve().slots[slot];
1252
- },
1253
- },
1254
- },
1255
- props: {},
1256
- styleSystem: style_system,
1257
- });
1258
-
1259
- return createElement(
1260
- "span",
1261
- createComponentSlotProps({
1262
- attrs: createComponentAnatomyAttrs(select_scope, slot),
1263
- mix: parts.hosts[slot].mix,
1264
- props: {
1265
- ...rest_props,
1266
- mix,
1267
- },
1268
- }),
1269
- children,
1270
- );
1271
- };
1272
- };
1273
- }
1274
-
1275
- function create_static_div_component<TProps extends { mix?: unknown }>(
1276
- slot: SelectRecipeSlot,
1277
- role?: string,
1278
- ): RemixComponent<TProps> {
1279
- return () => {
1280
- return (props: TProps): RemixNode => {
1281
- const { children, mix, ...rest_props } = props as TProps & {
1282
- children?: RemixNode;
1283
- mix?: unknown;
1284
- };
1285
- const parts = createComponentStyleTargets({
1286
- targets: {
1287
- [slot]: {
1288
- host: slot,
1289
- resolveSlot: () => {
1290
- return select_recipe.resolve().slots[slot];
1291
- },
1292
- },
1293
- },
1294
- props: {},
1295
- styleSystem: style_system,
1296
- });
1297
-
1298
- return createElement(
1299
- "div",
1300
- createComponentSlotProps({
1301
- attrs: createComponentAnatomyAttrs(select_scope, slot),
1302
- mix: parts.hosts[slot].mix,
1303
- props: {
1304
- ...rest_props,
1305
- mix,
1306
- role,
1307
- },
1308
- }),
1309
- children,
1310
- );
1311
- };
1312
- };
1313
- }
1314
-
1315
- function Group(
1316
- handle: Handle<SelectGroupProps, SelectGroupRuntimeContext>,
1317
- ): (props: SelectGroupProps) => RemixNode {
1318
- const group_context = create_group_label_relationship({
1319
- label_id: `${handle.id}-label`,
1320
- on_change: () => {
1321
- void handle.update();
1322
- },
1323
- });
1324
- handle.context.set(group_context);
1325
-
1326
- return (props: SelectGroupProps): RemixNode => {
1327
- const { children, mix, ...group_props } = props;
1328
- const parts = createComponentStyleTargets({
1329
- targets: {
1330
- [select_slot.group]: {
1331
- host: select_slot.group,
1332
- resolveSlot: () => {
1333
- return select_recipe.resolve().slots.group;
1334
- },
1335
- },
1336
- },
1337
- props: {},
1338
- styleSystem: style_system,
1339
- });
1340
-
1341
- return createElement(
1342
- "div",
1343
- createComponentSlotProps({
1344
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.group),
1345
- mix: parts.hosts[select_slot.group].mix,
1346
- props: {
1347
- ...group_props,
1348
- "aria-labelledby": group_context.get_labelled_by(),
1349
- mix,
1350
- role: "group",
1351
- },
1352
- }),
1353
- children,
1354
- );
1355
- };
1356
- }
1357
-
1358
- function GroupLabel(
1359
- handle: Handle<SelectGroupLabelProps>,
1360
- ): (props: SelectGroupLabelProps) => RemixNode {
1361
- const group_context = handle.context.get(Group);
1362
-
1363
- return (props: SelectGroupLabelProps): RemixNode => {
1364
- const { children, mix, ...group_label_props } = props;
1365
- const parts = createComponentStyleTargets({
1366
- targets: {
1367
- [select_slot.groupLabel]: {
1368
- host: select_slot.groupLabel,
1369
- resolveSlot: () => {
1370
- return select_recipe.resolve().slots.groupLabel;
1371
- },
1372
- },
1373
- },
1374
- props: {},
1375
- styleSystem: style_system,
1376
- });
1377
- const group_label_ref_mix = create_group_label_ref_mix(group_context);
1378
-
1379
- return createElement(
1380
- "div",
1381
- createComponentSlotProps({
1382
- attrs: createComponentAnatomyAttrs(
1383
- select_scope,
1384
- select_slot.groupLabel,
1385
- ),
1386
- mix: [group_label_ref_mix, parts.hosts[select_slot.groupLabel].mix],
1387
- props: {
1388
- ...group_label_props,
1389
- id: group_context.get_label_id(),
1390
- mix,
1391
- },
1392
- }),
1393
- children,
1394
- );
1395
- };
1396
- }
1397
-
1398
- function Option(
1399
- handle: Handle<SelectOptionProps>,
1400
- ): (props: SelectOptionProps) => RemixNode {
1401
- const context = handle.context.get(Root);
1402
- let option_node: HTMLElement | null = null;
1403
- let option_id: string | null = null;
1404
-
1405
- return (props: SelectOptionProps): RemixNode => {
1406
- const {
1407
- children,
1408
- disabled = false,
1409
- mix,
1410
- textValue,
1411
- value,
1412
- ...option_props
1413
- } = props;
1414
- const selected = context.get_value() === value;
1415
- const highlighted = context.get_highlighted_value() === value;
1416
- const option_text = textValue ?? infer_text_value(children);
1417
- const current_option_id = `${handle.id}-option`;
1418
- option_id = current_option_id;
1419
- const parts = createComponentStyleTargets({
1420
- targets: {
1421
- [select_slot.option]: {
1422
- host: select_slot.option,
1423
- conditions: mergeRecipeConditionSelectors(
1424
- option_conditions,
1425
- options.conditions?.[select_slot.option],
1426
- ),
1427
- resolveSlot: () => {
1428
- return select_recipe.resolve().slots.option;
1429
- },
1430
- },
1431
- },
1432
- props: {},
1433
- styleSystem: style_system,
1434
- });
1435
- const option_click_mix = on<HTMLElement, "click">("click", (event) => {
1436
- if (disabled) {
1437
- return;
1438
- }
1439
- context.select_value(value, {
1440
- event,
1441
- reason: selectValueChangeReason.pointer,
1442
- });
1443
- });
1444
- const option_pointer_down_mix = on<HTMLElement, "pointerdown">(
1445
- "pointerdown",
1446
- (event) => {
1447
- if (disabled) {
1448
- return;
1449
- }
1450
- event.preventDefault();
1451
- },
1452
- );
1453
- const option_pointer_move_mix = on<HTMLElement, "pointermove">(
1454
- "pointermove",
1455
- (event) => {
1456
- if (disabled) {
1457
- return;
1458
- }
1459
- context.highlight_value(value, {
1460
- event,
1461
- reason: selectHighlightChangeReason.pointer,
1462
- });
1463
- },
1464
- );
1465
- function register_option(node: HTMLElement): void {
1466
- context.register_option({
1467
- disabled,
1468
- id: current_option_id,
1469
- node,
1470
- text: option_text || node.textContent || "",
1471
- value,
1472
- });
1473
- }
1474
-
1475
- if (option_node) {
1476
- handle.queueTask((signal) => {
1477
- if (signal.aborted || !option_node) {
1478
- return;
1479
- }
1480
- register_option(option_node);
1481
- });
1482
- }
1483
-
1484
- const option_ref_mix = ref<HTMLElement>((node, signal) => {
1485
- option_node = node;
1486
- register_option(node);
1487
- signal.addEventListener("abort", () => {
1488
- if (option_id) {
1489
- context.unregister_option(option_id);
1490
- }
1491
- option_node = null;
1492
- });
1493
- });
1494
-
1495
- return createElement(
1496
- "div",
1497
- createComponentSlotProps({
1498
- attrs: createComponentAnatomyAttrs(select_scope, select_slot.option),
1499
- mix: [
1500
- option_ref_mix,
1501
- option_pointer_down_mix,
1502
- option_click_mix,
1503
- option_pointer_move_mix,
1504
- parts.hosts[select_slot.option].mix,
1505
- ],
1506
- props: {
1507
- ...option_props,
1508
- "aria-disabled": ariaTrue(disabled),
1509
- "aria-selected": ariaBoolean(selected),
1510
- [componentDataAttribute.disabled]: dataFlag(disabled),
1511
- [componentDataAttribute.highlighted]: dataFlag(highlighted),
1512
- [componentDataAttribute.selected]: dataFlag(selected),
1513
- [componentStateAttribute]: select_option_state(selected),
1514
- [select_option_value_attribute]: value,
1515
- id: current_option_id,
1516
- mix,
1517
- role: "option",
1518
- },
1519
- }),
1520
- children ?? option_text,
1521
- );
1522
- };
1523
- }
1524
-
1525
- return {
1526
- Group,
1527
- GroupLabel,
1528
- Icon: create_static_span_component<SelectIconProps>(select_slot.icon),
1529
- List,
1530
- Option,
1531
- OptionIndicator: create_static_span_component<SelectOptionIndicatorProps>(
1532
- select_slot.optionIndicator,
1533
- ),
1534
- OptionText: create_static_span_component<SelectOptionTextProps>(
1535
- select_slot.optionText,
1536
- ),
1537
- Popup,
1538
- Root,
1539
- Separator: create_static_div_component<SelectSeparatorProps>(
1540
- select_slot.separator,
1541
- "separator",
1542
- ),
1543
- Trigger,
1544
- Value,
1545
- };
1546
- }