@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,314 @@
1
+ import { useCallback, useMemo, type ReactNode } from 'react';
2
+
3
+ import {
4
+ AppliedFiltersManagerBar,
5
+ type AppliedFiltersManagerBarProps,
6
+ type FilterCategory,
7
+ } from '../AppliedFiltersManagerBar';
8
+ import { BulkActionBar, type BulkActionBarProps } from '../BulkActionBar';
9
+ import { TableControlBar, type TableControlBarProps } from './TableControlBar';
10
+
11
+ import type { useTableSelection } from '../../hooks/useTableSelection';
12
+
13
+ export interface UseTableControlBarProps {
14
+ /**
15
+ * Placeholder text for the search input
16
+ */
17
+ searchPlaceholder: string;
18
+
19
+ /**
20
+ * Current search value
21
+ */
22
+ searchValue: string;
23
+
24
+ /**
25
+ * Callback when search value changes
26
+ */
27
+ onSearchChange: (value: string) => void;
28
+
29
+ /**
30
+ * Key to force reset/remount of search input
31
+ * REQUIRED: Pass the resetKey from useEntityTable's search.resetKey
32
+ * This ensures the search input properly clears when filters are reset
33
+ * @example searchKey: search.resetKey
34
+ */
35
+ searchKey: string | number;
36
+
37
+ /**
38
+ * Table selection state from useTableSelection hook
39
+ */
40
+ selection: ReturnType<typeof useTableSelection>;
41
+
42
+ /**
43
+ * Entity name for the selection button (e.g., "Apps", "Devices", "Users")
44
+ */
45
+ entityName: string;
46
+
47
+ /**
48
+ * Optional custom filter controls to display between search and selection button
49
+ */
50
+ customFilters?: ReactNode;
51
+
52
+ /**
53
+ * Optional bulk action buttons to show in the action bar when items are selected
54
+ */
55
+ bulkActions?: ReactNode;
56
+
57
+ /**
58
+ * Optional custom actions to display on the right side instead of default Download CSV button
59
+ */
60
+ rightActions?: ReactNode;
61
+
62
+ /**
63
+ * Whether to show the Download CSV button
64
+ * @default true
65
+ */
66
+ showDownloadButton?: boolean;
67
+
68
+ /**
69
+ * Optional callback when Download CSV button is clicked
70
+ */
71
+ onDownloadCSV?: () => void;
72
+
73
+ /**
74
+ * Whether to show the selection button
75
+ * @default true
76
+ */
77
+ canSelectItems?: boolean;
78
+
79
+ /**
80
+ * Whether there is no data in the table (from useEntityTable's hasNoData)
81
+ * Used to auto-disable right-side actions when appropriate
82
+ * Note: Search input always remains enabled
83
+ * @default false
84
+ */
85
+ hasNoData?: boolean;
86
+
87
+ /**
88
+ * Whether there are active filters applied (search query, filters, etc.)
89
+ * Used to determine if actions should be disabled
90
+ * When true, keeps actions enabled even if hasNoData is true
91
+ * @default false
92
+ */
93
+ hasActiveFilters?: boolean;
94
+
95
+ /**
96
+ * Optional filter state (from useFilters hook)
97
+ * When provided, automatically renders AppliedFiltersManagerBar
98
+ */
99
+ filters?: FilterCategory[];
100
+
101
+ /**
102
+ * Optional callback to update filter state
103
+ * Required when filters is provided
104
+ */
105
+ onFiltersChange?: (filters: FilterCategory[]) => void;
106
+
107
+ /**
108
+ * Optional callback when a category is removed
109
+ * If not provided, defaults to clearing items for that category
110
+ */
111
+ onCategoryRemove?: (categoryName: string) => void;
112
+ }
113
+
114
+ export interface UseTableControlBarReturn {
115
+ /**
116
+ * JSX for the controls section (search, filters, selection button)
117
+ * Convenience: Pre-composed JSX for common use case
118
+ */
119
+ controls: ReactNode;
120
+
121
+ /**
122
+ * JSX for the action bar (bulk actions when items are selected)
123
+ * Convenience: Pre-composed JSX for common use case
124
+ */
125
+ actionBar: ReactNode | undefined;
126
+
127
+ /**
128
+ * Optional JSX for the applied filters bar
129
+ * Only present when filters prop is provided and has active items
130
+ * Convenience: Pre-composed JSX for common use case
131
+ */
132
+ filtersBar?: ReactNode;
133
+
134
+ /**
135
+ * Raw props for custom composition (escape hatch)
136
+ * Use when you need full control over rendering or need to insert custom elements
137
+ *
138
+ * @example
139
+ * ```tsx
140
+ * const { props } = useTableControlBar({...});
141
+ *
142
+ * return (
143
+ * <CustomWrapper>
144
+ * <TableControlBar {...props.controls} />
145
+ * <MyCustomThing />
146
+ * {props.filtersBar && <AppliedFiltersManagerBar {...props.filtersBar} />}
147
+ * {props.actionBar && <BulkActionBar {...props.actionBar} />}
148
+ * </CustomWrapper>
149
+ * );
150
+ * ```
151
+ */
152
+ props: {
153
+ controls: TableControlBarProps;
154
+ actionBar?: BulkActionBarProps;
155
+ filtersBar?: AppliedFiltersManagerBarProps;
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Hook to create standardized table control bar with search, filters, selection mode, and bulk actions
161
+ *
162
+ * Returns both pre-composed JSX (convenience) and raw props (flexibility).
163
+ *
164
+ * @example Common usage (pre-composed JSX)
165
+ * ```tsx
166
+ * const { controls, actionBar, filtersBar } = useTableControlBar({
167
+ * searchPlaceholder: "Search by App Name or Category",
168
+ * searchValue: search.query,
169
+ * onSearchChange: search.setQuery,
170
+ * searchKey: search.resetKey,
171
+ * selection,
172
+ * entityName: "Apps",
173
+ * hasNoData,
174
+ * hasActiveFilters,
175
+ * filters, // From useFilters hook
176
+ * onFiltersChange, // From useFilters hook
177
+ * customFilters: (
178
+ * <FilterMenu
179
+ * filters={filters}
180
+ * onFiltersChange={onFiltersChange}
181
+ * filterSchemas={filterSchemas}
182
+ * />
183
+ * ),
184
+ * bulkActions: (
185
+ * <Button variant="secondary" size="sm">
186
+ * Add to Favorites
187
+ * </Button>
188
+ * ),
189
+ * });
190
+ *
191
+ * <TableContainer controls={controls} actionBar={actionBar} filtersBar={filtersBar}>
192
+ * <Table>...</Table>
193
+ * </TableContainer>
194
+ * ```
195
+ *
196
+ * @example Custom composition (raw props)
197
+ * ```tsx
198
+ * const { props } = useTableControlBar({...});
199
+ *
200
+ * return (
201
+ * <CustomLayout>
202
+ * <TableControlBar {...props.controls} />
203
+ * <MyCustomElement />
204
+ * {props.filtersBar && <AppliedFiltersManagerBar {...props.filtersBar} />}
205
+ * {props.actionBar && <BulkActionBar {...props.actionBar} />}
206
+ * </CustomLayout>
207
+ * );
208
+ * ```
209
+ */
210
+ export function useTableControlBar({
211
+ searchPlaceholder,
212
+ searchValue,
213
+ onSearchChange,
214
+ searchKey,
215
+ selection,
216
+ entityName,
217
+ customFilters,
218
+ bulkActions,
219
+ rightActions,
220
+ showDownloadButton = true,
221
+ onDownloadCSV,
222
+ canSelectItems = true,
223
+ hasNoData = false,
224
+ hasActiveFilters = false,
225
+ filters,
226
+ onFiltersChange,
227
+ onCategoryRemove,
228
+ }: UseTableControlBarProps): UseTableControlBarReturn {
229
+ // Auto-disable right-side actions when there's no data AND no active filters
230
+ // This represents a true "empty state" where actions don't make sense
231
+ // But keep actions enabled when user is filtering/searching (even with no results)
232
+ const shouldDisableActions = hasNoData && !hasActiveFilters;
233
+
234
+ // Memoize default onCategoryRemove handler
235
+ const defaultCategoryRemove = useCallback(
236
+ (categoryName: string) => {
237
+ if (!onFiltersChange) return;
238
+ const updatedFilters =
239
+ filters?.map(f =>
240
+ f.categoryName === categoryName ? { ...f, items: [] } : f,
241
+ ) || [];
242
+ onFiltersChange(updatedFilters);
243
+ },
244
+ [filters, onFiltersChange],
245
+ );
246
+
247
+ const handleCategoryRemove = onCategoryRemove || defaultCategoryRemove;
248
+
249
+ // Build raw props for components (escape hatch for custom composition)
250
+ const controlsProps: TableControlBarProps = {
251
+ searchPlaceholder,
252
+ searchValue,
253
+ onSearchChange,
254
+ searchKey,
255
+ isSelectionMode: selection.isSelectionMode,
256
+ entityName,
257
+ onToggleSelection: selection.toggleSelectionMode,
258
+ customFilters,
259
+ rightActions,
260
+ showDownloadButton,
261
+ onDownloadCSV,
262
+ canSelectItems,
263
+ disabled: shouldDisableActions,
264
+ };
265
+
266
+ const actionBarProps: BulkActionBarProps | undefined =
267
+ selection.isSelectionMode
268
+ ? {
269
+ selectedCount: selection.selectedCount,
270
+ actions: bulkActions,
271
+ actionsDisabled: selection.selectedCount === 0,
272
+ }
273
+ : undefined;
274
+
275
+ // Memoize clear-all handler for filters bar
276
+ const handleClearAll = useCallback(() => {
277
+ if (!onFiltersChange || !filters) return;
278
+ onFiltersChange(filters.map(f => ({ ...f, items: [] })));
279
+ }, [filters, onFiltersChange]);
280
+
281
+ const filtersBarProps: AppliedFiltersManagerBarProps | undefined = useMemo(
282
+ () =>
283
+ filters && filters.some(f => f.items.length > 0)
284
+ ? {
285
+ filterCategories: filters,
286
+ onCategoryRemove: handleCategoryRemove,
287
+ onClearAll: handleClearAll,
288
+ }
289
+ : undefined,
290
+ [filters, handleCategoryRemove, handleClearAll],
291
+ );
292
+
293
+ // Pre-composed JSX (convenience for common use case)
294
+ const controls = <TableControlBar {...controlsProps} />;
295
+ const actionBar = actionBarProps ? (
296
+ <BulkActionBar {...actionBarProps} />
297
+ ) : undefined;
298
+ const filtersBar = filtersBarProps ? (
299
+ <AppliedFiltersManagerBar {...filtersBarProps} />
300
+ ) : undefined;
301
+
302
+ return {
303
+ // Convenience: Pre-composed JSX
304
+ controls,
305
+ actionBar,
306
+ filtersBar,
307
+ // Flexibility: Raw props for custom composition
308
+ props: {
309
+ controls: controlsProps,
310
+ actionBar: actionBarProps,
311
+ filtersBar: filtersBarProps,
312
+ },
313
+ };
314
+ }
@@ -0,0 +1,43 @@
1
+ import { Checkbox } from '@mantine/core';
2
+
3
+ export interface SelectionCheckboxProps {
4
+ checked: boolean;
5
+ onChange: () => void;
6
+ disabled?: boolean;
7
+ }
8
+
9
+ export function SelectionCheckbox({
10
+ checked,
11
+ onChange,
12
+ disabled,
13
+ }: SelectionCheckboxProps) {
14
+ return (
15
+ <Checkbox
16
+ checked={checked}
17
+ onChange={onChange}
18
+ disabled={disabled}
19
+ aria-label="Select row"
20
+ />
21
+ );
22
+ }
23
+
24
+ export interface SelectAllCheckboxProps {
25
+ checked: boolean;
26
+ indeterminate?: boolean;
27
+ onChange: () => void;
28
+ }
29
+
30
+ export function SelectAllCheckbox({
31
+ checked,
32
+ indeterminate,
33
+ onChange,
34
+ }: SelectAllCheckboxProps) {
35
+ return (
36
+ <Checkbox
37
+ checked={checked}
38
+ indeterminate={indeterminate}
39
+ onChange={onChange}
40
+ aria-label="Select all rows"
41
+ />
42
+ );
43
+ }
@@ -0,0 +1,5 @@
1
+ export { SelectionCheckbox, SelectAllCheckbox } from './TableSelection';
2
+ export type {
3
+ SelectionCheckboxProps,
4
+ SelectAllCheckboxProps,
5
+ } from './TableSelection';
@@ -0,0 +1,76 @@
1
+ # Tabs Component
2
+
3
+ Beautiful tabs matching Figma design specifications with smooth interactions and shadow effects.
4
+
5
+ ## Features
6
+
7
+ - 🎨 Figma-spec design with semantic color tokens
8
+ - 🌗 Automatic dark mode support
9
+ - ✨ Smooth hover states and transitions
10
+ - 📦 Simple, familiar Mantine API
11
+ - 🎯 Perfect 6px icon-text gap alignment
12
+ - 💅 Elevated shadow on active tab
13
+
14
+ ## Quick Start
15
+
16
+ ```tsx
17
+ import { Tabs } from '@acme/ui';
18
+ import { Monitor, Users } from 'lucide-react';
19
+
20
+ <Tabs defaultValue="devices">
21
+ <Tabs.List>
22
+ <Tabs.Tab value="devices" leftSection={<Monitor size={17} />}>
23
+ Devices
24
+ </Tabs.Tab>
25
+ <Tabs.Tab value="users" leftSection={<Users size={17} />}>
26
+ Users
27
+ </Tabs.Tab>
28
+ </Tabs.List>
29
+ </Tabs>;
30
+ ```
31
+
32
+ ## API
33
+
34
+ ### Tabs (Root)
35
+
36
+ | Prop | Type | Description |
37
+ | -------------- | --------------------------------- | ---------------------------------------------------------------------- |
38
+ | `value` | `string \| null` | Controlled active tab |
39
+ | `onChange` | `(value: string \| null) => void` | Tab change callback |
40
+ | `defaultValue` | `string` | Default active tab (uncontrolled) |
41
+ | `size` | `'default' \| 'sm'` | Tab size: default (standard) or sm (smaller label, icon, less padding) |
42
+ | `children` | `ReactNode` | Tabs.List and Tabs.Panel components |
43
+
44
+ ### Tabs.Tab
45
+
46
+ | Prop | Type | Description |
47
+ | -------------- | ----------- | ------------------------- |
48
+ | `value` | `string` | Unique tab identifier |
49
+ | `leftSection` | `ReactNode` | Icon/element before label |
50
+ | `rightSection` | `ReactNode` | Icon/element after label |
51
+ | `disabled` | `boolean` | Disable the tab |
52
+ | `children` | `ReactNode` | Tab label |
53
+
54
+ ## Design Specs
55
+
56
+ - **Border radius**: 10px
57
+ - **Icon-text gap**: 6px
58
+ - **Min height**: 29px
59
+ - **Font**: 14px / 600 / 21px line-height
60
+ - **Shadow**: `0px 1px 3px 0px rgba(0, 0, 0, 0.1), 0px 1px 2px -1px rgba(0, 0, 0, 0.1)`
61
+ - **Transition**: 150ms ease
62
+
63
+ ## Best Practices
64
+
65
+ - Use 16-17px icons for default size; use 14px icons for `size="sm"`
66
+ - Keep 2-5 tabs maximum
67
+ - Use concise labels (1-2 words)
68
+ - Center tabs with Flex for balance
69
+
70
+ ## More Examples
71
+
72
+ See **Storybook** for interactive examples and live demos.
73
+
74
+ ## Real-World Usage
75
+
76
+ `apps/web/src/routes/_auth.deployment.tsx`
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Tabs component styles – vanilla-extract with semantic design tokens
3
+ */
4
+
5
+ import { style } from '@vanilla-extract/css';
6
+
7
+ import { tokens } from '../../theme/themeContract.css';
8
+ import { textStyleVariants } from '../../tokens/text-styles';
9
+
10
+ export const tab = style({
11
+ selectors: {
12
+ '&[role="tab"]': {
13
+ fontWeight: 700,
14
+ lineHeight: 1.5,
15
+ paddingTop: tokens.spacing.xs,
16
+ paddingBottom: tokens.spacing.sm,
17
+ color: tokens.color.text.subduedStrong,
18
+ },
19
+ '&[role="tab"]:hover': {
20
+ color: tokens.color.text.primaryDefault,
21
+ backgroundColor: 'transparent',
22
+ fontWeight: 700,
23
+ },
24
+ '&[role="tab"][data-active="true"]': {
25
+ color: tokens.color.text.primaryDefault,
26
+ fontWeight: 700,
27
+ },
28
+ '&[role="tab"][data-active="true"]::before': { display: 'none' },
29
+ '&[role="tab"][data-active="true"]::after': { display: 'none' },
30
+ '&[role="tab"][data-active="true"]:hover': {
31
+ color: tokens.color.text.primaryDefault,
32
+ },
33
+ },
34
+ });
35
+
36
+ /** Small size: smaller label, icon, less padding/spacing */
37
+ export const tabSm = style({
38
+ selectors: {
39
+ '&[role="tab"]': {
40
+ paddingTop: tokens.spacing['2xs'],
41
+ paddingBottom: tokens.spacing.xs,
42
+ paddingLeft: tokens.spacing.sm,
43
+ paddingRight: tokens.spacing.sm,
44
+ fontSize: textStyleVariants.caption2.fontSize,
45
+ lineHeight: 1.5,
46
+ gap: tokens.spacing['2xs'],
47
+ },
48
+ },
49
+ });
50
+
51
+ /** List spacing for small tabs */
52
+ export const listSm = style({
53
+ gap: tokens.spacing.xs,
54
+ });
@@ -0,0 +1,47 @@
1
+ import figma from '@figma/code-connect';
2
+
3
+ import { Tabs } from './Tabs';
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
+ Tabs,
15
+ 'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=9%3A639',
16
+ {
17
+ props: {
18
+ // No matching props could be found for these Figma properties:
19
+ // "size": figma.enum('Size', {
20
+ // "Regular": "regular",
21
+ // "Large": "large",
22
+ // "Small": "small"
23
+ // }),
24
+ // "content": figma.enum('Content', {
25
+ // "Label": "label",
26
+ // "Icon": "icon",
27
+ // "Icon + Label": "icon---label"
28
+ // }),
29
+ // "parts": figma.enum('Parts', {
30
+ // "2 Parts": "2-parts",
31
+ // "3 Parts": "3-parts",
32
+ // "4 Parts": "4-parts",
33
+ // "5 Parts": "5-parts"
34
+ // })
35
+ },
36
+ example: _props => (
37
+ <Tabs defaultValue="first">
38
+ <Tabs.List>
39
+ <Tabs.Tab value="first">First</Tabs.Tab>
40
+ <Tabs.Tab value="second">Second</Tabs.Tab>
41
+ </Tabs.List>
42
+ <Tabs.Panel value="first">First content</Tabs.Panel>
43
+ <Tabs.Panel value="second">Second content</Tabs.Panel>
44
+ </Tabs>
45
+ ),
46
+ },
47
+ );
@@ -0,0 +1,96 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import {
4
+ Tabs as MantineTabs,
5
+ type TabsProps as MantineTabsProps,
6
+ } from '@mantine/core';
7
+
8
+ import * as classes from './Tabs.css';
9
+
10
+ /**
11
+ * Enhanced Tabs component with Figma design specifications.
12
+ *
13
+ * Features:
14
+ * - Smooth, pill-style tabs with shadow on active state
15
+ * - Consistent with design system colors and spacing
16
+ * - 6px gap between icon and text
17
+ * - 29px minimum height for consistent alignment
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <Tabs defaultValue="devices">
22
+ * <Tabs.List>
23
+ * <Tabs.Tab value="devices" leftSection={<Monitor size={17} />}>
24
+ * Devices
25
+ * </Tabs.Tab>
26
+ * <Tabs.Tab value="users" leftSection={<Users size={17} />}>
27
+ * Users
28
+ * </Tabs.Tab>
29
+ * </Tabs.List>
30
+ *
31
+ * <Tabs.Panel value="devices">
32
+ * Devices content
33
+ * </Tabs.Panel>
34
+ * <Tabs.Panel value="users">
35
+ * Users content
36
+ * </Tabs.Panel>
37
+ * </Tabs>
38
+ * ```
39
+ */
40
+ export type TabsSize = 'default' | 'sm';
41
+
42
+ export interface TabsProps extends MantineTabsProps {
43
+ /** The active tab value */
44
+ value?: string | null;
45
+ /** Callback when tab changes */
46
+ onChange?: (value: string | null) => void;
47
+ /** Default active tab */
48
+ defaultValue?: string;
49
+ /** Tab size: default (standard label/icon/padding) or sm (smaller label, icon, less padding) */
50
+ size?: TabsSize;
51
+ /** Children (typically Tabs.List and Tabs.Panel components) */
52
+ children: React.ReactNode;
53
+ }
54
+
55
+ const TabsRoot = forwardRef<HTMLDivElement, TabsProps>(
56
+ (
57
+ { children, variant = 'default', size = 'default', className, ...props },
58
+ ref,
59
+ ) => {
60
+ const tabClassName =
61
+ size === 'sm' ? `${classes.tab} ${classes.tabSm}` : classes.tab;
62
+ return (
63
+ <MantineTabs
64
+ ref={ref}
65
+ variant={variant}
66
+ className={className}
67
+ classNames={{
68
+ list: size === 'sm' ? classes.listSm : undefined,
69
+ tab: tabClassName,
70
+ }}
71
+ {...props}
72
+ >
73
+ {children}
74
+ </MantineTabs>
75
+ );
76
+ },
77
+ );
78
+
79
+ TabsRoot.displayName = 'Tabs';
80
+
81
+ /**
82
+ * Tabs component with sub-components.
83
+ *
84
+ * Use Tabs.List, Tabs.Tab, and Tabs.Panel for composition.
85
+ */
86
+ export const Tabs = Object.assign(TabsRoot, {
87
+ List: MantineTabs.List,
88
+ Tab: MantineTabs.Tab,
89
+ Panel: MantineTabs.Panel,
90
+ });
91
+
92
+ export type {
93
+ TabsListProps,
94
+ TabsPanelProps,
95
+ TabsTabProps,
96
+ } from '@mantine/core';
@@ -0,0 +1,8 @@
1
+ export { Tabs } from './Tabs';
2
+ export type {
3
+ TabsProps,
4
+ TabsSize,
5
+ TabsListProps,
6
+ TabsTabProps,
7
+ TabsPanelProps,
8
+ } from './Tabs';