@mindlogic-ai/logician-ui 5.0.0-alpha.14 → 5.0.0-alpha.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 (37) hide show
  1. package/dist/components/Avatar/Avatar.d.ts.map +1 -1
  2. package/dist/components/Avatar/Avatar.js +4 -1
  3. package/dist/components/Avatar/Avatar.js.map +1 -1
  4. package/dist/components/Avatar/Avatar.mjs +4 -1
  5. package/dist/components/Avatar/Avatar.mjs.map +1 -1
  6. package/dist/components/Select/Combobox.d.ts +1 -1
  7. package/dist/components/Select/Combobox.d.ts.map +1 -1
  8. package/dist/components/Select/Combobox.js +9 -2
  9. package/dist/components/Select/Combobox.js.map +1 -1
  10. package/dist/components/Select/Combobox.mjs +10 -3
  11. package/dist/components/Select/Combobox.mjs.map +1 -1
  12. package/dist/components/Select/Select.d.ts +1 -1
  13. package/dist/components/Select/Select.d.ts.map +1 -1
  14. package/dist/components/Select/Select.js +14 -2
  15. package/dist/components/Select/Select.js.map +1 -1
  16. package/dist/components/Select/Select.mjs +15 -3
  17. package/dist/components/Select/Select.mjs.map +1 -1
  18. package/dist/components/Select/Select.styles.d.ts +96 -0
  19. package/dist/components/Select/Select.styles.d.ts.map +1 -1
  20. package/dist/components/Select/Select.styles.js +75 -0
  21. package/dist/components/Select/Select.styles.js.map +1 -1
  22. package/dist/components/Select/Select.styles.mjs +74 -1
  23. package/dist/components/Select/Select.styles.mjs.map +1 -1
  24. package/dist/translations/Defaults.translations.json.js +10 -1
  25. package/dist/translations/Defaults.translations.json.js.map +1 -1
  26. package/dist/translations/Defaults.translations.json.mjs +10 -2
  27. package/dist/translations/Defaults.translations.json.mjs.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/Avatar/Avatar.recipe.test.tsx +5 -0
  30. package/src/components/Avatar/Avatar.tsx +8 -2
  31. package/src/components/Select/Combobox.tsx +28 -3
  32. package/src/components/Select/Select.clearTrigger.test.tsx +76 -0
  33. package/src/components/Select/Select.stories.tsx +81 -0
  34. package/src/components/Select/Select.styles.ts +81 -0
  35. package/src/components/Select/Select.tsx +28 -2
  36. package/src/components/Select/Select.usage.mdx +63 -0
  37. package/src/translations/Defaults.translations.json +7 -0
@@ -35,9 +35,26 @@ const fieldBase = {
35
35
  fontWeight: 'semibold',
36
36
  },
37
37
  };
38
+ /**
39
+ * Room for the × and its divider at the end of the value, while the × shows.
40
+ *
41
+ * The indicator group overlays the value. Chakra keeps the value off the
42
+ * chevron with `maxW: 80%` on a Select (too little in a narrow filter) and a
43
+ * 1.5× height padding on a Combobox, and neither counts the × with its
44
+ * divider. This reserves the whole group instead: padding on both sides, the
45
+ * × (`8`) with its `me` (`1`), the group `gap` (`1`) and the chevron (`1em`),
46
+ * so a long value ends in an ellipsis before the ×.
47
+ */
48
+ const clearTriggerRoom = (paddingVar) => ({
49
+ '[data-part=control]:has([data-part=clear-trigger]:not([hidden])) > &': {
50
+ pe: `calc(var(${paddingVar}) * 2 + 3.5em)`,
51
+ '& [data-part=value-text]': { maxW: 'none' },
52
+ },
53
+ });
38
54
  /** Select trigger — the clickable control. */
39
55
  const triggerStyles = {
40
56
  ...fieldBase,
57
+ ...clearTriggerRoom('--select-trigger-padding-x'),
41
58
  cursor: 'pointer',
42
59
  _open: {
43
60
  borderColor: 'primary.main',
@@ -76,6 +93,7 @@ const ghostTriggerStyles = {
76
93
  /** Combobox text input — the typeable control. */
77
94
  const inputStyles = {
78
95
  ...fieldBase,
96
+ ...clearTriggerRoom('--combobox-input-padding-x'),
79
97
  };
80
98
  /** Dropdown panel for both Select and Combobox. */
81
99
  const contentStyles = {
@@ -112,6 +130,61 @@ const itemStyles = {
112
130
  const indicatorStyles = {
113
131
  color: 'fg.muted',
114
132
  };
133
+ /**
134
+ * Clear (×) button for both Select and Combobox.
135
+ *
136
+ * Chakra's recipe gives this part a colour and no size, and its default icon
137
+ * is `1em`, so a bare `ClearTrigger` drew a 21px × next to a 14px chevron. The
138
+ * × is the secondary action, so it is smaller (`3.5`, 12px at 14px text) and
139
+ * lighter (`fg.icon`) than the chevron, on an `8` box. `& svg` rather than
140
+ * `_icon` so the size also wins over an icon passed with its own `boxSize`.
141
+ *
142
+ * On a touch screen the hit area widens to 44px, toward the value only: the
143
+ * chevron sits to the right and has to keep opening the list. The height stays
144
+ * the control's own height (`heightVar`), so the area never reaches the field
145
+ * above or below. Every control height is over the 24px KWCAG minimum.
146
+ */
147
+ const clearTriggerStyles = (heightVar) => ({
148
+ position: 'relative',
149
+ display: 'inline-flex',
150
+ alignItems: 'center',
151
+ justifyContent: 'center',
152
+ boxSize: '8',
153
+ me: '1',
154
+ color: 'fg.icon',
155
+ cursor: 'pointer',
156
+ _hover: { color: 'fg.default' },
157
+ // The divider marks where opening starts: it sits in the `me` gap, takes no
158
+ // clicks, and everything right of it belongs to the chevron.
159
+ _after: {
160
+ content: '""',
161
+ position: 'absolute',
162
+ insetEnd: '-1',
163
+ top: '50%',
164
+ transform: 'translateY(-50%)',
165
+ h: '4',
166
+ borderEndWidth: '1px',
167
+ borderColor: 'border.subtle',
168
+ pointerEvents: 'none',
169
+ },
170
+ // zag hides the × with the `hidden` attribute while nothing is selected,
171
+ // and the `display` above would otherwise override it.
172
+ _hidden: { display: 'none' },
173
+ '& svg': { width: '3.5', height: '3.5' },
174
+ '@media (pointer: coarse)': {
175
+ '&::before': {
176
+ content: '""',
177
+ position: 'absolute',
178
+ insetEnd: 0,
179
+ top: '50%',
180
+ transform: 'translateY(-50%)',
181
+ width: '44px',
182
+ height: `var(${heightVar})`,
183
+ },
184
+ },
185
+ });
186
+ const selectClearTriggerStyles = clearTriggerStyles('--select-trigger-height');
187
+ const comboboxClearTriggerStyles = clearTriggerStyles('--combobox-input-height');
115
188
 
116
- export { contentStyles, ghostContentStyles, ghostTriggerStyles, indicatorStyles, inputStyles, itemStyles, triggerStyles };
189
+ export { comboboxClearTriggerStyles, contentStyles, ghostContentStyles, ghostTriggerStyles, indicatorStyles, inputStyles, itemStyles, selectClearTriggerStyles, triggerStyles };
117
190
  //# sourceMappingURL=Select.styles.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Select.styles.mjs","sources":["../../../src/components/Select/Select.styles.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAAA;;;;;;;;AAQG;AAIH,MAAM,SAAS,GAAG;AAChB,IAAA,eAAe,EAAE,mCAAmC;AACpD,IAAA,eAAe,EAAE,mCAAmC;CACrD;AAED,MAAM,SAAS,GAAG;AAChB,IAAA,GAAG,SAAS;AACZ,IAAA,EAAE,EAAE,YAAY;AAChB,IAAA,KAAK,EAAE,YAAY;AACnB,IAAA,WAAW,EAAE,gBAAgB;AAC7B,IAAA,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE;AAClC,IAAA,MAAM,EAAE;AACN,QAAA,WAAW,EAAE,cAAc;AAC3B,QAAA,SAAS,EAAE,6CAA6C;AACzD,KAAA;AACD,IAAA,QAAQ,EAAE;AACR,QAAA,WAAW,EAAE,cAAc;AAC3B,QAAA,MAAM,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;AACvC,QAAA,MAAM,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;AACxC,KAAA;AACD,IAAA,SAAS,EAAE;AACT,QAAA,GAAG,eAAe;AAClB,QAAA,MAAM,EAAE,aAAa;AACrB,QAAA,UAAU,EAAE,UAAU;AACvB,KAAA;CACF;AAED;AACO,MAAM,aAAa,GAAG;AAC3B,IAAA,GAAG,SAAS;AACZ,IAAA,MAAM,EAAE,SAAS;AACjB,IAAA,KAAK,EAAE;AACL,QAAA,WAAW,EAAE,cAAc;AAC3B,QAAA,SAAS,EAAE,6CAA6C;AACzD,KAAA;;AAGH;;;;;;;AAOG;AACI,MAAM,kBAAkB,GAAG;AAChC,IAAA,EAAE,EAAE,aAAa;AACjB,IAAA,WAAW,EAAE,aAAa;;;;;AAK1B,IAAA,EAAE,EAAE,kDAAkD;IACtD,0BAA0B,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE;AAClE,IAAA,MAAM,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE;AACzC,IAAA,SAAS,EAAE;AACT,QAAA,GAAG,YAAY;;;;;AAKf,QAAA,KAAK,EAAE,aAAa;AACpB,QAAA,MAAM,EAAE,aAAa;AACrB,QAAA,UAAU,EAAE,UAAU;AACvB,KAAA;;AAGH;AACO,MAAM,WAAW,GAAG;AACzB,IAAA,GAAG,SAAS;;AAGd;AACO,MAAM,aAAa,GAAG;AAC3B,IAAA,EAAE,EAAE,YAAY;AAChB,IAAA,WAAW,EAAE,gBAAgB;AAC7B,IAAA,WAAW,EAAE,KAAK;AAClB,IAAA,YAAY,EAAE,KAAK;AACnB,IAAA,SAAS,EAAE,sCAAsC;AACjD,IAAA,OAAO,EAAE,KAAK;;AAGhB;;;;AAIG;AACI,MAAM,kBAAkB,GAAG;AAChC,IAAA,KAAK,EAAE,aAAa;AACpB,IAAA,IAAI,EAAE,wBAAwB;;AAGhC;AACO,MAAM,UAAU,GAAG;AACxB,IAAA,YAAY,EAAE,KAAK;AACnB,IAAA,MAAM,EAAE,SAAS;AACjB,IAAA,KAAK,EAAE,YAAY;AACnB,IAAA,YAAY,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE;AACpC,IAAA,QAAQ,EAAE;AACR,QAAA,EAAE,EAAE,kBAAkB;AACtB,QAAA,KAAK,EAAE,YAAY;AACnB,QAAA,UAAU,EAAE,MAAM;AACnB,KAAA;IACD,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE;;AAG1D;AACO,MAAM,eAAe,GAAG;AAC7B,IAAA,KAAK,EAAE,UAAU;;;;;"}
1
+ {"version":3,"file":"Select.styles.mjs","sources":["../../../src/components/Select/Select.styles.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -241,6 +241,13 @@ var close = {
241
241
  ja: "閉じる",
242
242
  es: "Cerrar"
243
243
  };
244
+ var clear_selection = {
245
+ ko: "선택 지우기",
246
+ en: "Clear selection",
247
+ zh: "清除选择",
248
+ ja: "選択をクリア",
249
+ es: "Borrar selección"
250
+ };
244
251
  var defaultTranslations = {
245
252
  get_started: get_started,
246
253
  next: next,
@@ -282,11 +289,13 @@ var defaultTranslations = {
282
289
  downloading: downloading,
283
290
  processing: processing,
284
291
  deleting: deleting,
285
- close: close
292
+ close: close,
293
+ clear_selection: clear_selection
286
294
  };
287
295
 
288
296
  exports.anonymous = anonymous;
289
297
  exports.clear = clear;
298
+ exports.clear_selection = clear_selection;
290
299
  exports.close = close;
291
300
  exports.component = component;
292
301
  exports.copied = copied;
@@ -1 +1 @@
1
- {"version":3,"file":"Defaults.translations.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Defaults.translations.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -237,6 +237,13 @@ var close = {
237
237
  ja: "閉じる",
238
238
  es: "Cerrar"
239
239
  };
240
+ var clear_selection = {
241
+ ko: "선택 지우기",
242
+ en: "Clear selection",
243
+ zh: "清除选择",
244
+ ja: "選択をクリア",
245
+ es: "Borrar selección"
246
+ };
240
247
  var defaultTranslations = {
241
248
  get_started: get_started,
242
249
  next: next,
@@ -278,8 +285,9 @@ var defaultTranslations = {
278
285
  downloading: downloading,
279
286
  processing: processing,
280
287
  deleting: deleting,
281
- close: close
288
+ close: close,
289
+ clear_selection: clear_selection
282
290
  };
283
291
 
284
- export { anonymous, clear, close, component, copied, copy, copy_error_details, defaultTranslations as default, deleting, download, downloading, error_boundary_default_message, error_boundary_subtitle, error_boundary_title, error_details_copied, error_id, get_started, go_next_page_button, month_range_placeholder, next, page, pagination_items_per_page, pagination_range_text, previous, processing, refresh_page, see_less, see_more, support_help_text, support_information, time, try_again, unknown, upload_your_file, user_id };
292
+ export { anonymous, clear, clear_selection, close, component, copied, copy, copy_error_details, defaultTranslations as default, deleting, download, downloading, error_boundary_default_message, error_boundary_subtitle, error_boundary_title, error_details_copied, error_id, get_started, go_next_page_button, month_range_placeholder, next, page, pagination_items_per_page, pagination_range_text, previous, processing, refresh_page, see_less, see_more, support_help_text, support_information, time, try_again, unknown, upload_your_file, user_id };
285
293
  //# sourceMappingURL=Defaults.translations.json.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Defaults.translations.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Defaults.translations.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindlogic-ai/logician-ui",
3
- "version": "5.0.0-alpha.14",
3
+ "version": "5.0.0-alpha.16",
4
4
  "description": "A comprehensive React design system built on Chakra UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -133,6 +133,11 @@ describe('Avatar.Group max', () => {
133
133
  expect(roots).toHaveLength(4);
134
134
  const counter = roots[3];
135
135
  expect(counter.textContent).toBe('+2');
136
+ // Phrasing content, so `<Avatar.Group as="span">` can sit in a button.
137
+ expect(counter.tagName).toBe('SPAN');
138
+ expect(counter.querySelector('[data-part="fallback"]')?.tagName).toBe(
139
+ 'SPAN'
140
+ );
136
141
  expect(declarations(counter)).toContain(
137
142
  '--chakra-colors-color-palette-surface:var(--chakra-colors-category-neutral-surface)'
138
143
  );
@@ -158,8 +158,14 @@ const AvatarGroup = forwardRef<HTMLDivElement, AvatarGroupProps>(
158
158
  <ChakraAvatarGroupTyped ref={ref} {...rest}>
159
159
  {visible}
160
160
  {overflow > 0 && (
161
- <AvatarRoot colorPalette="category.neutral" variant="subtle">
162
- <AvatarFallback>+{overflow}</AvatarFallback>
161
+ // Spans so a group inside a button (a popover trigger) stays phrasing
162
+ // content; the recipe sets the display, so nothing else changes.
163
+ <AvatarRoot
164
+ as="span"
165
+ colorPalette="category.neutral"
166
+ variant="subtle"
167
+ >
168
+ <AvatarFallback as="span">+{overflow}</AvatarFallback>
163
169
  </AvatarRoot>
164
170
  )}
165
171
  </ChakraAvatarGroupTyped>
@@ -1,10 +1,17 @@
1
1
  import { forwardRef } from 'react';
2
2
  import { Combobox as ChakraCombobox } from '@chakra-ui/react';
3
3
 
4
+ import { useTranslate } from '@/hooks/useTranslate';
4
5
  import { noZoomOnFocus } from '@/theme/formControls';
5
6
  import { mergeCss } from '@/utils/mergeCss';
6
7
 
7
- import { contentStyles, inputStyles, itemStyles } from './Select.styles';
8
+ import { CloseIcon } from '../Icon';
9
+ import {
10
+ comboboxClearTriggerStyles,
11
+ contentStyles,
12
+ inputStyles,
13
+ itemStyles,
14
+ } from './Select.styles';
8
15
 
9
16
  const Input = forwardRef<HTMLInputElement, ChakraCombobox.InputProps>(
10
17
  function ComboboxInput({ css, ...props }, ref) {
@@ -47,10 +54,28 @@ const Item = forwardRef<HTMLDivElement, ChakraCombobox.ItemProps>(
47
54
  }
48
55
  );
49
56
 
57
+ /** Same × as `Select.ClearTrigger`; see there. */
58
+ const ClearTrigger = forwardRef<
59
+ HTMLButtonElement,
60
+ ChakraCombobox.ClearTriggerProps
61
+ >(function ComboboxClearTrigger({ css, children, ...props }, ref) {
62
+ const translate = useTranslate();
63
+ return (
64
+ <ChakraCombobox.ClearTrigger
65
+ ref={ref}
66
+ aria-label={translate('clear_selection') as string}
67
+ {...props}
68
+ css={mergeCss(comboboxClearTriggerStyles, css)}
69
+ >
70
+ {children ?? <CloseIcon aria-hidden />}
71
+ </ChakraCombobox.ClearTrigger>
72
+ );
73
+ });
74
+
50
75
  /**
51
76
  * Compound Combobox primitives — Chakra's `Combobox` namespace with the
52
77
  * Logician design-system styling baked into the visual parts (`Input`,
53
- * `Content`, `Item`). Use these directly for full compositional control;
78
+ * `Content`, `Item`, `ClearTrigger`). Use these directly for full compositional control;
54
79
  * use `ComboboxField` for the common single-select case.
55
80
  */
56
81
  export const Combobox = {
@@ -62,7 +87,7 @@ export const Combobox = {
62
87
  Input,
63
88
  Trigger: ChakraCombobox.Trigger,
64
89
  IndicatorGroup: ChakraCombobox.IndicatorGroup,
65
- ClearTrigger: ChakraCombobox.ClearTrigger,
90
+ ClearTrigger,
66
91
  Positioner: ChakraCombobox.Positioner,
67
92
  Content,
68
93
  Empty: ChakraCombobox.Empty,
@@ -0,0 +1,76 @@
1
+ import { ChakraProvider, createListCollection } from '@chakra-ui/react';
2
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+
5
+ import { system } from '../../theme';
6
+ import { Select } from './Select';
7
+
8
+ const collection = createListCollection({
9
+ items: [
10
+ { label: 'React', value: 'react' },
11
+ { label: 'Vue', value: 'vue' },
12
+ ],
13
+ });
14
+
15
+ const renderClearable = (
16
+ clearTrigger: React.ReactNode,
17
+ onValueChange = vi.fn(),
18
+ defaultValue = ['react']
19
+ ) =>
20
+ render(
21
+ <ChakraProvider value={system}>
22
+ <Select.Root
23
+ collection={collection}
24
+ defaultValue={defaultValue}
25
+ onValueChange={onValueChange}
26
+ >
27
+ <Select.HiddenSelect />
28
+ <Select.Control>
29
+ <Select.Trigger>
30
+ <Select.ValueText />
31
+ </Select.Trigger>
32
+ <Select.IndicatorGroup>
33
+ {clearTrigger}
34
+ <Select.Indicator />
35
+ </Select.IndicatorGroup>
36
+ </Select.Control>
37
+ </Select.Root>
38
+ </ChakraProvider>
39
+ );
40
+
41
+ describe('Select.ClearTrigger', () => {
42
+ it('names itself in the library language instead of "Clear value"', () => {
43
+ renderClearable(<Select.ClearTrigger />);
44
+
45
+ const button = screen.getByRole('button', { name: '선택 지우기' });
46
+ expect(button.querySelector('svg')).not.toBeNull();
47
+ expect(screen.queryByRole('button', { name: 'Clear value' })).toBeNull();
48
+ });
49
+
50
+ it("keeps the caller's aria-label and still clears the value", async () => {
51
+ const onValueChange = vi.fn();
52
+ renderClearable(
53
+ <Select.ClearTrigger aria-label="조직 선택 지우기" />,
54
+ onValueChange
55
+ );
56
+
57
+ fireEvent.click(screen.getByRole('button', { name: '조직 선택 지우기' }));
58
+
59
+ await waitFor(() =>
60
+ expect(onValueChange).toHaveBeenCalledWith(
61
+ expect.objectContaining({ value: [] })
62
+ )
63
+ );
64
+ });
65
+
66
+ it('stays hidden while nothing is selected', () => {
67
+ renderClearable(<Select.ClearTrigger />, vi.fn(), []);
68
+
69
+ const button = document.querySelector<HTMLElement>(
70
+ '[data-part="clear-trigger"]'
71
+ );
72
+ expect(button).not.toBeNull();
73
+ expect(button).toHaveAttribute('hidden');
74
+ expect(getComputedStyle(button!).display).toBe('none');
75
+ });
76
+ });
@@ -22,6 +22,7 @@ import {
22
22
  contentStyles,
23
23
  indicatorStyles,
24
24
  itemStyles,
25
+ selectClearTriggerStyles,
25
26
  triggerStyles,
26
27
  } from './Select.styles';
27
28
  import { SelectField } from './SelectField';
@@ -198,6 +199,76 @@ export const MultiSelect = () => (
198
199
  </Box>
199
200
  );
200
201
 
202
+ /**
203
+ * An optional field the member can empty again. `ClearTrigger` goes before
204
+ * the chevron and shows only while something is selected. It draws its own
205
+ * icon and is named 「선택 지우기」 in the provider's language; pass
206
+ * `aria-label` to say what is cleared. A divider separates it from the
207
+ * chevron, and a long value ends in an ellipsis before it. On a touch screen
208
+ * its hit area widens to 44px toward the value, at the field's own height.
209
+ */
210
+ const longValueCollection = createListCollection({
211
+ items: [
212
+ {
213
+ label: 'Engineering / Platform / Developer Experience',
214
+ value: 'dx',
215
+ },
216
+ { label: 'Design', value: 'design' },
217
+ ],
218
+ });
219
+
220
+ export const Clearable = () => (
221
+ <Stack maxW="320px" gap={4}>
222
+ <Box maxW="180px">
223
+ <Select.Root collection={longValueCollection} defaultValue={['dx']}>
224
+ <Select.HiddenSelect />
225
+ <Select.Label>Team (180px)</Select.Label>
226
+ <Select.Control>
227
+ <Select.Trigger>
228
+ <Select.ValueText placeholder="Select a team" />
229
+ </Select.Trigger>
230
+ <Select.IndicatorGroup>
231
+ <Select.ClearTrigger />
232
+ <Select.Indicator />
233
+ </Select.IndicatorGroup>
234
+ </Select.Control>
235
+ </Select.Root>
236
+ </Box>
237
+ {(['xs', 'sm', 'md', 'lg'] as const).map((size) => (
238
+ <Select.Root
239
+ key={size}
240
+ size={size}
241
+ collection={frameworkCollection}
242
+ defaultValue={['react']}
243
+ >
244
+ <Select.HiddenSelect />
245
+ <Select.Label>{`Framework (${size})`}</Select.Label>
246
+ <Select.Control>
247
+ <Select.Trigger>
248
+ <Select.ValueText placeholder="Select a framework" />
249
+ </Select.Trigger>
250
+ <Select.IndicatorGroup>
251
+ <Select.ClearTrigger />
252
+ <Select.Indicator />
253
+ </Select.IndicatorGroup>
254
+ </Select.Control>
255
+ <Portal>
256
+ <Select.Positioner>
257
+ <Select.Content>
258
+ {frameworkCollection.items.map((item) => (
259
+ <Select.Item item={item} key={item.value}>
260
+ <Select.ItemText>{item.label}</Select.ItemText>
261
+ <Select.ItemIndicator />
262
+ </Select.Item>
263
+ ))}
264
+ </Select.Content>
265
+ </Select.Positioner>
266
+ </Portal>
267
+ </Select.Root>
268
+ ))}
269
+ </Stack>
270
+ );
271
+
201
272
  /* -------------------------------------------------------------------------
202
273
  * Custom children
203
274
  *
@@ -950,6 +1021,16 @@ export const UsageTokens: UsageStory = {
950
1021
  token: indicatorStyles.color,
951
1022
  use: <T en="Chevron" ko="펼침 화살표" />,
952
1023
  },
1024
+ {
1025
+ token: selectClearTriggerStyles.color,
1026
+ use: <T en="Clear (×) button" ko="선택 지우기(×) 버튼" />,
1027
+ },
1028
+ {
1029
+ token: selectClearTriggerStyles._hover.color,
1030
+ use: (
1031
+ <T en="Clear button on hover" ko="마우스를 올렸을 때 지우기 버튼" />
1032
+ ),
1033
+ },
953
1034
  {
954
1035
  token: contentStyles.bg,
955
1036
  use: <T en="List background" ko="목록 배경" />,
@@ -37,9 +37,27 @@ const fieldBase = {
37
37
  },
38
38
  };
39
39
 
40
+ /**
41
+ * Room for the × and its divider at the end of the value, while the × shows.
42
+ *
43
+ * The indicator group overlays the value. Chakra keeps the value off the
44
+ * chevron with `maxW: 80%` on a Select (too little in a narrow filter) and a
45
+ * 1.5× height padding on a Combobox, and neither counts the × with its
46
+ * divider. This reserves the whole group instead: padding on both sides, the
47
+ * × (`8`) with its `me` (`1`), the group `gap` (`1`) and the chevron (`1em`),
48
+ * so a long value ends in an ellipsis before the ×.
49
+ */
50
+ const clearTriggerRoom = (paddingVar: string) => ({
51
+ '[data-part=control]:has([data-part=clear-trigger]:not([hidden])) > &': {
52
+ pe: `calc(var(${paddingVar}) * 2 + 3.5em)`,
53
+ '& [data-part=value-text]': { maxW: 'none' },
54
+ },
55
+ });
56
+
40
57
  /** Select trigger — the clickable control. */
41
58
  export const triggerStyles = {
42
59
  ...fieldBase,
60
+ ...clearTriggerRoom('--select-trigger-padding-x'),
43
61
  cursor: 'pointer',
44
62
  _open: {
45
63
  borderColor: 'primary.main',
@@ -80,6 +98,7 @@ export const ghostTriggerStyles = {
80
98
  /** Combobox text input — the typeable control. */
81
99
  export const inputStyles = {
82
100
  ...fieldBase,
101
+ ...clearTriggerRoom('--combobox-input-padding-x'),
83
102
  };
84
103
 
85
104
  /** Dropdown panel for both Select and Combobox. */
@@ -120,3 +139,65 @@ export const itemStyles = {
120
139
  export const indicatorStyles = {
121
140
  color: 'fg.muted',
122
141
  };
142
+
143
+ /**
144
+ * Clear (×) button for both Select and Combobox.
145
+ *
146
+ * Chakra's recipe gives this part a colour and no size, and its default icon
147
+ * is `1em`, so a bare `ClearTrigger` drew a 21px × next to a 14px chevron. The
148
+ * × is the secondary action, so it is smaller (`3.5`, 12px at 14px text) and
149
+ * lighter (`fg.icon`) than the chevron, on an `8` box. `& svg` rather than
150
+ * `_icon` so the size also wins over an icon passed with its own `boxSize`.
151
+ *
152
+ * On a touch screen the hit area widens to 44px, toward the value only: the
153
+ * chevron sits to the right and has to keep opening the list. The height stays
154
+ * the control's own height (`heightVar`), so the area never reaches the field
155
+ * above or below. Every control height is over the 24px KWCAG minimum.
156
+ */
157
+ const clearTriggerStyles = (heightVar: string) => ({
158
+ position: 'relative',
159
+ display: 'inline-flex',
160
+ alignItems: 'center',
161
+ justifyContent: 'center',
162
+ boxSize: '8',
163
+ me: '1',
164
+ color: 'fg.icon',
165
+ cursor: 'pointer',
166
+ _hover: { color: 'fg.default' },
167
+ // The divider marks where opening starts: it sits in the `me` gap, takes no
168
+ // clicks, and everything right of it belongs to the chevron.
169
+ _after: {
170
+ content: '""',
171
+ position: 'absolute',
172
+ insetEnd: '-1',
173
+ top: '50%',
174
+ transform: 'translateY(-50%)',
175
+ h: '4',
176
+ borderEndWidth: '1px',
177
+ borderColor: 'border.subtle',
178
+ pointerEvents: 'none',
179
+ },
180
+ // zag hides the × with the `hidden` attribute while nothing is selected,
181
+ // and the `display` above would otherwise override it.
182
+ _hidden: { display: 'none' },
183
+ '& svg': { width: '3.5', height: '3.5' },
184
+ '@media (pointer: coarse)': {
185
+ '&::before': {
186
+ content: '""',
187
+ position: 'absolute',
188
+ insetEnd: 0,
189
+ top: '50%',
190
+ transform: 'translateY(-50%)',
191
+ width: '44px',
192
+ height: `var(${heightVar})`,
193
+ },
194
+ },
195
+ });
196
+
197
+ export const selectClearTriggerStyles = clearTriggerStyles(
198
+ '--select-trigger-height'
199
+ );
200
+
201
+ export const comboboxClearTriggerStyles = clearTriggerStyles(
202
+ '--combobox-input-height'
203
+ );
@@ -1,13 +1,16 @@
1
1
  import { forwardRef } from 'react';
2
2
  import { Select as ChakraSelect } from '@chakra-ui/react';
3
3
 
4
+ import { useTranslate } from '@/hooks/useTranslate';
4
5
  import { mergeCss } from '@/utils/mergeCss';
5
6
 
7
+ import { CloseIcon } from '../Icon';
6
8
  import {
7
9
  contentStyles,
8
10
  ghostTriggerStyles,
9
11
  indicatorStyles,
10
12
  itemStyles,
13
+ selectClearTriggerStyles,
11
14
  triggerStyles,
12
15
  } from './Select.styles';
13
16
  import type { SelectVariant } from './Select.types';
@@ -67,10 +70,33 @@ const Indicator = forwardRef<HTMLDivElement, ChakraSelect.IndicatorProps>(
67
70
  }
68
71
  );
69
72
 
73
+ /**
74
+ * The × that empties the selection. Draws `CloseIcon` when no children are
75
+ * passed, and names itself in the provider's language: Chakra's own name is
76
+ * the English "Clear value" in every locale. Pass `aria-label` to say what is
77
+ * being cleared ("조직 선택 지우기").
78
+ */
79
+ const ClearTrigger = forwardRef<
80
+ HTMLButtonElement,
81
+ ChakraSelect.ClearTriggerProps
82
+ >(function SelectClearTrigger({ css, children, ...props }, ref) {
83
+ const translate = useTranslate();
84
+ return (
85
+ <ChakraSelect.ClearTrigger
86
+ ref={ref}
87
+ aria-label={translate('clear_selection') as string}
88
+ {...props}
89
+ css={mergeCss(selectClearTriggerStyles, css)}
90
+ >
91
+ {children ?? <CloseIcon aria-hidden />}
92
+ </ChakraSelect.ClearTrigger>
93
+ );
94
+ });
95
+
70
96
  /**
71
97
  * Compound Select primitives — Chakra's `Select` namespace with the Logician
72
98
  * design-system styling baked into the visual parts (`Trigger`, `Content`,
73
- * `Item`, `Indicator`). Use these directly for full compositional control
99
+ * `Item`, `Indicator`, `ClearTrigger`). Use these directly for full compositional control
74
100
  * (multi-select, grouped options, custom layouts); use `SelectField` for the
75
101
  * common single-select case.
76
102
  */
@@ -84,7 +110,7 @@ export const Select = {
84
110
  ValueText: ChakraSelect.ValueText,
85
111
  IndicatorGroup: ChakraSelect.IndicatorGroup,
86
112
  Indicator,
87
- ClearTrigger: ChakraSelect.ClearTrigger,
113
+ ClearTrigger,
88
114
  HiddenSelect: ChakraSelect.HiddenSelect,
89
115
  Positioner: ChakraSelect.Positioner,
90
116
  Content,