@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.
- package/dist/components/NavList/NavListItem.d.ts.map +1 -1
- package/dist/components/NavList/NavListItem.js +19 -8
- package/dist/components/NavList/NavListItem.js.map +1 -1
- package/dist/components/NavList/NavListItem.mjs +19 -8
- package/dist/components/NavList/NavListItem.mjs.map +1 -1
- package/dist/components/Select/Combobox.d.ts +1 -1
- package/dist/components/Select/Combobox.d.ts.map +1 -1
- package/dist/components/Select/Combobox.js +9 -2
- package/dist/components/Select/Combobox.js.map +1 -1
- package/dist/components/Select/Combobox.mjs +10 -3
- package/dist/components/Select/Combobox.mjs.map +1 -1
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +14 -2
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.mjs +15 -3
- package/dist/components/Select/Select.mjs.map +1 -1
- package/dist/components/Select/Select.styles.d.ts +96 -0
- package/dist/components/Select/Select.styles.d.ts.map +1 -1
- package/dist/components/Select/Select.styles.js +75 -0
- package/dist/components/Select/Select.styles.js.map +1 -1
- package/dist/components/Select/Select.styles.mjs +74 -1
- package/dist/components/Select/Select.styles.mjs.map +1 -1
- package/dist/translations/Defaults.translations.json.js +10 -1
- package/dist/translations/Defaults.translations.json.js.map +1 -1
- package/dist/translations/Defaults.translations.json.mjs +10 -2
- package/dist/translations/Defaults.translations.json.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/NavList/NavList.stories.tsx +32 -0
- package/src/components/NavList/NavList.test.tsx +24 -0
- package/src/components/NavList/NavListItem.tsx +22 -9
- package/src/components/Select/Combobox.tsx +28 -3
- package/src/components/Select/Select.clearTrigger.test.tsx +76 -0
- package/src/components/Select/Select.stories.tsx +81 -0
- package/src/components/Select/Select.styles.ts +81 -0
- package/src/components/Select/Select.tsx +28 -2
- package/src/components/Select/Select.usage.mdx +63 -0
- 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;
|
|
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;
|
|
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
|
@@ -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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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 {
|
|
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
|
|
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="목록 배경" />,
|