@olympusoss/canvas 4.0.0 → 5.0.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/README.md +108 -0
- package/package.json +14 -3
- package/src/atoms/avatar/avatar.md +185 -0
- package/src/atoms/avatar/avatar.styles.ts +48 -0
- package/src/atoms/avatar/avatar.tsx +99 -0
- package/src/atoms/badge/badge.md +237 -0
- package/src/atoms/badge/badge.styles.ts +79 -0
- package/src/atoms/badge/badge.tsx +86 -0
- package/src/atoms/breadcrumb/breadcrumb.md +233 -0
- package/src/atoms/breadcrumb/breadcrumb.styles.ts +40 -0
- package/src/atoms/breadcrumb/breadcrumb.tsx +130 -0
- package/src/atoms/button/button.android.tsx +6 -0
- package/src/atoms/button/button.ios.tsx +6 -0
- package/src/atoms/button/button.md +184 -0
- package/src/atoms/button/button.shared.tsx +79 -0
- package/src/atoms/button/button.styles.ts +152 -0
- package/src/atoms/button/button.tsx +6 -0
- package/src/atoms/button-group/button-group.android.tsx +6 -0
- package/src/atoms/button-group/button-group.ios.tsx +6 -0
- package/src/atoms/button-group/button-group.md +120 -0
- package/src/atoms/button-group/button-group.shared.tsx +398 -0
- package/src/atoms/button-group/button-group.styles.ts +483 -0
- package/src/atoms/button-group/button-group.tsx +6 -0
- package/src/atoms/checkbox/checkbox.android.tsx +6 -0
- package/src/atoms/checkbox/checkbox.ios.tsx +6 -0
- package/src/atoms/checkbox/checkbox.md +150 -0
- package/src/atoms/checkbox/checkbox.shared.tsx +103 -0
- package/src/atoms/checkbox/checkbox.styles.ts +106 -0
- package/src/atoms/checkbox/checkbox.tsx +6 -0
- package/src/atoms/combobox/combobox.android.tsx +6 -0
- package/src/atoms/combobox/combobox.ios.tsx +6 -0
- package/src/atoms/combobox/combobox.md +213 -0
- package/src/atoms/combobox/combobox.shared.tsx +160 -0
- package/src/atoms/combobox/combobox.styles.ts +270 -0
- package/src/atoms/combobox/combobox.tsx +6 -0
- package/src/atoms/divider/divider.md +140 -0
- package/src/atoms/divider/divider.styles.ts +35 -0
- package/src/atoms/divider/divider.tsx +67 -0
- package/src/atoms/dropdown/dropdown.android.tsx +6 -0
- package/src/atoms/dropdown/dropdown.ios.tsx +6 -0
- package/src/atoms/dropdown/dropdown.md +221 -0
- package/src/atoms/dropdown/dropdown.shared.tsx +190 -0
- package/src/atoms/dropdown/dropdown.styles.ts +233 -0
- package/src/atoms/dropdown/dropdown.tsx +6 -0
- package/src/atoms/icon/icon.md +131 -0
- package/src/atoms/icon/icon.styles.ts +30 -0
- package/src/atoms/icon/icon.tsx +328 -0
- package/src/atoms/index.ts +24 -0
- package/src/atoms/input/input.android.tsx +6 -0
- package/src/atoms/input/input.ios.tsx +6 -0
- package/src/atoms/input/input.md +118 -0
- package/src/atoms/input/input.shared.tsx +203 -0
- package/src/atoms/input/input.styles.ts +286 -0
- package/src/atoms/input/input.tsx +6 -0
- package/src/atoms/kbd/kbd.md +91 -0
- package/src/atoms/kbd/kbd.styles.ts +33 -0
- package/src/atoms/kbd/kbd.tsx +27 -0
- package/src/atoms/listbox/listbox.md +177 -0
- package/src/atoms/listbox/listbox.styles.ts +60 -0
- package/src/atoms/listbox/listbox.tsx +113 -0
- package/src/atoms/pagination/pagination.android.tsx +6 -0
- package/src/atoms/pagination/pagination.ios.tsx +6 -0
- package/src/atoms/pagination/pagination.md +133 -0
- package/src/atoms/pagination/pagination.shared.tsx +289 -0
- package/src/atoms/pagination/pagination.styles.ts +245 -0
- package/src/atoms/pagination/pagination.tsx +6 -0
- package/src/atoms/popover/popover.android.tsx +8 -0
- package/src/atoms/popover/popover.ios.tsx +6 -0
- package/src/atoms/popover/popover.md +87 -0
- package/src/atoms/popover/popover.shared.tsx +124 -0
- package/src/atoms/popover/popover.styles.ts +144 -0
- package/src/atoms/popover/popover.tsx +6 -0
- package/src/atoms/radio/radio.android.tsx +6 -0
- package/src/atoms/radio/radio.ios.tsx +6 -0
- package/src/atoms/radio/radio.md +173 -0
- package/src/atoms/radio/radio.shared.tsx +98 -0
- package/src/atoms/radio/radio.styles.ts +109 -0
- package/src/atoms/radio/radio.tsx +6 -0
- package/src/atoms/select/select.android.tsx +6 -0
- package/src/atoms/select/select.ios.tsx +6 -0
- package/src/atoms/select/select.md +156 -0
- package/src/atoms/select/select.shared.tsx +143 -0
- package/src/atoms/select/select.styles.ts +310 -0
- package/src/atoms/select/select.tsx +6 -0
- package/src/atoms/skeleton/skeleton.md +135 -0
- package/src/atoms/skeleton/skeleton.styles.ts +117 -0
- package/src/atoms/skeleton/skeleton.tsx +145 -0
- package/src/atoms/spinner/spinner.android.tsx +7 -0
- package/src/atoms/spinner/spinner.ios.tsx +7 -0
- package/src/atoms/spinner/spinner.md +94 -0
- package/src/atoms/spinner/spinner.shared.tsx +92 -0
- package/src/atoms/spinner/spinner.styles.tsx +115 -0
- package/src/atoms/spinner/spinner.tsx +7 -0
- package/src/atoms/switch/switch.android.tsx +6 -0
- package/src/atoms/switch/switch.ios.tsx +6 -0
- package/src/atoms/switch/switch.md +91 -0
- package/src/atoms/switch/switch.shared.tsx +97 -0
- package/src/atoms/switch/switch.styles.ts +79 -0
- package/src/atoms/switch/switch.tsx +6 -0
- package/src/atoms/textarea/textarea.android.tsx +6 -0
- package/src/atoms/textarea/textarea.ios.tsx +6 -0
- package/src/atoms/textarea/textarea.md +140 -0
- package/src/atoms/textarea/textarea.shared.tsx +74 -0
- package/src/atoms/textarea/textarea.styles.ts +116 -0
- package/src/atoms/textarea/textarea.tsx +6 -0
- package/src/atoms/tooltip/tooltip.android.tsx +6 -0
- package/src/atoms/tooltip/tooltip.ios.tsx +7 -0
- package/src/atoms/tooltip/tooltip.md +122 -0
- package/src/atoms/tooltip/tooltip.shared.tsx +113 -0
- package/src/atoms/tooltip/tooltip.styles.ts +113 -0
- package/src/atoms/tooltip/tooltip.tsx +6 -0
- package/src/atoms/typography/typography.md +330 -0
- package/src/atoms/typography/typography.styles.ts +95 -0
- package/src/atoms/typography/typography.tsx +76 -0
- package/src/index.ts +12 -2
- package/src/molecules/action-panels/action-panels.md +133 -0
- package/src/molecules/action-panels/action-panels.styles.ts +39 -0
- package/src/molecules/action-panels/action-panels.tsx +113 -0
- package/src/molecules/alert/alert.md +119 -0
- package/src/molecules/alert/alert.styles.ts +88 -0
- package/src/molecules/alert/alert.tsx +74 -0
- package/src/molecules/alert-dialog/alert-dialog.android.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.ios.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.md +177 -0
- package/src/molecules/alert-dialog/alert-dialog.shared.tsx +187 -0
- package/src/molecules/alert-dialog/alert-dialog.styles.ts +248 -0
- package/src/molecules/alert-dialog/alert-dialog.tsx +6 -0
- package/src/molecules/card/card.md +190 -0
- package/src/molecules/card/card.styles.ts +67 -0
- package/src/molecules/card/card.tsx +176 -0
- package/src/molecules/code-block/code-block.md +159 -0
- package/src/molecules/code-block/code-block.styles.ts +167 -0
- package/src/molecules/code-block/code-block.tsx +176 -0
- package/src/molecules/description-lists/description-lists.md +129 -0
- package/src/molecules/description-lists/description-lists.styles.ts +102 -0
- package/src/molecules/description-lists/description-lists.tsx +133 -0
- package/src/molecules/empty-state/empty-state.md +218 -0
- package/src/molecules/empty-state/empty-state.styles.ts +63 -0
- package/src/molecules/empty-state/empty-state.tsx +77 -0
- package/src/molecules/feeds/feeds.md +102 -0
- package/src/molecules/feeds/feeds.styles.ts +120 -0
- package/src/molecules/feeds/feeds.tsx +167 -0
- package/src/molecules/field/field.md +117 -0
- package/src/molecules/field/field.styles.ts +85 -0
- package/src/molecules/field/field.tsx +175 -0
- package/src/molecules/fieldset/fieldset.md +141 -0
- package/src/molecules/fieldset/fieldset.styles.ts +79 -0
- package/src/molecules/fieldset/fieldset.tsx +182 -0
- package/src/molecules/form/form.md +137 -0
- package/src/molecules/form/form.styles.ts +39 -0
- package/src/molecules/form/form.tsx +246 -0
- package/src/molecules/grid-lists/grid-lists.md +114 -0
- package/src/molecules/grid-lists/grid-lists.styles.ts +79 -0
- package/src/molecules/grid-lists/grid-lists.tsx +157 -0
- package/src/molecules/index.ts +16 -0
- package/src/molecules/media-objects/media-objects.md +87 -0
- package/src/molecules/media-objects/media-objects.styles.ts +94 -0
- package/src/molecules/media-objects/media-objects.tsx +128 -0
- package/src/molecules/stacked-lists/stacked-lists.md +116 -0
- package/src/molecules/stacked-lists/stacked-lists.styles.ts +111 -0
- package/src/molecules/stacked-lists/stacked-lists.tsx +195 -0
- package/src/molecules/stats/stats.md +166 -0
- package/src/molecules/stats/stats.styles.ts +91 -0
- package/src/molecules/stats/stats.tsx +88 -0
- package/src/organisms/calendar/calendar.android.tsx +6 -0
- package/src/organisms/calendar/calendar.ios.tsx +6 -0
- package/src/organisms/calendar/calendar.md +114 -0
- package/src/organisms/calendar/calendar.shared.tsx +146 -0
- package/src/organisms/calendar/calendar.styles.ts +315 -0
- package/src/organisms/calendar/calendar.tsx +6 -0
- package/src/organisms/charts/charts.md +326 -0
- package/src/organisms/charts/charts.styles.ts +135 -0
- package/src/organisms/charts/charts.tsx +124 -0
- package/src/organisms/command/command.md +117 -0
- package/src/organisms/command/command.styles.ts +179 -0
- package/src/organisms/command/command.tsx +164 -0
- package/src/organisms/data-table/data-table.md +182 -0
- package/src/organisms/data-table/data-table.styles.ts +103 -0
- package/src/organisms/data-table/data-table.tsx +105 -0
- package/src/organisms/dialog/dialog.android.tsx +6 -0
- package/src/organisms/dialog/dialog.ios.tsx +6 -0
- package/src/organisms/dialog/dialog.md +271 -0
- package/src/organisms/dialog/dialog.shared.tsx +230 -0
- package/src/organisms/dialog/dialog.styles.ts +272 -0
- package/src/organisms/dialog/dialog.tsx +6 -0
- package/src/organisms/filter-panel/filter-panel.md +116 -0
- package/src/organisms/filter-panel/filter-panel.styles.ts +83 -0
- package/src/organisms/filter-panel/filter-panel.tsx +91 -0
- package/src/organisms/index.ts +13 -0
- package/src/organisms/navbars/navbars.android.tsx +6 -0
- package/src/organisms/navbars/navbars.ios.tsx +6 -0
- package/src/organisms/navbars/navbars.md +144 -0
- package/src/organisms/navbars/navbars.shared.tsx +137 -0
- package/src/organisms/navbars/navbars.styles.ts +251 -0
- package/src/organisms/navbars/navbars.tsx +6 -0
- package/src/organisms/overlays/overlays.android.tsx +6 -0
- package/src/organisms/overlays/overlays.ios.tsx +6 -0
- package/src/organisms/overlays/overlays.md +123 -0
- package/src/organisms/overlays/overlays.shared.tsx +175 -0
- package/src/organisms/overlays/overlays.styles.ts +309 -0
- package/src/organisms/overlays/overlays.tsx +6 -0
- package/src/organisms/row-menu/row-menu.android.tsx +6 -0
- package/src/organisms/row-menu/row-menu.ios.tsx +6 -0
- package/src/organisms/row-menu/row-menu.md +102 -0
- package/src/organisms/row-menu/row-menu.shared.tsx +105 -0
- package/src/organisms/row-menu/row-menu.styles.ts +262 -0
- package/src/organisms/row-menu/row-menu.tsx +6 -0
- package/src/organisms/sidebar/sidebar.android.tsx +6 -0
- package/src/organisms/sidebar/sidebar.ios.tsx +6 -0
- package/src/organisms/sidebar/sidebar.md +188 -0
- package/src/organisms/sidebar/sidebar.shared.tsx +167 -0
- package/src/organisms/sidebar/sidebar.styles.ts +262 -0
- package/src/organisms/sidebar/sidebar.tsx +6 -0
- package/src/organisms/stepper/stepper.android.tsx +6 -0
- package/src/organisms/stepper/stepper.ios.tsx +6 -0
- package/src/organisms/stepper/stepper.md +150 -0
- package/src/organisms/stepper/stepper.shared.tsx +158 -0
- package/src/organisms/stepper/stepper.styles.ts +280 -0
- package/src/organisms/stepper/stepper.tsx +6 -0
- package/src/organisms/tabs/tabs.android.tsx +6 -0
- package/src/organisms/tabs/tabs.ios.tsx +6 -0
- package/src/organisms/tabs/tabs.md +127 -0
- package/src/organisms/tabs/tabs.shared.tsx +281 -0
- package/src/organisms/tabs/tabs.styles.ts +398 -0
- package/src/organisms/tabs/tabs.tsx +6 -0
- package/src/style/color.ts +17 -0
- package/src/style/index.ts +14 -0
- package/src/style/primitives.ts +26 -0
- package/src/style/responsive.ts +45 -0
- package/src/style/shadow.ts +21 -0
- package/src/style/theme.tsx +56 -0
- package/src/style/tokens.ts +487 -0
- package/src/theme.ts +21 -0
- package/styles/canvas.css +128 -67
- package/tsconfig.json +4 -2
- package/src/cn.ts +0 -3
- package/styles/base.css +0 -17
- package/styles/components/alert.css +0 -66
- package/styles/components/app-shell.css +0 -46
- package/styles/components/avatar.css +0 -15
- package/styles/components/badge.css +0 -83
- package/styles/components/breadcrumb.css +0 -35
- package/styles/components/button-group.css +0 -23
- package/styles/components/button.css +0 -107
- package/styles/components/calendar.css +0 -73
- package/styles/components/card.css +0 -58
- package/styles/components/checkbox.css +0 -55
- package/styles/components/code-block.css +0 -18
- package/styles/components/combobox.css +0 -75
- package/styles/components/command.css +0 -94
- package/styles/components/data-table.css +0 -142
- package/styles/components/dialog.css +0 -72
- package/styles/components/dropdown.css +0 -54
- package/styles/components/empty-state.css +0 -17
- package/styles/components/field.css +0 -27
- package/styles/components/filter-panel.css +0 -58
- package/styles/components/form.css +0 -27
- package/styles/components/icon.css +0 -8
- package/styles/components/input-group.css +0 -45
- package/styles/components/input.css +0 -56
- package/styles/components/kbd.css +0 -15
- package/styles/components/page-header.css +0 -52
- package/styles/components/pagination.css +0 -48
- package/styles/components/popover.css +0 -14
- package/styles/components/radio.css +0 -28
- package/styles/components/row-menu.css +0 -69
- package/styles/components/section-card.css +0 -49
- package/styles/components/select.css +0 -57
- package/styles/components/separator.css +0 -32
- package/styles/components/sheet.css +0 -70
- package/styles/components/sidebar.css +0 -146
- package/styles/components/skeleton.css +0 -32
- package/styles/components/spinner.css +0 -26
- package/styles/components/stat-card.css +0 -71
- package/styles/components/stepper.css +0 -63
- package/styles/components/switch.css +0 -45
- package/styles/components/tabs.css +0 -40
- package/styles/components/textarea.css +0 -31
- package/styles/components/toast.css +0 -95
- package/styles/components/tooltip.css +0 -53
- package/styles/components/topbar.css +0 -24
- package/styles/components/typography.css +0 -105
- package/styles/patterns/backdrops.css +0 -35
- package/styles/patterns/density.css +0 -66
- package/styles/patterns/focus.css +0 -38
- package/styles/patterns/glass.css +0 -85
- package/styles/patterns/high-contrast.css +0 -70
- package/styles/patterns/reduced-motion.css +0 -12
- package/styles/patterns/scrollbar.css +0 -10
- package/styles/reset.css +0 -89
- package/styles/tokens/colors.css +0 -106
- package/styles/tokens/motion.css +0 -33
- package/styles/tokens/radius.css +0 -10
- package/styles/tokens/shadows.css +0 -35
- package/styles/tokens/spacing.css +0 -19
- package/styles/tokens/typography.css +0 -6
- package/styles/tokens/z-index.css +0 -12
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, palette, shadow } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Chart styles. Layout-only fragments are static objects; anything
|
|
5
|
+
// that reads a color is a function of the active tokens (so the surface follows
|
|
6
|
+
// light/dark, and reads as glass when the ThemeProvider's surface is "glass",
|
|
7
|
+
// since tokens.card is swapped translucent at the theming level). The bar fill
|
|
8
|
+
// per tone is the one palette-vs-token choice, resolved by `barFill`.
|
|
9
|
+
|
|
10
|
+
export type Tone = "primary" | "success" | "destructive";
|
|
11
|
+
|
|
12
|
+
// --- surface ----------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// The bordered, shadowed card surface (rounded-lg border border-border bg-card
|
|
15
|
+
// shadow-sm), mirroring the docs `cardCls`.
|
|
16
|
+
export function surface(tokens: ColorTokens): ViewStyle {
|
|
17
|
+
return {
|
|
18
|
+
borderRadius: 8,
|
|
19
|
+
borderWidth: 1,
|
|
20
|
+
borderColor: tokens.border,
|
|
21
|
+
backgroundColor: tokens.card,
|
|
22
|
+
...shadow("sm"),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Surface padding by density: p-4 (compact) vs p-5 (default).
|
|
27
|
+
export const surfacePadCompact: ViewStyle = { padding: 16 };
|
|
28
|
+
export const surfacePadDefault: ViewStyle = { padding: 20 };
|
|
29
|
+
|
|
30
|
+
// --- title ------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
// Heading: font-semibold text-card-foreground; size/inset switch by density.
|
|
33
|
+
export function title(tokens: ColorTokens): TextStyle {
|
|
34
|
+
return { fontWeight: "600", color: tokens["card-foreground"] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// mb-3 text-sm (compact) vs mb-4 text-base (default).
|
|
38
|
+
export const titleCompact: TextStyle = { marginBottom: 12, fontSize: 14, lineHeight: 20 };
|
|
39
|
+
export const titleDefault: TextStyle = { marginBottom: 16, fontSize: 16, lineHeight: 24 };
|
|
40
|
+
|
|
41
|
+
// --- bar fill ---------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
// The fill for each bar, by tone. Token primary by default; saturated palette
|
|
44
|
+
// hues for the success / destructive tones so the chart reads in either scheme.
|
|
45
|
+
export function barFill(tokens: ColorTokens, tone: Tone): string {
|
|
46
|
+
if (tone === "success") return palette["green-500"];
|
|
47
|
+
if (tone === "destructive") return palette["red-500"];
|
|
48
|
+
return tokens.primary;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --- horizontal layout ------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
// The horizontal stack of rows (flex-col); the gap is applied by the component.
|
|
54
|
+
export const horizontalStack: ViewStyle = { flexDirection: "column" };
|
|
55
|
+
|
|
56
|
+
// One horizontal row: a label, a track-aligned bar, and the value
|
|
57
|
+
// (flex-row items-center gap-2).
|
|
58
|
+
export const horizontalRow: ViewStyle = { flexDirection: "row", alignItems: "center", gap: 8 };
|
|
59
|
+
|
|
60
|
+
// The row's leading label (w-16 text-xs text-muted-foreground).
|
|
61
|
+
export function horizontalLabel(tokens: ColorTokens): TextStyle {
|
|
62
|
+
return { width: 64, fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The track that holds a horizontal bar (flex-1 flex-row items-center).
|
|
66
|
+
export const horizontalTrack: ViewStyle = {
|
|
67
|
+
flexGrow: 1,
|
|
68
|
+
flexShrink: 1,
|
|
69
|
+
flexBasis: "0%",
|
|
70
|
+
flexDirection: "row",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// A horizontal bar: h-3 rounded-r plus its fill and width (set by the
|
|
75
|
+
// component). The right corners are rounded; the left edge sits on the track.
|
|
76
|
+
export function horizontalBar(fill: string, width: number): ViewStyle {
|
|
77
|
+
return {
|
|
78
|
+
height: 12,
|
|
79
|
+
borderTopRightRadius: 4,
|
|
80
|
+
borderBottomRightRadius: 4,
|
|
81
|
+
backgroundColor: fill,
|
|
82
|
+
width,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// The trailing value (text-xs font-medium text-card-foreground).
|
|
87
|
+
export function horizontalValue(tokens: ColorTokens): TextStyle {
|
|
88
|
+
return { fontSize: 12, lineHeight: 16, fontWeight: "500", color: tokens["card-foreground"] };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --- vertical layout --------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
// The baseline-aligned row of columns (flex-row items-end); the gap and the
|
|
94
|
+
// plot height are applied by the component.
|
|
95
|
+
export const verticalBars: ViewStyle = { flexDirection: "row", alignItems: "flex-end" };
|
|
96
|
+
|
|
97
|
+
// One column wrapping a vertical bar (flex-1 items-stretch).
|
|
98
|
+
export const verticalColumn: ViewStyle = {
|
|
99
|
+
flexGrow: 1,
|
|
100
|
+
flexShrink: 1,
|
|
101
|
+
flexBasis: "0%",
|
|
102
|
+
alignItems: "stretch",
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// A vertical bar: rounded-t plus its fill and height (set by the component).
|
|
106
|
+
export function verticalBar(fill: string, height: number): ViewStyle {
|
|
107
|
+
return {
|
|
108
|
+
borderTopLeftRadius: 4,
|
|
109
|
+
borderTopRightRadius: 4,
|
|
110
|
+
backgroundColor: fill,
|
|
111
|
+
height,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// The hairline baseline under the bars (h-px w-full bg-border).
|
|
116
|
+
export function baseline(tokens: ColorTokens): ViewStyle {
|
|
117
|
+
return { height: 1, width: "100%", backgroundColor: tokens.border };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The labels row under the baseline (mt-2 flex-row); the gap is applied by the
|
|
121
|
+
// component.
|
|
122
|
+
export const verticalLabelsRow: ViewStyle = { marginTop: 8, flexDirection: "row" };
|
|
123
|
+
|
|
124
|
+
// One column's label (flex-1 text-center text-xs text-muted-foreground).
|
|
125
|
+
export function verticalLabel(tokens: ColorTokens): TextStyle {
|
|
126
|
+
return {
|
|
127
|
+
flexGrow: 1,
|
|
128
|
+
flexShrink: 1,
|
|
129
|
+
flexBasis: "0%",
|
|
130
|
+
textAlign: "center",
|
|
131
|
+
fontSize: 12,
|
|
132
|
+
lineHeight: 16,
|
|
133
|
+
color: tokens["muted-foreground"],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { View, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
2
|
+
import * as s from "./charts.styles.js";
|
|
3
|
+
import { type Tone } from "./charts.styles.js";
|
|
4
|
+
|
|
5
|
+
// Chart: a token-themed bar chart built entirely from View views, no SVG and no
|
|
6
|
+
// CSS grid. Each datum is a vertical (or horizontal) bar whose length is a
|
|
7
|
+
// pixel height computed in JS from its value against the axis max. A label sits
|
|
8
|
+
// under (or beside) each bar.
|
|
9
|
+
//
|
|
10
|
+
// This is Canvas's faithful, SVG-free take on the docs "charts" organism: the
|
|
11
|
+
// other chart types in that entry (sparkline, gauge, heatmap, stacked) lean on
|
|
12
|
+
// SVG and grid, so they are approximated by the bar chart here.
|
|
13
|
+
//
|
|
14
|
+
// Boolean-prop API: one boolean per option, grouped by axis, first-match
|
|
15
|
+
// precedence within an axis (mirrors Button's intentOf). Axes:
|
|
16
|
+
//
|
|
17
|
+
// - Tone (pick one; default is the primary fill): `success` paints bars green,
|
|
18
|
+
// `destructive` paints them red. Precedence: success > destructive > default.
|
|
19
|
+
// - Orientation: `horizontal` lays bars out as sideways rows; omit for the
|
|
20
|
+
// default vertical column chart.
|
|
21
|
+
// - Density: `compact` shrinks the plot area and tightens the gaps; omit for
|
|
22
|
+
// the default density.
|
|
23
|
+
|
|
24
|
+
export interface ChartDatum {
|
|
25
|
+
/** Bucket label shown under (vertical) or beside (horizontal) the bar. */
|
|
26
|
+
label: string;
|
|
27
|
+
/** The bar's magnitude. Compared against `max` to size the bar. */
|
|
28
|
+
value: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ChartProps {
|
|
32
|
+
/** The bars to render, in order. */
|
|
33
|
+
data: ChartDatum[];
|
|
34
|
+
/** Optional heading shown above the plot. */
|
|
35
|
+
title?: string;
|
|
36
|
+
/** Axis maximum. Defaults to the largest value in `data`. */
|
|
37
|
+
max?: number;
|
|
38
|
+
// Tone (pick one; default is the primary fill).
|
|
39
|
+
success?: boolean;
|
|
40
|
+
destructive?: boolean;
|
|
41
|
+
// Orientation (default is a vertical column chart).
|
|
42
|
+
horizontal?: boolean;
|
|
43
|
+
// Density (default is the standard plot size).
|
|
44
|
+
compact?: boolean;
|
|
45
|
+
/** Escape hatch for layout/positioning composition (mainly width). */
|
|
46
|
+
style?: StyleProp<ViewStyle>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Tone precedence when more than one is passed: first match wins.
|
|
50
|
+
function toneOf(p: ChartProps): Tone {
|
|
51
|
+
if (p.success) return "success";
|
|
52
|
+
if (p.destructive) return "destructive";
|
|
53
|
+
return "primary";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Plot length in px (bar travel: chart height for vertical, bar width for
|
|
57
|
+
// horizontal), by density.
|
|
58
|
+
const PLOT_LENGTH = { default: 140, compact: 96 } as const;
|
|
59
|
+
|
|
60
|
+
export function Chart(props: ChartProps) {
|
|
61
|
+
const { data, title, style } = props;
|
|
62
|
+
const tone = toneOf(props);
|
|
63
|
+
const horizontal = !!props.horizontal;
|
|
64
|
+
const compact = !!props.compact;
|
|
65
|
+
const { tokens } = useTheme();
|
|
66
|
+
|
|
67
|
+
const fill = s.barFill(tokens, tone);
|
|
68
|
+
const plot = compact ? PLOT_LENGTH.compact : PLOT_LENGTH.default;
|
|
69
|
+
// Axis max: the caller's, else the largest value, else 1 to avoid /0.
|
|
70
|
+
const values = data.map((d) => d.value);
|
|
71
|
+
const max = props.max != null && props.max > 0 ? props.max : Math.max(1, ...values);
|
|
72
|
+
|
|
73
|
+
// Map a value to a clamped pixel length along the plot axis.
|
|
74
|
+
const lengthPx = (value: number): number => {
|
|
75
|
+
const ratio = Math.max(0, Math.min(1, value / max));
|
|
76
|
+
return Math.max(2, Math.round(ratio * plot));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Per-bar gap by density: gap-1.5 (6) compact, gap-2 (8) default.
|
|
80
|
+
const gap = compact ? 6 : 8;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<View style={[s.surface(tokens), compact ? s.surfacePadCompact : s.surfacePadDefault, style]}>
|
|
84
|
+
{title != null && title !== "" ? (
|
|
85
|
+
<Text style={[s.title(tokens), compact ? s.titleCompact : s.titleDefault]}>{title}</Text>
|
|
86
|
+
) : null}
|
|
87
|
+
|
|
88
|
+
{horizontal ? (
|
|
89
|
+
// Horizontal: each row is a label, a track-aligned bar, and the value.
|
|
90
|
+
<View style={[s.horizontalStack, { gap }]}>
|
|
91
|
+
{data.map((d, i) => (
|
|
92
|
+
<View key={i} style={s.horizontalRow}>
|
|
93
|
+
<Text style={s.horizontalLabel(tokens)}>{d.label}</Text>
|
|
94
|
+
<View style={s.horizontalTrack}>
|
|
95
|
+
<View style={s.horizontalBar(fill, lengthPx(d.value))} />
|
|
96
|
+
</View>
|
|
97
|
+
<Text style={s.horizontalValue(tokens)}>{d.value}</Text>
|
|
98
|
+
</View>
|
|
99
|
+
))}
|
|
100
|
+
</View>
|
|
101
|
+
) : (
|
|
102
|
+
// Vertical: a baseline-aligned row of columns, each a bar over its label.
|
|
103
|
+
<View>
|
|
104
|
+
<View style={[s.verticalBars, { gap, height: plot }]}>
|
|
105
|
+
{data.map((d, i) => (
|
|
106
|
+
<View key={i} style={s.verticalColumn}>
|
|
107
|
+
<View style={s.verticalBar(fill, lengthPx(d.value))} />
|
|
108
|
+
</View>
|
|
109
|
+
))}
|
|
110
|
+
</View>
|
|
111
|
+
{/* Baseline under the bars. */}
|
|
112
|
+
<View style={s.baseline(tokens)} />
|
|
113
|
+
<View style={[s.verticalLabelsRow, { gap }]}>
|
|
114
|
+
{data.map((d, i) => (
|
|
115
|
+
<Text key={i} style={s.verticalLabel(tokens)}>
|
|
116
|
+
{d.label}
|
|
117
|
+
</Text>
|
|
118
|
+
))}
|
|
119
|
+
</View>
|
|
120
|
+
</View>
|
|
121
|
+
)}
|
|
122
|
+
</View>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Command Palette
|
|
2
|
+
|
|
3
|
+
Cmd+K search: navigation, actions, recent items.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Command
|
|
9
|
+
active={0}
|
|
10
|
+
placeholder="Type a command..."
|
|
11
|
+
trigger
|
|
12
|
+
groups={[
|
|
13
|
+
{ heading: "Actions", items: [
|
|
14
|
+
{ label: "New File", icon: "📄", shortcut: "Ctrl+N" },
|
|
15
|
+
{ label: "Open File", icon: "📂", shortcut: "Ctrl+O" },
|
|
16
|
+
{ label: "Save", icon: "💾", shortcut: "Ctrl+S" }
|
|
17
|
+
] },
|
|
18
|
+
{ heading: "Navigation", items: [
|
|
19
|
+
{ label: "Go to Dashboard", icon: "▸" },
|
|
20
|
+
{ label: "Go to Settings", icon: "▸" }
|
|
21
|
+
] }
|
|
22
|
+
]}
|
|
23
|
+
/>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Variants
|
|
27
|
+
|
|
28
|
+
### Mode - inline
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<Command
|
|
32
|
+
active={0}
|
|
33
|
+
placeholder="Type a command..."
|
|
34
|
+
groups={[
|
|
35
|
+
{ heading: "Actions", items: [
|
|
36
|
+
{ label: "New File", icon: "📄", shortcut: "Ctrl+N" },
|
|
37
|
+
{ label: "Open File", icon: "📂", shortcut: "Ctrl+O" },
|
|
38
|
+
{ label: "Save", icon: "💾", shortcut: "Ctrl+S" }
|
|
39
|
+
] },
|
|
40
|
+
{ heading: "Navigation", items: [
|
|
41
|
+
{ label: "Go to Dashboard", icon: "▸" },
|
|
42
|
+
{ label: "Go to Settings", icon: "▸" }
|
|
43
|
+
] }
|
|
44
|
+
]}
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Footer with key hints
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<Command
|
|
52
|
+
active={0}
|
|
53
|
+
placeholder="Type a command..."
|
|
54
|
+
trigger
|
|
55
|
+
footer
|
|
56
|
+
groups={[
|
|
57
|
+
{ heading: "Actions", items: [
|
|
58
|
+
{ label: "New File", icon: "📄", shortcut: "Ctrl+N" },
|
|
59
|
+
{ label: "Open File", icon: "📂", shortcut: "Ctrl+O" },
|
|
60
|
+
{ label: "Save", icon: "💾", shortcut: "Ctrl+S" }
|
|
61
|
+
] },
|
|
62
|
+
{ heading: "Navigation", items: [
|
|
63
|
+
{ label: "Go to Dashboard", icon: "▸" },
|
|
64
|
+
{ label: "Go to Settings", icon: "▸" }
|
|
65
|
+
] }
|
|
66
|
+
]}
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Do & Don't
|
|
71
|
+
|
|
72
|
+
### Trigger
|
|
73
|
+
|
|
74
|
+
**Do** — Surface the shortcut in a trailing kbd so the trigger advertises the faster keyboard path.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<Command trigger />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Don't** — A bare search button hides the keyboard shortcut, so power users never learn the ⌘K entry point.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<Pressable style={{ flexDirection: "row", alignItems: "center", gap: 8, alignSelf: "flex-start", borderRadius: 6, borderWidth: 1, borderColor: tokens.input, backgroundColor: "transparent", paddingHorizontal: 12, paddingVertical: 6 }}>
|
|
84
|
+
<Icon search muted size={14} />
|
|
85
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>Search...</Text>
|
|
86
|
+
</Pressable>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Inline
|
|
90
|
+
|
|
91
|
+
**Do** — Group commands under labels with separators and highlight the first match so results stay scannable.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Command open placeholder="Type a command..." active={0} groups={[
|
|
95
|
+
{ heading: "Actions", items: [
|
|
96
|
+
{ label: "New File", icon: "📄", shortcut: "Ctrl+N" },
|
|
97
|
+
{ label: "Save", icon: "💾", shortcut: "Ctrl+S" }
|
|
98
|
+
] },
|
|
99
|
+
{ heading: "Navigation", items: [
|
|
100
|
+
{ label: "Go to Dashboard", icon: "▸" },
|
|
101
|
+
{ label: "Go to Settings", icon: "▸" }
|
|
102
|
+
] }
|
|
103
|
+
]} />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Don't** — Dumping every command into one flat list with no labels makes the palette hard to scan.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Command open placeholder="Type a command..." active={-1} groups={[
|
|
110
|
+
{ items: [
|
|
111
|
+
{ label: "New File", icon: "📄", shortcut: "Ctrl+N" },
|
|
112
|
+
{ label: "Save", icon: "💾", shortcut: "Ctrl+S" },
|
|
113
|
+
{ label: "Go to Dashboard", icon: "▸" },
|
|
114
|
+
{ label: "Go to Settings", icon: "▸" }
|
|
115
|
+
] }
|
|
116
|
+
]} />
|
|
117
|
+
```
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, shadow } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Command styles. Layout-only fragments are static objects; anything
|
|
5
|
+
// that reads a color is a function of the active tokens (so the palette follows
|
|
6
|
+
// light/dark, and the popover surface reads as glass when the ThemeProvider's
|
|
7
|
+
// surface is "glass", since tokens.popover is swapped translucent at the theming
|
|
8
|
+
// level). The active-row highlight and the row press state both use the accent
|
|
9
|
+
// token: the highlight is composed in the component, the press state in the
|
|
10
|
+
// Pressable's style callback.
|
|
11
|
+
|
|
12
|
+
// --- card shell -------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// The floating palette card: fixed width, rounded, bordered, raised, clipping
|
|
15
|
+
// its rounded corners. (w-[420px] rounded-lg border border-border bg-popover
|
|
16
|
+
// shadow-xl overflow-hidden.)
|
|
17
|
+
export function card(tokens: ColorTokens): ViewStyle {
|
|
18
|
+
return {
|
|
19
|
+
width: 420,
|
|
20
|
+
borderRadius: 8,
|
|
21
|
+
borderWidth: 1,
|
|
22
|
+
borderColor: tokens.border,
|
|
23
|
+
backgroundColor: tokens.popover,
|
|
24
|
+
overflow: "hidden",
|
|
25
|
+
...shadow("xl"),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// In trigger mode the card floats below the collapsed trigger button.
|
|
30
|
+
// (absolute top-full left-0 z-50 mt-3.)
|
|
31
|
+
export const cardFloating: ViewStyle = {
|
|
32
|
+
position: "absolute",
|
|
33
|
+
top: "100%",
|
|
34
|
+
left: 0,
|
|
35
|
+
zIndex: 50,
|
|
36
|
+
marginTop: 12,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// --- search row -------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
// The search row at the top of the card: a leading glyph + the muted
|
|
42
|
+
// placeholder, with a hairline under it.
|
|
43
|
+
// (flex-row items-center gap-2 border-b border-border px-3 py-3.)
|
|
44
|
+
export function searchRow(tokens: ColorTokens): ViewStyle {
|
|
45
|
+
return {
|
|
46
|
+
flexDirection: "row",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
gap: 8,
|
|
49
|
+
borderBottomWidth: 1,
|
|
50
|
+
borderColor: tokens.border,
|
|
51
|
+
paddingHorizontal: 12,
|
|
52
|
+
paddingVertical: 12,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The magnifier glyph in the search row. (text-sm text-muted-foreground.)
|
|
57
|
+
export function searchGlyph(tokens: ColorTokens): TextStyle {
|
|
58
|
+
return { fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The muted placeholder text in the search row. (text-sm text-muted-foreground.)
|
|
62
|
+
export function searchPlaceholder(tokens: ColorTokens): TextStyle {
|
|
63
|
+
return { fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- result groups + rows ---------------------------------------------------
|
|
67
|
+
|
|
68
|
+
// The optional uppercase section heading above a group's rows.
|
|
69
|
+
// (uppercase text-xs text-muted-foreground px-3 pt-3 pb-1.)
|
|
70
|
+
export function groupHeading(tokens: ColorTokens): TextStyle {
|
|
71
|
+
return {
|
|
72
|
+
textTransform: "uppercase",
|
|
73
|
+
fontSize: 12,
|
|
74
|
+
lineHeight: 16,
|
|
75
|
+
color: tokens["muted-foreground"],
|
|
76
|
+
paddingHorizontal: 12,
|
|
77
|
+
paddingTop: 12,
|
|
78
|
+
paddingBottom: 4,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A result row: leading icon + label + optional trailing shortcut.
|
|
83
|
+
// (flex-row items-center gap-3 px-3 py-2; active:bg-accent is the press state.)
|
|
84
|
+
export const rowBase: ViewStyle = {
|
|
85
|
+
flexDirection: "row",
|
|
86
|
+
alignItems: "center",
|
|
87
|
+
gap: 12,
|
|
88
|
+
paddingHorizontal: 12,
|
|
89
|
+
paddingVertical: 8,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// The accent surface: used for both the highlighted (active) row and the row
|
|
93
|
+
// press state (active:bg-accent). (bg-accent.)
|
|
94
|
+
export function rowAccent(tokens: ColorTokens): ViewStyle {
|
|
95
|
+
return { backgroundColor: tokens.accent };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A row's leading glyph. (text-sm text-foreground.)
|
|
99
|
+
export function rowIcon(tokens: ColorTokens): TextStyle {
|
|
100
|
+
return { fontSize: 14, lineHeight: 20, color: tokens.foreground };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// A row's label, taking the remaining width. (text-sm text-foreground flex-1.)
|
|
104
|
+
export function rowLabel(tokens: ColorTokens): TextStyle {
|
|
105
|
+
return {
|
|
106
|
+
fontSize: 14,
|
|
107
|
+
lineHeight: 20,
|
|
108
|
+
color: tokens.foreground,
|
|
109
|
+
flexGrow: 1,
|
|
110
|
+
flexShrink: 1,
|
|
111
|
+
flexBasis: "0%",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// --- collapsed trigger ------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
// The wrapper around the collapsed trigger + the floating card.
|
|
118
|
+
// (relative w-full.)
|
|
119
|
+
export const triggerWrapper: ViewStyle = { position: "relative", width: "100%" };
|
|
120
|
+
|
|
121
|
+
// When the palette is open in trigger mode, the wrapper is lifted into its own
|
|
122
|
+
// stacking context above sibling content. react-native-web gives every
|
|
123
|
+
// positioned View an implicit stacking context, so the floating card's own
|
|
124
|
+
// `zIndex` is scoped INSIDE the `relative` wrapper and cannot rise above a later
|
|
125
|
+
// sibling. Raising the wrapper's zIndex while open lifts the whole control —
|
|
126
|
+
// trigger and palette together — above everything painted after it.
|
|
127
|
+
export const triggerWrapperLifted: ViewStyle = { zIndex: 50 };
|
|
128
|
+
|
|
129
|
+
// The collapsed full-width search trigger button.
|
|
130
|
+
// (flex-row items-center gap-2 w-full justify-start rounded-md border
|
|
131
|
+
// border-input bg-transparent px-3 py-1.5.)
|
|
132
|
+
export function triggerRow(tokens: ColorTokens): ViewStyle {
|
|
133
|
+
return {
|
|
134
|
+
flexDirection: "row",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
gap: 8,
|
|
137
|
+
width: "100%",
|
|
138
|
+
justifyContent: "flex-start",
|
|
139
|
+
borderRadius: 6,
|
|
140
|
+
borderWidth: 1,
|
|
141
|
+
borderColor: tokens.input,
|
|
142
|
+
backgroundColor: "transparent",
|
|
143
|
+
paddingHorizontal: 12,
|
|
144
|
+
paddingVertical: 6,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// The trigger button's "Search..." label. (text-sm text-foreground.)
|
|
149
|
+
export function triggerLabel(tokens: ColorTokens): TextStyle {
|
|
150
|
+
return { fontSize: 14, lineHeight: 20, color: tokens.foreground };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Pushes the trailing kbd cap to the right edge of the trigger. (ml-auto.)
|
|
154
|
+
export const triggerKbd: ViewStyle = { marginLeft: "auto" };
|
|
155
|
+
|
|
156
|
+
// --- footer hint bar --------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
// The footer hint bar below the list. (flex-row items-center gap-3 border-t
|
|
159
|
+
// border-border px-4 py-2.5.)
|
|
160
|
+
export function footerBar(tokens: ColorTokens): ViewStyle {
|
|
161
|
+
return {
|
|
162
|
+
flexDirection: "row",
|
|
163
|
+
alignItems: "center",
|
|
164
|
+
gap: 12,
|
|
165
|
+
borderTopWidth: 1,
|
|
166
|
+
borderColor: tokens.border,
|
|
167
|
+
paddingHorizontal: 16,
|
|
168
|
+
paddingVertical: 10,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// A single hint cluster in the footer (kbd cap(s) + its text).
|
|
173
|
+
// (flex-row items-center gap-1.)
|
|
174
|
+
export const footerHint: ViewStyle = { flexDirection: "row", alignItems: "center", gap: 4 };
|
|
175
|
+
|
|
176
|
+
// A hint's supporting text. (text-xs text-muted-foreground.)
|
|
177
|
+
export function footerText(tokens: ColorTokens): TextStyle {
|
|
178
|
+
return { fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] };
|
|
179
|
+
}
|