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