@keboola/design 1.2.0 → 1.3.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.
Files changed (173) hide show
  1. package/README.md +25 -0
  2. package/dist/icons.cjs +731 -0
  3. package/dist/icons.cjs.map +1 -0
  4. package/dist/icons.js +29 -0
  5. package/dist/icons.js.map +1 -0
  6. package/dist/index.cjs +429 -333
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.js +426 -331
  9. package/dist/index.js.map +1 -1
  10. package/dist/markdown-editor.cjs +270 -135
  11. package/dist/markdown-editor.cjs.map +1 -1
  12. package/dist/markdown-editor.js +270 -135
  13. package/dist/markdown-editor.js.map +1 -1
  14. package/dist/mermaid.cjs +24 -15
  15. package/dist/mermaid.cjs.map +1 -1
  16. package/dist/mermaid.js +24 -15
  17. package/dist/mermaid.js.map +1 -1
  18. package/dist/styles.css +1 -1
  19. package/package.json +12 -5
  20. package/src/blocks/AIKit/CollapsibleStepHeader.tsx +7 -8
  21. package/src/blocks/AIKit/InlineStepLabel.tsx +7 -7
  22. package/src/blocks/AIKit/MessageBubble.stories.tsx +11 -3
  23. package/src/blocks/AIKit/MessageBubble.tsx +1 -1
  24. package/src/blocks/AIKit/PlanApproval.tsx +9 -8
  25. package/src/blocks/AIKit/PlanRewritingIndicator.tsx +2 -2
  26. package/src/blocks/AIKit/PromptInput.stories.tsx +10 -2
  27. package/src/blocks/AIKit/PromptInputSubmit.tsx +12 -8
  28. package/src/blocks/AIKit/Question.tsx +8 -9
  29. package/src/blocks/AIKit/SuggestedPrompt.tsx +12 -5
  30. package/src/blocks/AIKit/ThinkingIndicator.tsx +2 -2
  31. package/src/blocks/AutoSaveTextarea/AutoSaveTextarea.tsx +4 -4
  32. package/src/blocks/ClipboardButton/ClipboardButton.tsx +7 -5
  33. package/src/blocks/ClipboardButton/ClipboardInline.tsx +2 -3
  34. package/src/blocks/ColorInput/ColorInput.tsx +4 -2
  35. package/src/blocks/ColorInput/ColorPicker.tsx +5 -2
  36. package/src/blocks/EmptyState/EmptyState.tsx +16 -0
  37. package/src/blocks/ErrorState/ErrorState.tsx +15 -0
  38. package/src/blocks/Form/formHook.ts +15 -0
  39. package/src/blocks/MultiActionButtonGroup/MultiActionButton.stories.tsx +12 -11
  40. package/src/blocks/MultiActionButtonGroup/MultiActionButton.tsx +4 -4
  41. package/src/blocks/MultiCombobox/MultiCombobox.tsx +28 -12
  42. package/src/blocks/PageHeader/AGENTS.md +63 -3
  43. package/src/blocks/PageHeader/PageHeader.stories.tsx +124 -28
  44. package/src/blocks/PageHeader/PageHeader.tsx +15 -0
  45. package/src/blocks/PageHeader/PageHeaderDetail.tsx +23 -1
  46. package/src/blocks/PageHeader/PageHeaderIndex.tsx +20 -1
  47. package/src/blocks/PageLayout/PageLayout.tsx +26 -0
  48. package/src/blocks/PageToolbar/PageToolbar.stories.tsx +348 -0
  49. package/src/blocks/PageToolbar/PageToolbar.tsx +119 -0
  50. package/src/blocks/PageToolbar/index.ts +1 -0
  51. package/src/blocks/PasswordInput/PasswordInput.tsx +10 -10
  52. package/src/blocks/SearchInput/SearchInput.tsx +30 -4
  53. package/src/blocks/SearchInputWithNavigation/SearchInputWithNavigation.tsx +9 -6
  54. package/src/blocks/SplitButton/SplitButton.stories.tsx +7 -4
  55. package/src/blocks/SplitButton/SplitButton.tsx +17 -4
  56. package/src/blocks/Tagger/TagItem.tsx +4 -1
  57. package/src/blocks/Tagger/TagSelectPopover/TagSelectCreateItem.tsx +3 -1
  58. package/src/blocks/Tagger/TagSelectPopover/TagSelectList.tsx +2 -2
  59. package/src/blocks/Tagger/Tagger.tsx +2 -2
  60. package/src/components/Alert/Alert.stories.tsx +11 -3
  61. package/src/components/Alert/Alert.tsx +19 -1
  62. package/src/components/Alert/AlertCollapsibleIcon.tsx +3 -4
  63. package/src/components/Alert/AlertIcon.tsx +18 -21
  64. package/src/components/Badge/Badge.stories.tsx +22 -4
  65. package/src/components/Badge/Badge.tsx +16 -0
  66. package/src/components/Badge/BadgeButton.tsx +7 -6
  67. package/src/components/Breadcrumb/BreadcrumbEllipsis.tsx +2 -2
  68. package/src/components/Breadcrumb/BreadcrumbSeparator.tsx +2 -2
  69. package/src/components/Button/Button.stories.tsx +14 -2
  70. package/src/components/Button/Button.tsx +42 -5
  71. package/src/components/Button/styles.ts +4 -6
  72. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +32 -10
  73. package/src/components/Card/Card.tsx +17 -0
  74. package/src/components/Checkbox/Checkbox.tsx +3 -6
  75. package/src/components/CodeEditor/highlighting.ts +1 -1
  76. package/src/components/CollapsiblePanel.tsx +2 -5
  77. package/src/components/Combobox/Combobox.tsx +12 -14
  78. package/src/components/Combobox/ComboboxChips.tsx +6 -1
  79. package/src/components/Combobox/ComboboxItem.tsx +3 -4
  80. package/src/components/Combobox/ComboboxItemIndicator.tsx +6 -4
  81. package/src/components/Combobox/types.ts +2 -6
  82. package/src/components/Command/Command.stories.tsx +47 -56
  83. package/src/components/Command/CommandItem/CheckIndicator.tsx +26 -14
  84. package/src/components/Command/CommandItem/CommandItem.tsx +6 -3
  85. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +8 -4
  86. package/src/components/DropdownMenu/DropdownMenuCheckboxItem.tsx +4 -1
  87. package/src/components/DropdownMenu/DropdownMenuItem.tsx +12 -6
  88. package/src/components/DropdownMenu/DropdownMenuItemIndicator.tsx +12 -6
  89. package/src/components/DropdownMenu/DropdownMenuRadioGroup/DropdownMenuRadioItem.tsx +8 -5
  90. package/src/components/DropdownMenu/DropdownMenuSub/DropdownMenuSubTrigger.tsx +2 -2
  91. package/src/components/DropdownMenu/ItemIcon.tsx +16 -8
  92. package/src/components/FormGroup/FormGroup.tsx +15 -0
  93. package/src/components/FullScreenModal/FullScreenModalHeader.tsx +4 -1
  94. package/src/components/Input/DateInput/DateInput.tsx +4 -1
  95. package/src/components/Input/DateRangeInput/DateRangeInput.tsx +4 -6
  96. package/src/components/Input/Input.tsx +18 -2
  97. package/src/components/Input/TextInput/TextInput.tsx +16 -0
  98. package/src/components/Input/TimeInput/TimeInput.tsx +4 -1
  99. package/src/components/InputGroup/InputGroup.stories.tsx +11 -3
  100. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +63 -1
  101. package/src/components/MarkdownEditor/MarkdownEditor.tsx +105 -15
  102. package/src/components/MarkdownEditor/nodes/ImageNode.tsx +10 -10
  103. package/src/components/MarkdownEditor/nodes/MermaidNode.tsx +8 -8
  104. package/src/components/MarkdownEditor/plugins/KeyboardPlugin.tsx +47 -3
  105. package/src/components/MarkdownEditor/plugins/MarkdownSyncPlugin.tsx +12 -3
  106. package/src/components/MarkdownEditor/plugins/ToolbarPlugin.tsx +20 -10
  107. package/src/components/MarkdownEditor/plugins/toolbar/AlignmentDropdown.tsx +20 -14
  108. package/src/components/MarkdownEditor/plugins/toolbar/BlockTypeDropdown.tsx +25 -16
  109. package/src/components/MarkdownEditor/plugins/toolbar/LinkPanel.tsx +3 -3
  110. package/src/components/MarkdownEditor/transformers.ts +10 -0
  111. package/src/components/MergeEditor/AcceptControls/AcceptButton.tsx +9 -4
  112. package/src/components/MergeEditor/AcceptControls/AcceptControls.tsx +25 -14
  113. package/src/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.tsx +2 -2
  114. package/src/components/MergeEditor/MergeEditor.stories.tsx +15 -0
  115. package/src/components/MergeEditor/MergeEditor.tsx +9 -0
  116. package/src/components/MergeEditor/MergePane.tsx +22 -4
  117. package/src/components/MergeEditor/decorations.ts +2 -2
  118. package/src/components/Modal/Modal.stories.tsx +6 -4
  119. package/src/components/Modal/Modal.tsx +26 -0
  120. package/src/components/Modal/ModalBody.tsx +5 -0
  121. package/src/components/Modal/ModalCloseIcon.tsx +2 -3
  122. package/src/components/Modal/ModalContent.tsx +8 -0
  123. package/src/components/Modal/ModalFooter.tsx +5 -0
  124. package/src/components/Modal/ModalHeader.tsx +11 -0
  125. package/src/components/Modal/ModalIcon.tsx +45 -19
  126. package/src/components/Navbar/Navbar.stories.tsx +26 -13
  127. package/src/components/Navbar/NavbarMenuButton.tsx +11 -10
  128. package/src/components/Pagination/PaginationEllipsis.tsx +2 -2
  129. package/src/components/Pagination/PaginationNext.tsx +2 -1
  130. package/src/components/Pagination/PaginationPrevious.tsx +4 -1
  131. package/src/components/PanelWithDetails.tsx +3 -4
  132. package/src/components/Popconfirm/Popconfirm.stories.tsx +13 -5
  133. package/src/components/RadioGroup/Radio.tsx +6 -12
  134. package/src/components/Search/ClearButton.tsx +2 -2
  135. package/src/components/Search/Search.tsx +3 -3
  136. package/src/components/Select/Select.tsx +16 -5
  137. package/src/components/Select/SelectContent.tsx +13 -4
  138. package/src/components/Select/SelectItem.tsx +6 -5
  139. package/src/components/Select/SelectTrigger.tsx +2 -2
  140. package/src/components/Sidebar/Sidebar.stories.tsx +55 -19
  141. package/src/components/Sidebar/SidebarGroup/SidebarGroupAction.tsx +8 -4
  142. package/src/components/Sidebar/SidebarMenu/SidebarMenuButton.tsx +4 -2
  143. package/src/components/Sidebar/SidebarMenu/SidebarMenuCard.tsx +3 -2
  144. package/src/components/Sidebar/SidebarMenu/SidebarMenuIcon.tsx +12 -12
  145. package/src/components/Sidebar/SidebarMenu/SidebarMenuSub.tsx +3 -7
  146. package/src/components/Sidebar/SidebarMenu/SidebarSearchInput.tsx +3 -5
  147. package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
  148. package/src/components/Skeleton/Skeleton.tsx +24 -1
  149. package/src/components/Table/Table.stories.tsx +223 -1
  150. package/src/components/Table/Table.tsx +17 -0
  151. package/src/components/Tabs.tsx +25 -2
  152. package/src/components/Toast/ToastDismiss.tsx +3 -3
  153. package/src/components/Toast/ToastRoot.tsx +24 -19
  154. package/src/components/Toggle/Toggle.tsx +11 -6
  155. package/src/components/Tooltip/Tooltip.tsx +23 -0
  156. package/src/components/ZoomableSvg/ZoomableSvg.tsx +8 -6
  157. package/src/icons/Keboola.tsx +28 -0
  158. package/src/icons/icons.stories.tsx +174 -0
  159. package/src/icons/index.ts +180 -0
  160. package/src/index.ts +1 -0
  161. package/src/patterns/AlertWithAction.stories.tsx +30 -0
  162. package/src/patterns/CardWithEmptyState.stories.tsx +66 -0
  163. package/src/patterns/ConfirmDialog.stories.tsx +72 -0
  164. package/src/patterns/CreatableMultiCombobox.stories.tsx +33 -0
  165. package/src/patterns/DataCardWithSkeleton.stories.tsx +48 -0
  166. package/src/patterns/DisabledButtonTooltip.stories.tsx +30 -0
  167. package/src/patterns/FilterToolbar.stories.tsx +71 -0
  168. package/src/patterns/InfoTooltip.stories.tsx +56 -0
  169. package/src/patterns/ListDetail.stories.tsx +65 -0
  170. package/src/patterns/ModalForm.stories.tsx +59 -0
  171. package/src/patterns/PageHeaderTabs.stories.tsx +48 -0
  172. package/src/styles.css +16 -8
  173. package/src/utils/legacyIcon.tsx +28 -0
@@ -3,6 +3,20 @@ import type { ComponentProps } from 'react';
3
3
  import type { Decorator, Meta, StoryObj } from '@storybook/react-vite';
4
4
  import type { InputType } from 'storybook/internal/types';
5
5
 
6
+ import {
7
+ Expand,
8
+ Gauge,
9
+ Keboola,
10
+ Menu,
11
+ Minus,
12
+ PanelLeft,
13
+ Pencil,
14
+ Plus,
15
+ Sparkles,
16
+ SquareTerminal,
17
+ Trash2,
18
+ } from '@keboola/design/icons';
19
+
6
20
  import { cn } from '../../utils';
7
21
  import { Button } from '../Button';
8
22
  import { ButtonGroup } from '../ButtonGroup';
@@ -41,13 +55,15 @@ const AppContent = ({
41
55
  <h3>Left Controls</h3>
42
56
  <ButtonGroup space="compact">
43
57
  <Sidebar.Trigger side="left">
44
- <Button argo icon="sidebar" variant="primary">
58
+ <Button argo variant="primary">
59
+ <PanelLeft className="tw-size-4" />
45
60
  Toggle
46
61
  </Button>
47
62
  </Sidebar.Trigger>
48
63
 
49
64
  <Sidebar.FillTrigger side="left">
50
- <Button argo icon="sidebar" variant="secondary">
65
+ <Button argo variant="secondary">
66
+ <PanelLeft className="tw-size-4" />
51
67
  Fill
52
68
  </Button>
53
69
  </Sidebar.FillTrigger>
@@ -60,13 +76,15 @@ const AppContent = ({
60
76
  <h3>Right Controls</h3>
61
77
  <ButtonGroup space="compact">
62
78
  <Sidebar.FillTrigger side="right">
63
- <Button argo icon="sidebar" variant="secondary">
79
+ <Button argo variant="secondary">
80
+ <PanelLeft className="tw-size-4" />
64
81
  Fill
65
82
  </Button>
66
83
  </Sidebar.FillTrigger>
67
84
 
68
85
  <Sidebar.Trigger side="right">
69
- <Button argo icon="sidebar" variant="primary">
86
+ <Button argo variant="primary">
87
+ <PanelLeft className="tw-size-4" />
70
88
  Toggle
71
89
  </Button>
72
90
  </Sidebar.Trigger>
@@ -183,10 +201,9 @@ export const Default: Story = {
183
201
  <Sidebar.Header>
184
202
  <Sidebar.Menu>
185
203
  <Sidebar.MenuItem>
186
- <Sidebar.MenuIcon
187
- icon={['fak', 'keboola']}
188
- className="!tw-size-8 tw-text-secondary-500"
189
- />
204
+ <Sidebar.MenuIcon className="!tw-size-8 tw-text-secondary-500">
205
+ <Keboola className="tw-size-4" />
206
+ </Sidebar.MenuIcon>
190
207
  Sticky Header
191
208
  </Sidebar.MenuItem>
192
209
  </Sidebar.Menu>
@@ -199,7 +216,9 @@ export const Default: Story = {
199
216
  <Sidebar.MenuItem>
200
217
  <Sidebar.MenuButton tooltip="Dashboard" asChild>
201
218
  <Link href="#">
202
- <Sidebar.MenuIcon icon={['far', 'gauge']} />
219
+ <Sidebar.MenuIcon>
220
+ <Gauge className="tw-size-4" />
221
+ </Sidebar.MenuIcon>
203
222
  Dashboard
204
223
  </Link>
205
224
  </Sidebar.MenuButton>
@@ -208,7 +227,9 @@ export const Default: Story = {
208
227
  <Sidebar.MenuItem>
209
228
  <Sidebar.MenuButton tooltip="AI Assistant" asChild>
210
229
  <Link href="#">
211
- <Sidebar.MenuIcon icon={['far', 'sparkles']} />
230
+ <Sidebar.MenuIcon>
231
+ <Sparkles className="tw-size-4" />
232
+ </Sidebar.MenuIcon>
212
233
  AI Assistant
213
234
  </Link>
214
235
  </Sidebar.MenuButton>
@@ -217,7 +238,9 @@ export const Default: Story = {
217
238
  <Sidebar.MenuItem>
218
239
  <Sidebar.MenuButton tooltip="Flows" asChild>
219
240
  <Link href="#">
220
- <Sidebar.MenuIcon icon={['far', 'bars-staggered']} />
241
+ <Sidebar.MenuIcon>
242
+ <Menu className="tw-size-4" />
243
+ </Sidebar.MenuIcon>
221
244
  Flows
222
245
  </Link>
223
246
  </Sidebar.MenuButton>
@@ -309,7 +332,8 @@ export const Default: Story = {
309
332
  <div className="tw-flex tw-flex-col tw-items-center tw-p-4">
310
333
  <h1>KAI</h1>
311
334
  <Sidebar.FillTrigger side="right">
312
- <Button block argo icon="sidebar" variant="secondary">
335
+ <Button block argo variant="secondary">
336
+ <PanelLeft className="tw-size-4" />
313
337
  Fill
314
338
  </Button>
315
339
  </Sidebar.FillTrigger>
@@ -341,12 +365,14 @@ export const Resizable: Story = {
341
365
  Width: {rightWidth}px — Drag left edge to resize
342
366
  </p>
343
367
  <Sidebar.FillTrigger side="right">
344
- <Button block argo icon="expand-alt" variant="secondary">
368
+ <Button block argo variant="secondary">
369
+ <Expand className="tw-size-4" />
345
370
  Fullscreen
346
371
  </Button>
347
372
  </Sidebar.FillTrigger>
348
373
  <Sidebar.Trigger side="right">
349
- <Button block argo icon="sidebar" variant="outline">
374
+ <Button block argo variant="outline">
375
+ <PanelLeft className="tw-size-4" />
350
376
  Collapse
351
377
  </Button>
352
378
  </Sidebar.Trigger>
@@ -446,7 +472,9 @@ export const MenuItems: Story = {
446
472
  <Sidebar.MenuItem>
447
473
  <Sidebar.MenuButton tooltip="Simple Anchor" asChild>
448
474
  <a href="https://www.keboole.com">
449
- <Sidebar.MenuIcon icon={['far', 'rectangle-terminal']} />
475
+ <Sidebar.MenuIcon>
476
+ <SquareTerminal className="tw-size-4" />
477
+ </Sidebar.MenuIcon>
450
478
  Simple Anchor
451
479
  </a>
452
480
  </Sidebar.MenuButton>
@@ -526,8 +554,12 @@ export const MenuAction: Story = {
526
554
 
527
555
  <DropdownMenu.Content side="right" align="start">
528
556
  <DropdownMenu.Group>
529
- <DropdownMenu.Item icon="pen">Edit </DropdownMenu.Item>
530
- <DropdownMenu.Item icon="trash">Delete </DropdownMenu.Item>
557
+ <DropdownMenu.Item icon={<Pencil className="tw-size-4" />}>
558
+ Edit{' '}
559
+ </DropdownMenu.Item>
560
+ <DropdownMenu.Item icon={<Trash2 className="tw-size-4" />}>
561
+ Delete{' '}
562
+ </DropdownMenu.Item>
531
563
  </DropdownMenu.Group>
532
564
  </DropdownMenu.Content>
533
565
  </DropdownMenu>
@@ -632,7 +664,9 @@ export const Group: Story = {
632
664
  <Sidebar.Content>
633
665
  <Sidebar.Group>
634
666
  <Sidebar.GroupLabel>Group A</Sidebar.GroupLabel>
635
- <Sidebar.GroupAction icon="plus" />
667
+ <Sidebar.GroupAction>
668
+ <Plus className="tw-size-4" />
669
+ </Sidebar.GroupAction>
636
670
  <Sidebar.GroupContent>
637
671
  <Sidebar.Menu>
638
672
  <Sidebar.MenuItem>
@@ -646,7 +680,9 @@ export const Group: Story = {
646
680
 
647
681
  <Sidebar.Group>
648
682
  <Sidebar.GroupLabel>Group B</Sidebar.GroupLabel>
649
- <Sidebar.GroupAction icon="minus" />
683
+ <Sidebar.GroupAction>
684
+ <Minus className="tw-size-4" />
685
+ </Sidebar.GroupAction>
650
686
  <Sidebar.GroupContent>
651
687
  <Sidebar.Menu>
652
688
  <Sidebar.MenuItem>
@@ -1,15 +1,14 @@
1
1
  import type { ComponentProps, ElementRef, Ref } from 'react';
2
2
 
3
3
  import { cn } from '../../../utils';
4
- import { Icon } from '../../Icon';
5
4
 
6
- type Props = Omit<ComponentProps<'button'>, 'children'> & Pick<ComponentProps<typeof Icon>, 'icon'>;
5
+ type Props = ComponentProps<'button'>;
7
6
 
8
7
  export const SidebarGroupAction = ({
9
8
  ref,
10
9
  className,
11
10
  type = 'button',
12
- icon,
11
+ children,
13
12
  ...props
14
13
  }: Props & { ref?: Ref<ElementRef<'button'>> }) => (
15
14
  <button
@@ -34,6 +33,11 @@ export const SidebarGroupAction = ({
34
33
  'tw-text-neutral-800',
35
34
  'tw-rounded-full',
36
35
 
36
+ // svg defaults: size + colour
37
+ '[&_svg]:tw-size-3',
38
+ '[&_svg]:tw-text-neutral-400',
39
+ 'hover:[&_svg]:tw-text-secondary-600',
40
+
37
41
  'hover:tw-bg-neutral-100',
38
42
 
39
43
  'focus-visible:tw-ring-4',
@@ -44,6 +48,6 @@ export const SidebarGroupAction = ({
44
48
  className,
45
49
  )}
46
50
  >
47
- <Icon icon={icon} className="tw-size-3 tw-text-neutral-400 hover:tw-text-secondary-600" />
51
+ {children}
48
52
  </button>
49
53
  );
@@ -2,6 +2,7 @@ import type { ComponentProps, ReactNode, Ref } from 'react';
2
2
  import { Root, Slottable } from '@radix-ui/react-slot';
3
3
 
4
4
  import { cn } from '../../../utils';
5
+ import { renderLegacyIcon } from '../../../utils/legacyIcon';
5
6
  import { Tooltip } from '../../Tooltip';
6
7
  import { useSidebarInstance } from '../SidebarInstanceContext';
7
8
 
@@ -10,7 +11,8 @@ import { SidebarMenuIcon } from './SidebarMenuIcon';
10
11
  type Props = ComponentProps<'button'> & {
11
12
  asChild?: boolean;
12
13
  tooltip?: ReactNode;
13
- } & Partial<Pick<ComponentProps<typeof SidebarMenuIcon>, 'icon'>>;
14
+ icon?: ReactNode;
15
+ };
14
16
 
15
17
  export const SidebarMenuButton = ({
16
18
  ref,
@@ -87,7 +89,7 @@ export const SidebarMenuButton = ({
87
89
  // Explicitly set the tabIndex to 0 to ensure the button is focusable; in Safari, anchors are not focusable by default.
88
90
  tabIndex={0}
89
91
  >
90
- {icon && <SidebarMenuIcon icon={icon} />}
92
+ {icon && <SidebarMenuIcon>{renderLegacyIcon(icon)}</SidebarMenuIcon>}
91
93
  <Slottable>{children}</Slottable>
92
94
  </Component>
93
95
  );
@@ -2,6 +2,7 @@ import { useCallback, useState } from 'react';
2
2
  import type { ComponentProps, HTMLAttributes, ReactNode } from 'react';
3
3
 
4
4
  import { cn } from '../../../utils';
5
+ import { renderLegacyIcon } from '../../../utils/legacyIcon';
5
6
  import { HoverCard } from '../../HoverCard';
6
7
  import { useSidebarInstance } from '../SidebarInstanceContext';
7
8
  import { useSidebar } from '../SidebarProvider';
@@ -25,7 +26,7 @@ const Content = ({ children, className }: { children: ReactNode; className?: str
25
26
  );
26
27
 
27
28
  type Props = HTMLAttributes<HTMLDivElement> & {
28
- icon: ComponentProps<typeof SidebarMenuIcon>['icon'];
29
+ icon: ReactNode;
29
30
  title: ReactNode;
30
31
  };
31
32
 
@@ -85,7 +86,7 @@ export const SidebarMenuCard = ({ children, className, icon, title, ...rest }: P
85
86
  >
86
87
  <div className="tw-flex tw-flex-col tw-gap-2">
87
88
  <Title>
88
- <SidebarMenuIcon className="tw-p-px" icon={icon} />
89
+ <SidebarMenuIcon className="tw-p-px">{renderLegacyIcon(icon)}</SidebarMenuIcon>
89
90
  {title}
90
91
  </Title>
91
92
  <Content className={className}>{children}</Content>
@@ -1,30 +1,30 @@
1
1
  import type { ComponentProps, ElementRef, Ref } from 'react';
2
2
 
3
3
  import { cn } from '../../../utils';
4
- import { Icon } from '../../Icon';
5
4
 
6
- type Props = {
7
- icon: ComponentProps<typeof Icon>['icon'];
8
- className?: string;
9
- };
5
+ type Props = ComponentProps<'span'>;
10
6
 
11
7
  export const SidebarMenuIcon = ({
12
8
  ref,
13
9
  className,
14
- icon,
10
+ children,
15
11
  ...rest
16
- }: Props & { ref?: Ref<ElementRef<'svg'>> }) => (
17
- <Icon
12
+ }: Props & { ref?: Ref<ElementRef<'span'>> }) => (
13
+ <span
18
14
  {...rest}
19
15
  ref={ref}
20
- icon={icon}
21
16
  className={cn(
22
- 'tw-size-4',
17
+ 'tw-inline-flex',
18
+ 'tw-items-center',
19
+ 'tw-justify-center',
23
20
  'tw-aspect-square',
24
- 'tw-text-sidebar-primary',
25
21
  'tw-shrink-0',
26
22
  'tw-p-0.5',
23
+ 'tw-text-sidebar-primary',
24
+ '[&_svg]:tw-size-4',
27
25
  className,
28
26
  )}
29
- />
27
+ >
28
+ {children}
29
+ </span>
30
30
  );
@@ -1,19 +1,18 @@
1
1
  import { useState } from 'react';
2
2
  import type { ComponentProps, ReactNode } from 'react';
3
3
 
4
+ import { ChevronDown } from '../../../icons';
4
5
  import { cn } from '../../../utils';
5
6
  import { Collapsible } from '../../Collapsible';
6
7
  import { DropdownMenu } from '../../DropdownMenu';
7
- import { Icon } from '../../Icon';
8
8
  import { Tooltip } from '../../Tooltip';
9
9
  import { useSidebarInstance } from '../SidebarInstanceContext';
10
10
 
11
11
  import { SidebarMenuButton } from './SidebarMenuButton';
12
- import type { SidebarMenuIcon } from './SidebarMenuIcon';
13
12
 
14
13
  type Props = {
15
14
  children: ReactNode;
16
- icon: ComponentProps<typeof SidebarMenuIcon>['icon'];
15
+ icon: ReactNode;
17
16
  isActive?: boolean;
18
17
  label: string;
19
18
  } & Pick<ComponentProps<typeof Collapsible>, 'defaultOpen' | 'open' | 'onOpenChange'>;
@@ -109,10 +108,7 @@ export const SidebarMenuSub = ({
109
108
  )}
110
109
  >
111
110
  {label}
112
- <Icon
113
- icon={['far', 'angle-down']}
114
- className="tw-ml-auto tw-transition-transform tw-duration-200 group-data-[state=open]:tw-rotate-180"
115
- />
111
+ <ChevronDown className="tw-ml-auto tw-size-5 tw-transition-transform tw-duration-200 group-data-[state=open]:tw-rotate-180" />
116
112
  </SidebarMenuButton>
117
113
  </Collapsible.Trigger>
118
114
  <Collapsible.Content>
@@ -1,8 +1,8 @@
1
1
  import { useRef } from 'react';
2
2
  import type { ComponentProps, ElementRef, Ref } from 'react';
3
3
 
4
+ import { Search } from '../../../icons';
4
5
  import { cn, mergeRefs } from '../../../utils';
5
- import { Icon } from '../../Icon';
6
6
  import { useSidebarInstance } from '../SidebarInstanceContext';
7
7
  import { useSidebar } from '../SidebarProvider';
8
8
 
@@ -49,14 +49,12 @@ export const SidebarSearchInput = ({
49
49
 
50
50
  return (
51
51
  <div className={cn(wrapperClassnames, className)}>
52
- <Icon
53
- icon="search"
54
- fixedWidth
52
+ <Search
55
53
  className={cn(
56
54
  'tw-pointer-events-none',
57
55
  'tw-absolute',
58
56
  'tw-left-2',
59
- 'tw-h-4',
57
+ 'tw-size-4',
60
58
 
61
59
  'tw-text-sidebar-primary',
62
60
  'group-data-[state=expanded]/sidebar:tw-text-neutral-400',
@@ -52,3 +52,55 @@ export const Group: Story = {
52
52
  );
53
53
  },
54
54
  };
55
+
56
+ export const BoxLoader: Story = {
57
+ render: () => (
58
+ <div className="tw-rounded-md tw-border tw-border-border tw-bg-background tw-p-4">
59
+ <Skeleton height="lg" className="tw-w-full" />
60
+ </div>
61
+ ),
62
+ parameters: {
63
+ docs: {
64
+ description: {
65
+ story: 'Box-shaped section placeholder — replaces the legacy `BoxLoader` card.',
66
+ },
67
+ },
68
+ },
69
+ };
70
+
71
+ export const ActionLoader: Story = {
72
+ render: () => (
73
+ <div className="tw-flex tw-gap-2">
74
+ <Skeleton height="lg" className="tw-w-24 tw-rounded-md" />
75
+ <Skeleton height="lg" className="tw-w-32 tw-rounded-md" />
76
+ </div>
77
+ ),
78
+ parameters: {
79
+ docs: {
80
+ description: {
81
+ story: 'Button-shaped placeholder for action rows — replaces the legacy `ActionLoader`.',
82
+ },
83
+ },
84
+ },
85
+ };
86
+
87
+ export const ListOfRows: Story = {
88
+ render: () => (
89
+ <div className="tw-flex tw-flex-col tw-gap-3">
90
+ <Skeleton height="sm" className="tw-w-1/2" />
91
+ <Skeleton height="xs" className="tw-w-full" />
92
+ <Skeleton height="xs" className="tw-w-5/6" />
93
+ <Skeleton height="xs" className="tw-w-4/5" />
94
+ <Skeleton height="xs" className="tw-w-3/4" />
95
+ <Skeleton height="xs" className="tw-w-5/6" />
96
+ </div>
97
+ ),
98
+ parameters: {
99
+ docs: {
100
+ description: {
101
+ story:
102
+ 'Page-loading placeholder — heading + stacked rows. Replaces the legacy `CustomLoader`.',
103
+ },
104
+ },
105
+ },
106
+ };
@@ -4,9 +4,15 @@ import { cn } from '../../utils';
4
4
 
5
5
  export type Height = 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
6
6
 
7
+ /**
8
+ * Props for `Skeleton`. `height` maps to the named token (xxs 12px → 2xl 48px); `count` renders multiple
9
+ * stacked placeholders sharing the same shape.
10
+ */
7
11
  type SkeletonProps = {
12
+ /** Named height token — `xxs` (12px) through `2xl` (48px). */
8
13
  height?: Height;
9
14
  className?: string;
15
+ /** Number of stacked placeholder rows. Use for list-style loading states. */
10
16
  count?: number;
11
17
  };
12
18
 
@@ -40,6 +46,19 @@ const ANIMATION_CLASS_NAME = cn(
40
46
  'before:tw-via-neutral-50',
41
47
  );
42
48
 
49
+ /**
50
+ * Animated placeholder shown in place of content that is still loading.
51
+ *
52
+ * **When to use:** during initial page or section load while the real content is fetched. Match the
53
+ * placeholder shape to the eventual content (one `Skeleton height="lg"` per title row, several `xs` for
54
+ * paragraph lines). For list shells use `count` to render N stacked placeholders.
55
+ * **When NOT to use:** for indeterminate background work without a layout slot use `DotsLoader` or
56
+ * `Button isLoading`; for empty / error states once the request resolves use `EmptyState` / `ErrorState`.
57
+ * **Composition:** drop directly into the slot the real content will occupy. `SkeletonList` is the typical
58
+ * shell for `Table` / `Card`-list pages.
59
+ *
60
+ * @see RECIPES.md#data-card-with-skeleton — for a loading card pattern
61
+ */
43
62
  export const Skeleton = ({ height = 'md', className, count = 1 }: SkeletonProps) => {
44
63
  const prefixId = useId();
45
64
  const keys = Array.from({ length: count }, (_, i) => `${prefixId}-${i}`);
@@ -47,7 +66,11 @@ export const Skeleton = ({ height = 'md', className, count = 1 }: SkeletonProps)
47
66
  return (
48
67
  <>
49
68
  {keys.map((key) => (
50
- <div key={key} className={cn(HEIGHT_CLASS_NAME[height], ANIMATION_CLASS_NAME, className)} />
69
+ <div
70
+ key={key}
71
+ data-skeleton="true"
72
+ className={cn(HEIGHT_CLASS_NAME[height], ANIMATION_CLASS_NAME, className)}
73
+ />
51
74
  ))}
52
75
  </>
53
76
  );