@fragments-sdk/ui 0.7.5 → 0.8.1

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 (127) hide show
  1. package/README.md +58 -25
  2. package/fragments.json +1 -1
  3. package/package.json +15 -5
  4. package/src/blocks/AppShell.block.ts +2 -2
  5. package/src/blocks/InsetDashboardLayout.block.ts +1 -1
  6. package/src/blocks/LoginForm.block.ts +14 -7
  7. package/src/components/Accordion/Accordion.fragment.tsx +10 -4
  8. package/src/components/Alert/Alert.fragment.tsx +2 -2
  9. package/src/components/Alert/Alert.module.scss +4 -4
  10. package/src/components/AppShell/AppShell.fragment.tsx +3 -3
  11. package/src/components/AppShell/index.tsx +2 -0
  12. package/src/components/Avatar/Avatar.fragment.tsx +7 -3
  13. package/src/components/Avatar/Avatar.module.scss +1 -1
  14. package/src/components/Avatar/index.tsx +37 -1
  15. package/src/components/Badge/Badge.fragment.tsx +5 -5
  16. package/src/components/Badge/Badge.module.scss +4 -4
  17. package/src/components/Badge/index.tsx +5 -1
  18. package/src/components/Box/Box.fragment.tsx +2 -2
  19. package/src/components/Box/index.tsx +5 -1
  20. package/src/components/Breadcrumbs/Breadcrumbs.fragment.tsx +2 -2
  21. package/src/components/Button/Button.fragment.tsx +19 -18
  22. package/src/components/Button/index.tsx +5 -1
  23. package/src/components/ButtonGroup/ButtonGroup.fragment.tsx +2 -2
  24. package/src/components/ButtonGroup/index.tsx +5 -1
  25. package/src/components/Card/Card.fragment.tsx +7 -7
  26. package/src/components/Chart/Chart.fragment.tsx +11 -3
  27. package/src/components/Chart/index.tsx +22 -4
  28. package/src/components/Checkbox/Checkbox.fragment.tsx +2 -2
  29. package/src/components/Checkbox/index.tsx +5 -1
  30. package/src/components/Chip/Chip.fragment.tsx +2 -7
  31. package/src/components/Chip/Chip.module.scss +2 -2
  32. package/src/components/CodeBlock/CodeBlock.fragment.tsx +11 -5
  33. package/src/components/CodeBlock/CodeBlock.module.scss +11 -53
  34. package/src/components/CodeBlock/index.tsx +13 -24
  35. package/src/components/Collapsible/Collapsible.fragment.tsx +2 -2
  36. package/src/components/ColorPicker/ColorPicker.fragment.tsx +2 -2
  37. package/src/components/ColorPicker/index.tsx +5 -1
  38. package/src/components/Combobox/Combobox.fragment.tsx +17 -9
  39. package/src/components/ConversationList/ConversationList.fragment.tsx +5 -5
  40. package/src/components/ConversationList/ConversationList.module.scss +1 -1
  41. package/src/components/DatePicker/DatePicker.fragment.tsx +245 -0
  42. package/src/components/DatePicker/DatePicker.module.scss +394 -0
  43. package/src/components/DatePicker/DatePicker.test.tsx +264 -0
  44. package/src/components/DatePicker/index.tsx +535 -0
  45. package/src/components/Dialog/Dialog.fragment.tsx +2 -2
  46. package/src/components/EmptyState/EmptyState.fragment.tsx +2 -2
  47. package/src/components/Field/Field.fragment.tsx +7 -6
  48. package/src/components/Fieldset/Fieldset.fragment.tsx +7 -6
  49. package/src/components/Form/Form.fragment.tsx +11 -5
  50. package/src/components/Form/index.tsx +5 -1
  51. package/src/components/Grid/Grid.fragment.tsx +6 -2
  52. package/src/components/Header/Header.fragment.tsx +38 -15
  53. package/src/components/Header/Header.module.scss +114 -1
  54. package/src/components/Header/Header.test.tsx +106 -1
  55. package/src/components/Header/index.tsx +100 -31
  56. package/src/components/Icon/Icon.fragment.tsx +8 -3
  57. package/src/components/Icon/index.tsx +5 -1
  58. package/src/components/Image/Image.fragment.tsx +4 -4
  59. package/src/components/Image/index.tsx +5 -1
  60. package/src/components/Input/Input.fragment.tsx +23 -5
  61. package/src/components/Input/Input.module.scss +1 -1
  62. package/src/components/Input/index.tsx +5 -1
  63. package/src/components/Link/Link.fragment.tsx +2 -6
  64. package/src/components/Link/index.tsx +5 -1
  65. package/src/components/List/List.fragment.tsx +2 -2
  66. package/src/components/Listbox/Listbox.fragment.tsx +2 -14
  67. package/src/components/Loading/Loading.fragment.tsx +2 -2
  68. package/src/components/Markdown/Markdown.fragment.tsx +2 -2
  69. package/src/components/Markdown/Markdown.module.scss +11 -3
  70. package/src/components/Markdown/index.tsx +5 -1
  71. package/src/components/Menu/Menu.fragment.tsx +2 -2
  72. package/src/components/Message/Message.fragment.tsx +10 -8
  73. package/src/components/Message/Message.module.scss +1 -1
  74. package/src/components/Popover/Popover.fragment.tsx +2 -2
  75. package/src/components/Progress/Progress.fragment.tsx +16 -2
  76. package/src/components/Progress/index.tsx +9 -2
  77. package/src/components/Prompt/Prompt.fragment.tsx +13 -2
  78. package/src/components/RadioGroup/RadioGroup.fragment.tsx +7 -2
  79. package/src/components/ScrollArea/ScrollArea.fragment.tsx +185 -0
  80. package/src/components/ScrollArea/ScrollArea.module.scss +136 -0
  81. package/src/components/ScrollArea/ScrollArea.test.tsx +38 -0
  82. package/src/components/ScrollArea/index.tsx +121 -0
  83. package/src/components/Select/Select.fragment.tsx +15 -7
  84. package/src/components/Separator/Separator.fragment.tsx +2 -2
  85. package/src/components/Separator/index.tsx +5 -1
  86. package/src/components/Sidebar/Sidebar.fragment.tsx +66 -13
  87. package/src/components/Sidebar/Sidebar.module.scss +69 -21
  88. package/src/components/Sidebar/Sidebar.test.tsx +31 -2
  89. package/src/components/Sidebar/index.tsx +69 -45
  90. package/src/components/Skeleton/Skeleton.fragment.tsx +7 -2
  91. package/src/components/Slider/Slider.fragment.tsx +2 -2
  92. package/src/components/Slider/index.tsx +5 -1
  93. package/src/components/Stack/Stack.fragment.tsx +4 -4
  94. package/src/components/Stack/index.tsx +5 -1
  95. package/src/components/Table/Table.fragment.tsx +31 -2
  96. package/src/components/Table/index.tsx +49 -6
  97. package/src/components/TableOfContents/TableOfContents.fragment.tsx +149 -0
  98. package/src/components/TableOfContents/TableOfContents.module.scss +66 -0
  99. package/src/components/TableOfContents/TableOfContents.test.tsx +126 -0
  100. package/src/components/TableOfContents/index.tsx +110 -0
  101. package/src/components/Tabs/Tabs.fragment.tsx +2 -2
  102. package/src/components/Text/Text.fragment.tsx +2 -2
  103. package/src/components/Text/Text.module.scss +6 -0
  104. package/src/components/Text/Text.test.tsx +5 -0
  105. package/src/components/Text/index.tsx +8 -1
  106. package/src/components/Textarea/Textarea.fragment.tsx +10 -2
  107. package/src/components/Textarea/index.tsx +5 -1
  108. package/src/components/Theme/Theme.fragment.tsx +2 -2
  109. package/src/components/Theme/ThemeToggle.module.scss +1 -1
  110. package/src/components/Theme/index.tsx +8 -1
  111. package/src/components/ThinkingIndicator/ThinkingIndicator.fragment.tsx +5 -4
  112. package/src/components/Toast/Toast.fragment.tsx +14 -2
  113. package/src/components/Toggle/Toggle.fragment.tsx +2 -2
  114. package/src/components/Toggle/index.tsx +5 -1
  115. package/src/components/ToggleGroup/ToggleGroup.fragment.tsx +5 -5
  116. package/src/components/Tooltip/Tooltip.fragment.tsx +20 -2
  117. package/src/components/Tooltip/index.tsx +6 -1
  118. package/src/components/VisuallyHidden/VisuallyHidden.fragment.tsx +2 -2
  119. package/src/components/VisuallyHidden/index.tsx +5 -1
  120. package/src/components/compound-pattern.test.ts +40 -0
  121. package/src/index.ts +29 -0
  122. package/src/recipes/AppShell.recipe.ts +2 -2
  123. package/src/recipes/LoginForm.recipe.ts +14 -7
  124. package/src/tokens/_computed.scss +12 -0
  125. package/src/tokens/_derive.scss +71 -0
  126. package/src/tokens/_mixins.scss +9 -0
  127. package/src/tokens/_variables.scss +26 -4
@@ -0,0 +1,66 @@
1
+ @use '../../tokens/variables' as *;
2
+
3
+ // Root nav wrapper
4
+ .root {
5
+ font-family: var(--fui-font-sans, $fui-font-sans);
6
+ font-size: var(--fui-font-size-sm, $fui-font-size-sm);
7
+ line-height: var(--fui-line-height-normal, $fui-line-height-normal);
8
+ }
9
+
10
+ // Title text ("On This Page")
11
+ .title {
12
+ margin: 0;
13
+ padding: 0 0 var(--fui-space-2, $fui-space-2) 0;
14
+ }
15
+
16
+ // List container
17
+ .list {
18
+ display: flex;
19
+ flex-direction: column;
20
+ list-style: none;
21
+ margin: 0;
22
+ padding: 0;
23
+ }
24
+
25
+ // Individual item
26
+ .item {
27
+ display: block;
28
+ }
29
+
30
+ // Link styling shared by all items
31
+ .link {
32
+ display: block;
33
+ padding: var(--fui-space-1, $fui-space-1) 0 var(--fui-space-1, $fui-space-1) var(--fui-space-3, $fui-space-3);
34
+ border-left: 2px solid transparent;
35
+ color: var(--fui-text-secondary, $fui-text-secondary);
36
+ font-size: var(--fui-font-size-xs, $fui-font-size-xs);
37
+ line-height: var(--fui-line-height-normal, $fui-line-height-normal);
38
+ text-decoration: none;
39
+ transition: color var(--fui-transition-fast, $fui-transition-fast),
40
+ border-color var(--fui-transition-fast, $fui-transition-fast);
41
+
42
+ &:hover {
43
+ color: var(--fui-text-primary, $fui-text-primary);
44
+ }
45
+
46
+ &:focus-visible {
47
+ outline: var(--fui-focus-ring-width, $fui-focus-ring-width) solid var(--fui-focus-ring-color, $fui-focus-ring-color);
48
+ outline-offset: var(--fui-focus-ring-offset, $fui-focus-ring-offset);
49
+ border-radius: var(--fui-radius-sm, $fui-radius-sm);
50
+ }
51
+ }
52
+
53
+ // Indent for depth > 2 (h3s)
54
+ .indent {
55
+ padding-left: var(--fui-space-6, $fui-space-6);
56
+ }
57
+
58
+ // Active state
59
+ .active {
60
+ border-left-color: var(--fui-color-accent, $fui-color-accent);
61
+ color: var(--fui-color-accent, $fui-color-accent);
62
+
63
+ &:hover {
64
+ color: var(--fui-color-accent, $fui-color-accent);
65
+ }
66
+ }
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { render, screen, userEvent, expectNoA11yViolations } from '../../test/utils';
3
+ import { TableOfContents } from './index';
4
+
5
+ describe('TableOfContents', () => {
6
+ it('renders a nav landmark with default aria-label', () => {
7
+ render(
8
+ <TableOfContents>
9
+ <TableOfContents.Item id="intro">Intro</TableOfContents.Item>
10
+ </TableOfContents>
11
+ );
12
+ expect(screen.getByRole('navigation', { name: 'Table of contents' })).toBeInTheDocument();
13
+ });
14
+
15
+ it('renders the default "On This Page" title', () => {
16
+ render(
17
+ <TableOfContents>
18
+ <TableOfContents.Item id="intro">Intro</TableOfContents.Item>
19
+ </TableOfContents>
20
+ );
21
+ expect(screen.getByText('On This Page')).toBeInTheDocument();
22
+ });
23
+
24
+ it('renders a custom title', () => {
25
+ render(
26
+ <TableOfContents title="Contents">
27
+ <TableOfContents.Item id="intro">Intro</TableOfContents.Item>
28
+ </TableOfContents>
29
+ );
30
+ expect(screen.getByText('Contents')).toBeInTheDocument();
31
+ expect(screen.queryByText('On This Page')).not.toBeInTheDocument();
32
+ });
33
+
34
+ it('hides the title when hideTitle is true', () => {
35
+ render(
36
+ <TableOfContents hideTitle>
37
+ <TableOfContents.Item id="intro">Intro</TableOfContents.Item>
38
+ </TableOfContents>
39
+ );
40
+ expect(screen.queryByText('On This Page')).not.toBeInTheDocument();
41
+ });
42
+
43
+ it('renders a custom aria-label', () => {
44
+ render(
45
+ <TableOfContents label="Page sections">
46
+ <TableOfContents.Item id="intro">Intro</TableOfContents.Item>
47
+ </TableOfContents>
48
+ );
49
+ expect(screen.getByRole('navigation', { name: 'Page sections' })).toBeInTheDocument();
50
+ });
51
+
52
+ it('renders items as links with correct href', () => {
53
+ render(
54
+ <TableOfContents>
55
+ <TableOfContents.Item id="setup">Setup</TableOfContents.Item>
56
+ <TableOfContents.Item id="props">Props</TableOfContents.Item>
57
+ </TableOfContents>
58
+ );
59
+ expect(screen.getByRole('link', { name: 'Setup' })).toHaveAttribute('href', '#setup');
60
+ expect(screen.getByRole('link', { name: 'Props' })).toHaveAttribute('href', '#props');
61
+ });
62
+
63
+ it('marks active item with aria-current', () => {
64
+ render(
65
+ <TableOfContents>
66
+ <TableOfContents.Item id="setup" active>Setup</TableOfContents.Item>
67
+ <TableOfContents.Item id="props">Props</TableOfContents.Item>
68
+ </TableOfContents>
69
+ );
70
+ expect(screen.getByRole('link', { name: 'Setup' })).toHaveAttribute('aria-current', 'true');
71
+ expect(screen.getByRole('link', { name: 'Props' })).not.toHaveAttribute('aria-current');
72
+ });
73
+
74
+ it('scrolls to heading on click', async () => {
75
+ const scrollIntoViewMock = vi.fn();
76
+ const heading = document.createElement('h2');
77
+ heading.id = 'setup';
78
+ heading.scrollIntoView = scrollIntoViewMock;
79
+ document.body.appendChild(heading);
80
+
81
+ const user = userEvent.setup();
82
+ render(
83
+ <TableOfContents>
84
+ <TableOfContents.Item id="setup">Setup</TableOfContents.Item>
85
+ </TableOfContents>
86
+ );
87
+
88
+ await user.click(screen.getByRole('link', { name: 'Setup' }));
89
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
90
+
91
+ document.body.removeChild(heading);
92
+ });
93
+
94
+ it('renders items in a list', () => {
95
+ render(
96
+ <TableOfContents>
97
+ <TableOfContents.Item id="a">A</TableOfContents.Item>
98
+ <TableOfContents.Item id="b">B</TableOfContents.Item>
99
+ <TableOfContents.Item id="c">C</TableOfContents.Item>
100
+ </TableOfContents>
101
+ );
102
+ expect(screen.getByRole('list')).toBeInTheDocument();
103
+ expect(screen.getAllByRole('listitem')).toHaveLength(3);
104
+ });
105
+
106
+ it('passes additional className to root', () => {
107
+ const { container } = render(
108
+ <TableOfContents className="custom-class">
109
+ <TableOfContents.Item id="a">A</TableOfContents.Item>
110
+ </TableOfContents>
111
+ );
112
+ expect(container.querySelector('.custom-class')).toBeInTheDocument();
113
+ });
114
+
115
+ it('has no accessibility violations', async () => {
116
+ const { container } = render(
117
+ <TableOfContents>
118
+ <TableOfContents.Item id="intro">Introduction</TableOfContents.Item>
119
+ <TableOfContents.Item id="setup" active>Setup</TableOfContents.Item>
120
+ <TableOfContents.Item id="api" indent>API Reference</TableOfContents.Item>
121
+ <TableOfContents.Item id="examples">Examples</TableOfContents.Item>
122
+ </TableOfContents>
123
+ );
124
+ await expectNoA11yViolations(container);
125
+ });
126
+ });
@@ -0,0 +1,110 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import styles from './TableOfContents.module.scss';
5
+ import { Text } from '../Text';
6
+ import '../../styles/globals.scss';
7
+
8
+ // ============================================
9
+ // Types
10
+ // ============================================
11
+
12
+ export interface TableOfContentsProps extends React.HTMLAttributes<HTMLElement> {
13
+ children: React.ReactNode;
14
+ /** Label for the nav landmark (default: "Table of contents") */
15
+ label?: string;
16
+ /** Title displayed above the list (default: "On This Page") */
17
+ title?: string;
18
+ /** Hide the title */
19
+ hideTitle?: boolean;
20
+ }
21
+
22
+ export interface TableOfContentsItemProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> {
23
+ children: React.ReactNode;
24
+ /** The heading ID to link to */
25
+ id: string;
26
+ /** Whether this item is currently active/visible */
27
+ active?: boolean;
28
+ /** Indent level — use for sub-headings (h3, h4, etc.) */
29
+ indent?: boolean;
30
+ }
31
+
32
+ // ============================================
33
+ // Components
34
+ // ============================================
35
+
36
+ function TableOfContentsRoot({
37
+ children,
38
+ label = 'Table of contents',
39
+ title = 'On This Page',
40
+ hideTitle = false,
41
+ className,
42
+ ...htmlProps
43
+ }: TableOfContentsProps) {
44
+ const classes = [styles.root, className].filter(Boolean).join(' ');
45
+
46
+ return (
47
+ <nav aria-label={label} className={classes} {...htmlProps}>
48
+ {!hideTitle && (
49
+ <Text as="p" variant="section-label" className={styles.title}>
50
+ {title}
51
+ </Text>
52
+ )}
53
+ <ul className={styles.list}>
54
+ {children}
55
+ </ul>
56
+ </nav>
57
+ );
58
+ }
59
+
60
+ function TableOfContentsItem({
61
+ children,
62
+ id,
63
+ active = false,
64
+ indent = false,
65
+ className,
66
+ onClick,
67
+ ...htmlProps
68
+ }: TableOfContentsItemProps) {
69
+ const linkClasses = [
70
+ styles.link,
71
+ indent && styles.indent,
72
+ active && styles.active,
73
+ className,
74
+ ].filter(Boolean).join(' ');
75
+
76
+ const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
77
+ e.preventDefault();
78
+ const el = document.getElementById(id);
79
+ if (el) {
80
+ el.scrollIntoView({ behavior: 'smooth' });
81
+ history.replaceState(null, '', `#${id}`);
82
+ }
83
+ onClick?.(e);
84
+ };
85
+
86
+ return (
87
+ <li className={styles.item}>
88
+ <a
89
+ href={`#${id}`}
90
+ className={linkClasses}
91
+ onClick={handleClick}
92
+ aria-current={active ? 'true' : undefined}
93
+ {...htmlProps}
94
+ >
95
+ {children}
96
+ </a>
97
+ </li>
98
+ );
99
+ }
100
+
101
+ // ============================================
102
+ // Export compound component
103
+ // ============================================
104
+
105
+ export const TableOfContents = Object.assign(TableOfContentsRoot, {
106
+ Item: TableOfContentsItem,
107
+ });
108
+
109
+ // Re-export individual components
110
+ export { TableOfContentsRoot, TableOfContentsItem };
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Tabs } from '.';
4
4
 
5
- export default defineSegment({
5
+ export default defineFragment({
6
6
  component: Tabs,
7
7
 
8
8
  meta: {
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Text } from '.';
4
4
 
5
- export default defineSegment({
5
+ export default defineFragment({
6
6
  component: Text,
7
7
 
8
8
  meta: {
@@ -1,4 +1,5 @@
1
1
  @use '../../tokens/variables' as *;
2
+ @use '../../tokens/mixins' as *;
2
3
 
3
4
  .text {
4
5
  font-family: var(--fui-font-sans, $fui-font-sans);
@@ -6,6 +7,11 @@
6
7
  margin: 0;
7
8
  }
8
9
 
10
+ // Variants
11
+ .variant-section-label {
12
+ @include section-label-text;
13
+ }
14
+
9
15
  // Font sizes
10
16
  .size-2xs {
11
17
  font-size: var(--fui-font-size-2xs, $fui-font-size-2xs);
@@ -22,6 +22,11 @@ describe('Text', () => {
22
22
  expect(el).toHaveClass('color-secondary');
23
23
  });
24
24
 
25
+ it('applies section-label variant class', () => {
26
+ render(<Text variant="section-label">Label</Text>);
27
+ expect(screen.getByText('Label')).toHaveClass('variant-section-label');
28
+ });
29
+
25
30
  it('applies truncate class', () => {
26
31
  render(<Text truncate>Long text that should truncate</Text>);
27
32
  expect(screen.getByText('Long text that should truncate')).toHaveClass('truncate');
@@ -5,6 +5,7 @@ import '../../styles/globals.scss';
5
5
  export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'> {
6
6
  children: React.ReactNode;
7
7
  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div' | 'strong' | 'em' | 'small' | 'mark' | 'del' | 'ins' | 'sub' | 'sup' | 'time' | 'address' | 'blockquote' | 'cite' | 'code' | 'abbr';
8
+ variant?: 'section-label';
8
9
  size?: '2xs' | 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
9
10
  weight?: 'normal' | 'medium' | 'semibold';
10
11
  color?: 'primary' | 'secondary' | 'tertiary';
@@ -15,11 +16,12 @@ export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'colo
15
16
  lineClamp?: number;
16
17
  }
17
18
 
18
- export const Text = React.forwardRef<HTMLElement, TextProps>(
19
+ const TextRoot = React.forwardRef<HTMLElement, TextProps>(
19
20
  function Text(
20
21
  {
21
22
  children,
22
23
  as: Component = 'span',
24
+ variant,
23
25
  size,
24
26
  weight,
25
27
  color,
@@ -34,6 +36,7 @@ export const Text = React.forwardRef<HTMLElement, TextProps>(
34
36
  ) {
35
37
  const classes = [
36
38
  styles.text,
39
+ variant && styles[`variant-${variant}`],
37
40
  size && styles[`size-${size}`],
38
41
  weight && styles[`weight-${weight}`],
39
42
  color && styles[`color-${color}`],
@@ -56,3 +59,7 @@ export const Text = React.forwardRef<HTMLElement, TextProps>(
56
59
  );
57
60
  }
58
61
  );
62
+
63
+ export const Text = Object.assign(TextRoot, {
64
+ Root: TextRoot,
65
+ });
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Textarea } from '.';
4
4
 
5
- export default defineSegment({
5
+ export default defineFragment({
6
6
  component: Textarea,
7
7
 
8
8
  meta: {
@@ -52,6 +52,14 @@ export default defineSegment({
52
52
  default: 3,
53
53
  description: 'Number of visible text rows',
54
54
  },
55
+ minRows: {
56
+ type: 'number',
57
+ description: 'Minimum number of rows when auto-resizing',
58
+ },
59
+ maxRows: {
60
+ type: 'number',
61
+ description: 'Maximum number of rows when auto-resizing',
62
+ },
55
63
  label: {
56
64
  type: 'string',
57
65
  description: 'Label text above the textarea',
@@ -49,7 +49,7 @@ function mergeAriaIds(...ids: Array<string | undefined>): string | undefined {
49
49
  return merged.length > 0 ? merged : undefined;
50
50
  }
51
51
 
52
- export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
52
+ const TextareaRoot = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
53
53
  function Textarea(
54
54
  {
55
55
  value,
@@ -143,3 +143,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
143
143
  );
144
144
  }
145
145
  );
146
+
147
+ export const Textarea = Object.assign(TextareaRoot, {
148
+ Root: TextareaRoot,
149
+ });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { ThemeProvider, ThemeToggle, useTheme } from '.';
4
4
 
5
5
  // Demo component to show hook usage
@@ -14,7 +14,7 @@ function ThemeDemo() {
14
14
  );
15
15
  }
16
16
 
17
- export default defineSegment({
17
+ export default defineFragment({
18
18
  component: ThemeProvider,
19
19
 
20
20
  meta: {
@@ -2,7 +2,7 @@
2
2
  @use '../../tokens/mixins' as *;
3
3
 
4
4
  // ============================================
5
- // Theme Toggle - Segmented Button Group
5
+ // Theme Toggle - Fragmented Button Group
6
6
  // ============================================
7
7
 
8
8
  .toggleGroup {
@@ -254,7 +254,7 @@ function ThemeProvider({
254
254
  }
255
255
 
256
256
  /**
257
- * ThemeToggle - Segmented button group to toggle between light and dark themes
257
+ * ThemeToggle - Fragmented button group to toggle between light and dark themes
258
258
  *
259
259
  * Can be used in two modes:
260
260
  * 1. Uncontrolled (default): Uses ThemeProvider context to get/set theme
@@ -340,4 +340,11 @@ function ThemeToggle({
340
340
  // Exports
341
341
  // ============================================
342
342
 
343
+ export const Theme = Object.assign(ThemeProvider, {
344
+ Root: ThemeProvider,
345
+ Provider: ThemeProvider,
346
+ Toggle: ThemeToggle,
347
+ useTheme,
348
+ });
349
+
343
350
  export { ThemeProvider, ThemeToggle, useTheme };
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { ThinkingIndicator } from '.';
4
4
 
5
- export default defineSegment({
5
+ export default defineFragment({
6
6
  component: ThinkingIndicator,
7
7
 
8
8
  meta: {
@@ -51,7 +51,8 @@ export default defineSegment({
51
51
  description: 'Status text',
52
52
  },
53
53
  variant: {
54
- type: '"dots" | "pulse" | "spinner"',
54
+ type: 'enum',
55
+ values: ['dots', 'pulse', 'spinner'],
55
56
  default: '"dots"',
56
57
  description: 'Animation style',
57
58
  },
@@ -61,7 +62,7 @@ export default defineSegment({
61
62
  description: 'Show elapsed time',
62
63
  },
63
64
  steps: {
64
- type: 'ThinkingStep[]',
65
+ type: 'array',
65
66
  description: 'Multi-step progress array',
66
67
  },
67
68
  },
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Toast, ToastProvider, useToast } from '.';
4
4
 
5
5
  // Demo component that triggers toasts
@@ -45,7 +45,7 @@ function ToastDemoWrapper() {
45
45
  );
46
46
  }
47
47
 
48
- export default defineSegment({
48
+ export default defineFragment({
49
49
  component: Toast,
50
50
 
51
51
  meta: {
@@ -108,6 +108,18 @@ export default defineSegment({
108
108
  type: 'object',
109
109
  description: 'Optional action button { label, onClick }',
110
110
  },
111
+ onDismiss: {
112
+ type: 'function',
113
+ description: 'Callback when toast should be dismissed',
114
+ },
115
+ onPause: {
116
+ type: 'function',
117
+ description: 'Callback when auto-dismiss timer should pause',
118
+ },
119
+ onResume: {
120
+ type: 'function',
121
+ description: 'Callback when auto-dismiss timer should resume',
122
+ },
111
123
  },
112
124
 
113
125
  relations: [
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Toggle } from '.';
4
4
 
5
5
  // Stateful wrapper for interactive demos
@@ -8,7 +8,7 @@ function StatefulToggle(props: React.ComponentProps<typeof Toggle>) {
8
8
  return <Toggle {...props} checked={checked} onChange={setChecked} />;
9
9
  }
10
10
 
11
- export default defineSegment({
11
+ export default defineFragment({
12
12
  component: Toggle,
13
13
 
14
14
  meta: {
@@ -20,7 +20,7 @@ export interface ToggleProps {
20
20
  'aria-describedby'?: string;
21
21
  }
22
22
 
23
- export const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
23
+ const ToggleRoot = React.forwardRef<HTMLButtonElement, ToggleProps>(
24
24
  function Toggle(
25
25
  {
26
26
  checked,
@@ -90,3 +90,7 @@ export const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
90
90
  );
91
91
  }
92
92
  );
93
+
94
+ export const Toggle = Object.assign(ToggleRoot, {
95
+ Root: ToggleRoot,
96
+ });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { ToggleGroup } from '.';
4
4
 
5
5
  function DefaultExample() {
@@ -92,7 +92,7 @@ function DisabledItemExample() {
92
92
  );
93
93
  }
94
94
 
95
- export default defineSegment({
95
+ export default defineFragment({
96
96
  component: ToggleGroup,
97
97
 
98
98
  meta: {
@@ -100,7 +100,7 @@ export default defineSegment({
100
100
  description: 'A group of toggle buttons where only one can be selected at a time. Useful for switching between views, modes, or options.',
101
101
  category: 'forms',
102
102
  status: 'stable',
103
- tags: ['toggle', 'group', 'segmented', 'control', 'tabs', 'switch'],
103
+ tags: ['toggle', 'group', 'fragmented', 'control', 'tabs', 'switch'],
104
104
  since: '0.2.0',
105
105
  },
106
106
 
@@ -108,7 +108,7 @@ export default defineSegment({
108
108
  when: [
109
109
  'Switching between mutually exclusive views or modes',
110
110
  'Selecting one option from a small set (2-5 options)',
111
- 'Segmented controls like view switchers',
111
+ 'Fragmented controls like view switchers',
112
112
  'Filter or sort options',
113
113
  ],
114
114
  whenNot: [
@@ -185,7 +185,7 @@ export default defineSegment({
185
185
  scenarioTags: [
186
186
  'forms.selection',
187
187
  'input.toggle',
188
- 'control.segmented',
188
+ 'control.fragmented',
189
189
  ],
190
190
  a11yRules: ['A11Y_GROUP_ROLE', 'A11Y_KEYBOARD_ACCESSIBLE'],
191
191
  },
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import { defineSegment } from '@fragments/core';
2
+ import { defineFragment } from '@fragments/core';
3
3
  import { Tooltip } from '.';
4
4
  import { Button } from '../Button';
5
5
 
6
- export default defineSegment({
6
+ export default defineFragment({
7
7
  component: Tooltip,
8
8
 
9
9
  meta: {
@@ -74,6 +74,11 @@ export default defineSegment({
74
74
  description: 'Delay before showing (ms)',
75
75
  default: '400',
76
76
  },
77
+ closeDelay: {
78
+ type: 'number',
79
+ description: 'Delay before hiding (ms)',
80
+ default: '0',
81
+ },
77
82
  arrow: {
78
83
  type: 'boolean',
79
84
  description: 'Show arrow pointing to trigger',
@@ -84,6 +89,19 @@ export default defineSegment({
84
89
  description: 'Disable the tooltip',
85
90
  default: 'false',
86
91
  },
92
+ open: {
93
+ type: 'boolean',
94
+ description: 'Controlled open state',
95
+ },
96
+ defaultOpen: {
97
+ type: 'boolean',
98
+ description: 'Default open state',
99
+ default: 'false',
100
+ },
101
+ onOpenChange: {
102
+ type: 'function',
103
+ description: 'Callback when open state changes',
104
+ },
87
105
  },
88
106
 
89
107
  relations: [