@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,86 @@
1
+ import { Box, Flex, Loader } from '@mantine/core';
2
+
3
+ import { Text } from './Typography';
4
+
5
+ /**
6
+ * Props for the InfiniteScrollSentinel component
7
+ */
8
+ export interface InfiniteScrollSentinelProps {
9
+ /** Ref to attach to the sentinel element for intersection observation */
10
+ sentinelRef: (node?: Element | null) => void;
11
+ /** Whether the next page is currently being fetched */
12
+ isFetchingNextPage: boolean;
13
+ /** Custom loading message (default: 'Loading more...') */
14
+ loadingMessage?: string;
15
+ /** Size of the loader spinner (default: 'md') */
16
+ loaderSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
17
+ /** Number of pages fetched so far (used to detect if infinite scrolling was used) */
18
+ totalPages?: number;
19
+ /** Message to show when all data has been loaded (only shown if totalPages > 1)
20
+ * @default "All items loaded"
21
+ */
22
+ endMessage?: string;
23
+ }
24
+
25
+ /**
26
+ * Component that renders an invisible sentinel element for infinite scroll
27
+ * and displays a loading indicator while fetching more data.
28
+ * Optionally shows an end message when all data has been loaded.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * const { ref } = useInfiniteScroll({ hasNextPage, isFetchingNextPage, fetchNextPage });
33
+ *
34
+ * return (
35
+ * <>
36
+ * {items.map(item => <Item key={item.id} {...item} />)}
37
+ * <InfiniteScrollSentinel
38
+ * sentinelRef={ref}
39
+ * isFetchingNextPage={isFetchingNextPage}
40
+ * loadingMessage="Loading more items..."
41
+ * totalPages={totalPages}
42
+ * endMessage="All items loaded"
43
+ * />
44
+ * </>
45
+ * );
46
+ * ```
47
+ */
48
+ export function InfiniteScrollSentinel({
49
+ sentinelRef,
50
+ isFetchingNextPage,
51
+ loadingMessage = 'Loading more...',
52
+ loaderSize = 'md',
53
+ totalPages,
54
+ endMessage = 'All items loaded',
55
+ }: InfiniteScrollSentinelProps) {
56
+ // Only show end message if:
57
+ // 1. Not currently fetching
58
+ // 2. totalPages > 1 (infinite scrolling was actively used)
59
+ const showEndMessage = !isFetchingNextPage && (totalPages ?? 0) > 1;
60
+
61
+ return (
62
+ <>
63
+ {/* Invisible sentinel element that triggers intersection observer */}
64
+ <Box ref={sentinelRef} h={20} />
65
+
66
+ {/* Loading indicator shown while fetching next page */}
67
+ {isFetchingNextPage && (
68
+ <Flex justify="center" align="center" gap="sm" py="xl">
69
+ <Loader size={loaderSize} type="dots" />
70
+ <Text variant="caption1.strong" c="text.subdued.default">
71
+ {loadingMessage}
72
+ </Text>
73
+ </Flex>
74
+ )}
75
+
76
+ {/* End message shown when all data has been loaded via infinite scroll */}
77
+ {showEndMessage && (
78
+ <Flex justify="center" align="center" py="xl">
79
+ <Text variant="caption1.strong" c="text.subdued.default">
80
+ {endMessage}
81
+ </Text>
82
+ </Flex>
83
+ )}
84
+ </>
85
+ );
86
+ }
@@ -0,0 +1,78 @@
1
+ import { InfiniteScrollSentinel } from './InfiniteScrollSentinel';
2
+ import { useInfiniteScroll } from '../hooks/useInfiniteScroll';
3
+
4
+ /**
5
+ * Pagination state returned by hooks that use infinite queries.
6
+ * Spread directly onto `<InfiniteScrollTrigger {...pagination} />`.
7
+ */
8
+ export interface InfiniteScrollState {
9
+ /** Whether there are more pages to fetch */
10
+ hasNextPage: boolean;
11
+ /** Whether the next page is currently being fetched */
12
+ isFetchingNextPage: boolean;
13
+ /** Function to call to fetch the next page */
14
+ fetchNextPage: () => void;
15
+ /** Number of pages fetched so far (used to detect if infinite scrolling was used) */
16
+ totalPages?: number;
17
+ }
18
+
19
+ /**
20
+ * Props for the InfiniteScrollTrigger component.
21
+ * Extends {@link InfiniteScrollState} with optional messages.
22
+ */
23
+ export interface InfiniteScrollTriggerProps extends InfiniteScrollState {
24
+ /** Custom loading message (default: 'Loading more...') */
25
+ loadingMessage?: string;
26
+ /** Message to show when all data has been loaded (only shown if totalPages > 1)
27
+ * @default "All items loaded"
28
+ */
29
+ endMessage?: string;
30
+ }
31
+
32
+ /**
33
+ * Self-contained infinite scroll trigger that combines the scroll observer
34
+ * and loading sentinel into a single drop-in component.
35
+ *
36
+ * Shows "All items loaded" by default when infinite scrolling completes.
37
+ * Customize with the endMessage prop.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * const { pagination } = useMyEntity();
42
+ *
43
+ * return (
44
+ * <>
45
+ * <MyTable items={items} />
46
+ * <InfiniteScrollTrigger {...pagination} endMessage="All apps loaded" />
47
+ * </>
48
+ * );
49
+ * ```
50
+ */
51
+ export function InfiniteScrollTrigger({
52
+ hasNextPage,
53
+ isFetchingNextPage,
54
+ fetchNextPage,
55
+ totalPages,
56
+ loadingMessage,
57
+ endMessage,
58
+ }: InfiniteScrollTriggerProps) {
59
+ const { ref } = useInfiniteScroll({
60
+ hasNextPage,
61
+ isFetchingNextPage,
62
+ fetchNextPage,
63
+ });
64
+
65
+ if (!hasNextPage) {
66
+ return null;
67
+ }
68
+
69
+ return (
70
+ <InfiniteScrollSentinel
71
+ sentinelRef={ref}
72
+ isFetchingNextPage={isFetchingNextPage}
73
+ loadingMessage={loadingMessage}
74
+ totalPages={totalPages}
75
+ endMessage={endMessage}
76
+ />
77
+ );
78
+ }
@@ -0,0 +1,47 @@
1
+ import figma from '@figma/code-connect';
2
+
3
+ import { InfoCard } from './InfoCard';
4
+
5
+ /**
6
+ * -- This file was auto-generated by Code Connect --
7
+ * `props` includes a mapping from your code props to Figma properties.
8
+ * You should check this is correct, and update the `example` function
9
+ * to return the code example you'd like to see in Figma
10
+ */
11
+
12
+ figma.connect(
13
+ InfoCard,
14
+ 'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=179%3A29234',
15
+ {
16
+ props: {
17
+ // These props were automatically mapped based on your linked code:
18
+ variant: figma.enum('Variant', {
19
+ Information: 'information',
20
+ Success: 'success',
21
+ Danger: 'danger',
22
+ Warning: 'warning',
23
+ Subdued: 'subdued',
24
+ }),
25
+ // No matching props could be found for these Figma properties:
26
+ // "mainSlot": figma.instance('Main Slot'),
27
+ // "headerSlot": figma.instance('Header Slot'),
28
+ // "footerSlot": figma.instance('Footer Slot'),
29
+ // "slotNo": figma.enum('Slot No.', {
30
+ // "1 Slot": "1-slot",
31
+ // "2 Slots": "2-slots",
32
+ // "3 Slots": "3-slots",
33
+ // "Slot No.4": "slot-no-4",
34
+ // "Slot No.5": "slot-no-5",
35
+ // "Slot No.6": "slot-no-6"
36
+ // })
37
+ },
38
+ example: props => (
39
+ <InfoCard
40
+ title="Deployment Instructions"
41
+ description="Ready to deploy to your customers? We provide comprehensive rollout scripts so you can get up and running quickly!"
42
+ buttonText="Show Deployment Instructions"
43
+ variant={props.variant}
44
+ />
45
+ ),
46
+ },
47
+ );
@@ -0,0 +1,216 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Box, Card, Group, Stack } from '@mantine/core';
4
+
5
+ import { Button, type ButtonProps } from './Button';
6
+ import { Text, Title } from './Typography';
7
+ import { tokens } from '../theme/themeContract.css';
8
+
9
+ type InfoCardVariant =
10
+ | 'information'
11
+ | 'success'
12
+ | 'danger'
13
+ | 'warning'
14
+ | 'subdued'
15
+ | 'default';
16
+ type InfoCardLayout = 'left' | 'centered';
17
+
18
+ interface InfoCardVariantStyles {
19
+ cardBackgroundColor: string;
20
+ iconBackgroundColor: string;
21
+ iconColor: string;
22
+ textColor: string;
23
+ }
24
+
25
+ const variantStyles: Record<InfoCardVariant, InfoCardVariantStyles> = {
26
+ information: {
27
+ cardBackgroundColor: tokens.color.background.informationFilled,
28
+ iconBackgroundColor: tokens.color.background.informationFilledHover,
29
+ iconColor: tokens.color.icon.inverse,
30
+ textColor: tokens.color.text.inverse,
31
+ },
32
+ success: {
33
+ cardBackgroundColor: tokens.color.background.successLight,
34
+ iconBackgroundColor: tokens.color.background.successLightHover,
35
+ iconColor: tokens.color.icon.successDefault,
36
+ textColor: tokens.color.text.default,
37
+ },
38
+ danger: {
39
+ cardBackgroundColor: tokens.color.background.dangerLight,
40
+ iconBackgroundColor: tokens.color.background.dangerLightHover,
41
+ iconColor: tokens.color.icon.dangerDefault,
42
+ textColor: tokens.color.text.default,
43
+ },
44
+ warning: {
45
+ cardBackgroundColor: tokens.color.background.warningLight,
46
+ iconBackgroundColor: tokens.color.background.warningLightHover,
47
+ iconColor: tokens.color.icon.warningDefault,
48
+ textColor: tokens.color.text.default,
49
+ },
50
+ subdued: {
51
+ cardBackgroundColor: tokens.color.background.subduedLight,
52
+ iconBackgroundColor: tokens.color.background.subduedLightHover,
53
+ iconColor: tokens.color.icon.default,
54
+ textColor: tokens.color.text.default,
55
+ },
56
+ default: {
57
+ cardBackgroundColor: tokens.color.background.default,
58
+ iconBackgroundColor: tokens.color.background.defaultHover,
59
+ iconColor: tokens.color.icon.default,
60
+ textColor: tokens.color.text.default,
61
+ },
62
+ };
63
+
64
+ export interface InfoCardProps {
65
+ /**
66
+ * The title of the card
67
+ */
68
+ title: string;
69
+ /**
70
+ * The description text
71
+ */
72
+ description: string;
73
+ /**
74
+ * The button text
75
+ */
76
+ buttonText: string;
77
+ /**
78
+ * Callback function when the button is clicked
79
+ */
80
+ onButtonClick?: () => void;
81
+ /**
82
+ * Visual variant of the card
83
+ */
84
+ variant?: InfoCardVariant;
85
+ /**
86
+ * Card content alignment
87
+ */
88
+ layout?: InfoCardLayout;
89
+ /**
90
+ * Optional icon displayed in the header
91
+ */
92
+ icon?: ReactNode;
93
+ /**
94
+ * Button color
95
+ */
96
+ buttonColor?: ButtonProps['color'];
97
+ /**
98
+ * Button variant
99
+ */
100
+ buttonVariant?: ButtonProps['variant'];
101
+
102
+ /**
103
+ * Button left section
104
+ */
105
+ buttonLeftSection?: ReactNode;
106
+ /**
107
+ * Button right section
108
+ */
109
+ buttonRightSection?: ReactNode;
110
+ }
111
+
112
+ /**
113
+ * InfoCard component
114
+ * A generic card for displaying information with a call-to-action button
115
+ */
116
+ export function InfoCard({
117
+ title,
118
+ description,
119
+ buttonText,
120
+ onButtonClick,
121
+ variant = 'information',
122
+ layout = 'left',
123
+ icon,
124
+ buttonColor = 'gray',
125
+ buttonVariant = 'primary',
126
+ buttonLeftSection,
127
+ buttonRightSection,
128
+ }: InfoCardProps) {
129
+ const styles = variantStyles[variant];
130
+
131
+ let stackAlignment: 'center' | 'flex-start' = 'flex-start';
132
+ let textAlign: 'center' | 'left' = 'left';
133
+ if (layout === 'centered') {
134
+ stackAlignment = 'center';
135
+ textAlign = 'center';
136
+ }
137
+
138
+ const renderHeader = () => {
139
+ if (!icon) {
140
+ return (
141
+ <Title
142
+ variant="heading4"
143
+ ta={textAlign}
144
+ style={{ color: styles.textColor }}
145
+ >
146
+ {title}
147
+ </Title>
148
+ );
149
+ }
150
+
151
+ return (
152
+ <Group gap="md" align="center" wrap="nowrap">
153
+ <Box
154
+ w={48}
155
+ h={48}
156
+ p="sm"
157
+ style={{
158
+ borderRadius: tokens.radius.full,
159
+ backgroundColor: styles.iconBackgroundColor,
160
+ color: styles.iconColor,
161
+ display: 'flex',
162
+ alignItems: 'center',
163
+ justifyContent: 'center',
164
+ flexShrink: 0,
165
+ }}
166
+ >
167
+ {icon}
168
+ </Box>
169
+ <Title
170
+ variant="heading4"
171
+ ta={textAlign}
172
+ style={{ color: styles.textColor }}
173
+ >
174
+ {title}
175
+ </Title>
176
+ </Group>
177
+ );
178
+ };
179
+
180
+ return (
181
+ <Card
182
+ p="xl"
183
+ radius="xl"
184
+ shadow="0"
185
+ h="100%"
186
+ withBorder={false}
187
+ style={{
188
+ backgroundColor: styles.cardBackgroundColor,
189
+ }}
190
+ >
191
+ <Stack gap="md" align={stackAlignment} h="100%">
192
+ {renderHeader()}
193
+
194
+ <Text
195
+ variant="caption1"
196
+ ta={textAlign}
197
+ style={{ color: styles.textColor }}
198
+ >
199
+ {description}
200
+ </Text>
201
+
202
+ <Button
203
+ variant={buttonVariant}
204
+ size="md"
205
+ radius="lg"
206
+ color={buttonColor}
207
+ onClick={onButtonClick}
208
+ leftSection={buttonLeftSection}
209
+ rightSection={buttonRightSection}
210
+ >
211
+ {buttonText}
212
+ </Button>
213
+ </Stack>
214
+ </Card>
215
+ );
216
+ }
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Group, Kbd } from '@mantine/core';
4
+
5
+ import { Text } from '../Typography';
6
+
7
+ export interface KbdHintProps {
8
+ keys: string[];
9
+ text?: ReactNode;
10
+ }
11
+
12
+ export function KbdHint({ keys, text }: KbdHintProps) {
13
+ return (
14
+ <Group gap={8} wrap="nowrap">
15
+ <Group gap={4} wrap="nowrap">
16
+ {keys.map(key => (
17
+ <Kbd key={key}>{key}</Kbd>
18
+ ))}
19
+ </Group>
20
+ {text ? <Text variant="caption1">{text}</Text> : null}
21
+ </Group>
22
+ );
23
+ }
@@ -0,0 +1,2 @@
1
+ export { KbdHint } from './KbdHint';
2
+ export type { KbdHintProps } from './KbdHint';
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Stack } from '@mantine/core';
4
+
5
+ import { Text } from '../Typography';
6
+
7
+ export interface LabeledFieldProps {
8
+ label: string;
9
+ children: ReactNode;
10
+ }
11
+
12
+ export function LabeledField({ label, children }: LabeledFieldProps) {
13
+ return (
14
+ <Stack gap="3xs">
15
+ <Text variant="caption1" c="text.subdued.default">
16
+ {label}
17
+ </Text>
18
+ {children}
19
+ </Stack>
20
+ );
21
+ }
@@ -0,0 +1,2 @@
1
+ export { LabeledField } from './LabeledField';
2
+ export type { LabeledFieldProps } from './LabeledField';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * LookupSelect component styles – vanilla-extract with semantic design tokens
3
+ */
4
+
5
+ import { style } from '@vanilla-extract/css';
6
+
7
+ import { mantineVars } from '../../theme/mantineVars';
8
+ import { tokens } from '../../theme/themeContract.css';
9
+
10
+ const focusRing = {
11
+ outline: `2px solid ${tokens.color.stroke.focusStrong}`,
12
+ outlineOffset: 2,
13
+ };
14
+
15
+ export const trigger = style({
16
+ all: 'unset',
17
+ boxSizing: 'border-box',
18
+ display: 'flex',
19
+ alignItems: 'center',
20
+ justifyContent: 'space-between',
21
+ width: '100%',
22
+ minHeight: 36,
23
+ height: 36,
24
+ padding: '5.5px 8px',
25
+ gap: 8,
26
+ borderRadius: 8,
27
+ border: `1px solid ${tokens.color.stroke.default}`,
28
+ background: tokens.color.background.input,
29
+ boxShadow: '0px 1px 2px 0px rgba(0, 0, 0, 0.05)',
30
+ fontFamily: 'inherit',
31
+ fontSize: 14,
32
+ lineHeight: '21px',
33
+ fontWeight: 400,
34
+ cursor: 'pointer',
35
+ transition: 'border-color 0.15s ease',
36
+ selectors: {
37
+ '&:hover:not(:disabled)': {
38
+ borderColor: tokens.color.stroke.subduedStrong,
39
+ },
40
+ '&:focus-visible': focusRing,
41
+ '&[aria-expanded="true"]': {
42
+ borderColor: tokens.color.stroke.focusDefault,
43
+ },
44
+ '&:disabled': {
45
+ cursor: 'not-allowed',
46
+ opacity: 0.6,
47
+ },
48
+ [`${mantineVars.darkSelector} &`]: {
49
+ borderColor: tokens.color.stroke.default,
50
+ backgroundColor: tokens.color.background.default,
51
+ },
52
+ },
53
+ });
54
+
55
+ export const triggerValue = style({
56
+ display: 'flex',
57
+ alignItems: 'center',
58
+ gap: 8,
59
+ flex: 1,
60
+ minWidth: 0,
61
+ overflow: 'hidden',
62
+ });
63
+
64
+ export const triggerLabel = style({
65
+ whiteSpace: 'nowrap',
66
+ overflow: 'hidden',
67
+ textOverflow: 'ellipsis',
68
+ });
69
+
70
+ export const placeholder = style({
71
+ color: tokens.color.text.subduedDefault,
72
+ });
73
+
74
+ export const chevron = style({
75
+ color: tokens.color.text.subduedDefault,
76
+ flexShrink: 0,
77
+ width: 16,
78
+ height: 16,
79
+ });
80
+
81
+ export const searchRow = style({
82
+ display: 'flex',
83
+ alignItems: 'center',
84
+ gap: 8,
85
+ padding: '6px 10px',
86
+ borderBottom: `1px solid ${tokens.color.stroke.light}`,
87
+ });
88
+
89
+ export const searchIcon = style({
90
+ color: tokens.color.icon.medium,
91
+ flexShrink: 0,
92
+ width: 14,
93
+ height: 14,
94
+ });
95
+
96
+ export const searchInput = style({
97
+ all: 'unset',
98
+ flex: 1,
99
+ minWidth: 0,
100
+ fontFamily: 'inherit',
101
+ fontSize: 14,
102
+ lineHeight: '21px',
103
+ color: tokens.color.text.default,
104
+ selectors: {
105
+ '&::placeholder': {
106
+ color: tokens.color.text.subduedDefault,
107
+ },
108
+ },
109
+ });
110
+
111
+ export const options = style({
112
+ maxHeight: 300,
113
+ overflowY: 'auto',
114
+ padding: 4,
115
+ });
116
+
117
+ export const option = style({
118
+ display: 'flex',
119
+ alignItems: 'center',
120
+ gap: 10,
121
+ minWidth: 0,
122
+ });
123
+
124
+ export const optionRoot = style({
125
+ selectors: {
126
+ '&[data-combobox-selected]': {
127
+ backgroundColor: tokens.color.background.subduedLight,
128
+ },
129
+ },
130
+ });
131
+
132
+ export const optionText = style({
133
+ display: 'flex',
134
+ flexDirection: 'column',
135
+ minWidth: 0,
136
+ gap: 2,
137
+ });
138
+
139
+ export const optionLabel = style({
140
+ whiteSpace: 'nowrap',
141
+ overflow: 'hidden',
142
+ textOverflow: 'ellipsis',
143
+ });
144
+
145
+ export const footer = style({
146
+ padding: '8px 12px',
147
+ borderTop: `1px solid ${tokens.color.stroke.light}`,
148
+ backgroundColor: tokens.color.background.subduedUltralight,
149
+ });