@olympusoss/canvas 3.2.1 → 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 +75 -65
- package/package.json +11 -5
- 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/styles/canvas.css +127 -74
- package/tsconfig.json +4 -2
- package/src/cn.ts +0 -3
- package/styles/atoms/avatar.css +0 -22
- package/styles/atoms/badge.css +0 -83
- package/styles/atoms/breadcrumb.css +0 -35
- package/styles/atoms/button-group.css +0 -23
- package/styles/atoms/button.css +0 -107
- package/styles/atoms/checkbox.css +0 -55
- package/styles/atoms/combobox.css +0 -76
- package/styles/atoms/dropdown.css +0 -54
- package/styles/atoms/icon.css +0 -8
- package/styles/atoms/input-group.css +0 -45
- package/styles/atoms/input.css +0 -56
- package/styles/atoms/kbd.css +0 -15
- package/styles/atoms/pagination.css +0 -48
- package/styles/atoms/popover.css +0 -14
- package/styles/atoms/radio.css +0 -28
- package/styles/atoms/select.css +0 -57
- package/styles/atoms/separator.css +0 -32
- package/styles/atoms/skeleton.css +0 -32
- package/styles/atoms/spinner.css +0 -26
- package/styles/atoms/switch.css +0 -45
- package/styles/atoms/textarea.css +0 -31
- package/styles/atoms/tooltip.css +0 -53
- package/styles/atoms/typography.css +0 -105
- package/styles/base.css +0 -17
- package/styles/molecules/alert.css +0 -66
- package/styles/molecules/card.css +0 -58
- package/styles/molecules/code-block.css +0 -18
- package/styles/molecules/empty-state.css +0 -17
- package/styles/molecules/field.css +0 -27
- package/styles/molecules/form.css +0 -27
- package/styles/molecules/page-header.css +0 -52
- package/styles/molecules/section-card.css +0 -49
- package/styles/molecules/stat-card.css +0 -71
- package/styles/molecules/toast.css +0 -95
- package/styles/organisms/app-shell.css +0 -46
- package/styles/organisms/calendar.css +0 -73
- package/styles/organisms/command.css +0 -95
- package/styles/organisms/data-table.css +0 -142
- package/styles/organisms/dialog.css +0 -72
- package/styles/organisms/filter-panel.css +0 -58
- package/styles/organisms/row-menu.css +0 -69
- package/styles/organisms/sheet.css +0 -70
- package/styles/organisms/sidebar.css +0 -146
- package/styles/organisms/stepper.css +0 -63
- package/styles/organisms/tabs.css +0 -40
- package/styles/organisms/topbar.css +0 -24
- package/styles/patterns/backdrops.css +0 -35
- package/styles/patterns/density.css +0 -66
- package/styles/patterns/focus.css +0 -22
- 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 -108
- 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
- package/styles/utilities/display.css +0 -66
- package/styles/utilities/flexbox.css +0 -240
- package/styles/utilities/gap.css +0 -288
- package/styles/utilities/grid.css +0 -138
- package/styles/utilities/position.css +0 -78
- package/styles/utilities/sizing.css +0 -138
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type GestureResponderEvent } from "react-native";
|
|
3
|
+
import { View, Pressable, Text, useTheme, type StyleProp, type ViewStyle, type TextStyle } from "../../style/index.js";
|
|
4
|
+
import { Icon } from "../icon/icon.js";
|
|
5
|
+
import * as s from "./breadcrumb.styles.js";
|
|
6
|
+
|
|
7
|
+
// A breadcrumb is a horizontal trail of links separated by a divider glyph, with
|
|
8
|
+
// the last item current (non-link, emphasized). Ancestors are muted links; the
|
|
9
|
+
// page you are on is plain foreground text at the end of the trail.
|
|
10
|
+
//
|
|
11
|
+
// Boolean-prop API, one axis: the separator style. Pick one of `chevron`
|
|
12
|
+
// (default), `slash`, or `dot`; first match wins (mirrors Button's intentOf). The
|
|
13
|
+
// foundation has no SVG utility here, so the chevron is a reading-direction glyph
|
|
14
|
+
// (the single-guillemet "›") rather than an icon, keeping every separator a Text
|
|
15
|
+
// glyph pointing in the reading direction.
|
|
16
|
+
|
|
17
|
+
export interface BreadcrumbProps {
|
|
18
|
+
/**
|
|
19
|
+
* The trail, ancestor-first. The last entry renders as the current page (plain
|
|
20
|
+
* emphasized text); the rest render as muted links.
|
|
21
|
+
*/
|
|
22
|
+
items?: string[];
|
|
23
|
+
// Separator style (pick one; default is the chevron glyph).
|
|
24
|
+
chevron?: boolean;
|
|
25
|
+
slash?: boolean;
|
|
26
|
+
dot?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Leading affordance: prepend a muted home-icon crumb (aria-labelled "Home")
|
|
29
|
+
* before the trail, followed by the separator. Off by default.
|
|
30
|
+
*/
|
|
31
|
+
homeIcon?: boolean;
|
|
32
|
+
/** Fired with the crumb label and its index when a link (non-last) is pressed. */
|
|
33
|
+
onItemPress?: (item: string, index: number) => void;
|
|
34
|
+
/** Escape hatch for layout/positioning composition. */
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Separator = "chevron" | "slash" | "dot";
|
|
39
|
+
|
|
40
|
+
// Separator precedence when more than one flag is passed: first match wins.
|
|
41
|
+
function separatorOf(p: BreadcrumbProps): Separator {
|
|
42
|
+
if (p.chevron) return "chevron";
|
|
43
|
+
if (p.slash) return "slash";
|
|
44
|
+
if (p.dot) return "dot";
|
|
45
|
+
return "chevron";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// The divider glyph per style. A centered middot for `dot`, a forward slash for
|
|
49
|
+
// `slash`, and a reading-direction single guillemet for `chevron` (stands in for
|
|
50
|
+
// the SVG chevron not rendered here).
|
|
51
|
+
const SEPARATOR_GLYPH: Record<Separator, string> = {
|
|
52
|
+
chevron: "›",
|
|
53
|
+
slash: "/",
|
|
54
|
+
dot: "·",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export interface BreadcrumbItemProps {
|
|
58
|
+
children?: ReactNode;
|
|
59
|
+
/** Render as the current page: emphasized foreground text, non-interactive. */
|
|
60
|
+
current?: boolean;
|
|
61
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
62
|
+
/** Escape hatch for layout/positioning composition. */
|
|
63
|
+
style?: StyleProp<TextStyle>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A single crumb. Current crumbs are plain emphasized text; ancestors are muted,
|
|
67
|
+
// pressable links.
|
|
68
|
+
export function BreadcrumbItem(props: BreadcrumbItemProps) {
|
|
69
|
+
const { children, current, onPress, style } = props;
|
|
70
|
+
const { tokens } = useTheme();
|
|
71
|
+
if (children == null) return null;
|
|
72
|
+
if (current) {
|
|
73
|
+
return <Text style={[s.current(tokens), style]} accessibilityRole="text">{children}</Text>;
|
|
74
|
+
}
|
|
75
|
+
return (
|
|
76
|
+
<Pressable onPress={onPress} accessibilityRole="link">
|
|
77
|
+
{({ pressed }) => (
|
|
78
|
+
<Text style={[s.link(tokens), pressed ? { opacity: 0.7 } : null, style]}>{children}</Text>
|
|
79
|
+
)}
|
|
80
|
+
</Pressable>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function Breadcrumb(props: BreadcrumbProps) {
|
|
85
|
+
const { items, homeIcon, onItemPress, style } = props;
|
|
86
|
+
const { tokens } = useTheme();
|
|
87
|
+
const trail = items ?? [];
|
|
88
|
+
const separator = separatorOf(props);
|
|
89
|
+
const glyph = SEPARATOR_GLYPH[separator];
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View style={[s.nav, style]} accessibilityRole="header">
|
|
93
|
+
{homeIcon ? (
|
|
94
|
+
<View style={s.crumb}>
|
|
95
|
+
<Pressable
|
|
96
|
+
onPress={() => onItemPress?.("Home", 0)}
|
|
97
|
+
accessibilityRole="link"
|
|
98
|
+
accessibilityLabel="Home"
|
|
99
|
+
style={({ pressed }) => (pressed ? { opacity: 0.7 } : null)}
|
|
100
|
+
>
|
|
101
|
+
<Icon home muted size={14} />
|
|
102
|
+
</Pressable>
|
|
103
|
+
{trail.length === 0 ? null : (
|
|
104
|
+
<Text style={s.separator(tokens)} accessibilityElementsHidden>
|
|
105
|
+
{glyph}
|
|
106
|
+
</Text>
|
|
107
|
+
)}
|
|
108
|
+
</View>
|
|
109
|
+
) : null}
|
|
110
|
+
{trail.map((item, index) => {
|
|
111
|
+
const last = index === trail.length - 1;
|
|
112
|
+
return (
|
|
113
|
+
<View key={`${index}-${item}`} style={s.crumb}>
|
|
114
|
+
<BreadcrumbItem
|
|
115
|
+
current={last}
|
|
116
|
+
onPress={last ? undefined : () => onItemPress?.(item, index)}
|
|
117
|
+
>
|
|
118
|
+
{item}
|
|
119
|
+
</BreadcrumbItem>
|
|
120
|
+
{last ? null : (
|
|
121
|
+
<Text style={s.separator(tokens)} accessibilityElementsHidden>
|
|
122
|
+
{glyph}
|
|
123
|
+
</Text>
|
|
124
|
+
)}
|
|
125
|
+
</View>
|
|
126
|
+
);
|
|
127
|
+
})}
|
|
128
|
+
</View>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createButton } from "./button.shared.js";
|
|
2
|
+
import { androidSkin } from "./button.styles.js";
|
|
3
|
+
|
|
4
|
+
// Material 3 Button. Metro resolves this file on Android; the docs import it for preview.
|
|
5
|
+
export const Button = createButton(androidSkin);
|
|
6
|
+
export type { ButtonProps } from "./button.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createButton } from "./button.shared.js";
|
|
2
|
+
import { iosSkin } from "./button.styles.js";
|
|
3
|
+
|
|
4
|
+
// iOS (HIG) Button. Metro resolves this file on iOS; the docs import it for preview.
|
|
5
|
+
export const Button = createButton(iosSkin);
|
|
6
|
+
export type { ButtonProps } from "./button.shared.js";
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Buttons
|
|
2
|
+
|
|
3
|
+
Six variants × four sizes × disabled / focus / hover states. Always semantic: variant communicates intent (default = primary action, destructive = irreversible, ghost = chrome).
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Button primary>Save changes</Button>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
### Variant - outline
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Button outline>Save changes</Button>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Variant - secondary
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Button secondary>Save changes</Button>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Variant - ghost
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<Button ghost>Save changes</Button>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Variant - destructive
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Button destructive>Save changes</Button>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Variant - link
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Button link>Save changes</Button>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Size - sm
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Button primary small>Save changes</Button>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Size - lg
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Button primary large>Save changes</Button>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Size - icon
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Button primary icon>+</Button>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Disabled
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<Button primary disabled>Save changes</Button>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### With icon
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<Button primary>+ Save changes</Button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Do & Don't
|
|
74
|
+
|
|
75
|
+
### Default (primary)
|
|
76
|
+
|
|
77
|
+
**Do** — One clear primary action; everything else is supporting.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
81
|
+
<Button primary>Save</Button>
|
|
82
|
+
<Button outline>Cancel</Button>
|
|
83
|
+
</View>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Don't** — Multiple primaries compete; nothing stands out.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
90
|
+
<Button primary>Save</Button>
|
|
91
|
+
<Button primary>Apply</Button>
|
|
92
|
+
<Button primary>Continue</Button>
|
|
93
|
+
</View>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Outline
|
|
97
|
+
|
|
98
|
+
**Do** — Promote the main action to default; keep the rest outline.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
102
|
+
<Button primary>Publish</Button>
|
|
103
|
+
<Button outline>Save draft</Button>
|
|
104
|
+
<Button outline>Schedule</Button>
|
|
105
|
+
</View>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Don't** — All-outline leaves no signal which action is primary.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
112
|
+
<Button outline>Save</Button>
|
|
113
|
+
<Button outline>Publish</Button>
|
|
114
|
+
<Button outline>Schedule</Button>
|
|
115
|
+
</View>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Secondary
|
|
119
|
+
|
|
120
|
+
**Do** — Default for the primary action; secondary for the next one down.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
124
|
+
<Button primary>Create account</Button>
|
|
125
|
+
<Button secondary>Import instead</Button>
|
|
126
|
+
</View>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Don't** — A secondary button as the main call to action under-sells it.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<Button secondary>Create account</Button>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Ghost
|
|
136
|
+
|
|
137
|
+
**Do** — Use ghost for tertiary and toolbar actions; keep the CTA filled.
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
141
|
+
<Button ghost>Cancel</Button>
|
|
142
|
+
<Button primary>Save changes</Button>
|
|
143
|
+
</View>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Don't** — A ghost button is too quiet to carry the primary action.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<Button ghost>Save changes</Button>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Destructive
|
|
153
|
+
|
|
154
|
+
**Do** — Reserve the destructive variant for irreversible actions like delete.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
158
|
+
<Button primary>Save changes</Button>
|
|
159
|
+
<Button destructive>Delete account</Button>
|
|
160
|
+
</View>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Don't** — Red on a safe action cries wolf; users learn to ignore it.
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<Button destructive>Save changes</Button>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Link
|
|
170
|
+
|
|
171
|
+
**Do** — Link variant for inline navigation; a filled button for the submit.
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
|
|
175
|
+
<Button primary>Submit</Button>
|
|
176
|
+
<Button link>Learn more</Button>
|
|
177
|
+
</View>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Don't** — A link-styled submit doesn't look pressable and gets lost.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<Button link>Submit form</Button>
|
|
184
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { ActivityIndicator, type GestureResponderEvent } from "react-native";
|
|
3
|
+
import { Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
4
|
+
import { type ButtonSkin, type Intent, type Size, FG_TOKEN } from "./button.styles.js";
|
|
5
|
+
|
|
6
|
+
// Shared Button shell. The structure (Pressable + optional loading spinner +
|
|
7
|
+
// label), the accessibility, and the intent/size precedence live here once; a
|
|
8
|
+
// platform file supplies only its skin (shape, sizing, label weight, press
|
|
9
|
+
// feedback) and calls createButton.
|
|
10
|
+
|
|
11
|
+
export interface ButtonProps {
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
14
|
+
// Intent (pick one; default is the primary action).
|
|
15
|
+
primary?: boolean;
|
|
16
|
+
secondary?: boolean;
|
|
17
|
+
destructive?: boolean;
|
|
18
|
+
outline?: boolean;
|
|
19
|
+
ghost?: boolean;
|
|
20
|
+
link?: boolean;
|
|
21
|
+
// Size (pick one).
|
|
22
|
+
small?: boolean;
|
|
23
|
+
large?: boolean;
|
|
24
|
+
icon?: boolean;
|
|
25
|
+
// Layout and state.
|
|
26
|
+
block?: boolean;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
/** Escape hatch for layout/positioning composition (margins, alignment). */
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Intent precedence when more than one is passed: first match wins.
|
|
34
|
+
function intentOf(p: ButtonProps): Intent {
|
|
35
|
+
if (p.primary) return "primary";
|
|
36
|
+
if (p.destructive) return "destructive";
|
|
37
|
+
if (p.secondary) return "secondary";
|
|
38
|
+
if (p.outline) return "outline";
|
|
39
|
+
if (p.ghost) return "ghost";
|
|
40
|
+
if (p.link) return "link";
|
|
41
|
+
return "primary";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Size precedence when more than one is passed: first match wins.
|
|
45
|
+
function sizeOf(p: ButtonProps): Size {
|
|
46
|
+
if (p.small) return "small";
|
|
47
|
+
if (p.large) return "large";
|
|
48
|
+
return "base";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Build a Button component from a platform skin. */
|
|
52
|
+
export function createButton(skin: ButtonSkin) {
|
|
53
|
+
return function Button(props: ButtonProps) {
|
|
54
|
+
const { children, onPress, loading, disabled, block, icon, style } = props;
|
|
55
|
+
const { tokens } = useTheme();
|
|
56
|
+
const intent = intentOf(props);
|
|
57
|
+
const size = sizeOf(props);
|
|
58
|
+
|
|
59
|
+
const container = skin.container(tokens, intent, size, { icon: !!icon, block: !!block, dim: !!(disabled || loading) });
|
|
60
|
+
const ripple = skin.ripple ? skin.ripple(tokens, intent) : undefined;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Pressable
|
|
64
|
+
onPress={onPress}
|
|
65
|
+
disabled={disabled || loading}
|
|
66
|
+
accessibilityRole="button"
|
|
67
|
+
android_ripple={ripple}
|
|
68
|
+
style={({ pressed }) => [
|
|
69
|
+
container,
|
|
70
|
+
skin.pressedOpacity != null && pressed ? { opacity: skin.pressedOpacity } : null,
|
|
71
|
+
style,
|
|
72
|
+
]}
|
|
73
|
+
>
|
|
74
|
+
{loading ? <ActivityIndicator size="small" color={tokens[FG_TOKEN[intent]]} /> : null}
|
|
75
|
+
{children != null ? <Text style={skin.label(tokens, intent, size)}>{children}</Text> : null}
|
|
76
|
+
</Pressable>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Button skins, one per platform. The BRAND survives on every platform
|
|
5
|
+
// (fills/labels use the indigo `primary` and the semantic tokens, never a platform
|
|
6
|
+
// default); only the native SHAPE, sizing, label weight, and press feedback change:
|
|
7
|
+
// iOS (HIG / iOS 26+ Liquid Glass): capsule (fully rounded), semibold SF-scale label, dim-on-press.
|
|
8
|
+
// Android (Material 3): fully-rounded pill, medium label, flat, ripple.
|
|
9
|
+
// Web: the established Canvas look (rounded-md, medium label, opacity press).
|
|
10
|
+
|
|
11
|
+
export type Intent = "primary" | "secondary" | "destructive" | "outline" | "ghost" | "link";
|
|
12
|
+
export type Size = "small" | "base" | "large";
|
|
13
|
+
|
|
14
|
+
export interface ButtonSkinOpts {
|
|
15
|
+
icon: boolean;
|
|
16
|
+
block: boolean;
|
|
17
|
+
/** disabled or loading: dim the control. */
|
|
18
|
+
dim: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ButtonSkin {
|
|
22
|
+
container: (t: ColorTokens, intent: Intent, size: Size, opts: ButtonSkinOpts) => ViewStyle;
|
|
23
|
+
label: (t: ColorTokens, intent: Intent, size: Size) => TextStyle;
|
|
24
|
+
/** iOS/web dim the fill on press; Android uses a ripple instead (null). */
|
|
25
|
+
pressedOpacity: number | null;
|
|
26
|
+
ripple: ((t: ColorTokens, intent: Intent) => { color: string; borderless: boolean }) | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// --- shared brand mapping (identical across platforms) ----------------------
|
|
30
|
+
|
|
31
|
+
/** Foreground token per intent: the label color and the loading-spinner color. */
|
|
32
|
+
export const FG_TOKEN: Record<Intent, keyof ColorTokens> = {
|
|
33
|
+
primary: "primary-foreground",
|
|
34
|
+
secondary: "secondary-foreground",
|
|
35
|
+
destructive: "destructive-foreground",
|
|
36
|
+
outline: "foreground",
|
|
37
|
+
ghost: "foreground",
|
|
38
|
+
link: "primary",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const DARK_FILL = new Set<Intent>(["primary", "destructive"]);
|
|
42
|
+
|
|
43
|
+
// Container fill/border per intent (brand colors, shared by all platforms).
|
|
44
|
+
function fill(t: ColorTokens, intent: Intent): ViewStyle {
|
|
45
|
+
switch (intent) {
|
|
46
|
+
case "primary": return { backgroundColor: t.primary };
|
|
47
|
+
case "secondary": return { backgroundColor: t.secondary };
|
|
48
|
+
case "destructive": return { backgroundColor: t.destructive };
|
|
49
|
+
case "outline": return { backgroundColor: "transparent", borderWidth: 1, borderColor: t.input };
|
|
50
|
+
case "ghost": return { backgroundColor: "transparent" };
|
|
51
|
+
case "link": return { backgroundColor: "transparent" };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Label color (+ underline for link) per intent.
|
|
56
|
+
function labelColor(t: ColorTokens, intent: Intent): TextStyle {
|
|
57
|
+
switch (intent) {
|
|
58
|
+
case "primary": return { color: t["primary-foreground"] };
|
|
59
|
+
case "secondary": return { color: t["secondary-foreground"] };
|
|
60
|
+
case "destructive": return { color: t["destructive-foreground"] };
|
|
61
|
+
case "outline": return { color: t.foreground };
|
|
62
|
+
case "ghost": return { color: t.foreground };
|
|
63
|
+
case "link": return { color: t.primary, textDecorationLine: "underline" };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Android ripple over a fill: a light ripple on dark fills, a dark one on light/
|
|
68
|
+
// transparent fills, so the Material press feedback reads on every variant.
|
|
69
|
+
function androidRipple(_t: ColorTokens, intent: Intent) {
|
|
70
|
+
return { color: DARK_FILL.has(intent) ? "rgba(255, 255, 255, 0.24)" : "rgba(0, 0, 0, 0.10)", borderless: false };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ROW: ViewStyle = { flexDirection: "row", alignItems: "center", justifyContent: "center" };
|
|
74
|
+
|
|
75
|
+
// ---------- Web: the established Canvas look ----------
|
|
76
|
+
export const webSkin: ButtonSkin = {
|
|
77
|
+
container: (t, intent, size, o) => ({
|
|
78
|
+
...ROW,
|
|
79
|
+
gap: 8,
|
|
80
|
+
borderRadius: 6,
|
|
81
|
+
...(o.icon
|
|
82
|
+
? sq(size === "small" ? 32 : size === "large" ? 48 : 40)
|
|
83
|
+
: size === "small" ? { paddingHorizontal: 12, paddingVertical: 6 }
|
|
84
|
+
: size === "large" ? { paddingHorizontal: 24, paddingVertical: 12 }
|
|
85
|
+
: { paddingHorizontal: 16, paddingVertical: 8 }),
|
|
86
|
+
...fill(t, intent),
|
|
87
|
+
...(o.block ? { width: "100%" } : null),
|
|
88
|
+
...(o.dim ? { opacity: 0.5 } : null),
|
|
89
|
+
}),
|
|
90
|
+
label: (t, intent, size) => ({
|
|
91
|
+
fontWeight: "500",
|
|
92
|
+
...(size === "large" ? FS(16, 24) : size === "small" ? FS(12, 16) : FS(14, 20)),
|
|
93
|
+
...labelColor(t, intent),
|
|
94
|
+
}),
|
|
95
|
+
pressedOpacity: 0.9,
|
|
96
|
+
ripple: null,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// ---------- iOS (HIG / iOS 26+ Liquid Glass): capsule, semibold, dim on press ----------
|
|
100
|
+
export const iosSkin: ButtonSkin = {
|
|
101
|
+
container: (t, intent, size, o) => ({
|
|
102
|
+
...ROW,
|
|
103
|
+
gap: 6,
|
|
104
|
+
borderRadius: 9999, // iOS 27 prominent buttons are capsules (full pill at every size); icon = circle
|
|
105
|
+
...(o.icon
|
|
106
|
+
? sq(size === "small" ? 36 : size === "large" ? 52 : 44)
|
|
107
|
+
: size === "small" ? { paddingHorizontal: 14, paddingVertical: 8 }
|
|
108
|
+
: size === "large" ? { paddingHorizontal: 24, paddingVertical: 15 }
|
|
109
|
+
: { paddingHorizontal: 20, paddingVertical: 12 }),
|
|
110
|
+
...fill(t, intent),
|
|
111
|
+
...(o.block ? { width: "100%" } : null),
|
|
112
|
+
...(o.dim ? { opacity: 0.5 } : null),
|
|
113
|
+
}),
|
|
114
|
+
label: (t, intent, size) => ({
|
|
115
|
+
fontWeight: "600",
|
|
116
|
+
...(size === "small" ? FS(15, 20) : FS(17, 22)),
|
|
117
|
+
...labelColor(t, intent),
|
|
118
|
+
}),
|
|
119
|
+
pressedOpacity: 0.8,
|
|
120
|
+
ripple: null,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// ---------- Android (Material 3 filled): pill, medium label, flat, ripple ----------
|
|
124
|
+
export const androidSkin: ButtonSkin = {
|
|
125
|
+
container: (t, intent, size, o) => ({
|
|
126
|
+
...ROW,
|
|
127
|
+
gap: 8,
|
|
128
|
+
borderRadius: 9999, // M3 filled button = fully rounded (stadium); icon = circle
|
|
129
|
+
...(o.icon
|
|
130
|
+
? sq(size === "small" ? 32 : size === "large" ? 48 : 40)
|
|
131
|
+
: size === "small" ? { paddingHorizontal: 16, paddingVertical: 6 }
|
|
132
|
+
: size === "large" ? { paddingHorizontal: 24, paddingVertical: 13 }
|
|
133
|
+
: { paddingHorizontal: 24, paddingVertical: 10 }),
|
|
134
|
+
...fill(t, intent),
|
|
135
|
+
...(o.block ? { width: "100%" } : null),
|
|
136
|
+
...(o.dim ? { opacity: 0.38 } : null), // M3 disabled opacity
|
|
137
|
+
}),
|
|
138
|
+
label: (t, intent, size) => ({
|
|
139
|
+
fontWeight: "500",
|
|
140
|
+
...(size === "small" ? FS(13, 18) : FS(14, 20)),
|
|
141
|
+
...labelColor(t, intent),
|
|
142
|
+
}),
|
|
143
|
+
pressedOpacity: null,
|
|
144
|
+
ripple: androidRipple,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
function sq(d: number): ViewStyle {
|
|
148
|
+
return { width: d, height: d };
|
|
149
|
+
}
|
|
150
|
+
function FS(fontSize: number, lineHeight: number): TextStyle {
|
|
151
|
+
return { fontSize, lineHeight };
|
|
152
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createButton } from "./button.shared.js";
|
|
2
|
+
import { webSkin } from "./button.styles.js";
|
|
3
|
+
|
|
4
|
+
// Web Button (the base; Metro falls back to it on native, web bundlers resolve it).
|
|
5
|
+
export const Button = createButton(webSkin);
|
|
6
|
+
export type { ButtonProps } from "./button.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createButtonGroup } from "./button-group.shared.js";
|
|
2
|
+
import { androidSkin } from "./button-group.styles.js";
|
|
3
|
+
|
|
4
|
+
// Material 3 SegmentedButton ButtonGroup. Metro resolves this file on Android; the docs import it for preview.
|
|
5
|
+
export const ButtonGroup = createButtonGroup(androidSkin);
|
|
6
|
+
export type { ButtonGroupProps } from "./button-group.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createButtonGroup } from "./button-group.shared.js";
|
|
2
|
+
import { iosSkin } from "./button-group.styles.js";
|
|
3
|
+
|
|
4
|
+
// iOS (UISegmentedControl) ButtonGroup. Metro resolves this file on iOS; the docs import it for preview.
|
|
5
|
+
export const ButtonGroup = createButtonGroup(iosSkin);
|
|
6
|
+
export type { ButtonGroupProps } from "./button-group.shared.js";
|