@fragments-sdk/ui 0.9.6 → 0.9.7
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/codeblock.cjs +25 -29
- package/dist/codeblock.cjs.map +1 -1
- package/dist/codeblock.js +25 -29
- package/dist/codeblock.js.map +1 -1
- package/dist/components/Chip/index.cjs +2 -1
- package/dist/components/Chip/index.cjs.map +1 -1
- package/dist/components/Chip/index.d.ts.map +1 -1
- package/dist/components/Chip/index.js +2 -1
- package/dist/components/Chip/index.js.map +1 -1
- package/dist/components/CodeBlock/index.d.ts.map +1 -1
- package/dist/components/Command/index.cjs +6 -0
- package/dist/components/Command/index.cjs.map +1 -1
- package/dist/components/Command/index.d.ts.map +1 -1
- package/dist/components/Command/index.js +6 -0
- package/dist/components/Command/index.js.map +1 -1
- package/dist/components/DataTable/index.cjs +26 -26
- package/dist/components/DataTable/index.cjs.map +1 -1
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +26 -26
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/Listbox/index.cjs +6 -0
- package/dist/components/Listbox/index.cjs.map +1 -1
- package/dist/components/Listbox/index.d.ts.map +1 -1
- package/dist/components/Listbox/index.js +6 -0
- package/dist/components/Listbox/index.js.map +1 -1
- package/dist/components/Loading/index.cjs +2 -12
- package/dist/components/Loading/index.cjs.map +1 -1
- package/dist/components/Loading/index.d.ts.map +1 -1
- package/dist/components/Loading/index.js +2 -12
- package/dist/components/Loading/index.js.map +1 -1
- package/dist/components/NavigationMenu/index.cjs +12 -1
- package/dist/components/NavigationMenu/index.cjs.map +1 -1
- package/dist/components/NavigationMenu/index.d.ts.map +1 -1
- package/dist/components/NavigationMenu/index.js +12 -1
- package/dist/components/NavigationMenu/index.js.map +1 -1
- package/dist/components/Skeleton/index.cjs +3 -3
- package/dist/components/Skeleton/index.cjs.map +1 -1
- package/dist/components/Skeleton/index.js +3 -3
- package/dist/components/Skeleton/index.js.map +1 -1
- package/dist/components/Stack/index.cjs +4 -3
- package/dist/components/Stack/index.cjs.map +1 -1
- package/dist/components/Stack/index.d.ts.map +1 -1
- package/dist/components/Stack/index.js +4 -3
- package/dist/components/Stack/index.js.map +1 -1
- package/dist/markdown.cjs +1 -1
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +1 -1
- package/dist/markdown.js.map +1 -1
- package/fragments.json +1 -1
- package/package.json +1 -1
- package/src/components/Chip/index.tsx +3 -1
- package/src/components/CodeBlock/index.tsx +35 -41
- package/src/components/ColorPicker/ColorPicker.fragment.tsx +17 -15
- package/src/components/Command/index.tsx +1 -0
- package/src/components/DataTable/index.tsx +45 -45
- package/src/components/Listbox/index.tsx +1 -0
- package/src/components/Loading/index.tsx +6 -12
- package/src/components/Markdown/index.tsx +2 -2
- package/src/components/Menu/Menu.fragment.tsx +17 -15
- package/src/components/NavigationMenu/index.tsx +6 -1
- package/src/components/Skeleton/index.tsx +3 -3
- package/src/components/Slider/Slider.fragment.tsx +19 -17
- package/src/components/Stack/index.tsx +4 -3
package/package.json
CHANGED
|
@@ -113,8 +113,10 @@ const ChipBase = React.forwardRef<HTMLButtonElement, ChipProps>(
|
|
|
113
113
|
}
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
+
const EMPTY_CHIP_GROUP: string[] = [];
|
|
117
|
+
|
|
116
118
|
function ChipGroupInner(
|
|
117
|
-
{ children, value: controlledValue, defaultValue =
|
|
119
|
+
{ children, value: controlledValue, defaultValue = EMPTY_CHIP_GROUP, onChange, className }: ChipGroupProps,
|
|
118
120
|
ref: React.Ref<HTMLDivElement>
|
|
119
121
|
) {
|
|
120
122
|
const [internalValue, setInternalValue] = React.useState<string[]>(defaultValue);
|
|
@@ -544,8 +544,7 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
|
|
|
544
544
|
ref
|
|
545
545
|
) {
|
|
546
546
|
const [copied, setCopied] = useState(false);
|
|
547
|
-
const [
|
|
548
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
547
|
+
const [highlight, setHighlight] = useState<{ html: string; loading: boolean }>({ html: '', loading: true });
|
|
549
548
|
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
|
550
549
|
|
|
551
550
|
const trimmedCode = useMemo(() => normalizeCode(code), [code]);
|
|
@@ -570,50 +569,45 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
|
|
|
570
569
|
// Apply syntax highlighting
|
|
571
570
|
useEffect(() => {
|
|
572
571
|
let cancelled = false;
|
|
573
|
-
|
|
572
|
+
setHighlight((prev) => ({ ...prev, loading: true }));
|
|
574
573
|
|
|
575
|
-
|
|
574
|
+
const run = async () => {
|
|
575
|
+
loadShikiDeps();
|
|
576
576
|
|
|
577
|
-
|
|
578
|
-
if (_shikiFailed && process.env.NODE_ENV === "development") {
|
|
579
|
-
console.warn(
|
|
580
|
-
"[@fragments-sdk/ui] CodeBlock: shiki is not installed. " +
|
|
581
|
-
"Install it with: npm install shiki"
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
// Fallback to plain text without syntax highlighting
|
|
585
|
-
setHighlightedHtml(`<pre class="shiki"><code>${escapeHtml(visibleCode)}</code></pre>`);
|
|
586
|
-
setIsLoading(false);
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
577
|
+
const fallbackHtml = `<pre class="shiki"><code>${escapeHtml(visibleCode)}</code></pre>`;
|
|
589
578
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const processed = processShikiHtml(html, {
|
|
597
|
-
showLineNumbers,
|
|
598
|
-
startLineNumber,
|
|
599
|
-
highlightLines: highlightSet,
|
|
600
|
-
addedLines: addedSet,
|
|
601
|
-
removedLines: removedSet,
|
|
602
|
-
});
|
|
603
|
-
setHighlightedHtml(processed);
|
|
604
|
-
setIsLoading(false);
|
|
579
|
+
if (_shikiFailed || !_codeToHtml) {
|
|
580
|
+
if (_shikiFailed && process.env.NODE_ENV === "development") {
|
|
581
|
+
console.warn(
|
|
582
|
+
"[@fragments-sdk/ui] CodeBlock: shiki is not installed. " +
|
|
583
|
+
"Install it with: npm install shiki"
|
|
584
|
+
);
|
|
605
585
|
}
|
|
606
|
-
|
|
607
|
-
|
|
586
|
+
return fallbackHtml;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
const html = await _codeToHtml(visibleCode, { lang: language, theme });
|
|
591
|
+
return processShikiHtml(html, {
|
|
592
|
+
showLineNumbers,
|
|
593
|
+
startLineNumber,
|
|
594
|
+
highlightLines: highlightSet,
|
|
595
|
+
addedLines: addedSet,
|
|
596
|
+
removedLines: removedSet,
|
|
597
|
+
});
|
|
598
|
+
} catch (err) {
|
|
608
599
|
if (process.env.NODE_ENV !== "production") {
|
|
609
600
|
console.error("Syntax highlighting failed:", err);
|
|
610
601
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
602
|
+
return fallbackHtml;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
run().then((html) => {
|
|
607
|
+
if (!cancelled) {
|
|
608
|
+
setHighlight({ html, loading: false });
|
|
609
|
+
}
|
|
610
|
+
});
|
|
617
611
|
|
|
618
612
|
return () => {
|
|
619
613
|
cancelled = true;
|
|
@@ -701,7 +695,7 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
|
|
|
701
695
|
{copied ? <CheckIcon className={styles.icon} /> : <CopyIcon className={styles.icon} />}
|
|
702
696
|
</button>
|
|
703
697
|
)}
|
|
704
|
-
{
|
|
698
|
+
{highlight.loading ? (
|
|
705
699
|
<div className={styles.loading} style={codeContainerStyle}>
|
|
706
700
|
<pre>
|
|
707
701
|
<code>{visibleCode}</code>
|
|
@@ -711,7 +705,7 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
|
|
|
711
705
|
<div
|
|
712
706
|
className={styles.codeContainer}
|
|
713
707
|
style={codeContainerStyle}
|
|
714
|
-
dangerouslySetInnerHTML={{ __html:
|
|
708
|
+
dangerouslySetInnerHTML={{ __html: highlight.html }}
|
|
715
709
|
/>
|
|
716
710
|
)}
|
|
717
711
|
{persistentCopy && (
|
|
@@ -2,6 +2,22 @@ import React from 'react';
|
|
|
2
2
|
import { defineFragment } from '@fragments-sdk/cli/core';
|
|
3
3
|
import { ColorPicker } from '.';
|
|
4
4
|
|
|
5
|
+
function ControlledColorPickerDemo() {
|
|
6
|
+
const [color, setColor] = React.useState('#ef4444');
|
|
7
|
+
return (
|
|
8
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
9
|
+
<ColorPicker
|
|
10
|
+
label="Accent Color"
|
|
11
|
+
value={color}
|
|
12
|
+
onChange={setColor}
|
|
13
|
+
/>
|
|
14
|
+
<div style={{ fontSize: '14px', color: 'var(--fui-text-secondary)' }}>
|
|
15
|
+
Selected: {color}
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
export default defineFragment({
|
|
6
22
|
component: ColorPicker,
|
|
7
23
|
|
|
@@ -133,21 +149,7 @@ export default defineFragment({
|
|
|
133
149
|
{
|
|
134
150
|
name: 'Controlled',
|
|
135
151
|
description: 'Controlled color picker that logs changes',
|
|
136
|
-
render: () =>
|
|
137
|
-
const [color, setColor] = React.useState('#ef4444');
|
|
138
|
-
return (
|
|
139
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
140
|
-
<ColorPicker
|
|
141
|
-
label="Accent Color"
|
|
142
|
-
value={color}
|
|
143
|
-
onChange={setColor}
|
|
144
|
-
/>
|
|
145
|
-
<div style={{ fontSize: '14px', color: 'var(--fui-text-secondary)' }}>
|
|
146
|
-
Selected: {color}
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
);
|
|
150
|
-
},
|
|
152
|
+
render: () => <ControlledColorPickerDemo />,
|
|
151
153
|
},
|
|
152
154
|
{
|
|
153
155
|
name: 'Multiple Pickers',
|
|
@@ -400,6 +400,7 @@ function CommandItem({
|
|
|
400
400
|
data-active={isActive || undefined}
|
|
401
401
|
data-disabled={disabled || undefined}
|
|
402
402
|
onClick={handleClick}
|
|
403
|
+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } }}
|
|
403
404
|
onMouseEnter={handleMouseEnter}
|
|
404
405
|
className={[
|
|
405
406
|
styles.item,
|
|
@@ -102,6 +102,41 @@ export interface DataTableProps<T> extends Omit<React.HTMLAttributes<HTMLTableEl
|
|
|
102
102
|
bordered?: boolean;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function getColumnSizeStyle(
|
|
106
|
+
column: {
|
|
107
|
+
getSize: () => number;
|
|
108
|
+
columnDef: { size?: number; minSize?: number; maxSize?: number };
|
|
109
|
+
}
|
|
110
|
+
): React.CSSProperties | undefined {
|
|
111
|
+
const { size, minSize, maxSize } = column.columnDef;
|
|
112
|
+
const hasExplicitSize = size !== undefined || minSize !== undefined || maxSize !== undefined;
|
|
113
|
+
|
|
114
|
+
if (!hasExplicitSize) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const resolvedSize = column.getSize();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
width: resolvedSize,
|
|
122
|
+
minWidth: minSize ?? resolvedSize,
|
|
123
|
+
maxWidth: maxSize ?? resolvedSize,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isInteractiveTarget(
|
|
128
|
+
target: EventTarget | null,
|
|
129
|
+
currentTarget: HTMLTableRowElement
|
|
130
|
+
) {
|
|
131
|
+
if (!(target instanceof Element)) return false;
|
|
132
|
+
|
|
133
|
+
const interactiveElement = target.closest(
|
|
134
|
+
'button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [role="switch"]'
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return Boolean(interactiveElement && currentTarget.contains(interactiveElement));
|
|
138
|
+
}
|
|
139
|
+
|
|
105
140
|
function DataTableRoot<T>({
|
|
106
141
|
columns: userColumns,
|
|
107
142
|
data,
|
|
@@ -178,6 +213,16 @@ function DataTableRoot<T>({
|
|
|
178
213
|
return [checkboxColumn, ...userColumns];
|
|
179
214
|
}, [userColumns, showCheckbox, selectable]);
|
|
180
215
|
|
|
216
|
+
const hasExplicitColumnSizing = React.useMemo(
|
|
217
|
+
() =>
|
|
218
|
+
columns.some((column) =>
|
|
219
|
+
column.size !== undefined ||
|
|
220
|
+
column.minSize !== undefined ||
|
|
221
|
+
column.maxSize !== undefined
|
|
222
|
+
),
|
|
223
|
+
[columns]
|
|
224
|
+
);
|
|
225
|
+
|
|
181
226
|
if (_tableFailed || !_useReactTable) {
|
|
182
227
|
if (_tableFailed && process.env.NODE_ENV === 'development') {
|
|
183
228
|
console.warn(
|
|
@@ -213,16 +258,6 @@ function DataTableRoot<T>({
|
|
|
213
258
|
|
|
214
259
|
const isEmpty = data.length === 0;
|
|
215
260
|
|
|
216
|
-
const hasExplicitColumnSizing = React.useMemo(
|
|
217
|
-
() =>
|
|
218
|
-
columns.some((column) =>
|
|
219
|
-
column.size !== undefined ||
|
|
220
|
-
column.minSize !== undefined ||
|
|
221
|
-
column.maxSize !== undefined
|
|
222
|
-
),
|
|
223
|
-
[columns]
|
|
224
|
-
);
|
|
225
|
-
|
|
226
261
|
const rootClasses = [
|
|
227
262
|
styles.table,
|
|
228
263
|
hasExplicitColumnSizing && styles.fixedLayout,
|
|
@@ -233,28 +268,6 @@ function DataTableRoot<T>({
|
|
|
233
268
|
.filter(Boolean)
|
|
234
269
|
.join(' ');
|
|
235
270
|
|
|
236
|
-
const getColumnSizeStyle = (
|
|
237
|
-
column: {
|
|
238
|
-
getSize: () => number;
|
|
239
|
-
columnDef: { size?: number; minSize?: number; maxSize?: number };
|
|
240
|
-
}
|
|
241
|
-
): React.CSSProperties | undefined => {
|
|
242
|
-
const { size, minSize, maxSize } = column.columnDef;
|
|
243
|
-
const hasExplicitSize = size !== undefined || minSize !== undefined || maxSize !== undefined;
|
|
244
|
-
|
|
245
|
-
if (!hasExplicitSize) {
|
|
246
|
-
return undefined;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const resolvedSize = column.getSize();
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
width: resolvedSize,
|
|
253
|
-
minWidth: minSize ?? resolvedSize,
|
|
254
|
-
maxWidth: maxSize ?? resolvedSize,
|
|
255
|
-
};
|
|
256
|
-
};
|
|
257
|
-
|
|
258
271
|
if (isEmpty) {
|
|
259
272
|
return (
|
|
260
273
|
<div className={styles.emptyState}>
|
|
@@ -263,19 +276,6 @@ function DataTableRoot<T>({
|
|
|
263
276
|
);
|
|
264
277
|
}
|
|
265
278
|
|
|
266
|
-
const isInteractiveTarget = (
|
|
267
|
-
target: EventTarget | null,
|
|
268
|
-
currentTarget: HTMLTableRowElement
|
|
269
|
-
) => {
|
|
270
|
-
if (!(target instanceof Element)) return false;
|
|
271
|
-
|
|
272
|
-
const interactiveElement = target.closest(
|
|
273
|
-
'button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [role="switch"]'
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
return Boolean(interactiveElement && currentTarget.contains(interactiveElement));
|
|
277
|
-
};
|
|
278
|
-
|
|
279
279
|
return (
|
|
280
280
|
<div className={[styles.wrapper, bordered && styles.bordered].filter(Boolean).join(' ')}>
|
|
281
281
|
<table
|
|
@@ -243,6 +243,7 @@ function ListboxItem({
|
|
|
243
243
|
aria-disabled={disabled}
|
|
244
244
|
data-active={context?.activeId === itemId || undefined}
|
|
245
245
|
onClick={disabled ? undefined : onClick}
|
|
246
|
+
onKeyDown={disabled ? undefined : (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick?.(); } }}
|
|
246
247
|
onMouseEnter={handleMouseEnter}
|
|
247
248
|
className={classes}
|
|
248
249
|
style={style}
|
|
@@ -118,17 +118,11 @@ const LoadingRoot = React.forwardRef<HTMLDivElement, LoadingProps>(
|
|
|
118
118
|
.filter(Boolean)
|
|
119
119
|
.join(' ');
|
|
120
120
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return <PulseAnimation className={styles.pulse} />;
|
|
127
|
-
case 'spinner':
|
|
128
|
-
default:
|
|
129
|
-
return <SpinnerIcon className={styles.spinnerIcon} />;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
121
|
+
const animation = variant === 'dots'
|
|
122
|
+
? <DotsAnimation className={styles.dots} />
|
|
123
|
+
: variant === 'pulse'
|
|
124
|
+
? <PulseAnimation className={styles.pulse} />
|
|
125
|
+
: <SpinnerIcon className={styles.spinnerIcon} />;
|
|
132
126
|
|
|
133
127
|
const content = (
|
|
134
128
|
<div
|
|
@@ -139,7 +133,7 @@ const LoadingRoot = React.forwardRef<HTMLDivElement, LoadingProps>(
|
|
|
139
133
|
aria-live="polite"
|
|
140
134
|
{...htmlProps}
|
|
141
135
|
>
|
|
142
|
-
{
|
|
136
|
+
{animation}
|
|
143
137
|
</div>
|
|
144
138
|
);
|
|
145
139
|
|
|
@@ -59,8 +59,8 @@ function FallbackRenderer({ content, className }: { content: string; className?:
|
|
|
59
59
|
const paragraphs = content.split(/\n{2,}/);
|
|
60
60
|
return (
|
|
61
61
|
<div className={className}>
|
|
62
|
-
{paragraphs.map((p
|
|
63
|
-
<p key={
|
|
62
|
+
{paragraphs.map((p) => (
|
|
63
|
+
<p key={p}>{p}</p>
|
|
64
64
|
))}
|
|
65
65
|
</div>
|
|
66
66
|
);
|
|
@@ -3,6 +3,22 @@ import { defineFragment } from '@fragments-sdk/cli/core';
|
|
|
3
3
|
import { Menu } from '.';
|
|
4
4
|
import { Button } from '../Button';
|
|
5
5
|
|
|
6
|
+
function CheckedItemsMenuDemo() {
|
|
7
|
+
const [view, setView] = React.useState('grid');
|
|
8
|
+
return (
|
|
9
|
+
<Menu>
|
|
10
|
+
<Menu.Trigger asChild>
|
|
11
|
+
<Button variant="secondary">View</Button>
|
|
12
|
+
</Menu.Trigger>
|
|
13
|
+
<Menu.Content>
|
|
14
|
+
<Menu.Item checked={view === 'grid'} onSelect={() => setView('grid')}>Grid</Menu.Item>
|
|
15
|
+
<Menu.Item checked={view === 'list'} onSelect={() => setView('list')}>List</Menu.Item>
|
|
16
|
+
<Menu.Item checked={view === 'board'} onSelect={() => setView('board')}>Board</Menu.Item>
|
|
17
|
+
</Menu.Content>
|
|
18
|
+
</Menu>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
export default defineFragment({
|
|
7
23
|
component: Menu,
|
|
8
24
|
|
|
@@ -181,21 +197,7 @@ export default defineFragment({
|
|
|
181
197
|
{
|
|
182
198
|
name: 'With Checked Items',
|
|
183
199
|
description: 'Filter menu with check marks indicating active selections',
|
|
184
|
-
render: () =>
|
|
185
|
-
const [view, setView] = React.useState('grid');
|
|
186
|
-
return (
|
|
187
|
-
<Menu>
|
|
188
|
-
<Menu.Trigger asChild>
|
|
189
|
-
<Button variant="secondary">View</Button>
|
|
190
|
-
</Menu.Trigger>
|
|
191
|
-
<Menu.Content>
|
|
192
|
-
<Menu.Item checked={view === 'grid'} onSelect={() => setView('grid')}>Grid</Menu.Item>
|
|
193
|
-
<Menu.Item checked={view === 'list'} onSelect={() => setView('list')}>List</Menu.Item>
|
|
194
|
-
<Menu.Item checked={view === 'board'} onSelect={() => setView('board')}>Board</Menu.Item>
|
|
195
|
-
</Menu.Content>
|
|
196
|
-
</Menu>
|
|
197
|
-
);
|
|
198
|
-
},
|
|
200
|
+
render: () => <CheckedItemsMenuDemo />,
|
|
199
201
|
},
|
|
200
202
|
{
|
|
201
203
|
name: 'With Submenu',
|
|
@@ -840,7 +840,12 @@ function MobileCollapsibleSection({
|
|
|
840
840
|
{label}
|
|
841
841
|
</Collapsible.Trigger>
|
|
842
842
|
<Collapsible.Content>
|
|
843
|
-
<div
|
|
843
|
+
<div
|
|
844
|
+
className={styles.drawerCollapsibleContent}
|
|
845
|
+
onClick={onLinkClick}
|
|
846
|
+
onKeyDown={(e) => { if (e.key === 'Enter') onLinkClick(); }}
|
|
847
|
+
role="group"
|
|
848
|
+
>
|
|
844
849
|
{children}
|
|
845
850
|
</div>
|
|
846
851
|
</Collapsible.Content>
|
|
@@ -134,11 +134,11 @@ function SkeletonText({
|
|
|
134
134
|
|
|
135
135
|
return (
|
|
136
136
|
<div className={containerClasses} aria-hidden="true">
|
|
137
|
-
{Array.from({ length: lines }, (_,
|
|
138
|
-
const isLast =
|
|
137
|
+
{Array.from({ length: lines }, (_, lineIdx) => {
|
|
138
|
+
const isLast = lineIdx === lines - 1;
|
|
139
139
|
return (
|
|
140
140
|
<div
|
|
141
|
-
key={
|
|
141
|
+
key={`line-${lineIdx}`}
|
|
142
142
|
className={styles.textLine}
|
|
143
143
|
style={isLast && lines > 1 ? { width: `${lastLineWidth}%` } : undefined}
|
|
144
144
|
/>
|
|
@@ -2,6 +2,24 @@ import React from 'react';
|
|
|
2
2
|
import { defineFragment } from '@fragments-sdk/cli/core';
|
|
3
3
|
import { Slider } from '.';
|
|
4
4
|
|
|
5
|
+
function ControlledSliderDemo() {
|
|
6
|
+
const [value, setValue] = React.useState(50);
|
|
7
|
+
return (
|
|
8
|
+
<div style={{ width: '300px', display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
9
|
+
<Slider
|
|
10
|
+
label="Opacity"
|
|
11
|
+
value={value}
|
|
12
|
+
onChange={setValue}
|
|
13
|
+
showValue
|
|
14
|
+
valueSuffix="%"
|
|
15
|
+
/>
|
|
16
|
+
<div style={{ fontSize: '14px', color: 'var(--fui-text-secondary)' }}>
|
|
17
|
+
Current value: {value}%
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
export default defineFragment({
|
|
6
24
|
component: Slider,
|
|
7
25
|
|
|
@@ -165,23 +183,7 @@ export default defineFragment({
|
|
|
165
183
|
{
|
|
166
184
|
name: 'Controlled',
|
|
167
185
|
description: 'Controlled slider with external state',
|
|
168
|
-
render: () =>
|
|
169
|
-
const [value, setValue] = React.useState(50);
|
|
170
|
-
return (
|
|
171
|
-
<div style={{ width: '300px', display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
172
|
-
<Slider
|
|
173
|
-
label="Opacity"
|
|
174
|
-
value={value}
|
|
175
|
-
onChange={setValue}
|
|
176
|
-
showValue
|
|
177
|
-
valueSuffix="%"
|
|
178
|
-
/>
|
|
179
|
-
<div style={{ fontSize: '14px', color: 'var(--fui-text-secondary)' }}>
|
|
180
|
-
Current value: {value}%
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
},
|
|
186
|
+
render: () => <ControlledSliderDemo />,
|
|
185
187
|
},
|
|
186
188
|
{
|
|
187
189
|
name: 'Disabled',
|
|
@@ -147,11 +147,12 @@ const StackRoot = React.forwardRef<HTMLElement, StackProps>(
|
|
|
147
147
|
) : separator;
|
|
148
148
|
|
|
149
149
|
const items: React.ReactNode[] = [];
|
|
150
|
-
validChildren.forEach((child,
|
|
150
|
+
validChildren.forEach((child, idx) => {
|
|
151
151
|
items.push(child);
|
|
152
|
-
if (
|
|
152
|
+
if (idx < validChildren.length - 1) {
|
|
153
|
+
const childKey = React.isValidElement(child) && child.key != null ? child.key : `idx-${idx}`;
|
|
153
154
|
items.push(
|
|
154
|
-
<React.Fragment key={`sep-${
|
|
155
|
+
<React.Fragment key={`sep-${childKey}`}>{separatorEl}</React.Fragment>
|
|
155
156
|
);
|
|
156
157
|
}
|
|
157
158
|
});
|