@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,113 @@
|
|
|
1
|
+
import { View, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
2
|
+
import { Card } from "../card/card.js";
|
|
3
|
+
import { Button } from "../../atoms/button/button.js";
|
|
4
|
+
import { Switch } from "../../atoms/switch/switch.js";
|
|
5
|
+
import * as s from "./action-panels.styles.js";
|
|
6
|
+
import { type Tone, type Layout } from "./action-panels.styles.js";
|
|
7
|
+
|
|
8
|
+
// An action panel is a settings card: a headline and a line of consequence copy
|
|
9
|
+
// on one side, and a single action (a Button) that acts on it. It surfaces one
|
|
10
|
+
// decision at a time, the safe-default version always pairing the action with the
|
|
11
|
+
// copy that explains the stakes.
|
|
12
|
+
//
|
|
13
|
+
// Boolean-prop API, grouped by axis, first-match precedence within an axis
|
|
14
|
+
// (mirrors Button's intentOf):
|
|
15
|
+
//
|
|
16
|
+
// - Tone: `destructive` paints the headline red and renders a destructive
|
|
17
|
+
// (red) Button; omit for the neutral tone, where the action is a primary
|
|
18
|
+
// Button. This is the "danger zone" switch.
|
|
19
|
+
// - Layout (pick one): `inline` floats the action to the right of the copy, the
|
|
20
|
+
// two reading as one side-by-side row; omit for the default, where the action
|
|
21
|
+
// sits on its own line below the copy.
|
|
22
|
+
// - Affordance: `toggle` makes the action an on/off Switch (its state is
|
|
23
|
+
// `checked`) instead of a Button; the panel reads as a setting row, always
|
|
24
|
+
// laid out inline with the Switch pinned to the right. Omit for the default
|
|
25
|
+
// Button action.
|
|
26
|
+
//
|
|
27
|
+
// The axes are orthogonal: `<ActionPanel destructive inline />` is a red
|
|
28
|
+
// action sitting to the right of its danger copy, and `<ActionPanel toggle
|
|
29
|
+
// checked title="..." description="..." />` is a settings row whose switch is on.
|
|
30
|
+
|
|
31
|
+
export interface ActionPanelProps {
|
|
32
|
+
/** The headline: what the action acts on. */
|
|
33
|
+
title?: string;
|
|
34
|
+
/** The consequence copy beneath the title: what happens when the action fires. */
|
|
35
|
+
description?: string;
|
|
36
|
+
/** The action button label. */
|
|
37
|
+
actionLabel?: string;
|
|
38
|
+
/** Fired when the action button is pressed. */
|
|
39
|
+
onAction?: () => void;
|
|
40
|
+
// Tone (omit for the neutral, primary-action default).
|
|
41
|
+
destructive?: boolean;
|
|
42
|
+
// Layout (pick one; default stacks the action below the copy).
|
|
43
|
+
inline?: boolean;
|
|
44
|
+
// Affordance: render the action as an on/off Switch instead of a Button. The
|
|
45
|
+
// panel always lays out inline in this mode.
|
|
46
|
+
toggle?: boolean;
|
|
47
|
+
/** The Switch on/off state when `toggle` is set. */
|
|
48
|
+
checked?: boolean;
|
|
49
|
+
/** Fired with the next checked value when the toggle Switch is flipped. */
|
|
50
|
+
onToggle?: (next: boolean) => void;
|
|
51
|
+
/** Escape hatch for layout/positioning composition (mainly width). */
|
|
52
|
+
style?: StyleProp<ViewStyle>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Tone precedence when more than one flag is passed: first match wins.
|
|
56
|
+
function toneOf(p: ActionPanelProps): Tone {
|
|
57
|
+
if (p.destructive) return "destructive";
|
|
58
|
+
return "neutral";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Layout precedence when more than one flag is passed: first match wins.
|
|
62
|
+
function layoutOf(p: ActionPanelProps): Layout {
|
|
63
|
+
if (p.inline) return "inline";
|
|
64
|
+
return "stacked";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function ActionPanel(props: ActionPanelProps) {
|
|
68
|
+
const { title, description, actionLabel, onAction, toggle, checked, onToggle, style } = props;
|
|
69
|
+
const { tokens, dark } = useTheme();
|
|
70
|
+
const tone = toneOf(props);
|
|
71
|
+
// The toggle affordance always reads as an inline settings row.
|
|
72
|
+
const layout = toggle ? "inline" : layoutOf(props);
|
|
73
|
+
|
|
74
|
+
// The copy block: title above its consequence line. In the inline layout it
|
|
75
|
+
// grows to push the action to the right; stacked, it sits above the action.
|
|
76
|
+
const copy = (
|
|
77
|
+
<View style={[s.copyBlock, layout === "inline" ? s.copyGrow : null]}>
|
|
78
|
+
{title != null ? <Text style={s.titleText(tokens, dark, tone)}>{title}</Text> : null}
|
|
79
|
+
{description != null ? <Text style={s.descriptionText(tokens)}>{description}</Text> : null}
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// The action. In toggle mode it is an on/off Switch pinned to the right;
|
|
84
|
+
// otherwise a destructive (red) Button in the danger zone or a primary Button
|
|
85
|
+
// otherwise, small to sit comfortably inside the panel.
|
|
86
|
+
const action = toggle ? (
|
|
87
|
+
<View style={s.actionPinned}>
|
|
88
|
+
<Switch checked={checked} onValueChange={onToggle} />
|
|
89
|
+
</View>
|
|
90
|
+
) : actionLabel != null ? (
|
|
91
|
+
<View style={layout === "inline" ? s.actionPinned : s.actionStacked}>
|
|
92
|
+
<Button small destructive={tone === "destructive"} primary={tone !== "destructive"} onPress={onAction}>
|
|
93
|
+
{actionLabel}
|
|
94
|
+
</Button>
|
|
95
|
+
</View>
|
|
96
|
+
) : null;
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Card padded style={[s.cardWidth, style]}>
|
|
100
|
+
{layout === "inline" ? (
|
|
101
|
+
<View style={s.inlineBody}>
|
|
102
|
+
{copy}
|
|
103
|
+
{action}
|
|
104
|
+
</View>
|
|
105
|
+
) : (
|
|
106
|
+
<View style={s.stackedBody}>
|
|
107
|
+
{copy}
|
|
108
|
+
{action}
|
|
109
|
+
</View>
|
|
110
|
+
)}
|
|
111
|
+
</Card>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Alerts
|
|
2
|
+
|
|
3
|
+
Inline notification banners: info, success, warning, and error, plus a full-width announcement bar. For a blocking confirmation prompt, see Alert Dialog.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Alert
|
|
9
|
+
info
|
|
10
|
+
icon="ℹ"
|
|
11
|
+
title="Heads up"
|
|
12
|
+
description="Maintenance window scheduled for Sunday 2:00 UTC."
|
|
13
|
+
dismissible
|
|
14
|
+
/>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Variants
|
|
18
|
+
|
|
19
|
+
### Variant - success
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Alert
|
|
23
|
+
success
|
|
24
|
+
icon="✓"
|
|
25
|
+
title="All set"
|
|
26
|
+
description="Your changes have been saved successfully."
|
|
27
|
+
dismissible
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Variant - warning
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Alert
|
|
35
|
+
warning
|
|
36
|
+
icon="⚠"
|
|
37
|
+
title="Action required"
|
|
38
|
+
description="Your trial expires in 3 days."
|
|
39
|
+
dismissible
|
|
40
|
+
/>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Variant - destructive
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Alert
|
|
47
|
+
error
|
|
48
|
+
icon="✕"
|
|
49
|
+
title="Something went wrong"
|
|
50
|
+
description="Could not save your changes. Please try again."
|
|
51
|
+
dismissible
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Do & Don't
|
|
56
|
+
|
|
57
|
+
### info
|
|
58
|
+
|
|
59
|
+
**Do** — Reserve info for passive, non-urgent context (notices, tips); escalate to warning or destructive when action is required.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<Alert info icon="ℹ" title="Heads up" description="Maintenance window scheduled for Sunday 2:00 UTC." />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Don't** — Dressing an act-now message in the neutral info tone hides the urgency; users skim past it like an FYI.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Alert info icon="ℹ" title="Trial expires today" description="Upgrade now or you'll lose access to your projects." />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### success
|
|
72
|
+
|
|
73
|
+
**Do** — Make confirmations transient: auto-dismiss or give a Dismiss control so the success state clears once acknowledged.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Alert success icon="✓" title="Saved" description="Your changes have been saved successfully.">
|
|
77
|
+
<View style={{ marginTop: 12, flexDirection: "row", gap: 8 }}>
|
|
78
|
+
<Button ghost small>Dismiss</Button>
|
|
79
|
+
</View>
|
|
80
|
+
</Alert>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Don't** — A success banner pinned with no way to dismiss it lingers as visual noise long after the action is done.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
<Alert success icon="✓" title="Saved" description="Your changes have been saved successfully." />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### warning
|
|
90
|
+
|
|
91
|
+
**Do** — State the consequence, the deadline, and the action: name what's wrong and give a button to resolve it.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Alert warning icon="⚠" title="Action required" description="Your trial expires in 3 days. Upgrade to keep your projects.">
|
|
95
|
+
<View style={{ marginTop: 12, flexDirection: "row", gap: 8 }}>
|
|
96
|
+
<Button primary small>Upgrade plan</Button>
|
|
97
|
+
</View>
|
|
98
|
+
</Alert>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Don't** — A warning with no specifics or next step leaves the user guessing what to fix and by when.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<Alert warning icon="⚠" title="Action required" description="Something needs your attention." />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### destructive
|
|
108
|
+
|
|
109
|
+
**Do** — Match the variant to the severity: reserve destructive for genuine failures, success for confirmations.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<Alert error icon="✕" title="Something went wrong" description="Could not save your changes. Please try again." />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Don't** — Using the destructive variant for non-errors cries wolf; users learn to tune out red and miss real failures.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
<Alert error icon="✕" title="Saved" description="Your changes have been saved successfully." />
|
|
119
|
+
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, palette } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Alert styles. The toned alerts (info / success / warning / error)
|
|
5
|
+
// draw from the Tailwind palette: a soft 50/200 surface with a 600/700/800 type
|
|
6
|
+
// ramp in light mode, flipping to a 950/800 surface with a 200/300/400 ramp in
|
|
7
|
+
// dark mode (the old dark: variant, resolved by branching on `dark`). The neutral
|
|
8
|
+
// default rides the semantic card / border / foreground tokens so it follows the
|
|
9
|
+
// theme (including glass) on its own.
|
|
10
|
+
|
|
11
|
+
export type Tone = "info" | "success" | "warning" | "error" | "neutral";
|
|
12
|
+
|
|
13
|
+
// flex-row items-start gap-3 rounded-lg border px-4 py-3
|
|
14
|
+
export const alertBase: ViewStyle = {
|
|
15
|
+
flexDirection: "row",
|
|
16
|
+
alignItems: "flex-start",
|
|
17
|
+
gap: 12,
|
|
18
|
+
borderRadius: 8,
|
|
19
|
+
borderWidth: 1,
|
|
20
|
+
paddingHorizontal: 16,
|
|
21
|
+
paddingVertical: 12,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// flex-1 gap-1 — the content column beside the icon.
|
|
25
|
+
export const content: ViewStyle = { flexGrow: 1, flexShrink: 1, flexBasis: "0%", gap: 4 };
|
|
26
|
+
|
|
27
|
+
// text-base leading-5 — the leading glyph.
|
|
28
|
+
export const iconType: TextStyle = { fontSize: 16, lineHeight: 20 };
|
|
29
|
+
|
|
30
|
+
// text-sm font-semibold — the title.
|
|
31
|
+
export const titleType: TextStyle = { fontSize: 14, lineHeight: 20, fontWeight: "600" };
|
|
32
|
+
|
|
33
|
+
// text-sm — the description / body.
|
|
34
|
+
export const bodyType: TextStyle = { fontSize: 14, lineHeight: 20 };
|
|
35
|
+
|
|
36
|
+
// -mr-1 -mt-0.5 h-6 w-6 items-center justify-center rounded-md
|
|
37
|
+
// (active:opacity-70 is applied by the component's Pressable).
|
|
38
|
+
export const dismissButton: ViewStyle = {
|
|
39
|
+
marginRight: -4,
|
|
40
|
+
marginTop: -2,
|
|
41
|
+
height: 24,
|
|
42
|
+
width: 24,
|
|
43
|
+
alignItems: "center",
|
|
44
|
+
justifyContent: "center",
|
|
45
|
+
borderRadius: 6,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// text-base leading-none — the dismiss glyph.
|
|
49
|
+
export const dismissType: TextStyle = { fontSize: 16, lineHeight: 16 };
|
|
50
|
+
|
|
51
|
+
// The palette hue per toned alert (neutral rides the semantic tokens).
|
|
52
|
+
const TONE_HUE: Record<Exclude<Tone, "neutral">, string> = {
|
|
53
|
+
info: "blue",
|
|
54
|
+
success: "green",
|
|
55
|
+
warning: "amber",
|
|
56
|
+
error: "red",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Container border + fill. Light: 200 border / 50 surface; dark: 800 / 950.
|
|
60
|
+
// Neutral: semantic card surface with the border token.
|
|
61
|
+
export function container(tokens: ColorTokens, dark: boolean, tone: Tone): ViewStyle {
|
|
62
|
+
if (tone === "neutral") return { borderColor: tokens.border, backgroundColor: tokens.card };
|
|
63
|
+
const hue = TONE_HUE[tone];
|
|
64
|
+
return dark
|
|
65
|
+
? { borderColor: palette[`${hue}-800`], backgroundColor: palette[`${hue}-950`] }
|
|
66
|
+
: { borderColor: palette[`${hue}-200`], backgroundColor: palette[`${hue}-50`] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Icon color. Light: 600; dark: 400. Neutral: muted-foreground.
|
|
70
|
+
export function iconColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
|
|
71
|
+
if (tone === "neutral") return { color: tokens["muted-foreground"] };
|
|
72
|
+
const hue = TONE_HUE[tone];
|
|
73
|
+
return { color: dark ? palette[`${hue}-400`] : palette[`${hue}-600`] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Title color. Light: 800; dark: 200. Neutral: foreground.
|
|
77
|
+
export function titleColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
|
|
78
|
+
if (tone === "neutral") return { color: tokens.foreground };
|
|
79
|
+
const hue = TONE_HUE[tone];
|
|
80
|
+
return { color: dark ? palette[`${hue}-200`] : palette[`${hue}-800`] };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Body color. Light: 700; dark: 300. Neutral: muted-foreground.
|
|
84
|
+
export function bodyColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
|
|
85
|
+
if (tone === "neutral") return { color: tokens["muted-foreground"] };
|
|
86
|
+
const hue = TONE_HUE[tone];
|
|
87
|
+
return { color: dark ? palette[`${hue}-300`] : palette[`${hue}-700`] };
|
|
88
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { View, Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
3
|
+
import * as s from "./alert.styles.js";
|
|
4
|
+
import { type Tone } from "./alert.styles.js";
|
|
5
|
+
|
|
6
|
+
// A bordered banner that surfaces an inline notification: a leading icon glyph,
|
|
7
|
+
// a bold title, and a description. Configured by a tone axis (info / success /
|
|
8
|
+
// warning / error, plus a neutral default), and an optional `icon` glyph.
|
|
9
|
+
//
|
|
10
|
+
// Boolean-prop API: one boolean per tone, first-match precedence within the
|
|
11
|
+
// axis (mirrors Button's intentOf / Badge's statusOf). Each tone is theme-aware:
|
|
12
|
+
// a soft 50/200 surface with a 600/700/800 type ramp in light mode, and a
|
|
13
|
+
// 950/800 surface with a 200/300/400 ramp in dark mode (branching on the active
|
|
14
|
+
// scheme). The neutral default uses the semantic card / border / foreground tokens.
|
|
15
|
+
|
|
16
|
+
export interface AlertProps {
|
|
17
|
+
// Content.
|
|
18
|
+
title?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
// A leading glyph (a single Text character; the foundation has no icon set).
|
|
21
|
+
icon?: ReactNode;
|
|
22
|
+
// Tone (pick one; omit for the neutral default).
|
|
23
|
+
info?: boolean;
|
|
24
|
+
success?: boolean;
|
|
25
|
+
warning?: boolean;
|
|
26
|
+
error?: boolean;
|
|
27
|
+
/** Shows a trailing dismiss control. */
|
|
28
|
+
dismissible?: boolean;
|
|
29
|
+
/** Fired when the dismiss control is pressed. */
|
|
30
|
+
onDismiss?: () => void;
|
|
31
|
+
children?: ReactNode;
|
|
32
|
+
/** Escape hatch for layout/positioning composition (width, margins). */
|
|
33
|
+
style?: StyleProp<ViewStyle>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Tone precedence when more than one is passed: first match wins.
|
|
37
|
+
function toneOf(p: AlertProps): Tone {
|
|
38
|
+
if (p.error) return "error";
|
|
39
|
+
if (p.warning) return "warning";
|
|
40
|
+
if (p.success) return "success";
|
|
41
|
+
if (p.info) return "info";
|
|
42
|
+
return "neutral";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function Alert(props: AlertProps) {
|
|
46
|
+
const { title, description, icon, children, dismissible, onDismiss, style } = props;
|
|
47
|
+
const { tokens, dark } = useTheme();
|
|
48
|
+
const tone = toneOf(props);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<View style={[s.alertBase, s.container(tokens, dark, tone), style]}>
|
|
52
|
+
{icon != null ? <Text style={[s.iconType, s.iconColor(tokens, dark, tone)]}>{icon}</Text> : null}
|
|
53
|
+
<View style={s.content}>
|
|
54
|
+
{title != null && title !== "" ? (
|
|
55
|
+
<Text style={[s.titleType, s.titleColor(tokens, dark, tone)]}>{title}</Text>
|
|
56
|
+
) : null}
|
|
57
|
+
{description != null && description !== "" ? (
|
|
58
|
+
<Text style={[s.bodyType, s.bodyColor(tokens, dark, tone)]}>{description}</Text>
|
|
59
|
+
) : null}
|
|
60
|
+
{children}
|
|
61
|
+
</View>
|
|
62
|
+
{dismissible ? (
|
|
63
|
+
<Pressable
|
|
64
|
+
onPress={onDismiss}
|
|
65
|
+
accessibilityRole="button"
|
|
66
|
+
accessibilityLabel="Dismiss"
|
|
67
|
+
style={({ pressed }) => [s.dismissButton, pressed ? { opacity: 0.7 } : null]}
|
|
68
|
+
>
|
|
69
|
+
<Text style={[s.dismissType, s.iconColor(tokens, dark, tone)]}>×</Text>
|
|
70
|
+
</Pressable>
|
|
71
|
+
) : null}
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createAlertDialog } from "./alert-dialog.shared.js";
|
|
2
|
+
import { androidSkin } from "./alert-dialog.styles.js";
|
|
3
|
+
|
|
4
|
+
// Material 3 AlertDialog. Metro resolves this file on Android; the docs import it for preview.
|
|
5
|
+
export const AlertDialog = createAlertDialog(androidSkin);
|
|
6
|
+
export type { AlertDialogProps } from "./alert-dialog.shared.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createAlertDialog } from "./alert-dialog.shared.js";
|
|
2
|
+
import { iosSkin } from "./alert-dialog.styles.js";
|
|
3
|
+
|
|
4
|
+
// iOS (iOS 27 / Liquid Glass alert) AlertDialog. Metro resolves this file on iOS; the docs import it for preview.
|
|
5
|
+
export const AlertDialog = createAlertDialog(iosSkin);
|
|
6
|
+
export type { AlertDialogProps } from "./alert-dialog.shared.js";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Alert Dialog
|
|
2
|
+
|
|
3
|
+
Catalyst-style confirmation dialog: a centered panel over a dimmed, blurred backdrop, with a title, description, optional body, and action buttons. Reserve it for decisions that must block the rest of the app.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<AlertDialog
|
|
9
|
+
title="Delete this identity?"
|
|
10
|
+
description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
|
|
11
|
+
confirmLabel="Delete"
|
|
12
|
+
destructive
|
|
13
|
+
trigger="Delete identity…"
|
|
14
|
+
/>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Variants
|
|
18
|
+
|
|
19
|
+
### Size - xs
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<AlertDialog
|
|
23
|
+
title="Delete this identity?"
|
|
24
|
+
description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
|
|
25
|
+
confirmLabel="Delete"
|
|
26
|
+
destructive
|
|
27
|
+
narrow
|
|
28
|
+
trigger="Delete identity…"
|
|
29
|
+
/>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Size - sm
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<AlertDialog
|
|
36
|
+
title="Delete this identity?"
|
|
37
|
+
description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
|
|
38
|
+
confirmLabel="Delete"
|
|
39
|
+
destructive
|
|
40
|
+
small
|
|
41
|
+
trigger="Delete identity…"
|
|
42
|
+
/>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Size - lg
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<AlertDialog
|
|
49
|
+
title="Delete this identity?"
|
|
50
|
+
description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
|
|
51
|
+
confirmLabel="Delete"
|
|
52
|
+
destructive
|
|
53
|
+
large
|
|
54
|
+
trigger="Delete identity…"
|
|
55
|
+
/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Body field
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<AlertDialog
|
|
62
|
+
title="Delete this identity?"
|
|
63
|
+
description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
|
|
64
|
+
confirmLabel="Delete"
|
|
65
|
+
destructive
|
|
66
|
+
withInput
|
|
67
|
+
trigger="Delete identity…"
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Do & Don't
|
|
72
|
+
|
|
73
|
+
### Reserve the dialog for blocking decisions
|
|
74
|
+
|
|
75
|
+
**Do** — Use an inline banner or toast for passive feedback; reserve the dialog for decisions that must be confirmed.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<Alert success title="Saved" description="Your changes have been saved." />
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Don't** — A blocking alert dialog for passive confirmation interrupts the user for no reason.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<AlertDialog open small title="Saved" description="Your changes have been saved." confirmLabel="OK" />
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Name the action on the confirm button
|
|
88
|
+
|
|
89
|
+
**Do** — Label the confirm button with the verb it performs (Delete, Archive, Sign out).
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<AlertDialog open small destructive title="Delete this identity?" cancelLabel="Cancel" confirmLabel="Delete" />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Don't** — Generic Yes / No forces the user to re-read the title to know what they are confirming.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<AlertDialog open small destructive title="Delete this identity?" cancelLabel="No" confirmLabel="Yes" />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### xs
|
|
102
|
+
|
|
103
|
+
**Do** — Reserve xs for a terse one-line question with short button labels and no body content.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<AlertDialog open narrow destructive title="Remove device?" cancelLabel="Cancel" confirmLabel="Remove" />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Don't** — A long title, multi-line description, and wordy buttons get cramped in the xs width and wrap awkwardly.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<AlertDialog open narrow destructive title="Remove this trusted device?" description="It will need to re-authenticate, and any pending background syncs from it will be cancelled the next time it connects." cancelLabel="Cancel" confirmLabel="Remove device" />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### sm
|
|
116
|
+
|
|
117
|
+
**Do** — Use sm for a single short confirmation with a one-line description; move real forms to a full dialog.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
<AlertDialog open small title="Transfer ownership?" description="You will lose admin access to this workspace." cancelLabel="Cancel" confirmLabel="Transfer" />
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Don't** — Packing a multi-field form into sm makes it feel like a form crammed into a confirmation popup.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<View style={{ alignItems: "center", justifyContent: "center", borderRadius: 8, backgroundColor: alpha("#000000", 0.5), padding: 32, minHeight: 200 }}>
|
|
127
|
+
<View style={{ width: "100%", maxWidth: 384, borderRadius: 8, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.popover, padding: 24, ...shadow("xl") }}>
|
|
128
|
+
<Text style={{ fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens["popover-foreground"] }}>Transfer ownership</Text>
|
|
129
|
+
<View style={{ marginTop: 16, gap: 16 }}>
|
|
130
|
+
<View>
|
|
131
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>New owner email</Text>
|
|
132
|
+
<Input placeholder="owner@example.com" />
|
|
133
|
+
</View>
|
|
134
|
+
<View>
|
|
135
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>Reason</Text>
|
|
136
|
+
<Input placeholder="Optional note" />
|
|
137
|
+
</View>
|
|
138
|
+
<View>
|
|
139
|
+
<Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>Type TRANSFER to confirm</Text>
|
|
140
|
+
<Input placeholder="TRANSFER" />
|
|
141
|
+
</View>
|
|
142
|
+
</View>
|
|
143
|
+
<View style={{ flexDirection: "row", justifyContent: "flex-end", gap: 8, marginTop: 24 }}>
|
|
144
|
+
<Button outline small>Cancel</Button>
|
|
145
|
+
<Button primary small>Transfer</Button>
|
|
146
|
+
</View>
|
|
147
|
+
</View>
|
|
148
|
+
</View>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### md
|
|
152
|
+
|
|
153
|
+
**Do** — md is the default home for a typical destructive confirm with a sentence or two of description.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<AlertDialog open destructive title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Don't** — Squeezing a description-carrying confirm into a smaller width crowds the copy against the edges.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<AlertDialog open narrow destructive title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### lg
|
|
166
|
+
|
|
167
|
+
**Do** — Reserve lg for dialogs that earn the width: a body field or a longer explanation to read.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<AlertDialog open large destructive withInput title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Don't** — A bare yes/no in lg leaves a wide, empty panel that reads as heavier than the trivial decision it asks for.
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<AlertDialog open large title="Sign out?" cancelLabel="Cancel" confirmLabel="Sign out" />
|
|
177
|
+
```
|