@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,105 @@
1
+ import type { MouseEvent, ReactNode } from 'react';
2
+
3
+ import { ChevronRight } from 'lucide-react';
4
+
5
+ import { Box, Flex } from '@mantine/core';
6
+
7
+ import { IconButton } from '../IconButton';
8
+
9
+ import type { Row } from '@tanstack/react-table';
10
+
11
+ export interface RowExpandToggleProps<TData> {
12
+ /**
13
+ * The TanStack Table row this toggle represents.
14
+ */
15
+ row: Row<TData>;
16
+ /**
17
+ * Optional content rendered after the chevron (typically the cell's label).
18
+ */
19
+ children?: ReactNode;
20
+ /**
21
+ * Pixels of inline-start indentation multiplied by `row.depth`.
22
+ * @default 16
23
+ */
24
+ indentPx?: number;
25
+ /**
26
+ * aria-label for the button when the row is collapsed.
27
+ * @default "Expand row"
28
+ */
29
+ expandAriaLabel?: string;
30
+ /**
31
+ * aria-label for the button when the row is expanded.
32
+ * @default "Collapse row"
33
+ */
34
+ collapseAriaLabel?: string;
35
+ }
36
+
37
+ /**
38
+ * Cell helper for composing an expand/collapse chevron + children inside a
39
+ * consumer-owned `DataTable` column. Use this when `renderExpandToggle` is
40
+ * set to `"custom"` on the table.
41
+ *
42
+ * Renders:
43
+ * - depth-based inline-start padding (`row.depth * indentPx`),
44
+ * - a chevron button when `row.getCanExpand()` is true (clicking it toggles
45
+ * expansion and stops propagation so the containing row's click handler is
46
+ * not triggered),
47
+ * - a same-width spacer when the row cannot expand so sibling cells align
48
+ * vertically,
49
+ * - then `children`.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * columnHelper.accessor('label', {
54
+ * cell: (info) => (
55
+ * <RowExpandToggle row={info.row}>
56
+ * <Text variant="caption1.stronger">{info.getValue()}</Text>
57
+ * </RowExpandToggle>
58
+ * ),
59
+ * });
60
+ * ```
61
+ */
62
+ export function RowExpandToggle<TData>({
63
+ row,
64
+ children,
65
+ indentPx = 16,
66
+ expandAriaLabel = 'Expand row',
67
+ collapseAriaLabel = 'Collapse row',
68
+ }: RowExpandToggleProps<TData>) {
69
+ const canExpand = row.getCanExpand();
70
+ const isExpanded = row.getIsExpanded();
71
+
72
+ const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
73
+ event.stopPropagation();
74
+ row.getToggleExpandedHandler()();
75
+ };
76
+
77
+ return (
78
+ <Flex
79
+ align="center"
80
+ gap={4}
81
+ style={{ paddingInlineStart: row.depth * indentPx }}
82
+ >
83
+ {canExpand ? (
84
+ <IconButton
85
+ variant="ghost"
86
+ size="xs"
87
+ aria-label={isExpanded ? collapseAriaLabel : expandAriaLabel}
88
+ aria-expanded={isExpanded}
89
+ onClick={handleClick}
90
+ >
91
+ <ChevronRight
92
+ size={14}
93
+ style={{
94
+ transition: 'transform 120ms',
95
+ transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
96
+ }}
97
+ />
98
+ </IconButton>
99
+ ) : (
100
+ <Box w={22} h={22} aria-hidden />
101
+ )}
102
+ {children}
103
+ </Flex>
104
+ );
105
+ }
@@ -0,0 +1,190 @@
1
+ import type { MouseEvent, ReactNode } from 'react';
2
+
3
+ import { ChevronRight } from 'lucide-react';
4
+
5
+ import { Box, Flex } from '@mantine/core';
6
+
7
+ import { tokens } from '../../theme/themeContract.css';
8
+ import { IconButton } from '../IconButton';
9
+ import { Text } from '../Typography';
10
+
11
+ import type { Row } from '@tanstack/react-table';
12
+
13
+ export interface RowGroupHeaderAggregate {
14
+ /**
15
+ * Secondary label shown before the value, e.g. "Subtotal".
16
+ * Rendered in a subdued text color.
17
+ */
18
+ label?: ReactNode;
19
+ /**
20
+ * The aggregate value itself (formatted), e.g. "$492,800".
21
+ */
22
+ value: ReactNode;
23
+ }
24
+
25
+ export interface RowGroupHeaderProps<TData> {
26
+ /**
27
+ * The TanStack Table row this group header represents. Used for the
28
+ * chevron expand/collapse state and to derive `aria-expanded`.
29
+ */
30
+ row: Row<TData>;
31
+ /**
32
+ * Main label text for the group.
33
+ */
34
+ label: ReactNode;
35
+ /**
36
+ * Optional colored accent square (10×10, 2px radius) rendered between
37
+ * chevron and label. Accepts any CSS color value — pass a design-system
38
+ * token value, not a class name.
39
+ */
40
+ accentColor?: string;
41
+ /**
42
+ * Optional count/metadata pill shown after the label, e.g. "3 items".
43
+ */
44
+ count?: ReactNode;
45
+ /**
46
+ * Optional right-aligned aggregate (e.g. subtotal).
47
+ */
48
+ aggregate?: RowGroupHeaderAggregate;
49
+ /**
50
+ * Optional extra content rendered at the far right, after the aggregate.
51
+ * Use for kebab menus, "edit" buttons, etc.
52
+ */
53
+ actions?: ReactNode;
54
+ /**
55
+ * Background color applied to the primitive's outer flex. Pass `null` to
56
+ * render transparently (e.g. when the container already paints its own
57
+ * background and you don't want a colour strip from this component).
58
+ *
59
+ * When used inside a `DataTable` via `renderGroupRow`, the `<tr>` itself
60
+ * also paints `groupRowBackground` (which defaults to the same token), so
61
+ * this background and the row background line up seamlessly — including
62
+ * the `<td>`'s horizontal padding strip, which the `<tr>` background fills.
63
+ *
64
+ * @default tokens.color.background.subduedUltralight
65
+ */
66
+ background?: string | null;
67
+ /**
68
+ * aria-label for the chevron when the group is collapsed.
69
+ * @default "Expand row"
70
+ */
71
+ expandAriaLabel?: string;
72
+ /**
73
+ * aria-label for the chevron when the group is expanded.
74
+ * @default "Collapse row"
75
+ */
76
+ collapseAriaLabel?: string;
77
+ }
78
+
79
+ /**
80
+ * Group header content for use inside a `DataTable`'s `renderGroupRow` prop.
81
+ *
82
+ * Implements the standard group-row layout from the design system:
83
+ *
84
+ * ```
85
+ * [chevron] [accent] [label] [count] [aggregate] [actions]
86
+ * ```
87
+ *
88
+ * The chevron is driven by TanStack's expansion API; clicking it toggles
89
+ * expansion and stops propagation so that if the row body is also wired up
90
+ * to toggle, the button doesn't double-fire.
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * <DataTable
95
+ * renderGroupRow={(row) => (
96
+ * <RowGroupHeader
97
+ * row={row}
98
+ * accentColor="#017854"
99
+ * label={row.original.name}
100
+ * count={`${row.subRows.length} items`}
101
+ * aggregate={{ label: 'Subtotal', value: formatCurrency(sum) }}
102
+ * />
103
+ * )}
104
+ * />
105
+ * ```
106
+ */
107
+ export function RowGroupHeader<TData>({
108
+ row,
109
+ label,
110
+ accentColor,
111
+ count,
112
+ aggregate,
113
+ actions,
114
+ background = tokens.color.background.subduedUltralight,
115
+ expandAriaLabel = 'Expand row',
116
+ collapseAriaLabel = 'Collapse row',
117
+ }: RowGroupHeaderProps<TData>) {
118
+ const canExpand = row.getCanExpand();
119
+ const isExpanded = row.getIsExpanded();
120
+
121
+ const handleChevronClick = (event: MouseEvent<HTMLButtonElement>) => {
122
+ event.stopPropagation();
123
+ row.getToggleExpandedHandler()();
124
+ };
125
+
126
+ return (
127
+ <Flex align="center" gap={8} w="100%" h="100%" bg={background ?? undefined}>
128
+ <Flex align="center" gap={8} style={{ flex: '1 0 0', minWidth: 0 }}>
129
+ {canExpand ? (
130
+ <IconButton
131
+ variant="ghost"
132
+ size="xs"
133
+ aria-label={isExpanded ? collapseAriaLabel : expandAriaLabel}
134
+ aria-expanded={isExpanded}
135
+ onClick={handleChevronClick}
136
+ >
137
+ <ChevronRight
138
+ size={14}
139
+ style={{
140
+ transition: 'transform 120ms',
141
+ transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
142
+ }}
143
+ />
144
+ </IconButton>
145
+ ) : (
146
+ <Box w={22} h={22} aria-hidden />
147
+ )}
148
+ {accentColor && (
149
+ <Box
150
+ w={10}
151
+ h={10}
152
+ style={{
153
+ backgroundColor: accentColor,
154
+ borderRadius: 2,
155
+ flexShrink: 0,
156
+ }}
157
+ aria-hidden
158
+ />
159
+ )}
160
+ <Text variant="body1.stronger">{label}</Text>
161
+ {count != null && (
162
+ <Flex
163
+ align="center"
164
+ style={{
165
+ backgroundColor: '#eef0f3',
166
+ borderRadius: 10,
167
+ padding: '1px 6px',
168
+ flexShrink: 0,
169
+ }}
170
+ >
171
+ <Text variant="caption3.stronger" c="text.subdued.default">
172
+ {count}
173
+ </Text>
174
+ </Flex>
175
+ )}
176
+ </Flex>
177
+ {aggregate && (
178
+ <Flex align="center" gap={4} style={{ flexShrink: 0 }}>
179
+ {aggregate.label != null && (
180
+ <Text variant="caption3" c="text.subdued.default">
181
+ {aggregate.label}
182
+ </Text>
183
+ )}
184
+ <Text variant="body1.stronger">{aggregate.value}</Text>
185
+ </Flex>
186
+ )}
187
+ {actions}
188
+ </Flex>
189
+ );
190
+ }
@@ -0,0 +1,86 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { createColumnHelper, type CellContext } from '@tanstack/react-table';
4
+
5
+ /**
6
+ * Options for creating an actions column
7
+ */
8
+ export interface CreateActionsColumnOptions<TData> {
9
+ /**
10
+ * Render function for the cell content (receives TanStack Table cell context)
11
+ */
12
+ cell: (context: CellContext<TData, unknown>) => ReactNode;
13
+ /**
14
+ * Column ID
15
+ * @default 'actions'
16
+ */
17
+ id?: string;
18
+ /**
19
+ * Column width in pixels
20
+ * @default 60
21
+ */
22
+ size?: number;
23
+ /**
24
+ * Pin column to left or right so it stays in view when scrolling.
25
+ * Pass null to leave the column unpinned.
26
+ * @default 'right'
27
+ */
28
+ pinned?: 'left' | 'right' | null;
29
+ }
30
+
31
+ /**
32
+ * Helper function to create a right-aligned actions column with consistent behavior
33
+ *
34
+ * Automatically:
35
+ * - Sets right alignment via meta.align
36
+ * - Wraps cell content with stopPropagation to prevent row clicks
37
+ * - Uses display column (non-sortable)
38
+ * - Sets sensible defaults for size and id
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * import { createActionsColumn } from '@scalepad/ui';
43
+ *
44
+ * const columns = [
45
+ * // ... other columns
46
+ * createActionsColumn<UserData>({
47
+ * cell: ({ row }) => (
48
+ * <Menu>
49
+ * <Menu.Target>
50
+ * <IconButton><MoreVertical /></IconButton>
51
+ * </Menu.Target>
52
+ * <Menu.Dropdown>
53
+ * <Menu.Item>Edit</Menu.Item>
54
+ * </Menu.Dropdown>
55
+ * </Menu>
56
+ * ),
57
+ * }),
58
+ * ];
59
+ * ```
60
+ */
61
+ export function createActionsColumn<TData>({
62
+ cell,
63
+ id = 'actions',
64
+ size = 60,
65
+ pinned = 'right',
66
+ }: CreateActionsColumnOptions<TData>) {
67
+ const col = createColumnHelper<TData>();
68
+
69
+ return col.display({
70
+ id,
71
+ size,
72
+ meta: {
73
+ align: 'right',
74
+ ...(pinned !== null && { pinned }),
75
+ },
76
+ cell: context => (
77
+ <div
78
+ role="presentation"
79
+ onClick={e => e.stopPropagation()}
80
+ onKeyDown={e => e.stopPropagation()}
81
+ >
82
+ {cell(context)}
83
+ </div>
84
+ ),
85
+ });
86
+ }
@@ -0,0 +1,25 @@
1
+ export {
2
+ createActionsColumn,
3
+ type CreateActionsColumnOptions,
4
+ } from './createActionsColumn';
5
+ export {
6
+ DataTable,
7
+ type DataTableColumnConfigurator,
8
+ type DataTableColumnConfiguratorItem,
9
+ type DataTableProps,
10
+ } from './DataTable';
11
+ export { RowExpandToggle, type RowExpandToggleProps } from './RowExpandToggle';
12
+ export {
13
+ RowGroupHeader,
14
+ type RowGroupHeaderAggregate,
15
+ type RowGroupHeaderProps,
16
+ } from './RowGroupHeader';
17
+
18
+ /**
19
+ * Column meta supported by DataTable / useServerTable.
20
+ * Use when defining columns to get typed meta (e.g. pinned).
21
+ */
22
+ export type DataTableColumnMeta = {
23
+ pinned?: 'left' | 'right' | null;
24
+ align?: string;
25
+ };
@@ -0,0 +1,59 @@
1
+ import { Flex } from '@mantine/core';
2
+ import {
3
+ DatePicker,
4
+ type DatesRangeValue,
5
+ type DateValue,
6
+ } from '@mantine/dates';
7
+
8
+ import type { DateRange } from './types';
9
+
10
+ interface CustomRangePickerProps {
11
+ startDate: DateValue;
12
+ endDate: DateValue;
13
+ minDate?: DateValue;
14
+ maxDate: DateValue;
15
+ onChange?: (range: DateRange) => void;
16
+ }
17
+
18
+ export function CustomRangePicker({
19
+ startDate,
20
+ endDate,
21
+ minDate,
22
+ maxDate,
23
+ onChange,
24
+ }: CustomRangePickerProps) {
25
+ const value: DatesRangeValue | undefined =
26
+ startDate && endDate ? [startDate, endDate] : undefined;
27
+
28
+ const handleDateChange = (dates: DatesRangeValue) => {
29
+ if (dates) {
30
+ const [start, end] = dates;
31
+ onChange?.({
32
+ startDate: start ?? null,
33
+ endDate: end ?? null,
34
+ });
35
+ } else {
36
+ onChange?.({
37
+ startDate: null,
38
+ endDate: null,
39
+ });
40
+ }
41
+ };
42
+
43
+ // Convert DateValue to Date | string | undefined for Mantine DatePicker
44
+ const minDateValue = minDate === null ? undefined : minDate;
45
+ const maxDateValue = maxDate === null ? undefined : maxDate;
46
+
47
+ return (
48
+ <Flex gap="md" align="flex-start" justify="center">
49
+ <DatePicker
50
+ type="range"
51
+ value={value}
52
+ onChange={handleDateChange}
53
+ minDate={minDateValue}
54
+ maxDate={maxDateValue}
55
+ numberOfColumns={2}
56
+ />
57
+ </Flex>
58
+ );
59
+ }