@fragments-sdk/ui 0.6.2 → 0.6.4
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/fragments.json +1 -1
- package/package.json +2 -2
- package/src/components/Combobox/Combobox.fragment.tsx +50 -0
- package/src/components/Combobox/Combobox.module.scss +15 -4
- package/src/components/Combobox/index.tsx +8 -1
- package/src/components/Menu/Menu.module.scss +2 -2
- package/src/components/Popover/Popover.module.scss +2 -2
- package/src/components/Select/Select.fragment.tsx +48 -0
- package/src/components/Select/Select.module.scss +15 -4
- package/src/components/Select/index.tsx +13 -6
- package/src/components/Text/Text.module.scss +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragments-sdk/ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Customizable UI components built on Base UI headless primitives",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": "^19.0.0",
|
|
37
37
|
"sass": "^1.83.0",
|
|
38
38
|
"typescript": "^5.7.0",
|
|
39
|
-
"@fragments-sdk/cli": "0.4.
|
|
39
|
+
"@fragments-sdk/cli": "0.4.4"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"src",
|
|
@@ -95,6 +95,11 @@ export default defineSegment({
|
|
|
95
95
|
description: 'Auto-highlight first matching item while filtering',
|
|
96
96
|
default: 'true',
|
|
97
97
|
},
|
|
98
|
+
maxVisibleItems: {
|
|
99
|
+
type: 'number',
|
|
100
|
+
description: 'Maximum visible options before scrolling. Shows half of the next item as a scroll hint.',
|
|
101
|
+
default: '4',
|
|
102
|
+
},
|
|
98
103
|
},
|
|
99
104
|
|
|
100
105
|
relations: [
|
|
@@ -111,6 +116,7 @@ export default defineSegment({
|
|
|
111
116
|
'placeholder: string - input placeholder text',
|
|
112
117
|
'disabled: boolean - disable combobox',
|
|
113
118
|
'autoHighlight: boolean - auto-highlight first match',
|
|
119
|
+
'maxVisibleItems: number - max visible options before scrolling (default 4)',
|
|
114
120
|
],
|
|
115
121
|
scenarioTags: [
|
|
116
122
|
'form.combobox',
|
|
@@ -204,6 +210,50 @@ export default defineSegment({
|
|
|
204
210
|
</StatefulCombobox>
|
|
205
211
|
),
|
|
206
212
|
},
|
|
213
|
+
{
|
|
214
|
+
name: 'Scrollable List',
|
|
215
|
+
description: 'Long list with scroll hint — shows 4 items with half-peek of the 5th',
|
|
216
|
+
render: () => (
|
|
217
|
+
<StatefulCombobox placeholder="Search languages...">
|
|
218
|
+
<Combobox.Input />
|
|
219
|
+
<Combobox.Content>
|
|
220
|
+
<Combobox.Empty>No results found</Combobox.Empty>
|
|
221
|
+
<Combobox.Item value="js">JavaScript</Combobox.Item>
|
|
222
|
+
<Combobox.Item value="ts">TypeScript</Combobox.Item>
|
|
223
|
+
<Combobox.Item value="py">Python</Combobox.Item>
|
|
224
|
+
<Combobox.Item value="rs">Rust</Combobox.Item>
|
|
225
|
+
<Combobox.Item value="go">Go</Combobox.Item>
|
|
226
|
+
<Combobox.Item value="rb">Ruby</Combobox.Item>
|
|
227
|
+
<Combobox.Item value="java">Java</Combobox.Item>
|
|
228
|
+
<Combobox.Item value="swift">Swift</Combobox.Item>
|
|
229
|
+
<Combobox.Item value="kt">Kotlin</Combobox.Item>
|
|
230
|
+
<Combobox.Item value="cpp">C++</Combobox.Item>
|
|
231
|
+
</Combobox.Content>
|
|
232
|
+
</StatefulCombobox>
|
|
233
|
+
),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'Custom Max Visible Items',
|
|
237
|
+
description: 'Show 6 items before scrolling with half-peek scroll hint',
|
|
238
|
+
render: () => (
|
|
239
|
+
<StatefulCombobox placeholder="Search cities...">
|
|
240
|
+
<Combobox.Input />
|
|
241
|
+
<Combobox.Content maxVisibleItems={6}>
|
|
242
|
+
<Combobox.Empty>No results found</Combobox.Empty>
|
|
243
|
+
<Combobox.Item value="nyc">New York</Combobox.Item>
|
|
244
|
+
<Combobox.Item value="lon">London</Combobox.Item>
|
|
245
|
+
<Combobox.Item value="tok">Tokyo</Combobox.Item>
|
|
246
|
+
<Combobox.Item value="par">Paris</Combobox.Item>
|
|
247
|
+
<Combobox.Item value="syd">Sydney</Combobox.Item>
|
|
248
|
+
<Combobox.Item value="ber">Berlin</Combobox.Item>
|
|
249
|
+
<Combobox.Item value="tor">Toronto</Combobox.Item>
|
|
250
|
+
<Combobox.Item value="sin">Singapore</Combobox.Item>
|
|
251
|
+
<Combobox.Item value="dub">Dubai</Combobox.Item>
|
|
252
|
+
<Combobox.Item value="sao">São Paulo</Combobox.Item>
|
|
253
|
+
</Combobox.Content>
|
|
254
|
+
</StatefulCombobox>
|
|
255
|
+
),
|
|
256
|
+
},
|
|
207
257
|
{
|
|
208
258
|
name: 'Disabled',
|
|
209
259
|
description: 'Disabled combobox',
|
|
@@ -152,9 +152,9 @@
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// Positioner
|
|
155
|
+
// Positioner — z-index must exceed Dialog positioner (51)
|
|
156
156
|
.positioner {
|
|
157
|
-
z-index:
|
|
157
|
+
z-index: 52;
|
|
158
158
|
outline: none;
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -162,9 +162,20 @@
|
|
|
162
162
|
.popup {
|
|
163
163
|
@include surface-elevated;
|
|
164
164
|
|
|
165
|
+
// Item height derived from text-base font + vertical padding
|
|
166
|
+
--_item-h: calc(
|
|
167
|
+
var(--fui-font-size-sm, #{$fui-font-size-sm}) * var(--fui-line-height-normal, #{$fui-line-height-normal}) +
|
|
168
|
+
var(--fui-space-2, #{$fui-space-2}) * 2
|
|
169
|
+
);
|
|
170
|
+
|
|
165
171
|
min-width: var(--anchor-width);
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
// Show N items + half-peek scroll hint (default 4.5 items)
|
|
173
|
+
// !important needed to override Base UI's inline max-height: 100%
|
|
174
|
+
max-height: calc(
|
|
175
|
+
var(--_item-h) * var(--fui-select-max-items, 4.5) +
|
|
176
|
+
var(--fui-space-1, #{$fui-space-1}) * 2
|
|
177
|
+
) !important;
|
|
178
|
+
overflow-y: auto !important;
|
|
168
179
|
padding: var(--fui-space-1, $fui-space-1);
|
|
169
180
|
box-shadow: var(--fui-shadow-md, $fui-shadow-md);
|
|
170
181
|
|
|
@@ -42,6 +42,8 @@ export interface ComboboxContentProps extends React.HTMLAttributes<HTMLDivElemen
|
|
|
42
42
|
children: React.ReactNode;
|
|
43
43
|
sideOffset?: number;
|
|
44
44
|
align?: 'start' | 'center' | 'end';
|
|
45
|
+
/** Maximum number of visible options before scrolling. Shows half of the next item as a scroll hint. @default 4 */
|
|
46
|
+
maxVisibleItems?: number;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export interface ComboboxItemProps {
|
|
@@ -310,10 +312,15 @@ function ComboboxContent({
|
|
|
310
312
|
className,
|
|
311
313
|
sideOffset = 4,
|
|
312
314
|
align = 'start',
|
|
315
|
+
maxVisibleItems,
|
|
313
316
|
...htmlProps
|
|
314
317
|
}: ComboboxContentProps) {
|
|
315
318
|
const popupClasses = [styles.popup, className].filter(Boolean).join(' ');
|
|
316
319
|
|
|
320
|
+
const popupStyle = maxVisibleItems != null
|
|
321
|
+
? { '--fui-select-max-items': maxVisibleItems + 0.5, ...htmlProps.style } as React.CSSProperties
|
|
322
|
+
: htmlProps.style;
|
|
323
|
+
|
|
317
324
|
return (
|
|
318
325
|
<BaseCombobox.Portal>
|
|
319
326
|
<BaseCombobox.Positioner
|
|
@@ -322,7 +329,7 @@ function ComboboxContent({
|
|
|
322
329
|
align={align}
|
|
323
330
|
className={styles.positioner}
|
|
324
331
|
>
|
|
325
|
-
<BaseCombobox.Popup {...htmlProps} className={popupClasses}>
|
|
332
|
+
<BaseCombobox.Popup {...htmlProps} className={popupClasses} style={popupStyle}>
|
|
326
333
|
{children}
|
|
327
334
|
</BaseCombobox.Popup>
|
|
328
335
|
</BaseCombobox.Positioner>
|
|
@@ -81,6 +81,11 @@ export default defineSegment({
|
|
|
81
81
|
description: 'Disable the select',
|
|
82
82
|
default: 'false',
|
|
83
83
|
},
|
|
84
|
+
maxVisibleItems: {
|
|
85
|
+
type: 'number',
|
|
86
|
+
description: 'Maximum visible options before scrolling. Shows half of the next item as a scroll hint.',
|
|
87
|
+
default: '4',
|
|
88
|
+
},
|
|
84
89
|
},
|
|
85
90
|
|
|
86
91
|
relations: [
|
|
@@ -95,6 +100,7 @@ export default defineSegment({
|
|
|
95
100
|
'onValueChange: (value) => void - selection handler',
|
|
96
101
|
'placeholder: string - placeholder text',
|
|
97
102
|
'disabled: boolean - disable select',
|
|
103
|
+
'maxVisibleItems: number - max visible options before scrolling (default 4)',
|
|
98
104
|
],
|
|
99
105
|
scenarioTags: [
|
|
100
106
|
'form.select',
|
|
@@ -166,6 +172,48 @@ export default defineSegment({
|
|
|
166
172
|
</StatefulSelect>
|
|
167
173
|
),
|
|
168
174
|
},
|
|
175
|
+
{
|
|
176
|
+
name: 'Scrollable List',
|
|
177
|
+
description: 'Long list with scroll hint — shows 4 items with half-peek of the 5th to indicate more',
|
|
178
|
+
render: () => (
|
|
179
|
+
<StatefulSelect placeholder="Select a timezone">
|
|
180
|
+
<Select.Trigger />
|
|
181
|
+
<Select.Content>
|
|
182
|
+
<Select.Item value="utc-8">Pacific Time (UTC-8)</Select.Item>
|
|
183
|
+
<Select.Item value="utc-7">Mountain Time (UTC-7)</Select.Item>
|
|
184
|
+
<Select.Item value="utc-6">Central Time (UTC-6)</Select.Item>
|
|
185
|
+
<Select.Item value="utc-5">Eastern Time (UTC-5)</Select.Item>
|
|
186
|
+
<Select.Item value="utc-4">Atlantic Time (UTC-4)</Select.Item>
|
|
187
|
+
<Select.Item value="utc+0">GMT (UTC+0)</Select.Item>
|
|
188
|
+
<Select.Item value="utc+1">Central European (UTC+1)</Select.Item>
|
|
189
|
+
<Select.Item value="utc+5.5">India Standard (UTC+5:30)</Select.Item>
|
|
190
|
+
<Select.Item value="utc+8">China Standard (UTC+8)</Select.Item>
|
|
191
|
+
<Select.Item value="utc+9">Japan Standard (UTC+9)</Select.Item>
|
|
192
|
+
</Select.Content>
|
|
193
|
+
</StatefulSelect>
|
|
194
|
+
),
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'Custom Max Visible Items',
|
|
198
|
+
description: 'Show 6 items before scrolling with half-peek scroll hint',
|
|
199
|
+
render: () => (
|
|
200
|
+
<StatefulSelect placeholder="Select a color">
|
|
201
|
+
<Select.Trigger />
|
|
202
|
+
<Select.Content maxVisibleItems={6}>
|
|
203
|
+
<Select.Item value="red">Red</Select.Item>
|
|
204
|
+
<Select.Item value="orange">Orange</Select.Item>
|
|
205
|
+
<Select.Item value="yellow">Yellow</Select.Item>
|
|
206
|
+
<Select.Item value="green">Green</Select.Item>
|
|
207
|
+
<Select.Item value="blue">Blue</Select.Item>
|
|
208
|
+
<Select.Item value="indigo">Indigo</Select.Item>
|
|
209
|
+
<Select.Item value="violet">Violet</Select.Item>
|
|
210
|
+
<Select.Item value="pink">Pink</Select.Item>
|
|
211
|
+
<Select.Item value="teal">Teal</Select.Item>
|
|
212
|
+
<Select.Item value="cyan">Cyan</Select.Item>
|
|
213
|
+
</Select.Content>
|
|
214
|
+
</StatefulSelect>
|
|
215
|
+
),
|
|
216
|
+
},
|
|
169
217
|
{
|
|
170
218
|
name: 'Disabled',
|
|
171
219
|
description: 'Disabled select',
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// Positioner
|
|
75
|
+
// Positioner — z-index must exceed Dialog positioner (51)
|
|
76
76
|
.positioner {
|
|
77
|
-
z-index:
|
|
77
|
+
z-index: 52;
|
|
78
78
|
outline: none;
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -82,9 +82,20 @@
|
|
|
82
82
|
.popup {
|
|
83
83
|
@include surface-elevated;
|
|
84
84
|
|
|
85
|
+
// Item height derived from text-base font + vertical padding
|
|
86
|
+
--_item-h: calc(
|
|
87
|
+
var(--fui-font-size-sm, #{$fui-font-size-sm}) * var(--fui-line-height-normal, #{$fui-line-height-normal}) +
|
|
88
|
+
var(--fui-space-2, #{$fui-space-2}) * 2
|
|
89
|
+
);
|
|
90
|
+
|
|
85
91
|
min-width: var(--anchor-width);
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
// Show N items + half-peek scroll hint (default 4.5 items)
|
|
93
|
+
// !important needed to override Base UI's inline max-height: 100%
|
|
94
|
+
max-height: calc(
|
|
95
|
+
var(--_item-h) * var(--fui-select-max-items, 4.5) +
|
|
96
|
+
var(--fui-space-1, #{$fui-space-1}) * 2
|
|
97
|
+
) !important;
|
|
98
|
+
overflow-y: auto !important;
|
|
88
99
|
padding: var(--fui-space-1, $fui-space-1);
|
|
89
100
|
box-shadow: var(--fui-shadow-md, $fui-shadow-md);
|
|
90
101
|
|
|
@@ -39,6 +39,8 @@ export interface SelectContentProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
39
39
|
children: React.ReactNode;
|
|
40
40
|
sideOffset?: number;
|
|
41
41
|
align?: 'start' | 'center' | 'end';
|
|
42
|
+
/** Maximum number of visible options before scrolling. Shows half of the next item as a scroll hint. @default 4 */
|
|
43
|
+
maxVisibleItems?: number;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export interface SelectItemProps {
|
|
@@ -236,10 +238,15 @@ function SelectContent({
|
|
|
236
238
|
className,
|
|
237
239
|
sideOffset = 4,
|
|
238
240
|
align = 'start',
|
|
241
|
+
maxVisibleItems,
|
|
239
242
|
...htmlProps
|
|
240
243
|
}: SelectContentProps) {
|
|
241
244
|
const popupClasses = [styles.popup, className].filter(Boolean).join(' ');
|
|
242
245
|
|
|
246
|
+
const popupStyle = maxVisibleItems != null
|
|
247
|
+
? { '--fui-select-max-items': maxVisibleItems + 0.5, ...htmlProps.style } as React.CSSProperties
|
|
248
|
+
: htmlProps.style;
|
|
249
|
+
|
|
243
250
|
return (
|
|
244
251
|
<BaseSelect.Portal>
|
|
245
252
|
<BaseSelect.Positioner
|
|
@@ -247,7 +254,7 @@ function SelectContent({
|
|
|
247
254
|
align={align}
|
|
248
255
|
className={styles.positioner}
|
|
249
256
|
>
|
|
250
|
-
<BaseSelect.Popup {...htmlProps} className={popupClasses}>
|
|
257
|
+
<BaseSelect.Popup {...htmlProps} className={popupClasses} style={popupStyle}>
|
|
251
258
|
{children}
|
|
252
259
|
</BaseSelect.Popup>
|
|
253
260
|
</BaseSelect.Positioner>
|
|
@@ -256,18 +263,18 @@ function SelectContent({
|
|
|
256
263
|
}
|
|
257
264
|
|
|
258
265
|
function SelectItem({ children, value, disabled, className }: SelectItemProps) {
|
|
259
|
-
const
|
|
266
|
+
const { itemsRef, incrementItemsVersion } = React.useContext(SelectContext);
|
|
260
267
|
const classes = [styles.item, className].filter(Boolean).join(' ');
|
|
261
268
|
|
|
262
269
|
// Register this item's children in the registry so the trigger can display them
|
|
263
270
|
React.useEffect(() => {
|
|
264
|
-
|
|
271
|
+
itemsRef.current.set(value, children);
|
|
265
272
|
// Trigger re-render of trigger to show the registered content
|
|
266
|
-
|
|
273
|
+
incrementItemsVersion();
|
|
267
274
|
return () => {
|
|
268
|
-
|
|
275
|
+
itemsRef.current.delete(value);
|
|
269
276
|
};
|
|
270
|
-
}, [
|
|
277
|
+
}, [itemsRef, incrementItemsVersion, value, children]);
|
|
271
278
|
|
|
272
279
|
return (
|
|
273
280
|
<BaseSelect.Item value={value} disabled={disabled} className={classes}>
|