@fragments-sdk/ui 0.9.6 → 0.10.0
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/README.md +32 -24
- 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/components/Theme/index.cjs +86 -1
- package/dist/components/Theme/index.cjs.map +1 -1
- package/dist/components/Theme/index.d.ts +44 -1
- package/dist/components/Theme/index.d.ts.map +1 -1
- package/dist/components/Theme/index.js +86 -1
- package/dist/components/Theme/index.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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 +6 -2
- 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/src/components/Theme/index.tsx +168 -1
- package/src/index.ts +6 -0
- package/src/tokens/_seeds.scss +20 -0
|
@@ -13,7 +13,9 @@ export interface ThemeProviderProps {
|
|
|
13
13
|
children: React.ReactNode;
|
|
14
14
|
/** Default theme mode for uncontrolled usage */
|
|
15
15
|
defaultMode?: ThemeMode;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Use `defaultMode` instead. This alias will be removed in v1.0.
|
|
18
|
+
*/
|
|
17
19
|
defaultTheme?: ThemeMode;
|
|
18
20
|
/** Controlled theme mode */
|
|
19
21
|
mode?: ThemeMode;
|
|
@@ -183,6 +185,14 @@ function ThemeProvider({
|
|
|
183
185
|
}: ThemeProviderProps) {
|
|
184
186
|
const systemPreference = useSystemPreference();
|
|
185
187
|
|
|
188
|
+
// Warn on deprecated prop usage (dev only)
|
|
189
|
+
if (process.env.NODE_ENV !== 'production' && defaultTheme !== undefined) {
|
|
190
|
+
console.warn(
|
|
191
|
+
'[Fragments] ThemeProvider: `defaultTheme` is deprecated. Use `defaultMode` instead. ' +
|
|
192
|
+
'`defaultTheme` will be removed in v1.0.'
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
186
196
|
// Resolve default: defaultMode takes precedence, then defaultTheme, then 'system'
|
|
187
197
|
const resolvedDefault = defaultMode ?? defaultTheme ?? 'system';
|
|
188
198
|
|
|
@@ -353,3 +363,160 @@ export const Theme = Object.assign(ThemeProvider, {
|
|
|
353
363
|
});
|
|
354
364
|
|
|
355
365
|
export { ThemeProvider, ThemeToggle, useTheme };
|
|
366
|
+
|
|
367
|
+
// ============================================
|
|
368
|
+
// configureTheme — JS-only seed configuration
|
|
369
|
+
// ============================================
|
|
370
|
+
|
|
371
|
+
export type NeutralPalette = 'stone' | 'ice' | 'earth' | 'sand' | 'fire';
|
|
372
|
+
export type DensityPreset = 'compact' | 'default' | 'relaxed';
|
|
373
|
+
export type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';
|
|
374
|
+
|
|
375
|
+
export interface ConfigureThemeOptions {
|
|
376
|
+
/** Brand/accent color as hex */
|
|
377
|
+
brand?: string;
|
|
378
|
+
/** Neutral palette name */
|
|
379
|
+
neutral?: NeutralPalette;
|
|
380
|
+
/** Spacing density preset */
|
|
381
|
+
density?: DensityPreset;
|
|
382
|
+
/** Border radius style */
|
|
383
|
+
radiusStyle?: RadiusStyle;
|
|
384
|
+
/** Danger/error color as hex */
|
|
385
|
+
danger?: string;
|
|
386
|
+
/** Success color as hex */
|
|
387
|
+
success?: string;
|
|
388
|
+
/** Warning color as hex */
|
|
389
|
+
warning?: string;
|
|
390
|
+
/** Info color as hex */
|
|
391
|
+
info?: string;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// -- Radius presets (match _radius.scss) --
|
|
395
|
+
|
|
396
|
+
const RADIUS_PRESETS: Record<RadiusStyle, Record<string, string>> = {
|
|
397
|
+
sharp: { sm: '0', md: '0', lg: '0', xl: '0' },
|
|
398
|
+
subtle: { sm: '0.125rem', md: '0.25rem', lg: '0.375rem', xl: '0.5rem' },
|
|
399
|
+
default: { sm: '0.25rem', md: '0.429rem', lg: '0.571rem', xl: '0.857rem' },
|
|
400
|
+
rounded: { sm: '0.375rem', md: '0.5rem', lg: '0.75rem', xl: '1rem' },
|
|
401
|
+
pill: { sm: '0.5rem', md: '0.75rem', lg: '1rem', xl: '1.5rem' },
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// -- Density presets (match _density.scss) --
|
|
405
|
+
|
|
406
|
+
interface DensityConfig {
|
|
407
|
+
baseUnit: number;
|
|
408
|
+
baseFontSize: number;
|
|
409
|
+
buttonHeights: [number, number, number]; // sm, md, lg
|
|
410
|
+
inputHeights: [number, number, number]; // sm, default, lg
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const DENSITY_CONFIGS: Record<DensityPreset, DensityConfig> = {
|
|
414
|
+
compact: { baseUnit: 6, baseFontSize: 14, buttonHeights: [24, 30, 36], inputHeights: [24, 32, 36] },
|
|
415
|
+
default: { baseUnit: 7, baseFontSize: 14, buttonHeights: [28, 36, 44], inputHeights: [28, 40, 44] },
|
|
416
|
+
relaxed: { baseUnit: 8, baseFontSize: 14, buttonHeights: [32, 40, 48], inputHeights: [32, 44, 48] },
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
function pxToRem(px: number, baseFontSize: number): string {
|
|
420
|
+
return `${px / baseFontSize}rem`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function hexToRgb(hex: string): [number, number, number] | null {
|
|
424
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
425
|
+
if (!result) return null;
|
|
426
|
+
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function adjustLightness(hex: string, amount: number): string {
|
|
430
|
+
const rgb = hexToRgb(hex);
|
|
431
|
+
if (!rgb) return hex;
|
|
432
|
+
const [r, g, b] = rgb;
|
|
433
|
+
const adjust = (v: number) => Math.max(0, Math.min(255, Math.round(v + amount)));
|
|
434
|
+
return `#${adjust(r).toString(16).padStart(2, '0')}${adjust(g).toString(16).padStart(2, '0')}${adjust(b).toString(16).padStart(2, '0')}`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function setVar(el: HTMLElement, name: string, value: string) {
|
|
438
|
+
el.style.setProperty(name, value);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Configure theme seeds at runtime via JS. Sets CSS custom properties on
|
|
443
|
+
* `:root` without requiring SCSS. Call this once at app startup.
|
|
444
|
+
*
|
|
445
|
+
* Note: For full control over all 120+ tokens, use the SCSS `@use...with()`
|
|
446
|
+
* approach. `configureTheme` covers the most commonly customized tokens.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```ts
|
|
450
|
+
* import { configureTheme } from '@fragments-sdk/ui';
|
|
451
|
+
*
|
|
452
|
+
* configureTheme({
|
|
453
|
+
* brand: '#6366f1',
|
|
454
|
+
* neutral: 'ice',
|
|
455
|
+
* density: 'compact',
|
|
456
|
+
* radiusStyle: 'rounded',
|
|
457
|
+
* });
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export function configureTheme(options: ConfigureThemeOptions): void {
|
|
461
|
+
if (typeof document === 'undefined') return;
|
|
462
|
+
|
|
463
|
+
const root = document.documentElement;
|
|
464
|
+
|
|
465
|
+
// -- Brand / Accent --
|
|
466
|
+
if (options.brand) {
|
|
467
|
+
setVar(root, '--fui-color-accent', options.brand);
|
|
468
|
+
setVar(root, '--fui-color-accent-hover', adjustLightness(options.brand, -20));
|
|
469
|
+
setVar(root, '--fui-color-accent-active', adjustLightness(options.brand, -40));
|
|
470
|
+
setVar(root, '--fui-focus-ring-color', `${options.brand}66`); // 40% alpha
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// -- Semantic colors --
|
|
474
|
+
if (options.danger) {
|
|
475
|
+
setVar(root, '--fui-color-danger', options.danger);
|
|
476
|
+
setVar(root, '--fui-color-danger-hover', adjustLightness(options.danger, -20));
|
|
477
|
+
}
|
|
478
|
+
if (options.success) setVar(root, '--fui-color-success', options.success);
|
|
479
|
+
if (options.warning) setVar(root, '--fui-color-warning', options.warning);
|
|
480
|
+
if (options.info) setVar(root, '--fui-color-info', options.info);
|
|
481
|
+
|
|
482
|
+
// -- Radius --
|
|
483
|
+
if (options.radiusStyle) {
|
|
484
|
+
const r = RADIUS_PRESETS[options.radiusStyle];
|
|
485
|
+
if (r) {
|
|
486
|
+
setVar(root, '--fui-radius-sm', r.sm);
|
|
487
|
+
setVar(root, '--fui-radius-md', r.md);
|
|
488
|
+
setVar(root, '--fui-radius-lg', r.lg);
|
|
489
|
+
setVar(root, '--fui-radius-xl', r.xl);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// -- Density --
|
|
494
|
+
if (options.density) {
|
|
495
|
+
const d = DENSITY_CONFIGS[options.density];
|
|
496
|
+
if (d) {
|
|
497
|
+
const unitRem = d.baseUnit / d.baseFontSize;
|
|
498
|
+
|
|
499
|
+
// Spacing scale
|
|
500
|
+
setVar(root, '--fui-space-1', `${unitRem}rem`);
|
|
501
|
+
setVar(root, '--fui-space-2', `${unitRem * 2}rem`);
|
|
502
|
+
setVar(root, '--fui-space-3', `${unitRem * 3}rem`);
|
|
503
|
+
setVar(root, '--fui-space-4', `${unitRem * 4}rem`);
|
|
504
|
+
setVar(root, '--fui-space-5', `${unitRem * 5}rem`);
|
|
505
|
+
setVar(root, '--fui-space-6', `${unitRem * 6}rem`);
|
|
506
|
+
setVar(root, '--fui-space-8', `${unitRem * 8}rem`);
|
|
507
|
+
setVar(root, '--fui-space-10', `${unitRem * 10}rem`);
|
|
508
|
+
setVar(root, '--fui-space-12', `${unitRem * 12}rem`);
|
|
509
|
+
|
|
510
|
+
// Component heights
|
|
511
|
+
setVar(root, '--fui-button-height-sm', pxToRem(d.buttonHeights[0], d.baseFontSize));
|
|
512
|
+
setVar(root, '--fui-button-height-md', pxToRem(d.buttonHeights[1], d.baseFontSize));
|
|
513
|
+
setVar(root, '--fui-button-height-lg', pxToRem(d.buttonHeights[2], d.baseFontSize));
|
|
514
|
+
setVar(root, '--fui-input-height-sm', pxToRem(d.inputHeights[0], d.baseFontSize));
|
|
515
|
+
setVar(root, '--fui-input-height', pxToRem(d.inputHeights[1], d.baseFontSize));
|
|
516
|
+
setVar(root, '--fui-input-height-lg', pxToRem(d.inputHeights[2], d.baseFontSize));
|
|
517
|
+
|
|
518
|
+
// Base unit
|
|
519
|
+
setVar(root, '--fui-base-unit', `${d.baseUnit}px`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -305,13 +305,19 @@ export {
|
|
|
305
305
|
|
|
306
306
|
// Theme
|
|
307
307
|
export {
|
|
308
|
+
Theme,
|
|
308
309
|
ThemeProvider,
|
|
309
310
|
ThemeToggle,
|
|
310
311
|
useTheme,
|
|
312
|
+
configureTheme,
|
|
311
313
|
type ThemeProviderProps,
|
|
312
314
|
type ThemeToggleProps,
|
|
313
315
|
type ThemeMode,
|
|
314
316
|
type UseThemeReturn,
|
|
317
|
+
type ConfigureThemeOptions,
|
|
318
|
+
type NeutralPalette,
|
|
319
|
+
type DensityPreset,
|
|
320
|
+
type RadiusStyle,
|
|
315
321
|
} from './components/Theme';
|
|
316
322
|
|
|
317
323
|
// Header
|
package/src/tokens/_seeds.scss
CHANGED
|
@@ -38,6 +38,26 @@ $fui-density: "default" !default;
|
|
|
38
38
|
/// @type String
|
|
39
39
|
$fui-radius-style: "default" !default;
|
|
40
40
|
|
|
41
|
+
// --------------------------------------------
|
|
42
|
+
// Seed Validation
|
|
43
|
+
// --------------------------------------------
|
|
44
|
+
// Fail fast at compile time if invalid seed values are provided.
|
|
45
|
+
|
|
46
|
+
$_valid-neutrals: "stone", "ice", "earth", "sand", "fire";
|
|
47
|
+
@if not index($_valid-neutrals, $fui-neutral) {
|
|
48
|
+
@error "Invalid $fui-neutral: '#{$fui-neutral}'. Must be one of: #{$_valid-neutrals}";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$_valid-densities: "compact", "default", "relaxed";
|
|
52
|
+
@if not index($_valid-densities, $fui-density) {
|
|
53
|
+
@error "Invalid $fui-density: '#{$fui-density}'. Must be one of: #{$_valid-densities}";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$_valid-radii: "sharp", "subtle", "default", "rounded", "pill";
|
|
57
|
+
@if not index($_valid-radii, $fui-radius-style) {
|
|
58
|
+
@error "Invalid $fui-radius-style: '#{$fui-radius-style}'. Must be one of: #{$_valid-radii}";
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
// --------------------------------------------
|
|
42
62
|
// Semantic Color Overrides (Optional)
|
|
43
63
|
// --------------------------------------------
|