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