@mindlogic-ai/logician-ui 5.0.0-alpha.15 → 5.0.0-alpha.17

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 (38) hide show
  1. package/dist/components/NavList/NavListItem.d.ts.map +1 -1
  2. package/dist/components/NavList/NavListItem.js +19 -8
  3. package/dist/components/NavList/NavListItem.js.map +1 -1
  4. package/dist/components/NavList/NavListItem.mjs +19 -8
  5. package/dist/components/NavList/NavListItem.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/NavList/NavList.stories.tsx +32 -0
  30. package/src/components/NavList/NavList.test.tsx +24 -0
  31. package/src/components/NavList/NavListItem.tsx +22 -9
  32. package/src/components/Select/Combobox.tsx +28 -3
  33. package/src/components/Select/Select.clearTrigger.test.tsx +76 -0
  34. package/src/components/Select/Select.stories.tsx +81 -0
  35. package/src/components/Select/Select.styles.ts +81 -0
  36. package/src/components/Select/Select.tsx +28 -2
  37. package/src/components/Select/Select.usage.mdx +63 -0
  38. package/src/translations/Defaults.translations.json +7 -0
@@ -37,9 +37,26 @@ const fieldBase = {
37
37
  fontWeight: 'semibold',
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) => ({
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
+ });
40
56
  /** Select trigger — the clickable control. */
41
57
  const triggerStyles = {
42
58
  ...fieldBase,
59
+ ...clearTriggerRoom('--select-trigger-padding-x'),
43
60
  cursor: 'pointer',
44
61
  _open: {
45
62
  borderColor: 'primary.main',
@@ -78,6 +95,7 @@ const ghostTriggerStyles = {
78
95
  /** Combobox text input — the typeable control. */
79
96
  const inputStyles = {
80
97
  ...fieldBase,
98
+ ...clearTriggerRoom('--combobox-input-padding-x'),
81
99
  };
82
100
  /** Dropdown panel for both Select and Combobox. */
83
101
  const contentStyles = {
@@ -114,12 +132,69 @@ const itemStyles = {
114
132
  const indicatorStyles = {
115
133
  color: 'fg.muted',
116
134
  };
135
+ /**
136
+ * Clear (×) button for both Select and Combobox.
137
+ *
138
+ * Chakra's recipe gives this part a colour and no size, and its default icon
139
+ * is `1em`, so a bare `ClearTrigger` drew a 21px × next to a 14px chevron. The
140
+ * × is the secondary action, so it is smaller (`3.5`, 12px at 14px text) and
141
+ * lighter (`fg.icon`) than the chevron, on an `8` box. `& svg` rather than
142
+ * `_icon` so the size also wins over an icon passed with its own `boxSize`.
143
+ *
144
+ * On a touch screen the hit area widens to 44px, toward the value only: the
145
+ * chevron sits to the right and has to keep opening the list. The height stays
146
+ * the control's own height (`heightVar`), so the area never reaches the field
147
+ * above or below. Every control height is over the 24px KWCAG minimum.
148
+ */
149
+ const clearTriggerStyles = (heightVar) => ({
150
+ position: 'relative',
151
+ display: 'inline-flex',
152
+ alignItems: 'center',
153
+ justifyContent: 'center',
154
+ boxSize: '8',
155
+ me: '1',
156
+ color: 'fg.icon',
157
+ cursor: 'pointer',
158
+ _hover: { color: 'fg.default' },
159
+ // The divider marks where opening starts: it sits in the `me` gap, takes no
160
+ // clicks, and everything right of it belongs to the chevron.
161
+ _after: {
162
+ content: '""',
163
+ position: 'absolute',
164
+ insetEnd: '-1',
165
+ top: '50%',
166
+ transform: 'translateY(-50%)',
167
+ h: '4',
168
+ borderEndWidth: '1px',
169
+ borderColor: 'border.subtle',
170
+ pointerEvents: 'none',
171
+ },
172
+ // zag hides the × with the `hidden` attribute while nothing is selected,
173
+ // and the `display` above would otherwise override it.
174
+ _hidden: { display: 'none' },
175
+ '& svg': { width: '3.5', height: '3.5' },
176
+ '@media (pointer: coarse)': {
177
+ '&::before': {
178
+ content: '""',
179
+ position: 'absolute',
180
+ insetEnd: 0,
181
+ top: '50%',
182
+ transform: 'translateY(-50%)',
183
+ width: '44px',
184
+ height: `var(${heightVar})`,
185
+ },
186
+ },
187
+ });
188
+ const selectClearTriggerStyles = clearTriggerStyles('--select-trigger-height');
189
+ const comboboxClearTriggerStyles = clearTriggerStyles('--combobox-input-height');
117
190
 
191
+ exports.comboboxClearTriggerStyles = comboboxClearTriggerStyles;
118
192
  exports.contentStyles = contentStyles;
119
193
  exports.ghostContentStyles = ghostContentStyles;
120
194
  exports.ghostTriggerStyles = ghostTriggerStyles;
121
195
  exports.indicatorStyles = indicatorStyles;
122
196
  exports.inputStyles = inputStyles;
123
197
  exports.itemStyles = itemStyles;
198
+ exports.selectClearTriggerStyles = selectClearTriggerStyles;
124
199
  exports.triggerStyles = triggerStyles;
125
200
  //# sourceMappingURL=Select.styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Select.styles.js","sources":["../../../src/components/Select/Select.styles.ts"],"sourcesContent":[null],"names":["disabledControl","disabledText"],"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,GAAGA,+BAAe;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,GAAGC,4BAAY;;;;;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.js","sources":["../../../src/components/Select/Select.styles.ts"],"sourcesContent":[null],"names":["disabledControl","disabledText"],"mappings":"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"}
@@ -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.15",
3
+ "version": "5.0.0-alpha.17",
4
4
  "description": "A comprehensive React design system built on Chakra UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -489,6 +489,38 @@ export const TooltipsAndNewWindow: Story = {
489
489
  ),
490
490
  };
491
491
 
492
+ /**
493
+ * `tooltipPlacement="end"`: the description opens beside the row instead of
494
+ * above it. On a row with a `trailingAction` it is placed from the whole row,
495
+ * past the action, not from the link, which stops before the action.
496
+ */
497
+ export const EndTooltipWithAction: Story = {
498
+ render: (args) => (
499
+ <Rail w="220px">
500
+ <NavList {...args} aria-label="에이전트">
501
+ {[
502
+ ['회의록', '회의를 녹음하고 요약을 받아요'],
503
+ ['이미지 생성', 'AI로 생성한 이미지를 관리하세요'],
504
+ ].map(([title, description]) => (
505
+ <NavList.Item
506
+ key={title}
507
+ href="#"
508
+ tooltip={description}
509
+ tooltipPlacement="end"
510
+ trailingAction={
511
+ <IconButton aria-label={`${title} 더 보기`} size="xs">
512
+ <DotsHorizontalIcon />
513
+ </IconButton>
514
+ }
515
+ >
516
+ {title}
517
+ </NavList.Item>
518
+ ))}
519
+ </NavList>
520
+ </Rail>
521
+ ),
522
+ };
523
+
492
524
  /**
493
525
  * Session rows: `trailingActionVisibility="hover"` keeps the more-menu out of
494
526
  * the way until the row is hovered or focused, or its menu is open. It stays
@@ -568,6 +568,30 @@ describe('NavList', () => {
568
568
  }
569
569
  );
570
570
 
571
+ it('an end tooltip on a row with a trailing action measures from the whole <li>', async () => {
572
+ const liRect = vi.spyOn(HTMLLIElement.prototype, 'getBoundingClientRect');
573
+ renderUi(
574
+ <NavList aria-label="메뉴">
575
+ <NavList.Item
576
+ onClick={() => {}}
577
+ tooltip="회의를 녹음하고 요약을 받아요"
578
+ tooltipPlacement="end"
579
+ trailingAction={<button type="button">고정</button>}
580
+ >
581
+ 회의록
582
+ </NavList.Item>
583
+ </NavList>
584
+ );
585
+ await hover(screen.getByRole('button', { name: '회의록' }));
586
+ await waitFor(() =>
587
+ expect(
588
+ document.querySelector('[data-part="content"][data-state="open"]')
589
+ ).not.toBeNull()
590
+ );
591
+ expect(liRect).toHaveBeenCalled();
592
+ liRect.mockRestore();
593
+ });
594
+
571
595
  it('a disabled row still carries its tooltip as a description, closed', () => {
572
596
  renderUi(
573
597
  <NavList aria-label="메뉴">
@@ -398,18 +398,31 @@ export const NavListItem = forwardRef<HTMLElement, NavListItemProps>(
398
398
  typeof label === 'string' || typeof label === 'number'
399
399
  ? String(label)
400
400
  : undefined;
401
+ // Beside the icon, towards the content: the rail is on the right of
402
+ // a right-to-left page. Popper placements are physical.
403
+ const placement =
404
+ isCollapsed || tooltipPlacement === 'end'
405
+ ? tooltipDir === 'rtl'
406
+ ? 'left'
407
+ : 'right'
408
+ : 'top';
409
+ // An expanded row's end tooltip measures from the whole `<li>`, not the
410
+ // row link: with a trailing action the link stops short of the row's
411
+ // end, and the tooltip would sit over the action.
412
+ const anchorsToItem = !isCollapsed && tooltipPlacement === 'end';
401
413
  const withTooltip = (
402
414
  <Tooltip
403
415
  content={tooltipContent}
404
- // Beside the icon, towards the content: the rail is on the right of
405
- // a right-to-left page. Popper placements are physical.
406
- placement={
407
- isCollapsed || tooltipPlacement === 'end'
408
- ? tooltipDir === 'rtl'
409
- ? 'left'
410
- : 'right'
411
- : 'top'
412
- }
416
+ placement={placement}
417
+ {...(anchorsToItem && {
418
+ positioning: {
419
+ placement,
420
+ getAnchorRect: (el) =>
421
+ el instanceof HTMLElement
422
+ ? (el.closest('li')?.getBoundingClientRect() ?? null)
423
+ : null,
424
+ },
425
+ })}
413
426
  onOpenChange={({ open }) => setIsTooltipOpen(open)}
414
427
  // Zag only checks that this is set — it takes the content's id and
415
428
  // role away, so the trigger is never described by the tooltip. Not
@@ -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="목록 배경" />,