@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.
- package/.ai/rules/date-handling.md +39 -0
- package/.ai/rules/figma-design-system.md +372 -0
- package/.ai/rules/figma-lm-design-system-keys.md +680 -0
- package/.ai/rules/file-extensions.md +13 -0
- package/.ai/rules/modal-confirmation-mutation.md +56 -0
- package/.ai/rules/react-hooks.md +29 -0
- package/.ai/rules/styling.md +83 -0
- package/AGENTS.md +37 -0
- package/README.md +125 -0
- package/figma.config.json +9 -0
- package/package.json +127 -0
- package/scripts/install-ai-rules.mjs +136 -0
- package/src/ThemeProvider.tsx +57 -0
- package/src/charts.ts +32 -0
- package/src/components/ActionCard/ActionCard.css.ts +60 -0
- package/src/components/ActionCard/ActionCard.tsx +154 -0
- package/src/components/ActionCard/index.ts +2 -0
- package/src/components/Anchor/Anchor.tsx +47 -0
- package/src/components/Anchor/index.ts +2 -0
- package/src/components/AppliedFiltersManagerBar/AppliedFiltersManagerBar.tsx +105 -0
- package/src/components/AppliedFiltersManagerBar/FilterBadge.css.ts +23 -0
- package/src/components/AppliedFiltersManagerBar/FilterBadge.tsx +50 -0
- package/src/components/AppliedFiltersManagerBar/index.ts +5 -0
- package/src/components/Badge/Badge.css.ts +72 -0
- package/src/components/Badge/Badge.figma.tsx +43 -0
- package/src/components/Badge/Badge.tsx +159 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/BreadCrumb/BreadCrumb.tsx +62 -0
- package/src/components/BreadCrumb/index.ts +2 -0
- package/src/components/BulkActionBar/BulkActionBar.css.ts +26 -0
- package/src/components/BulkActionBar/BulkActionBar.tsx +164 -0
- package/src/components/BulkActionBar/index.ts +2 -0
- package/src/components/Button/Button.css.ts +272 -0
- package/src/components/Button/Button.figma.tsx +74 -0
- package/src/components/Button/Button.tsx +84 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Charts/ChartTooltip.figma.tsx +33 -0
- package/src/components/Charts/ChartTooltip.tsx +101 -0
- package/src/components/Charts/MiniBarSparkline.tsx +75 -0
- package/src/components/Charts/StackedPatternBarChart.tsx +494 -0
- package/src/components/Charts/TrendAreaChart.css.ts +23 -0
- package/src/components/Charts/TrendAreaChart.tsx +210 -0
- package/src/components/Charts/index.ts +12 -0
- package/src/components/CodePanel/CodePanel.css.ts +113 -0
- package/src/components/CodePanel/CodePanel.tsx +121 -0
- package/src/components/CodePanel/index.ts +2 -0
- package/src/components/CommentComposer/CommentComposer.css.ts +60 -0
- package/src/components/CommentComposer/CommentComposer.tsx +181 -0
- package/src/components/CommentComposer/index.ts +2 -0
- package/src/components/ConfirmationModal/ConfirmationModal.tsx +149 -0
- package/src/components/ConfirmationModal/index.ts +2 -0
- package/src/components/ConfirmationTooltip/ConfirmationTooltip.tsx +132 -0
- package/src/components/ConfirmationTooltip/index.ts +2 -0
- package/src/components/DataDialog.figma.tsx +33 -0
- package/src/components/DataDialog.tsx +46 -0
- package/src/components/DataTable/DataTable.tsx +1042 -0
- package/src/components/DataTable/RowExpandToggle.tsx +105 -0
- package/src/components/DataTable/RowGroupHeader.tsx +190 -0
- package/src/components/DataTable/createActionsColumn.tsx +86 -0
- package/src/components/DataTable/index.ts +25 -0
- package/src/components/DatePicker/CustomRangePicker.tsx +59 -0
- package/src/components/DatePicker/DateInput.tsx +329 -0
- package/src/components/DatePicker/DateNavigator.tsx +486 -0
- package/src/components/DatePicker/DatePicker.tsx +242 -0
- package/src/components/DatePicker/MonthlyRangePicker.tsx +231 -0
- package/src/components/DatePicker/QuarterlyRangePicker.tsx +224 -0
- package/src/components/DatePicker/QuickPicksSidebar.tsx +242 -0
- package/src/components/DatePicker/YearlyRangePicker.tsx +171 -0
- package/src/components/DatePicker/index.ts +7 -0
- package/src/components/DatePicker/types.ts +12 -0
- package/src/components/DesignSystemPrimitives/FluidGrid.tsx +44 -0
- package/src/components/DesignSystemPrimitives/InteractivePrimitives.tsx +177 -0
- package/src/components/DesignSystemPrimitives/LayoutPrimitives.tsx +220 -0
- package/src/components/DesignSystemPrimitives/LayoutPrimitives.types.tsx +15 -0
- package/src/components/DesignSystemPrimitives/SurfacePrimitives.tsx +46 -0
- package/src/components/DesignSystemPrimitives/index.ts +55 -0
- package/src/components/Details/Details.css.ts +74 -0
- package/src/components/Details/Details.tsx +140 -0
- package/src/components/Details/index.ts +2 -0
- package/src/components/DownloadCard/DownloadCard.css.ts +22 -0
- package/src/components/DownloadCard/DownloadCard.tsx +63 -0
- package/src/components/DownloadCard/index.ts +2 -0
- package/src/components/Drawer/Drawer.css.ts +32 -0
- package/src/components/Drawer/Drawer.tsx +236 -0
- package/src/components/Drawer/hooks/useDetailDrawer.ts +61 -0
- package/src/components/Drawer/hooks/useDetailDrawerNavigation.ts +125 -0
- package/src/components/Drawer/hooks/useDetailDrawerNavigationContext.ts +66 -0
- package/src/components/EditableRichText/EditableRichText.css.ts +72 -0
- package/src/components/EditableRichText/EditableRichText.tsx +324 -0
- package/src/components/EditableRichText/index.ts +2 -0
- package/src/components/EditableSelect/EditableSelect.css.ts +62 -0
- package/src/components/EditableSelect/EditableSelect.tsx +224 -0
- package/src/components/EditableSelect/index.ts +2 -0
- package/src/components/EditableText/EditableText.tsx +377 -0
- package/src/components/EditableText/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.figma.tsx +33 -0
- package/src/components/EmptyState/EmptyState.tsx +230 -0
- package/src/components/EmptyState/index.ts +2 -0
- package/src/components/ErrorBoundary.tsx +135 -0
- package/src/components/ErrorState/ErrorState.tsx +197 -0
- package/src/components/ErrorState/index.ts +2 -0
- package/src/components/FeatureCard.tsx +42 -0
- package/src/components/FilterMenu/FilterMenu.figma.tsx +30 -0
- package/src/components/FilterMenu/FilterMenu.tsx +198 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/BooleanFilterSubmenu.tsx +46 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/SearchableFilterSubmenu.tsx +239 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/index.ts +8 -0
- package/src/components/FilterMenu/defaultFilterSchemas.ts +63 -0
- package/src/components/FilterMenu/helpers.ts +115 -0
- package/src/components/FilterMenu/index.ts +35 -0
- package/src/components/FilterMenu/types.ts +101 -0
- package/src/components/IconButton/IconButton.css.ts +272 -0
- package/src/components/IconButton/IconButton.figma.tsx +47 -0
- package/src/components/IconButton/IconButton.tsx +72 -0
- package/src/components/IconButton/README.md +230 -0
- package/src/components/IconButton/index.ts +2 -0
- package/src/components/InfiniteScrollSentinel.tsx +86 -0
- package/src/components/InfiniteScrollTrigger.tsx +78 -0
- package/src/components/InfoCard.figma.tsx +47 -0
- package/src/components/InfoCard.tsx +216 -0
- package/src/components/KbdHint/KbdHint.tsx +23 -0
- package/src/components/KbdHint/index.ts +2 -0
- package/src/components/LabeledField/LabeledField.tsx +21 -0
- package/src/components/LabeledField/index.ts +2 -0
- package/src/components/LookupSelect/LookupSelect.css.ts +149 -0
- package/src/components/LookupSelect/LookupSelect.tsx +325 -0
- package/src/components/LookupSelect/index.ts +2 -0
- package/src/components/Menu/Menu.css.ts +89 -0
- package/src/components/Menu/Menu.tsx +105 -0
- package/src/components/Menu/index.ts +2 -0
- package/src/components/MessageBox/MessageBox.tsx +168 -0
- package/src/components/MessageBox/index.ts +2 -0
- package/src/components/MetricDisplay/MetricDisplay.tsx +55 -0
- package/src/components/MetricDisplay/index.ts +1 -0
- package/src/components/MultiSelect/MultiSelect.tsx +278 -0
- package/src/components/MultiSelect/index.ts +2 -0
- package/src/components/Notifications/Notifications.tsx +12 -0
- package/src/components/Notifications/README.md +93 -0
- package/src/components/Notifications/index.ts +4 -0
- package/src/components/Notifications/showToast.tsx +100 -0
- package/src/components/PropertyRow/PropertyRow.tsx +96 -0
- package/src/components/PropertyRow/index.ts +2 -0
- package/src/components/RadioTile/RadioTile.tsx +253 -0
- package/src/components/RadioTile/index.ts +2 -0
- package/src/components/RichText/FormattingToolbar.css.ts +69 -0
- package/src/components/RichText/FormattingToolbar.tsx +112 -0
- package/src/components/RichText/RichTextInline.css.ts +54 -0
- package/src/components/RichText/RichTextInline.tsx +318 -0
- package/src/components/RichText/formattingCommands.ts +181 -0
- package/src/components/RichText/formattingTypes.ts +34 -0
- package/src/components/RichText/index.ts +49 -0
- package/src/components/RichText/richTextExtensions.ts +111 -0
- package/src/components/RichText/richTextHelpers.ts +65 -0
- package/src/components/RichText/richTextImage.ts +253 -0
- package/src/components/RichText/richTextImageHandlers.ts +244 -0
- package/src/components/RichText/richTextProse.css.ts +261 -0
- package/src/components/RichTextEditor/RichTextEditor.css.ts +82 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +204 -0
- package/src/components/RichTextEditor/index.ts +2 -0
- package/src/components/RichTextView/RichTextView.css.ts +11 -0
- package/src/components/RichTextView/RichTextView.tsx +114 -0
- package/src/components/RichTextView/index.ts +2 -0
- package/src/components/Schedule/Schedule.tsx +35 -0
- package/src/components/SchedulePicker/SchedulePicker.css.ts +42 -0
- package/src/components/SchedulePicker/SchedulePicker.tsx +130 -0
- package/src/components/SchedulePicker/index.ts +2 -0
- package/src/components/SearchableList/types.ts +30 -0
- package/src/components/SearchableSubMenu/SearchableSubMenu.css.ts +25 -0
- package/src/components/SearchableSubMenu/SearchableSubMenu.tsx +139 -0
- package/src/components/SearchableSubMenu/index.ts +2 -0
- package/src/components/Select/README.md +114 -0
- package/src/components/Select/Select.css.ts +110 -0
- package/src/components/Select/Select.tsx +133 -0
- package/src/components/Select/index.ts +2 -0
- package/src/components/SelectCreatable/SelectCreatable.css.ts +16 -0
- package/src/components/SelectCreatable/SelectCreatable.tsx +203 -0
- package/src/components/SelectCreatable/index.ts +2 -0
- package/src/components/SettingsCard/SettingsCard.tsx +98 -0
- package/src/components/SettingsCard/index.ts +2 -0
- package/src/components/Sidebar/Sidebar.css.ts +91 -0
- package/src/components/Sidebar/Sidebar.tsx +129 -0
- package/src/components/Sidebar/index.ts +5 -0
- package/src/components/SimpleList/SimpleList.css.ts +12 -0
- package/src/components/SimpleList/SimpleList.tsx +44 -0
- package/src/components/SimpleList/index.ts +2 -0
- package/src/components/SimpleTable/SimpleTable.tsx +296 -0
- package/src/components/SimpleTable/index.ts +2 -0
- package/src/components/SlashRichTextEditor/SelectionBubbleMenu.css.ts +62 -0
- package/src/components/SlashRichTextEditor/SelectionBubbleMenu.tsx +85 -0
- package/src/components/SlashRichTextEditor/SlashCommandMenu.css.ts +124 -0
- package/src/components/SlashRichTextEditor/SlashCommandMenu.tsx +168 -0
- package/src/components/SlashRichTextEditor/SlashRichTextEditor.css.ts +81 -0
- package/src/components/SlashRichTextEditor/SlashRichTextEditor.tsx +538 -0
- package/src/components/SlashRichTextEditor/SlashSuggestionExtension.ts +48 -0
- package/src/components/SlashRichTextEditor/index.ts +13 -0
- package/src/components/SlashRichTextEditor/types.ts +48 -0
- package/src/components/StatCard/StatCard.css.ts +70 -0
- package/src/components/StatCard/StatCard.tsx +201 -0
- package/src/components/StatCard/index.ts +1 -0
- package/src/components/StatusBadge/StatusBadge.tsx +70 -0
- package/src/components/StatusBadge/index.ts +2 -0
- package/src/components/StatusIndicator/StatusIndicator.tsx +67 -0
- package/src/components/StatusIndicator/index.ts +6 -0
- package/src/components/SubNavigation/SubNavigation.css.ts +72 -0
- package/src/components/SubNavigation/SubNavigation.tsx +104 -0
- package/src/components/SubNavigation/index.ts +2 -0
- package/src/components/SuspenseLoader.tsx +22 -0
- package/src/components/Table/SortableColumnHeader.tsx +99 -0
- package/src/components/Table/TableSkeletonRows.figma.tsx +22 -0
- package/src/components/Table/TableSkeletonRows.tsx +113 -0
- package/src/components/Table/index.ts +9 -0
- package/src/components/TableActionsMenu.tsx +58 -0
- package/src/components/TableCard.tsx +29 -0
- package/src/components/TableContainer/TableContainer.tsx +86 -0
- package/src/components/TableContainer/index.ts +2 -0
- package/src/components/TableControlBar/TableControlBar.tsx +156 -0
- package/src/components/TableControlBar/TableSelectionButton.tsx +57 -0
- package/src/components/TableControlBar/index.ts +13 -0
- package/src/components/TableControlBar/useTableControlBar.tsx +314 -0
- package/src/components/TableSelection/TableSelection.tsx +43 -0
- package/src/components/TableSelection/index.ts +5 -0
- package/src/components/Tabs/README.md +76 -0
- package/src/components/Tabs/Tabs.css.ts +54 -0
- package/src/components/Tabs/Tabs.figma.tsx +47 -0
- package/src/components/Tabs/Tabs.tsx +96 -0
- package/src/components/Tabs/index.ts +8 -0
- package/src/components/TextInput/README.md +98 -0
- package/src/components/TextInput/SearchTextInput.figma.tsx +22 -0
- package/src/components/TextInput/SearchTextInput.tsx +150 -0
- package/src/components/TextInput/TextInput.figma.tsx +44 -0
- package/src/components/TextInput/TextInput.tsx +42 -0
- package/src/components/TextInput/index.ts +4 -0
- package/src/components/ThemeSwitcher.figma.tsx +28 -0
- package/src/components/ThemeSwitcher.tsx +69 -0
- package/src/components/TrendBadge/TrendBadge.tsx +76 -0
- package/src/components/TrendBadge/index.ts +2 -0
- package/src/components/TruncatedText.tsx +115 -0
- package/src/components/Typography/Text.tsx +74 -0
- package/src/components/Typography/Title.tsx +100 -0
- package/src/components/Typography/index.ts +4 -0
- package/src/geist-fonts.ts +48 -0
- package/src/hooks/index.ts +31 -0
- package/src/hooks/useFilters.ts +152 -0
- package/src/hooks/useInfiniteScroll.ts +62 -0
- package/src/hooks/usePlatform.ts +33 -0
- package/src/hooks/useServerTable.ts +495 -0
- package/src/hooks/useTableSelection.ts +102 -0
- package/src/hooks/useTableSort.ts +259 -0
- package/src/index.ts +483 -0
- package/src/mantine.ts +25 -0
- package/src/theme/mantineVars.ts +12 -0
- package/src/theme/themeContract.css.ts +131 -0
- package/src/theme/themeVars.ts +31 -0
- package/src/theme.ts +168 -0
- package/src/tokens/color-types.ts +107 -0
- package/src/tokens/colors.ts +243 -0
- package/src/tokens/index.ts +14 -0
- package/src/tokens/radius.ts +17 -0
- package/src/tokens/semantic-colors.ts +224 -0
- package/src/tokens/semantic-tokens-css.ts +53 -0
- package/src/tokens/shadows.ts +11 -0
- package/src/tokens/spacing.ts +20 -0
- package/src/tokens/text-styles.ts +179 -0
- package/src/tokens/typography.ts +40 -0
- package/src/tokens/zIndex.ts +27 -0
- package/src/types/mantine-theme.d.ts +17 -0
- package/src/types/tanstack-table.d.ts +22 -0
- package/src/utils/avatar.ts +150 -0
- package/src/utils/chartHelpers.ts +53 -0
- package/src/utils/color-props.ts +77 -0
- package/src/utils/createDesignComponent.tsx +104 -0
- package/src/utils/nestFlatRows.ts +111 -0
- 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,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,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
|
+
});
|