gbs-add-block 1.2.15 → 1.2.16

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 (129) hide show
  1. package/README.md +2 -2
  2. package/index.cjs +214 -21
  3. package/package.json +1 -1
  4. package/source/beta-components/breadcrumb/react/props.ts +2 -2
  5. package/source/beta-components/breadcrumb/styles.css +8 -8
  6. package/source/beta-components/button/react/props.ts +2 -2
  7. package/source/beta-components/button/styles.css +15 -15
  8. package/source/beta-components/checkbox/react/Checkbox.tsx +147 -146
  9. package/source/beta-components/checkbox/react/CheckboxGroup.tsx +8 -8
  10. package/source/beta-components/checkbox/react/props.ts +2 -2
  11. package/source/beta-components/checkbox/styles.css +11 -11
  12. package/source/beta-components/combobox/README.md +3 -3
  13. package/source/beta-components/combobox/react/MultiSelect.tsx +265 -263
  14. package/source/beta-components/combobox/react/Popover.tsx +38 -70
  15. package/source/beta-components/combobox/react/Select.tsx +200 -198
  16. package/source/beta-components/combobox/react/icons.tsx +6 -29
  17. package/source/beta-components/combobox/react/props.ts +4 -2
  18. package/source/beta-components/combobox/styles.css +18 -18
  19. package/source/beta-components/data-grid/react/Popover.tsx +35 -0
  20. package/source/beta-components/{datagrid → data-grid}/react/context.ts +2 -2
  21. package/source/beta-components/{datagrid → data-grid}/react/icons.tsx +8 -35
  22. package/source/beta-components/{datagrid → data-grid}/styles.css +24 -24
  23. package/source/beta-components/{datepicker → date-picker}/README.md +3 -3
  24. package/source/beta-components/{datepicker → date-picker}/react/DatePicker.tsx +249 -249
  25. package/source/beta-components/{datepicker → date-picker}/react/DateRangePicker.tsx +291 -291
  26. package/source/beta-components/date-picker/react/Popover.tsx +45 -0
  27. package/source/beta-components/date-picker/react/icons.tsx +15 -0
  28. package/source/beta-components/{datepicker → date-picker}/react/props.ts +2 -2
  29. package/source/beta-components/{datepicker → date-picker}/styles.css +18 -18
  30. package/source/beta-components/dialog/react/icons.tsx +16 -47
  31. package/source/beta-components/dialog/react/props.ts +2 -2
  32. package/source/beta-components/dialog/styles.css +21 -21
  33. package/source/beta-components/{fileuploader → file-uploader}/react/FileUploader.tsx +511 -515
  34. package/source/beta-components/{fileuploader → file-uploader}/react/icons.tsx +7 -27
  35. package/source/beta-components/{fileuploader → file-uploader}/react/props.ts +2 -2
  36. package/source/beta-components/{fileuploader → file-uploader}/styles.css +17 -17
  37. package/source/beta-components/input/core/count.ts +3 -12
  38. package/source/beta-components/input/react/Input.tsx +219 -219
  39. package/source/beta-components/input/react/OtpInput.tsx +256 -256
  40. package/source/beta-components/input/react/icons.tsx +3 -21
  41. package/source/beta-components/input/react/props.ts +2 -2
  42. package/source/beta-components/input/styles.css +15 -15
  43. package/source/beta-components/modal/react/Modal.tsx +242 -238
  44. package/source/beta-components/modal/react/icons.tsx +1 -19
  45. package/source/beta-components/modal/react/props.ts +2 -2
  46. package/source/beta-components/modal/styles.css +17 -13
  47. package/source/beta-components/shared/README.md +54 -0
  48. package/source/beta-components/shared/__tests__/boundaries.test.ts +95 -0
  49. package/source/beta-components/shared/__tests__/core.test.ts +55 -0
  50. package/source/beta-components/shared/core/cx.ts +9 -0
  51. package/source/beta-components/shared/core/field.ts +35 -0
  52. package/source/beta-components/shared/core/text.ts +14 -0
  53. package/source/beta-components/shared/index.ts +7 -0
  54. package/source/beta-components/shared/react/Popover.tsx +147 -0
  55. package/source/beta-components/shared/react/icons.tsx +74 -0
  56. package/source/beta-components/shared/react/useControllableState.ts +42 -0
  57. package/source/beta-components/shared/version.json +4 -0
  58. package/source/beta-components/shared/version.ts +6 -0
  59. package/source/beta-components/spinner/react/props.ts +2 -2
  60. package/source/beta-components/spinner/styles.css +5 -5
  61. package/source/beta-components/tabs/react/Tabs.tsx +258 -260
  62. package/source/beta-components/tabs/styles.css +15 -15
  63. package/source/beta-components/textarea/core/count.ts +3 -12
  64. package/source/beta-components/textarea/react/Textarea.tsx +208 -209
  65. package/source/beta-components/textarea/react/props.ts +2 -2
  66. package/source/beta-components/textarea/styles.css +12 -12
  67. package/source/beta-components/toaster/react/icons.tsx +19 -52
  68. package/source/beta-components/toaster/react/props.ts +2 -2
  69. package/source/beta-components/toaster/styles.css +18 -18
  70. package/source/beta-components/datagrid/react/Popover.tsx +0 -79
  71. package/source/beta-components/datepicker/react/Popover.tsx +0 -87
  72. package/source/beta-components/datepicker/react/icons.tsx +0 -32
  73. /package/source/beta-components/{datagrid → data-grid}/README.md +0 -0
  74. /package/source/beta-components/{datagrid → data-grid}/__tests__/core.test.ts +0 -0
  75. /package/source/beta-components/{datagrid → data-grid}/__tests__/export.test.ts +0 -0
  76. /package/source/beta-components/{datagrid → data-grid}/core/columnHelper.ts +0 -0
  77. /package/source/beta-components/{datagrid → data-grid}/core/columns.ts +0 -0
  78. /package/source/beta-components/{datagrid → data-grid}/core/filtering.ts +0 -0
  79. /package/source/beta-components/{datagrid → data-grid}/core/grid.ts +0 -0
  80. /package/source/beta-components/{datagrid → data-grid}/core/index.ts +0 -0
  81. /package/source/beta-components/{datagrid → data-grid}/core/rows.ts +0 -0
  82. /package/source/beta-components/{datagrid → data-grid}/core/sorting.ts +0 -0
  83. /package/source/beta-components/{datagrid → data-grid}/core/state.ts +0 -0
  84. /package/source/beta-components/{datagrid → data-grid}/core/store.ts +0 -0
  85. /package/source/beta-components/{datagrid → data-grid}/core/types.ts +0 -0
  86. /package/source/beta-components/{datagrid → data-grid}/core/values.ts +0 -0
  87. /package/source/beta-components/{datagrid → data-grid}/core/virtual.ts +0 -0
  88. /package/source/beta-components/{datagrid → data-grid}/export/csv.ts +0 -0
  89. /package/source/beta-components/{datagrid → data-grid}/export/download.ts +0 -0
  90. /package/source/beta-components/{datagrid → data-grid}/export/pdf.ts +0 -0
  91. /package/source/beta-components/{datagrid → data-grid}/export/table.ts +0 -0
  92. /package/source/beta-components/{datagrid → data-grid}/export/xlsx.ts +0 -0
  93. /package/source/beta-components/{datagrid → data-grid}/export/zip.ts +0 -0
  94. /package/source/beta-components/{datagrid → data-grid}/index.ts +0 -0
  95. /package/source/beta-components/{datagrid → data-grid}/react/Cell.tsx +0 -0
  96. /package/source/beta-components/{datagrid → data-grid}/react/ColumnMenu.tsx +0 -0
  97. /package/source/beta-components/{datagrid → data-grid}/react/DataGrid.tsx +0 -0
  98. /package/source/beta-components/{datagrid → data-grid}/react/FilterForm.tsx +0 -0
  99. /package/source/beta-components/{datagrid → data-grid}/react/HeaderRow.tsx +0 -0
  100. /package/source/beta-components/{datagrid → data-grid}/react/Pagination.tsx +0 -0
  101. /package/source/beta-components/{datagrid → data-grid}/react/Row.tsx +0 -0
  102. /package/source/beta-components/{datagrid → data-grid}/react/Toolbar.tsx +0 -0
  103. /package/source/beta-components/{datagrid → data-grid}/react/Viewport.tsx +0 -0
  104. /package/source/beta-components/{datagrid → data-grid}/react/hooks.ts +0 -0
  105. /package/source/beta-components/{datagrid → data-grid}/react/keyboard.ts +0 -0
  106. /package/source/beta-components/{datagrid → data-grid}/react/locale.ts +0 -0
  107. /package/source/beta-components/{datepicker → date-picker}/__tests__/core.test.ts +0 -0
  108. /package/source/beta-components/{datepicker → date-picker}/core/calendar.ts +0 -0
  109. /package/source/beta-components/{datepicker → date-picker}/core/format.ts +0 -0
  110. /package/source/beta-components/{datepicker → date-picker}/core/index.ts +0 -0
  111. /package/source/beta-components/{datepicker → date-picker}/core/types.ts +0 -0
  112. /package/source/beta-components/{datepicker → date-picker}/index.ts +0 -0
  113. /package/source/beta-components/{datepicker → date-picker}/react/Calendar.tsx +0 -0
  114. /package/source/beta-components/{datepicker → date-picker}/react/CalendarPanel.tsx +0 -0
  115. /package/source/beta-components/{datepicker → date-picker}/react/MonthYearPanels.tsx +0 -0
  116. /package/source/beta-components/{datepicker → date-picker}/react/locale.ts +0 -0
  117. /package/source/beta-components/{datepicker → date-picker}/react/useDatePicker.ts +0 -0
  118. /package/source/beta-components/{fileuploader → file-uploader}/README.md +0 -0
  119. /package/source/beta-components/{fileuploader → file-uploader}/__tests__/core.test.ts +0 -0
  120. /package/source/beta-components/{fileuploader → file-uploader}/core/format.ts +0 -0
  121. /package/source/beta-components/{fileuploader → file-uploader}/core/index.ts +0 -0
  122. /package/source/beta-components/{fileuploader → file-uploader}/core/store.ts +0 -0
  123. /package/source/beta-components/{fileuploader → file-uploader}/core/transport.ts +0 -0
  124. /package/source/beta-components/{fileuploader → file-uploader}/core/types.ts +0 -0
  125. /package/source/beta-components/{fileuploader → file-uploader}/core/validate.ts +0 -0
  126. /package/source/beta-components/{fileuploader → file-uploader}/index.ts +0 -0
  127. /package/source/beta-components/{fileuploader → file-uploader}/react/FileRow.tsx +0 -0
  128. /package/source/beta-components/{fileuploader → file-uploader}/react/locale.ts +0 -0
  129. /package/source/beta-components/{fileuploader → file-uploader}/react/useFileUploader.ts +0 -0
@@ -1,198 +1,200 @@
1
- "use client";
2
-
3
- import { useId, useImperativeHandle, useMemo, useRef, type Ref } from "react";
4
- import type { ComboboxOption, OptionValue } from "../core/types";
5
- import { defaultComboboxText } from "./locale";
6
- import { ChevronIcon, XIcon } from "./icons";
7
- import { Listbox } from "./Listbox";
8
- import { ComboboxPopover } from "./Popover";
9
- import { cx, type ComboboxHandle, type ComboboxSharedProps } from "./props";
10
- import { useCombobox } from "./useCombobox";
11
-
12
- export interface SelectProps<V extends OptionValue = string> extends ComboboxSharedProps<V> {
13
- value?: V | null;
14
- defaultValue?: V | null;
15
- onChange?(value: NoInfer<V> | null, option: ComboboxOption<NoInfer<V>> | null): void;
16
- /** Keep the list open after choosing. Default false. */
17
- closeOnSelect?: boolean;
18
- ref?: Ref<ComboboxHandle<V>>;
19
- }
20
-
21
- /** Single-select combobox: a value picker with option search. */
22
- export function Select<V extends OptionValue = string>(props: SelectProps<V>) {
23
- const {
24
- ref,
25
- value,
26
- defaultValue,
27
- onChange,
28
- closeOnSelect = true,
29
- options,
30
- label,
31
- placeholder = "Select…",
32
- description,
33
- error,
34
- required = false,
35
- disabled = false,
36
- clearable = true,
37
- size = "md",
38
- name,
39
- id: idProp,
40
- className,
41
- classNames,
42
- style,
43
- localeText,
44
- maxHeight = 280,
45
- virtualize = true,
46
- emptyMessage,
47
- searchable = true,
48
- loading = false,
49
- hasMore,
50
- onLoadMore,
51
- renderOption,
52
- } = props;
53
-
54
- const reactId = useId();
55
- const id = idProp ?? reactId;
56
- const text = useMemo(() => ({ ...defaultComboboxText, ...localeText }), [localeText]);
57
-
58
- const controlRef = useRef<HTMLDivElement>(null);
59
- const searchRef = useRef<HTMLInputElement>(null);
60
- const listRef = useRef<HTMLDivElement>(null);
61
-
62
- const combobox = useCombobox<V>({
63
- controlRef,
64
- searchRef,
65
- listRef,
66
- ...props,
67
- multiple: false,
68
- closeOnSelect,
69
- value: value === undefined ? undefined : value === null ? [] : [value],
70
- defaultValue: defaultValue === null || defaultValue === undefined ? [] : [defaultValue],
71
- onValuesChange: (values, selectedOptions) =>
72
- onChange?.(values[0] ?? null, selectedOptions[0] ?? null),
73
- text,
74
- id,
75
- });
76
-
77
- const selected = combobox.selectedOptions[0];
78
-
79
- useImperativeHandle(ref, () => combobox.handle, [combobox.handle]);
80
-
81
- return (
82
- <div
83
- className={cx("cb-root", classNames?.root, className)}
84
- style={style}
85
- data-size={size}
86
- data-state={combobox.open ? "open" : "closed"}
87
- data-invalid={error ? "" : undefined}
88
- >
89
- {label && (
90
- <span id={`${id}-label`} className={cx("cb-label", classNames?.label)} data-required={required || undefined}>
91
- {label}
92
- </span>
93
- )}
94
-
95
- <div
96
- ref={controlRef}
97
- id={id}
98
- role="combobox"
99
- tabIndex={disabled ? -1 : 0}
100
- aria-expanded={combobox.open}
101
- aria-haspopup="listbox"
102
- aria-controls={combobox.open ? combobox.listboxId : undefined}
103
- aria-labelledby={label ? `${id}-label` : undefined}
104
- aria-describedby={cx(description ? `${id}-description` : "", error ? `${id}-error` : "") || undefined}
105
- aria-required={required || undefined}
106
- aria-invalid={error ? true : undefined}
107
- aria-disabled={disabled || undefined}
108
- className={cx("cb-control", classNames?.control)}
109
- data-disabled={disabled || undefined}
110
- data-invalid={error ? "" : undefined}
111
- onPointerDown={combobox.onControlPointerDown}
112
- onClick={combobox.onControlClick}
113
- onKeyDown={combobox.onKeyDown}
114
- >
115
- <span className={cx("cb-value", classNames?.value)}>
116
- {selected ? (
117
- <>
118
- {selected.icon && <span className="cb-value-icon">{selected.icon}</span>}
119
- {selected.label}
120
- </>
121
- ) : (
122
- <span className="cb-placeholder">{placeholder}</span>
123
- )}
124
- </span>
125
-
126
- <span className="cb-actions">
127
- {clearable && selected && !disabled && (
128
- <button
129
- type="button"
130
- tabIndex={-1}
131
- className="cb-icon-button"
132
- aria-label={text.clear}
133
- onClick={(event) => {
134
- event.stopPropagation();
135
- combobox.clear();
136
- }}
137
- >
138
- <XIcon width={14} height={14} />
139
- </button>
140
- )}
141
- <ChevronIcon className="cb-chevron" />
142
- </span>
143
- </div>
144
-
145
- {name && <input type="hidden" name={name} value={selected?.value ?? ""} />}
146
- {description && (
147
- <span id={`${id}-description`} className="cb-description">
148
- {description}
149
- </span>
150
- )}
151
- {error && (
152
- <span id={`${id}-error`} className="cb-error" role="alert">
153
- {error}
154
- </span>
155
- )}
156
-
157
- {combobox.anchor && (
158
- <ComboboxPopover
159
- anchor={combobox.anchor}
160
- onClose={combobox.onPopoverClose}
161
- className={classNames?.popover}
162
- >
163
- <Listbox<V>
164
- listboxId={combobox.listboxId}
165
- labelledBy={label ? `${id}-label` : id}
166
- items={combobox.items}
167
- optionCount={combobox.entries.length}
168
- activeIndex={combobox.activeIndex}
169
- multiple={false}
170
- size={size}
171
- maxHeight={maxHeight}
172
- virtualize={virtualize}
173
- loading={loading}
174
- hasMore={hasMore}
175
- searchable={searchable}
176
- search={combobox.search}
177
- searchRef={searchRef}
178
- listRef={listRef}
179
- text={text}
180
- classNames={classNames}
181
- emptyMessage={emptyMessage}
182
- createLabel={combobox.createLabel}
183
- getOptionId={combobox.getOptionId}
184
- isSelected={combobox.isSelected}
185
- onSelect={combobox.selectOption}
186
- onActiveIndexChange={combobox.setActiveIndex}
187
- onSearchChange={combobox.setSearch}
188
- onKeyDown={combobox.onKeyDown}
189
- onLoadMore={onLoadMore}
190
- onCreate={combobox.commitCreate}
191
- renderOption={renderOption}
192
- />
193
- </ComboboxPopover>
194
- )}
195
- {options.length === 0 && loading && <span className="cb-sr-only">{text.loading}</span>}
196
- </div>
197
- );
198
- }
1
+ "use client";
2
+
3
+ import { useId, useImperativeHandle, useMemo, useRef, type Ref } from "react";
4
+ import { describeField } from "../../shared/core/field";
5
+ import type { ComboboxOption, OptionValue } from "../core/types";
6
+ import { defaultComboboxText } from "./locale";
7
+ import { ChevronIcon, XIcon } from "./icons";
8
+ import { Listbox } from "./Listbox";
9
+ import { ComboboxPopover } from "./Popover";
10
+ import { cx, type ComboboxHandle, type ComboboxSharedProps } from "./props";
11
+ import { useCombobox } from "./useCombobox";
12
+
13
+ export interface SelectProps<V extends OptionValue = string> extends ComboboxSharedProps<V> {
14
+ value?: V | null;
15
+ defaultValue?: V | null;
16
+ onChange?(value: NoInfer<V> | null, option: ComboboxOption<NoInfer<V>> | null): void;
17
+ /** Keep the list open after choosing. Default false. */
18
+ closeOnSelect?: boolean;
19
+ ref?: Ref<ComboboxHandle<V>>;
20
+ }
21
+
22
+ /** Single-select combobox: a value picker with option search. */
23
+ export function Select<V extends OptionValue = string>(props: SelectProps<V>) {
24
+ const {
25
+ ref,
26
+ value,
27
+ defaultValue,
28
+ onChange,
29
+ closeOnSelect = true,
30
+ options,
31
+ label,
32
+ placeholder = "Select…",
33
+ description,
34
+ error,
35
+ required = false,
36
+ disabled = false,
37
+ clearable = true,
38
+ size = "md",
39
+ name,
40
+ id: idProp,
41
+ className,
42
+ classNames,
43
+ style,
44
+ localeText,
45
+ maxHeight = 280,
46
+ virtualize = true,
47
+ emptyMessage,
48
+ searchable = true,
49
+ loading = false,
50
+ hasMore,
51
+ onLoadMore,
52
+ renderOption,
53
+ } = props;
54
+
55
+ const reactId = useId();
56
+ const id = idProp ?? reactId;
57
+ const text = useMemo(() => ({ ...defaultComboboxText, ...localeText }), [localeText]);
58
+
59
+ const controlRef = useRef<HTMLDivElement>(null);
60
+ const searchRef = useRef<HTMLInputElement>(null);
61
+ const listRef = useRef<HTMLDivElement>(null);
62
+
63
+ const combobox = useCombobox<V>({
64
+ controlRef,
65
+ searchRef,
66
+ listRef,
67
+ ...props,
68
+ multiple: false,
69
+ closeOnSelect,
70
+ value: value === undefined ? undefined : value === null ? [] : [value],
71
+ defaultValue: defaultValue === null || defaultValue === undefined ? [] : [defaultValue],
72
+ onValuesChange: (values, selectedOptions) =>
73
+ onChange?.(values[0] ?? null, selectedOptions[0] ?? null),
74
+ text,
75
+ id,
76
+ });
77
+
78
+ const selected = combobox.selectedOptions[0];
79
+
80
+ useImperativeHandle(ref, () => combobox.handle, [combobox.handle]);
81
+
82
+ return (
83
+ <div
84
+ className={cx("cb-root", classNames?.root, className)}
85
+ style={style}
86
+ data-size={size}
87
+ data-state={combobox.open ? "open" : "closed"}
88
+ data-invalid={error ? "" : undefined}
89
+ >
90
+ {label && (
91
+ <span id={`${id}-label`} className={cx("cb-label", classNames?.label)} data-required={required || undefined}>
92
+ {label}
93
+ </span>
94
+ )}
95
+
96
+ <div
97
+ ref={controlRef}
98
+ id={id}
99
+ role="combobox"
100
+ tabIndex={disabled ? -1 : 0}
101
+ aria-expanded={combobox.open}
102
+ aria-haspopup="listbox"
103
+ aria-controls={combobox.open ? combobox.listboxId : undefined}
104
+ aria-labelledby={label ? `${id}-label` : undefined}
105
+ aria-label={label ? undefined : props["aria-label"]}
106
+ aria-describedby={describeField(id, { description, error })}
107
+ aria-required={required || undefined}
108
+ aria-invalid={error ? true : undefined}
109
+ aria-disabled={disabled || undefined}
110
+ className={cx("cb-control", classNames?.control)}
111
+ data-disabled={disabled || undefined}
112
+ data-invalid={error ? "" : undefined}
113
+ onPointerDown={combobox.onControlPointerDown}
114
+ onClick={combobox.onControlClick}
115
+ onKeyDown={combobox.onKeyDown}
116
+ >
117
+ <span className={cx("cb-value", classNames?.value)}>
118
+ {selected ? (
119
+ <>
120
+ {selected.icon && <span className="cb-value-icon">{selected.icon}</span>}
121
+ {selected.label}
122
+ </>
123
+ ) : (
124
+ <span className="cb-placeholder">{placeholder}</span>
125
+ )}
126
+ </span>
127
+
128
+ <span className="cb-actions">
129
+ {clearable && selected && !disabled && (
130
+ <button
131
+ type="button"
132
+ tabIndex={-1}
133
+ className="cb-icon-button"
134
+ aria-label={text.clear}
135
+ onClick={(event) => {
136
+ event.stopPropagation();
137
+ combobox.clear();
138
+ }}
139
+ >
140
+ <XIcon width={14} height={14} />
141
+ </button>
142
+ )}
143
+ <ChevronIcon className="cb-chevron" />
144
+ </span>
145
+ </div>
146
+
147
+ {name && <input type="hidden" name={name} value={selected?.value ?? ""} />}
148
+ {description && (
149
+ <span id={`${id}-description`} className="cb-description">
150
+ {description}
151
+ </span>
152
+ )}
153
+ {error && (
154
+ <span id={`${id}-error`} className="cb-error" role="alert">
155
+ {error}
156
+ </span>
157
+ )}
158
+
159
+ {combobox.anchor && (
160
+ <ComboboxPopover
161
+ anchor={combobox.anchor}
162
+ onClose={combobox.onPopoverClose}
163
+ className={classNames?.popover}
164
+ >
165
+ <Listbox<V>
166
+ listboxId={combobox.listboxId}
167
+ labelledBy={label ? `${id}-label` : id}
168
+ items={combobox.items}
169
+ optionCount={combobox.entries.length}
170
+ activeIndex={combobox.activeIndex}
171
+ multiple={false}
172
+ size={size}
173
+ maxHeight={maxHeight}
174
+ virtualize={virtualize}
175
+ loading={loading}
176
+ hasMore={hasMore}
177
+ searchable={searchable}
178
+ search={combobox.search}
179
+ searchRef={searchRef}
180
+ listRef={listRef}
181
+ text={text}
182
+ classNames={classNames}
183
+ emptyMessage={emptyMessage}
184
+ createLabel={combobox.createLabel}
185
+ getOptionId={combobox.getOptionId}
186
+ isSelected={combobox.isSelected}
187
+ onSelect={combobox.selectOption}
188
+ onActiveIndexChange={combobox.setActiveIndex}
189
+ onSearchChange={combobox.setSearch}
190
+ onKeyDown={combobox.onKeyDown}
191
+ onLoadMore={onLoadMore}
192
+ onCreate={combobox.commitCreate}
193
+ renderOption={renderOption}
194
+ />
195
+ </ComboboxPopover>
196
+ )}
197
+ {options.length === 0 && loading && <span className="cb-sr-only">{text.loading}</span>}
198
+ </div>
199
+ );
200
+ }
@@ -1,34 +1,11 @@
1
- import type { SVGProps } from "react";
1
+ import { SpinnerIcon as SharedSpinner, Svg, type IconProps } from "../../shared/react/icons";
2
+ import { cx } from "./props";
2
3
 
3
- type IconProps = SVGProps<SVGSVGElement>;
4
+ export { CheckIcon, ChevronDownIcon as ChevronIcon, SearchIcon, XIcon } from "../../shared/react/icons";
4
5
 
5
- function Svg(props: IconProps) {
6
- return (
7
- <svg
8
- width="16"
9
- height="16"
10
- viewBox="0 0 24 24"
11
- fill="none"
12
- stroke="currentColor"
13
- strokeWidth="2"
14
- strokeLinecap="round"
15
- strokeLinejoin="round"
16
- aria-hidden="true"
17
- focusable="false"
18
- {...props}
19
- />
20
- );
21
- }
22
-
23
- export const SearchIcon = (p: IconProps) => (
24
- <Svg {...p}><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></Svg>
25
- );
26
- export const CheckIcon = (p: IconProps) => <Svg {...p}><path d="M20 6 9 17l-5-5" /></Svg>;
27
- export const ChevronIcon = (p: IconProps) => <Svg {...p}><path d="m6 9 6 6 6-6" /></Svg>;
28
- export const XIcon = (p: IconProps) => <Svg {...p}><path d="M18 6 6 18M6 6l12 12" /></Svg>;
29
6
  export const PlusIcon = (p: IconProps) => <Svg {...p}><path d="M12 5v14M5 12h14" /></Svg>;
7
+
8
+ /** The class is what spins it; the arc itself is shared. */
30
9
  export const SpinnerIcon = (p: IconProps) => (
31
- <Svg {...p} className={["cb-spinner", p.className].filter(Boolean).join(" ")}>
32
- <path d="M12 3a9 9 0 1 0 9 9" />
33
- </Svg>
10
+ <SharedSpinner {...p} className={cx("cb-spinner", p.className)} />
34
11
  );
@@ -80,7 +80,9 @@ export interface ComboboxSharedProps<V extends OptionValue = string> {
80
80
  style?: CSSProperties;
81
81
  localeText?: Partial<ComboboxLocaleText>;
82
82
  onOpenChange?(open: boolean): void;
83
+ /** Names the control when there is no visible `label`. */
84
+ "aria-label"?: string;
83
85
  }
84
86
 
85
- export const cx = (...names: (string | false | null | undefined)[]) =>
86
- names.filter(Boolean).join(" ");
87
+ // Shared with every other component, so class handling cannot drift.
88
+ export { cx } from "../../shared/core/cx";
@@ -1,30 +1,30 @@
1
1
  /*
2
2
  * Combobox styles (Select and MultiSelect).
3
3
  *
4
- * Tokens default to the DataGrid's --dg-* variables when that stylesheet is
5
- * present, so both components share one theme, and fall back to the same
6
- * palette when used on their own. Rules live in the `components` layer, so
4
+ * Tokens read the shared --gbs-* variables when they are set on an ancestor
5
+ * (set them on :root to theme every component at once) and fall back to the
6
+ * same default palette otherwise. Rules live in the `components` layer, so
7
7
  * utility classes passed through `className` / `classNames` override them.
8
8
  */
9
9
  @layer theme, base, components, utilities;
10
10
 
11
11
  @layer components {
12
12
  .cb-root {
13
- --cb-bg: var(--dg-bg, light-dark(#ffffff, #0b0b0e));
14
- --cb-fg: var(--dg-fg, light-dark(#18181b, #f4f4f5));
15
- --cb-muted: var(--dg-muted, light-dark(#71717a, #a1a1aa));
16
- --cb-border: var(--dg-border, light-dark(#e4e4e7, #27272a));
17
- --cb-hover: var(--dg-hover, light-dark(#f4f4f5, #1f1f23));
18
- --cb-input-bg: var(--dg-input-bg, light-dark(#ffffff, #121216));
19
- --cb-accent: var(--dg-accent, light-dark(#2563eb, #60a5fa));
20
- --cb-accent-fg: var(--dg-accent-fg, light-dark(#ffffff, #0b1220));
21
- --cb-accent-soft: var(--dg-accent-soft, light-dark(#eff6ff, #172554));
22
- --cb-accent-strong: var(--dg-accent-strong, light-dark(#1d4ed8, #bfdbfe));
23
- --cb-focus: var(--dg-focus, light-dark(#2563eb, #60a5fa));
24
- --cb-danger: var(--dg-danger, light-dark(#dc2626, #f87171));
25
- --cb-shadow: var(--dg-shadow, 0 10px 30px -8px light-dark(rgb(0 0 0 / 0.18), rgb(0 0 0 / 0.6)));
26
- --cb-radius: var(--dg-radius, 8px);
27
- --cb-font-size: var(--dg-font-size, 13px);
13
+ --cb-bg: var(--gbs-bg, var(--dg-bg, light-dark(#ffffff, #0b0b0e)));
14
+ --cb-fg: var(--gbs-fg, var(--dg-fg, light-dark(#18181b, #f4f4f5)));
15
+ --cb-muted: var(--gbs-muted, var(--dg-muted, light-dark(#71717a, #a1a1aa)));
16
+ --cb-border: var(--gbs-border, var(--dg-border, light-dark(#e4e4e7, #27272a)));
17
+ --cb-hover: var(--gbs-hover, var(--dg-hover, light-dark(#f4f4f5, #1f1f23)));
18
+ --cb-input-bg: var(--gbs-input-bg, var(--dg-input-bg, light-dark(#ffffff, #121216)));
19
+ --cb-accent: var(--gbs-accent, var(--dg-accent, light-dark(#2563eb, #60a5fa)));
20
+ --cb-accent-fg: var(--gbs-accent-fg, var(--dg-accent-fg, light-dark(#ffffff, #0b1220)));
21
+ --cb-accent-soft: var(--gbs-accent-soft, var(--dg-accent-soft, light-dark(#eff6ff, #172554)));
22
+ --cb-accent-strong: var(--gbs-accent-strong, var(--dg-accent-strong, light-dark(#1d4ed8, #bfdbfe)));
23
+ --cb-focus: var(--gbs-focus, var(--dg-focus, light-dark(#2563eb, #60a5fa)));
24
+ --cb-danger: var(--gbs-danger, var(--dg-danger, light-dark(#dc2626, #f87171)));
25
+ --cb-shadow: var(--gbs-shadow, var(--dg-shadow, 0 10px 30px -8px light-dark(rgb(0 0 0 / 0.18), rgb(0 0 0 / 0.6))));
26
+ --cb-radius: var(--gbs-radius, var(--dg-radius, 8px));
27
+ --cb-font-size: var(--gbs-font-size, var(--dg-font-size, 13px));
28
28
  --cb-height: 36px;
29
29
  --cb-px: 10px;
30
30
 
@@ -0,0 +1,35 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { AnchoredPopover } from "../../shared/react/Popover";
5
+ import { cx } from "./context";
6
+
7
+ interface PopoverProps {
8
+ anchor: HTMLElement;
9
+ onClose(): void;
10
+ label: string;
11
+ align?: "start" | "end";
12
+ className?: string;
13
+ children: ReactNode;
14
+ }
15
+
16
+ /**
17
+ * The grid's column and toolbar menus. Focus returns to the header button or
18
+ * toolbar control that opened it, so keyboard work carries on where it left off
19
+ * instead of restarting at the top of the page.
20
+ */
21
+ export function Popover({ anchor, onClose, label, align = "start", className, children }: PopoverProps) {
22
+ return (
23
+ <AnchoredPopover
24
+ anchor={anchor}
25
+ onClose={onClose}
26
+ label={label}
27
+ align={align}
28
+ className={cx("dg-popover", className)}
29
+ autoFocus="[data-autofocus], button, input, select, textarea"
30
+ returnFocus
31
+ >
32
+ {children}
33
+ </AnchoredPopover>
34
+ );
35
+ }
@@ -53,5 +53,5 @@ export function useGridContext<T>(): GridContextValue<T> {
53
53
  /** Marks a cell without an in-flight async edit. */
54
54
  export const NO_PENDING: unique symbol = Symbol("dg.noPending");
55
55
 
56
- export const cx = (...names: (string | false | null | undefined)[]) =>
57
- names.filter(Boolean).join(" ");
56
+ // Shared with every other component, so class handling cannot drift.
57
+ export { cx } from "../../shared/core/cx";
@@ -1,28 +1,13 @@
1
- import type { SVGProps } from "react";
1
+ import { Svg, type IconProps } from "../../shared/react/icons";
2
2
 
3
- type IconProps = SVGProps<SVGSVGElement>;
3
+ export {
4
+ CheckIcon,
5
+ ChevronLeftIcon,
6
+ ChevronRightIcon,
7
+ SearchIcon,
8
+ XIcon,
9
+ } from "../../shared/react/icons";
4
10
 
5
- function Svg(props: IconProps) {
6
- return (
7
- <svg
8
- width="16"
9
- height="16"
10
- viewBox="0 0 24 24"
11
- fill="none"
12
- stroke="currentColor"
13
- strokeWidth="2"
14
- strokeLinecap="round"
15
- strokeLinejoin="round"
16
- aria-hidden="true"
17
- focusable="false"
18
- {...props}
19
- />
20
- );
21
- }
22
-
23
- export const SearchIcon = (p: IconProps) => (
24
- <Svg {...p}><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></Svg>
25
- );
26
11
  export const ArrowUpIcon = (p: IconProps) => (
27
12
  <Svg {...p}><path d="M12 19V5M5 12l7-7 7 7" /></Svg>
28
13
  );
@@ -32,21 +17,12 @@ export const ArrowDownIcon = (p: IconProps) => (
32
17
  export const MoreIcon = (p: IconProps) => (
33
18
  <Svg {...p}><path d="M12 5h.01M12 12h.01M12 19h.01" strokeWidth="3" /></Svg>
34
19
  );
35
- export const ChevronLeftIcon = (p: IconProps) => (
36
- <Svg {...p}><path d="m15 18-6-6 6-6" /></Svg>
37
- );
38
- export const ChevronRightIcon = (p: IconProps) => (
39
- <Svg {...p}><path d="m9 18 6-6-6-6" /></Svg>
40
- );
41
20
  export const ChevronsLeftIcon = (p: IconProps) => (
42
21
  <Svg {...p}><path d="m11 17-5-5 5-5M18 17l-5-5 5-5" /></Svg>
43
22
  );
44
23
  export const ChevronsRightIcon = (p: IconProps) => (
45
24
  <Svg {...p}><path d="m13 17 5-5-5-5M6 17l5-5-5-5" /></Svg>
46
25
  );
47
- export const XIcon = (p: IconProps) => (
48
- <Svg {...p}><path d="M18 6 6 18M6 6l12 12" /></Svg>
49
- );
50
26
  export const ColumnsIcon = (p: IconProps) => (
51
27
  <Svg {...p}><rect x="3" y="4" width="18" height="16" rx="2" /><path d="M9 4v16M15 4v16" /></Svg>
52
28
  );
@@ -67,6 +43,3 @@ export const EyeOffIcon = (p: IconProps) => (
67
43
  export const MoveIcon = (p: IconProps) => (
68
44
  <Svg {...p}><path d="M5 12h14M9 8l-4 4 4 4M15 8l4 4-4 4" /></Svg>
69
45
  );
70
- export const CheckIcon = (p: IconProps) => (
71
- <Svg {...p}><path d="M20 6 9 17l-5-5" /></Svg>
72
- );