@scalepad/ui 0.1.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 (273) hide show
  1. package/.ai/rules/date-handling.md +39 -0
  2. package/.ai/rules/figma-design-system.md +372 -0
  3. package/.ai/rules/figma-lm-design-system-keys.md +680 -0
  4. package/.ai/rules/file-extensions.md +13 -0
  5. package/.ai/rules/modal-confirmation-mutation.md +56 -0
  6. package/.ai/rules/react-hooks.md +29 -0
  7. package/.ai/rules/styling.md +83 -0
  8. package/AGENTS.md +37 -0
  9. package/README.md +125 -0
  10. package/figma.config.json +9 -0
  11. package/package.json +127 -0
  12. package/scripts/install-ai-rules.mjs +136 -0
  13. package/src/ThemeProvider.tsx +57 -0
  14. package/src/charts.ts +32 -0
  15. package/src/components/ActionCard/ActionCard.css.ts +60 -0
  16. package/src/components/ActionCard/ActionCard.tsx +154 -0
  17. package/src/components/ActionCard/index.ts +2 -0
  18. package/src/components/Anchor/Anchor.tsx +47 -0
  19. package/src/components/Anchor/index.ts +2 -0
  20. package/src/components/AppliedFiltersManagerBar/AppliedFiltersManagerBar.tsx +105 -0
  21. package/src/components/AppliedFiltersManagerBar/FilterBadge.css.ts +23 -0
  22. package/src/components/AppliedFiltersManagerBar/FilterBadge.tsx +50 -0
  23. package/src/components/AppliedFiltersManagerBar/index.ts +5 -0
  24. package/src/components/Badge/Badge.css.ts +72 -0
  25. package/src/components/Badge/Badge.figma.tsx +43 -0
  26. package/src/components/Badge/Badge.tsx +159 -0
  27. package/src/components/Badge/index.ts +2 -0
  28. package/src/components/BreadCrumb/BreadCrumb.tsx +62 -0
  29. package/src/components/BreadCrumb/index.ts +2 -0
  30. package/src/components/BulkActionBar/BulkActionBar.css.ts +26 -0
  31. package/src/components/BulkActionBar/BulkActionBar.tsx +164 -0
  32. package/src/components/BulkActionBar/index.ts +2 -0
  33. package/src/components/Button/Button.css.ts +272 -0
  34. package/src/components/Button/Button.figma.tsx +74 -0
  35. package/src/components/Button/Button.tsx +84 -0
  36. package/src/components/Button/index.ts +2 -0
  37. package/src/components/Charts/ChartTooltip.figma.tsx +33 -0
  38. package/src/components/Charts/ChartTooltip.tsx +101 -0
  39. package/src/components/Charts/MiniBarSparkline.tsx +75 -0
  40. package/src/components/Charts/StackedPatternBarChart.tsx +494 -0
  41. package/src/components/Charts/TrendAreaChart.css.ts +23 -0
  42. package/src/components/Charts/TrendAreaChart.tsx +210 -0
  43. package/src/components/Charts/index.ts +12 -0
  44. package/src/components/CodePanel/CodePanel.css.ts +113 -0
  45. package/src/components/CodePanel/CodePanel.tsx +121 -0
  46. package/src/components/CodePanel/index.ts +2 -0
  47. package/src/components/CommentComposer/CommentComposer.css.ts +60 -0
  48. package/src/components/CommentComposer/CommentComposer.tsx +181 -0
  49. package/src/components/CommentComposer/index.ts +2 -0
  50. package/src/components/ConfirmationModal/ConfirmationModal.tsx +149 -0
  51. package/src/components/ConfirmationModal/index.ts +2 -0
  52. package/src/components/ConfirmationTooltip/ConfirmationTooltip.tsx +132 -0
  53. package/src/components/ConfirmationTooltip/index.ts +2 -0
  54. package/src/components/DataDialog.figma.tsx +33 -0
  55. package/src/components/DataDialog.tsx +46 -0
  56. package/src/components/DataTable/DataTable.tsx +1042 -0
  57. package/src/components/DataTable/RowExpandToggle.tsx +105 -0
  58. package/src/components/DataTable/RowGroupHeader.tsx +190 -0
  59. package/src/components/DataTable/createActionsColumn.tsx +86 -0
  60. package/src/components/DataTable/index.ts +25 -0
  61. package/src/components/DatePicker/CustomRangePicker.tsx +59 -0
  62. package/src/components/DatePicker/DateInput.tsx +329 -0
  63. package/src/components/DatePicker/DateNavigator.tsx +486 -0
  64. package/src/components/DatePicker/DatePicker.tsx +242 -0
  65. package/src/components/DatePicker/MonthlyRangePicker.tsx +231 -0
  66. package/src/components/DatePicker/QuarterlyRangePicker.tsx +224 -0
  67. package/src/components/DatePicker/QuickPicksSidebar.tsx +242 -0
  68. package/src/components/DatePicker/YearlyRangePicker.tsx +171 -0
  69. package/src/components/DatePicker/index.ts +7 -0
  70. package/src/components/DatePicker/types.ts +12 -0
  71. package/src/components/DesignSystemPrimitives/FluidGrid.tsx +44 -0
  72. package/src/components/DesignSystemPrimitives/InteractivePrimitives.tsx +177 -0
  73. package/src/components/DesignSystemPrimitives/LayoutPrimitives.tsx +220 -0
  74. package/src/components/DesignSystemPrimitives/LayoutPrimitives.types.tsx +15 -0
  75. package/src/components/DesignSystemPrimitives/SurfacePrimitives.tsx +46 -0
  76. package/src/components/DesignSystemPrimitives/index.ts +55 -0
  77. package/src/components/Details/Details.css.ts +74 -0
  78. package/src/components/Details/Details.tsx +140 -0
  79. package/src/components/Details/index.ts +2 -0
  80. package/src/components/DownloadCard/DownloadCard.css.ts +22 -0
  81. package/src/components/DownloadCard/DownloadCard.tsx +63 -0
  82. package/src/components/DownloadCard/index.ts +2 -0
  83. package/src/components/Drawer/Drawer.css.ts +32 -0
  84. package/src/components/Drawer/Drawer.tsx +236 -0
  85. package/src/components/Drawer/hooks/useDetailDrawer.ts +61 -0
  86. package/src/components/Drawer/hooks/useDetailDrawerNavigation.ts +125 -0
  87. package/src/components/Drawer/hooks/useDetailDrawerNavigationContext.ts +66 -0
  88. package/src/components/EditableRichText/EditableRichText.css.ts +72 -0
  89. package/src/components/EditableRichText/EditableRichText.tsx +324 -0
  90. package/src/components/EditableRichText/index.ts +2 -0
  91. package/src/components/EditableSelect/EditableSelect.css.ts +62 -0
  92. package/src/components/EditableSelect/EditableSelect.tsx +224 -0
  93. package/src/components/EditableSelect/index.ts +2 -0
  94. package/src/components/EditableText/EditableText.tsx +377 -0
  95. package/src/components/EditableText/index.ts +2 -0
  96. package/src/components/EmptyState/EmptyState.figma.tsx +33 -0
  97. package/src/components/EmptyState/EmptyState.tsx +230 -0
  98. package/src/components/EmptyState/index.ts +2 -0
  99. package/src/components/ErrorBoundary.tsx +135 -0
  100. package/src/components/ErrorState/ErrorState.tsx +197 -0
  101. package/src/components/ErrorState/index.ts +2 -0
  102. package/src/components/FeatureCard.tsx +42 -0
  103. package/src/components/FilterMenu/FilterMenu.figma.tsx +30 -0
  104. package/src/components/FilterMenu/FilterMenu.tsx +198 -0
  105. package/src/components/FilterMenu/FilterSubMenuTypes/BooleanFilterSubmenu.tsx +46 -0
  106. package/src/components/FilterMenu/FilterSubMenuTypes/SearchableFilterSubmenu.tsx +239 -0
  107. package/src/components/FilterMenu/FilterSubMenuTypes/index.ts +8 -0
  108. package/src/components/FilterMenu/defaultFilterSchemas.ts +63 -0
  109. package/src/components/FilterMenu/helpers.ts +115 -0
  110. package/src/components/FilterMenu/index.ts +35 -0
  111. package/src/components/FilterMenu/types.ts +101 -0
  112. package/src/components/IconButton/IconButton.css.ts +272 -0
  113. package/src/components/IconButton/IconButton.figma.tsx +47 -0
  114. package/src/components/IconButton/IconButton.tsx +72 -0
  115. package/src/components/IconButton/README.md +230 -0
  116. package/src/components/IconButton/index.ts +2 -0
  117. package/src/components/InfiniteScrollSentinel.tsx +86 -0
  118. package/src/components/InfiniteScrollTrigger.tsx +78 -0
  119. package/src/components/InfoCard.figma.tsx +47 -0
  120. package/src/components/InfoCard.tsx +216 -0
  121. package/src/components/KbdHint/KbdHint.tsx +23 -0
  122. package/src/components/KbdHint/index.ts +2 -0
  123. package/src/components/LabeledField/LabeledField.tsx +21 -0
  124. package/src/components/LabeledField/index.ts +2 -0
  125. package/src/components/LookupSelect/LookupSelect.css.ts +149 -0
  126. package/src/components/LookupSelect/LookupSelect.tsx +325 -0
  127. package/src/components/LookupSelect/index.ts +2 -0
  128. package/src/components/Menu/Menu.css.ts +89 -0
  129. package/src/components/Menu/Menu.tsx +105 -0
  130. package/src/components/Menu/index.ts +2 -0
  131. package/src/components/MessageBox/MessageBox.tsx +168 -0
  132. package/src/components/MessageBox/index.ts +2 -0
  133. package/src/components/MetricDisplay/MetricDisplay.tsx +55 -0
  134. package/src/components/MetricDisplay/index.ts +1 -0
  135. package/src/components/MultiSelect/MultiSelect.tsx +278 -0
  136. package/src/components/MultiSelect/index.ts +2 -0
  137. package/src/components/Notifications/Notifications.tsx +12 -0
  138. package/src/components/Notifications/README.md +93 -0
  139. package/src/components/Notifications/index.ts +4 -0
  140. package/src/components/Notifications/showToast.tsx +100 -0
  141. package/src/components/PropertyRow/PropertyRow.tsx +96 -0
  142. package/src/components/PropertyRow/index.ts +2 -0
  143. package/src/components/RadioTile/RadioTile.tsx +253 -0
  144. package/src/components/RadioTile/index.ts +2 -0
  145. package/src/components/RichText/FormattingToolbar.css.ts +69 -0
  146. package/src/components/RichText/FormattingToolbar.tsx +112 -0
  147. package/src/components/RichText/RichTextInline.css.ts +54 -0
  148. package/src/components/RichText/RichTextInline.tsx +318 -0
  149. package/src/components/RichText/formattingCommands.ts +181 -0
  150. package/src/components/RichText/formattingTypes.ts +34 -0
  151. package/src/components/RichText/index.ts +49 -0
  152. package/src/components/RichText/richTextExtensions.ts +111 -0
  153. package/src/components/RichText/richTextHelpers.ts +65 -0
  154. package/src/components/RichText/richTextImage.ts +253 -0
  155. package/src/components/RichText/richTextImageHandlers.ts +244 -0
  156. package/src/components/RichText/richTextProse.css.ts +261 -0
  157. package/src/components/RichTextEditor/RichTextEditor.css.ts +82 -0
  158. package/src/components/RichTextEditor/RichTextEditor.tsx +204 -0
  159. package/src/components/RichTextEditor/index.ts +2 -0
  160. package/src/components/RichTextView/RichTextView.css.ts +11 -0
  161. package/src/components/RichTextView/RichTextView.tsx +114 -0
  162. package/src/components/RichTextView/index.ts +2 -0
  163. package/src/components/Schedule/Schedule.tsx +35 -0
  164. package/src/components/SchedulePicker/SchedulePicker.css.ts +42 -0
  165. package/src/components/SchedulePicker/SchedulePicker.tsx +130 -0
  166. package/src/components/SchedulePicker/index.ts +2 -0
  167. package/src/components/SearchableList/types.ts +30 -0
  168. package/src/components/SearchableSubMenu/SearchableSubMenu.css.ts +25 -0
  169. package/src/components/SearchableSubMenu/SearchableSubMenu.tsx +139 -0
  170. package/src/components/SearchableSubMenu/index.ts +2 -0
  171. package/src/components/Select/README.md +114 -0
  172. package/src/components/Select/Select.css.ts +110 -0
  173. package/src/components/Select/Select.tsx +133 -0
  174. package/src/components/Select/index.ts +2 -0
  175. package/src/components/SelectCreatable/SelectCreatable.css.ts +16 -0
  176. package/src/components/SelectCreatable/SelectCreatable.tsx +203 -0
  177. package/src/components/SelectCreatable/index.ts +2 -0
  178. package/src/components/SettingsCard/SettingsCard.tsx +98 -0
  179. package/src/components/SettingsCard/index.ts +2 -0
  180. package/src/components/Sidebar/Sidebar.css.ts +91 -0
  181. package/src/components/Sidebar/Sidebar.tsx +129 -0
  182. package/src/components/Sidebar/index.ts +5 -0
  183. package/src/components/SimpleList/SimpleList.css.ts +12 -0
  184. package/src/components/SimpleList/SimpleList.tsx +44 -0
  185. package/src/components/SimpleList/index.ts +2 -0
  186. package/src/components/SimpleTable/SimpleTable.tsx +296 -0
  187. package/src/components/SimpleTable/index.ts +2 -0
  188. package/src/components/SlashRichTextEditor/SelectionBubbleMenu.css.ts +62 -0
  189. package/src/components/SlashRichTextEditor/SelectionBubbleMenu.tsx +85 -0
  190. package/src/components/SlashRichTextEditor/SlashCommandMenu.css.ts +124 -0
  191. package/src/components/SlashRichTextEditor/SlashCommandMenu.tsx +168 -0
  192. package/src/components/SlashRichTextEditor/SlashRichTextEditor.css.ts +81 -0
  193. package/src/components/SlashRichTextEditor/SlashRichTextEditor.tsx +538 -0
  194. package/src/components/SlashRichTextEditor/SlashSuggestionExtension.ts +48 -0
  195. package/src/components/SlashRichTextEditor/index.ts +13 -0
  196. package/src/components/SlashRichTextEditor/types.ts +48 -0
  197. package/src/components/StatCard/StatCard.css.ts +70 -0
  198. package/src/components/StatCard/StatCard.tsx +201 -0
  199. package/src/components/StatCard/index.ts +1 -0
  200. package/src/components/StatusBadge/StatusBadge.tsx +70 -0
  201. package/src/components/StatusBadge/index.ts +2 -0
  202. package/src/components/StatusIndicator/StatusIndicator.tsx +67 -0
  203. package/src/components/StatusIndicator/index.ts +6 -0
  204. package/src/components/SubNavigation/SubNavigation.css.ts +72 -0
  205. package/src/components/SubNavigation/SubNavigation.tsx +104 -0
  206. package/src/components/SubNavigation/index.ts +2 -0
  207. package/src/components/SuspenseLoader.tsx +22 -0
  208. package/src/components/Table/SortableColumnHeader.tsx +99 -0
  209. package/src/components/Table/TableSkeletonRows.figma.tsx +22 -0
  210. package/src/components/Table/TableSkeletonRows.tsx +113 -0
  211. package/src/components/Table/index.ts +9 -0
  212. package/src/components/TableActionsMenu.tsx +58 -0
  213. package/src/components/TableCard.tsx +29 -0
  214. package/src/components/TableContainer/TableContainer.tsx +86 -0
  215. package/src/components/TableContainer/index.ts +2 -0
  216. package/src/components/TableControlBar/TableControlBar.tsx +156 -0
  217. package/src/components/TableControlBar/TableSelectionButton.tsx +57 -0
  218. package/src/components/TableControlBar/index.ts +13 -0
  219. package/src/components/TableControlBar/useTableControlBar.tsx +314 -0
  220. package/src/components/TableSelection/TableSelection.tsx +43 -0
  221. package/src/components/TableSelection/index.ts +5 -0
  222. package/src/components/Tabs/README.md +76 -0
  223. package/src/components/Tabs/Tabs.css.ts +54 -0
  224. package/src/components/Tabs/Tabs.figma.tsx +47 -0
  225. package/src/components/Tabs/Tabs.tsx +96 -0
  226. package/src/components/Tabs/index.ts +8 -0
  227. package/src/components/TextInput/README.md +98 -0
  228. package/src/components/TextInput/SearchTextInput.figma.tsx +22 -0
  229. package/src/components/TextInput/SearchTextInput.tsx +150 -0
  230. package/src/components/TextInput/TextInput.figma.tsx +44 -0
  231. package/src/components/TextInput/TextInput.tsx +42 -0
  232. package/src/components/TextInput/index.ts +4 -0
  233. package/src/components/ThemeSwitcher.figma.tsx +28 -0
  234. package/src/components/ThemeSwitcher.tsx +69 -0
  235. package/src/components/TrendBadge/TrendBadge.tsx +76 -0
  236. package/src/components/TrendBadge/index.ts +2 -0
  237. package/src/components/TruncatedText.tsx +115 -0
  238. package/src/components/Typography/Text.tsx +74 -0
  239. package/src/components/Typography/Title.tsx +100 -0
  240. package/src/components/Typography/index.ts +4 -0
  241. package/src/geist-fonts.ts +48 -0
  242. package/src/hooks/index.ts +31 -0
  243. package/src/hooks/useFilters.ts +152 -0
  244. package/src/hooks/useInfiniteScroll.ts +62 -0
  245. package/src/hooks/usePlatform.ts +33 -0
  246. package/src/hooks/useServerTable.ts +495 -0
  247. package/src/hooks/useTableSelection.ts +102 -0
  248. package/src/hooks/useTableSort.ts +259 -0
  249. package/src/index.ts +483 -0
  250. package/src/mantine.ts +25 -0
  251. package/src/theme/mantineVars.ts +12 -0
  252. package/src/theme/themeContract.css.ts +131 -0
  253. package/src/theme/themeVars.ts +31 -0
  254. package/src/theme.ts +168 -0
  255. package/src/tokens/color-types.ts +107 -0
  256. package/src/tokens/colors.ts +243 -0
  257. package/src/tokens/index.ts +14 -0
  258. package/src/tokens/radius.ts +17 -0
  259. package/src/tokens/semantic-colors.ts +224 -0
  260. package/src/tokens/semantic-tokens-css.ts +53 -0
  261. package/src/tokens/shadows.ts +11 -0
  262. package/src/tokens/spacing.ts +20 -0
  263. package/src/tokens/text-styles.ts +179 -0
  264. package/src/tokens/typography.ts +40 -0
  265. package/src/tokens/zIndex.ts +27 -0
  266. package/src/types/mantine-theme.d.ts +17 -0
  267. package/src/types/tanstack-table.d.ts +22 -0
  268. package/src/utils/avatar.ts +150 -0
  269. package/src/utils/chartHelpers.ts +53 -0
  270. package/src/utils/color-props.ts +77 -0
  271. package/src/utils/createDesignComponent.tsx +104 -0
  272. package/src/utils/nestFlatRows.ts +111 -0
  273. package/src/utils/withStaticComponents.ts +6 -0
@@ -0,0 +1,99 @@
1
+ import { ChevronDown, ChevronsUpDown, ChevronUp } from 'lucide-react';
2
+
3
+ import { Group, UnstyledButton } from '@mantine/core';
4
+
5
+ import { Text } from '../Typography';
6
+
7
+ export interface SortableColumnHeaderProps {
8
+ /**
9
+ * Column label text
10
+ */
11
+ label: string;
12
+ /**
13
+ * Props to spread on the header (from useTableSort.getSortProps)
14
+ */
15
+ sortProps: {
16
+ onClick: () => void;
17
+ 'aria-sort': 'ascending' | 'descending' | 'none';
18
+ };
19
+ /**
20
+ * Text alignment
21
+ * @default 'left'
22
+ */
23
+ align?: 'left' | 'right' | 'center';
24
+ }
25
+
26
+ /**
27
+ * Reusable sortable column header component
28
+ *
29
+ * Displays a column label with a sort indicator icon that changes based on sort state:
30
+ * - No sort: ChevronsUpDown (dimmed)
31
+ * - Ascending: ChevronUp
32
+ * - Descending: ChevronDown
33
+ *
34
+ * Uses a semantic button element for full keyboard and screen reader accessibility.
35
+ * The button responds to Enter/Space key presses and announces sort state via aria-sort.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * const sort = useTableSort({ ... });
40
+ *
41
+ * <Table.Th>
42
+ * <SortableColumnHeader
43
+ * label="Name"
44
+ * sortProps={sort.getSortProps('name')}
45
+ * />
46
+ * </Table.Th>
47
+ *
48
+ * <Table.Th>
49
+ * <SortableColumnHeader
50
+ * label="Count"
51
+ * sortProps={sort.getSortProps('count')}
52
+ * align="right"
53
+ * />
54
+ * </Table.Th>
55
+ * ```
56
+ */
57
+ export function SortableColumnHeader({
58
+ label,
59
+ sortProps,
60
+ align = 'left',
61
+ }: SortableColumnHeaderProps) {
62
+ const { onClick, 'aria-sort': ariaSort } = sortProps;
63
+
64
+ // Determine which icon to show based on sort state
65
+ const SortIcon =
66
+ ariaSort === 'ascending'
67
+ ? ChevronUp
68
+ : ariaSort === 'descending'
69
+ ? ChevronDown
70
+ : ChevronsUpDown;
71
+
72
+ // Dim the icon when no sort is applied
73
+ const iconOpacity = ariaSort === 'none' ? 0.5 : 1;
74
+
75
+ // Determine justification based on alignment
76
+ const justifyContent =
77
+ align === 'right'
78
+ ? 'flex-end'
79
+ : align === 'center'
80
+ ? 'center'
81
+ : 'flex-start';
82
+
83
+ return (
84
+ <UnstyledButton
85
+ onClick={onClick}
86
+ aria-sort={ariaSort}
87
+ aria-label={`Sort by ${label}`}
88
+ style={{
89
+ width: '100%',
90
+ userSelect: 'none',
91
+ }}
92
+ >
93
+ <Group gap="4px" justify={justifyContent} wrap="nowrap">
94
+ <Text variant="caption2.stronger">{label}</Text>
95
+ <SortIcon size={16} opacity={iconOpacity} aria-hidden="true" />
96
+ </Group>
97
+ </UnstyledButton>
98
+ );
99
+ }
@@ -0,0 +1,22 @@
1
+ import figma from '@figma/code-connect';
2
+
3
+ import { TableSkeletonRows } from './TableSkeletonRows';
4
+
5
+ /**
6
+ * -- This file was auto-generated by Code Connect --
7
+ * None of your props could be automatically mapped to Figma properties.
8
+ * You should update the `props` object to include a mapping from your
9
+ * code props to Figma properties, and update the `example` function to
10
+ * return the code example you'd like to see in Figma
11
+ */
12
+
13
+ figma.connect(
14
+ TableSkeletonRows,
15
+ 'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=303%3A246698',
16
+ {
17
+ props: {},
18
+ example: _props => (
19
+ <TableSkeletonRows columns={[{ width: 160 }, { width: 80 }]} rowCount={3} />
20
+ ),
21
+ }
22
+ );
@@ -0,0 +1,113 @@
1
+ import { Flex, Skeleton, Table } from '@mantine/core';
2
+
3
+ export interface SkeletonColumnConfig {
4
+ /**
5
+ * Skeleton width (number in px or string like '100%')
6
+ */
7
+ width: number | string;
8
+ /**
9
+ * Skeleton height in px (defaults to 16)
10
+ */
11
+ height?: number;
12
+ /**
13
+ * Whether this is a circular skeleton (e.g., for avatars)
14
+ */
15
+ circle?: boolean;
16
+ /**
17
+ * Alignment - adds marginLeft: auto for 'right', auto margins for 'center'
18
+ */
19
+ align?: 'left' | 'right' | 'center';
20
+ /**
21
+ * Border radius for pill-shaped skeletons (e.g., badges)
22
+ */
23
+ radius?: 'xl' | 'lg' | 'md' | 'sm' | 'xs';
24
+ /**
25
+ * Whether this is a flex cell with multiple items (e.g., avatar + name)
26
+ */
27
+ flex?: boolean;
28
+ /**
29
+ * Gap between flex items (only used when flex=true)
30
+ */
31
+ gap?: 'xs' | 'sm' | 'md' | 'lg';
32
+ /**
33
+ * Cell padding override (use Mantine spacing tokens)
34
+ */
35
+ px?: number | string;
36
+ }
37
+
38
+ export interface TableSkeletonRowsProps {
39
+ /**
40
+ * Column configuration for skeleton cells
41
+ */
42
+ columns: SkeletonColumnConfig[];
43
+ /**
44
+ * Number of rows to render
45
+ */
46
+ rowCount: number;
47
+ }
48
+
49
+ /**
50
+ * TableSkeletonRows component
51
+ * Renders skeleton loading rows for tables with configurable column layouts
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * const columns = [
56
+ * { width: 120, flex: true, circle: true }, // Avatar + name
57
+ * { width: 60, align: 'right' }, // Number column
58
+ * { width: 70, radius: 'xl', align: 'right' }, // Badge column
59
+ * ];
60
+ *
61
+ * <Table.Tbody>
62
+ * {loading ? (
63
+ * <TableSkeletonRows columns={columns} rowCount={5} />
64
+ * ) : (
65
+ * data.map(row => <Table.Tr key={row.id}>...</Table.Tr>)
66
+ * )}
67
+ * </Table.Tbody>
68
+ * ```
69
+ */
70
+ export function TableSkeletonRows({
71
+ columns,
72
+ rowCount,
73
+ }: TableSkeletonRowsProps) {
74
+ // Helper to determine margin style based on alignment
75
+ const getAlignmentStyle = (align?: 'left' | 'right' | 'center') => {
76
+ if (!align || align === 'left') return undefined;
77
+ if (align === 'right') return { marginLeft: 'auto' };
78
+ if (align === 'center') return { marginLeft: 'auto', marginRight: 'auto' };
79
+ return undefined;
80
+ };
81
+
82
+ return (
83
+ <>
84
+ {Array.from({ length: rowCount }).map((_, rowIndex) => (
85
+ <Table.Tr key={`skeleton-row-${rowIndex}`}>
86
+ {columns.map((col, colIndex) => (
87
+ <Table.Td key={`skeleton-cell-${rowIndex}-${colIndex}`} px={col.px}>
88
+ {col.flex ? (
89
+ // Flex cell with multiple items (e.g., avatar + name)
90
+ <Flex gap={col.gap ?? 'xs'} align="center">
91
+ {col.circle && <Skeleton circle height={26} width={26} />}
92
+ <Skeleton
93
+ height={col.height ?? 16}
94
+ width={col.width}
95
+ radius={col.radius}
96
+ />
97
+ </Flex>
98
+ ) : (
99
+ // Single skeleton cell
100
+ <Skeleton
101
+ height={col.height ?? 16}
102
+ width={col.width}
103
+ radius={col.radius}
104
+ style={getAlignmentStyle(col.align)}
105
+ />
106
+ )}
107
+ </Table.Td>
108
+ ))}
109
+ </Table.Tr>
110
+ ))}
111
+ </>
112
+ );
113
+ }
@@ -0,0 +1,9 @@
1
+ export {
2
+ SortableColumnHeader,
3
+ type SortableColumnHeaderProps,
4
+ } from './SortableColumnHeader';
5
+ export {
6
+ TableSkeletonRows,
7
+ type TableSkeletonRowsProps,
8
+ type SkeletonColumnConfig,
9
+ } from './TableSkeletonRows';
@@ -0,0 +1,58 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { MoreVertical } from 'lucide-react';
4
+
5
+ import { IconButton } from './IconButton';
6
+ import { Menu } from './Menu';
7
+
8
+ export interface TableActionsMenuProps {
9
+ /**
10
+ * Menu items to display in the dropdown
11
+ */
12
+ children: ReactNode;
13
+ /**
14
+ * Aria label for the action button
15
+ * @default "Row actions"
16
+ */
17
+ ariaLabel?: string;
18
+ }
19
+
20
+ /**
21
+ * Reusable action menu for table rows with three-dot icon
22
+ *
23
+ * Handles all the boilerplate for rendering a consistent action menu:
24
+ * - Three-dot icon button
25
+ * - Menu dropdown with proper positioning
26
+ * - Consistent styling and accessibility
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * createActionsColumn<UserData>({
31
+ * cell: ({ row }) => (
32
+ * <TableActionsMenu>
33
+ * <Menu.Item onClick={() => handleEdit(row.original)}>
34
+ * Edit
35
+ * </Menu.Item>
36
+ * <Menu.Item onClick={() => handleDelete(row.original)}>
37
+ * Delete
38
+ * </Menu.Item>
39
+ * </TableActionsMenu>
40
+ * ),
41
+ * })
42
+ * ```
43
+ */
44
+ export function TableActionsMenu({
45
+ children,
46
+ ariaLabel = 'Row actions',
47
+ }: TableActionsMenuProps) {
48
+ return (
49
+ <Menu position="bottom-end" withArrow>
50
+ <Menu.Target>
51
+ <IconButton variant="ghost" color="gray" aria-label={ariaLabel}>
52
+ <MoreVertical size={16} />
53
+ </IconButton>
54
+ </Menu.Target>
55
+ <Menu.Dropdown miw={260}>{children}</Menu.Dropdown>
56
+ </Menu>
57
+ );
58
+ }
@@ -0,0 +1,29 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Paper, type PaperProps } from '@mantine/core';
4
+
5
+ export interface TableCardProps extends PaperProps {
6
+ children: ReactNode;
7
+ /**
8
+ * Whether to show a border around the table card
9
+ * @default true
10
+ */
11
+ withBorder?: boolean;
12
+ }
13
+
14
+ /**
15
+ * TableCard Component
16
+ * Wraps tables with a consistent card-style background and border.
17
+ * Use this as the default wrapper for all Table components.
18
+ */
19
+ export function TableCard({
20
+ children,
21
+ withBorder = true,
22
+ ...props
23
+ }: TableCardProps) {
24
+ return (
25
+ <Paper withBorder={withBorder} radius="lg" shadow="0" p={0} {...props}>
26
+ {children}
27
+ </Paper>
28
+ );
29
+ }
@@ -0,0 +1,86 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Box, Flex, Paper } from '@mantine/core';
4
+
5
+ export interface TableContainerProps {
6
+ /**
7
+ * The controls section (search, filters, buttons, etc.)
8
+ */
9
+ controls?: ReactNode;
10
+ /**
11
+ * Optional filters bar (e.g., applied filter badges)
12
+ * Appears between controls and children
13
+ */
14
+ filtersBar?: ReactNode;
15
+ /**
16
+ * Optional action bar (e.g., bulk actions when items are selected)
17
+ */
18
+ actionBar?: ReactNode;
19
+ /**
20
+ * The table or empty state content
21
+ */
22
+ children: ReactNode;
23
+ /**
24
+ * Whether to show a border around the table container
25
+ * @default false
26
+ */
27
+ withBorder?: boolean;
28
+ /**
29
+ * When true, hides controls and filters bar (e.g. during initial load)
30
+ * @default false
31
+ */
32
+ isLoading?: boolean;
33
+ }
34
+
35
+ /**
36
+ * TableContainer Component
37
+ *
38
+ * A reusable container for tables with controls, optional filters bar, and optional action bar.
39
+ * Wraps table controls, filters bar, action bar, and the table itself in a bordered Paper container.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <TableContainer
44
+ * controls={
45
+ * <Flex justify="space-between" align="center" gap="md" wrap="wrap">
46
+ * <TextInput placeholder="Search..." />
47
+ * <Button>Export</Button>
48
+ * </Flex>
49
+ * }
50
+ * filtersBar={
51
+ * <AppliedFiltersManagerBar
52
+ * filterCategories={filters}
53
+ * onClearAll={clearFilters}
54
+ * />
55
+ * }
56
+ * actionBar={
57
+ * <BulkActionBar
58
+ * selectedCount={3}
59
+ * onDelete={handleDelete}
60
+ * onClose={handleClose}
61
+ * />
62
+ * }
63
+ * >
64
+ * <MyTable data={data} withBorder={false} />
65
+ * </TableContainer>
66
+ * ```
67
+ */
68
+ export function TableContainer({
69
+ controls,
70
+ filtersBar,
71
+ actionBar,
72
+ children,
73
+ withBorder = true,
74
+ isLoading = false,
75
+ }: TableContainerProps) {
76
+ return (
77
+ <Paper radius="lg" p={0} py="md" withBorder={withBorder}>
78
+ <Flex direction="column" gap="lg">
79
+ {!isLoading && <Box px="md">{controls}</Box>}
80
+ {!isLoading && filtersBar && <Box px="md">{filtersBar}</Box>}
81
+ {children}
82
+ {actionBar}
83
+ </Flex>
84
+ </Paper>
85
+ );
86
+ }
@@ -0,0 +1,2 @@
1
+ export { TableContainer } from './TableContainer';
2
+ export type { TableContainerProps } from './TableContainer';
@@ -0,0 +1,156 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { CloudDownload } from 'lucide-react';
4
+
5
+ import { Flex } from '@mantine/core';
6
+
7
+ import { IconButton } from '../IconButton';
8
+ import { SearchTextInput } from '../TextInput';
9
+ import { TableSelectionButton } from './TableSelectionButton';
10
+
11
+ export interface TableControlBarProps {
12
+ /**
13
+ * Placeholder text for the search input
14
+ */
15
+ searchPlaceholder: string;
16
+
17
+ /**
18
+ * Current search value
19
+ */
20
+ searchValue: string;
21
+
22
+ /**
23
+ * Callback when search value changes
24
+ */
25
+ onSearchChange: (value: string) => void;
26
+
27
+ /**
28
+ * Optional key to force reset/remount of search input
29
+ * Change this value to clear the search input state
30
+ */
31
+ searchKey?: string | number;
32
+
33
+ /**
34
+ * Whether selection mode is active
35
+ */
36
+ isSelectionMode: boolean;
37
+
38
+ /**
39
+ * Entity name for the selection button (e.g., "Apps", "Devices", "Users")
40
+ */
41
+ entityName: string;
42
+
43
+ /**
44
+ * Callback to toggle selection mode
45
+ */
46
+ onToggleSelection: () => void;
47
+
48
+ /**
49
+ * Optional custom filter controls to display between search and selection button
50
+ */
51
+ customFilters?: ReactNode;
52
+
53
+ /**
54
+ * Optional custom actions to display on the right side instead of default Download CSV button
55
+ */
56
+ rightActions?: ReactNode;
57
+
58
+ /**
59
+ * Whether to show the Download CSV button
60
+ * @default true
61
+ */
62
+ showDownloadButton?: boolean;
63
+
64
+ /**
65
+ * Optional callback when Download CSV button is clicked
66
+ */
67
+ onDownloadCSV?: () => void;
68
+
69
+ /**
70
+ * Whether to show the selection button
71
+ * @default true
72
+ */
73
+ canSelectItems?: boolean;
74
+
75
+ /**
76
+ * Whether right-side action controls are disabled (selection button and download CSV).
77
+ * Search input and custom filters remain enabled to allow filtering/searching.
78
+ * @default false
79
+ */
80
+ disabled?: boolean;
81
+ }
82
+
83
+ /**
84
+ * Main container component for standardized table controls
85
+ *
86
+ * Displays search, filters, selection button, and action buttons in a consistent layout
87
+ *
88
+ * @example
89
+ * ```tsx
90
+ * <TableControlBar
91
+ * searchPlaceholder="Search by Name"
92
+ * searchValue={searchQuery}
93
+ * onSearchChange={setSearchQuery}
94
+ * isSelectionMode={selection.isSelectionMode}
95
+ * entityName="Devices"
96
+ * onToggleSelection={selection.toggleSelectionMode}
97
+ * customFilters={<FilterButton />}
98
+ * rightActions={<ExportButton />}
99
+ * />
100
+ * ```
101
+ */
102
+ export function TableControlBar({
103
+ searchPlaceholder,
104
+ searchValue,
105
+ onSearchChange,
106
+ searchKey,
107
+ isSelectionMode,
108
+ entityName,
109
+ onToggleSelection,
110
+ customFilters,
111
+ rightActions,
112
+ showDownloadButton = true,
113
+ onDownloadCSV,
114
+ canSelectItems = true,
115
+ disabled = false,
116
+ }: TableControlBarProps) {
117
+ return (
118
+ <Flex justify="space-between" align="center" gap="md" wrap="wrap">
119
+ {/* Left side: Search and filters - always enabled to allow filtering/searching */}
120
+ <Flex gap="sm" align="center">
121
+ <SearchTextInput
122
+ key={searchKey}
123
+ placeholder={searchPlaceholder}
124
+ value={searchValue}
125
+ onChange={e => onSearchChange(e.currentTarget.value)}
126
+ debounceMs={300}
127
+ w="320px"
128
+ />
129
+ {customFilters}
130
+ </Flex>
131
+
132
+ {/* Right side: Selection button and actions - disabled when no data */}
133
+ <Flex gap="sm" align="center">
134
+ {canSelectItems && (
135
+ <TableSelectionButton
136
+ isSelectionMode={isSelectionMode}
137
+ entityName={entityName}
138
+ onClick={onToggleSelection}
139
+ disabled={disabled}
140
+ />
141
+ )}
142
+ {rightActions ||
143
+ (showDownloadButton && (
144
+ <IconButton
145
+ variant="outline"
146
+ size="sm"
147
+ onClick={onDownloadCSV}
148
+ disabled={disabled}
149
+ >
150
+ <CloudDownload size={16} />
151
+ </IconButton>
152
+ ))}
153
+ </Flex>
154
+ </Flex>
155
+ );
156
+ }
@@ -0,0 +1,57 @@
1
+ import { CheckCheck, X } from 'lucide-react';
2
+
3
+ import { Button } from '../Button';
4
+
5
+ export interface TableSelectionButtonProps {
6
+ /**
7
+ * Whether selection mode is active
8
+ */
9
+ isSelectionMode: boolean;
10
+
11
+ /**
12
+ * Entity name for the button label (e.g., "Apps", "Devices", "Users")
13
+ */
14
+ entityName: string;
15
+
16
+ /**
17
+ * Callback when button is clicked to toggle selection mode
18
+ */
19
+ onClick: () => void;
20
+
21
+ /**
22
+ * Whether the button is disabled
23
+ * @default false
24
+ */
25
+ disabled?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Standardized selection toggle button for tables
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <TableSelectionButton
34
+ * isSelectionMode={selection.isSelectionMode}
35
+ * entityName="Devices"
36
+ * onClick={selection.toggleSelectionMode}
37
+ * />
38
+ * ```
39
+ */
40
+ export function TableSelectionButton({
41
+ isSelectionMode,
42
+ entityName,
43
+ onClick,
44
+ disabled = false,
45
+ }: TableSelectionButtonProps) {
46
+ return (
47
+ <Button
48
+ variant={isSelectionMode ? 'primary' : 'outline'}
49
+ leftSection={isSelectionMode ? <X size={16} /> : <CheckCheck size={16} />}
50
+ size="sm"
51
+ onClick={onClick}
52
+ disabled={disabled}
53
+ >
54
+ {isSelectionMode ? 'Cancel Selection' : `Select ${entityName}`}
55
+ </Button>
56
+ );
57
+ }
@@ -0,0 +1,13 @@
1
+ // Components
2
+ export { TableControlBar, type TableControlBarProps } from './TableControlBar';
3
+ export {
4
+ TableSelectionButton,
5
+ type TableSelectionButtonProps,
6
+ } from './TableSelectionButton';
7
+
8
+ // Hook
9
+ export {
10
+ useTableControlBar,
11
+ type UseTableControlBarProps,
12
+ type UseTableControlBarReturn,
13
+ } from './useTableControlBar';