@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,176 @@
|
|
|
1
|
+
import { type GestureResponderEvent } from "react-native";
|
|
2
|
+
import { View, Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
3
|
+
import * as s from "./code-block.styles.js";
|
|
4
|
+
import { type Variant } from "./code-block.styles.js";
|
|
5
|
+
|
|
6
|
+
// CodeBlock: a muted, rounded surface that shows preformatted code in a
|
|
7
|
+
// monospace face. Newlines in `code` survive verbatim because RN Text honors
|
|
8
|
+
// "\n", so a multi-line string renders as multiple lines without any markup.
|
|
9
|
+
//
|
|
10
|
+
// Boolean-prop API: one boolean per option, grouped by axis, first-match
|
|
11
|
+
// precedence within an axis (mirrors Badge's toneOf). Axes:
|
|
12
|
+
//
|
|
13
|
+
// - Variant (pick one; default is the plain block):
|
|
14
|
+
// `terminal` > `numbered` > `inline` > plain.
|
|
15
|
+
// - plain: the muted code surface, padded, monospace.
|
|
16
|
+
// - terminal: a dark window with a chrome bar (traffic-light dots + a "bash"
|
|
17
|
+
// label) over a dark body, for shell transcripts. A leading "$ " prompt is
|
|
18
|
+
// rendered for command lines.
|
|
19
|
+
// - numbered: the plain surface with a left gutter of right-aligned line
|
|
20
|
+
// numbers, one per line.
|
|
21
|
+
// - inline: a short, single token rendered as an inline pill (rounded bg-muted
|
|
22
|
+
// chip), for code mentioned mid-sentence.
|
|
23
|
+
// - `copy` (orthogonal): show a copy affordance pinned to the top-right corner.
|
|
24
|
+
// Ignored by the inline variant, which is too small to carry one.
|
|
25
|
+
// - `wrap` (orthogonal): let long lines wrap instead of overflowing. Ignored by
|
|
26
|
+
// the inline variant.
|
|
27
|
+
//
|
|
28
|
+
// RN has no font-mono utility, so the monospace face is requested via an inline
|
|
29
|
+
// `style={s.MONO}` on each code Text (the same approach Badge's `mono` uses).
|
|
30
|
+
|
|
31
|
+
export interface CodeBlockProps {
|
|
32
|
+
/** The code to render. Newlines are preserved (RN Text honors "\n"). */
|
|
33
|
+
code?: string;
|
|
34
|
+
/** Optional filename or language label for the header bar. */
|
|
35
|
+
filename?: string;
|
|
36
|
+
language?: string;
|
|
37
|
+
|
|
38
|
+
// Variant (pick one; default is the plain block).
|
|
39
|
+
terminal?: boolean;
|
|
40
|
+
numbered?: boolean;
|
|
41
|
+
inline?: boolean;
|
|
42
|
+
|
|
43
|
+
// Orthogonal modifiers.
|
|
44
|
+
copy?: boolean;
|
|
45
|
+
wrap?: boolean;
|
|
46
|
+
|
|
47
|
+
/** Called when the copy affordance is pressed (text is passed back). */
|
|
48
|
+
onCopy?: (code: string, event: GestureResponderEvent) => void;
|
|
49
|
+
|
|
50
|
+
/** Escape hatch for layout/positioning composition (mainly width, margins). */
|
|
51
|
+
style?: StyleProp<ViewStyle>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Variant precedence when more than one is passed: first match wins.
|
|
55
|
+
function variantOf(p: CodeBlockProps): Variant {
|
|
56
|
+
if (p.terminal) return "terminal";
|
|
57
|
+
if (p.numbered) return "numbered";
|
|
58
|
+
if (p.inline) return "inline";
|
|
59
|
+
return "plain";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const DEFAULT_CODE = 'const theme = getTheme();\nsetTheme(theme === "dark" ? "light" : "dark");';
|
|
63
|
+
|
|
64
|
+
// A small, neutral chip pinned to the top-right corner for the copy affordance.
|
|
65
|
+
function CopyButton({
|
|
66
|
+
text,
|
|
67
|
+
onCopy,
|
|
68
|
+
dark,
|
|
69
|
+
}: {
|
|
70
|
+
text: string;
|
|
71
|
+
onCopy?: (code: string, event: GestureResponderEvent) => void;
|
|
72
|
+
dark?: boolean;
|
|
73
|
+
}) {
|
|
74
|
+
const { tokens } = useTheme();
|
|
75
|
+
const isDark = !!dark;
|
|
76
|
+
return (
|
|
77
|
+
<Pressable
|
|
78
|
+
style={({ pressed }) => [s.copyButton(tokens, isDark), pressed ? { opacity: 0.8 } : null]}
|
|
79
|
+
onPress={(e) => onCopy?.(text, e)}
|
|
80
|
+
accessibilityRole="button"
|
|
81
|
+
accessibilityLabel="Copy code"
|
|
82
|
+
>
|
|
83
|
+
<Text style={s.copyText(tokens, isDark)}>Copy</Text>
|
|
84
|
+
</Pressable>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function CodeBlock(props: CodeBlockProps) {
|
|
89
|
+
const { code = DEFAULT_CODE, filename, language, copy, wrap, onCopy, style } = props;
|
|
90
|
+
const variant = variantOf(props);
|
|
91
|
+
const { tokens } = useTheme();
|
|
92
|
+
const lines = code.split("\n");
|
|
93
|
+
|
|
94
|
+
// Inline: a short token rendered as an inline pill. No header, copy, or wrap.
|
|
95
|
+
if (variant === "inline") {
|
|
96
|
+
return (
|
|
97
|
+
<View style={[s.inlineBox(tokens), style]}>
|
|
98
|
+
<Text style={[s.codeText(tokens), s.MONO, { fontSize: 13 }]}>{code}</Text>
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Terminal: a dark window with a chrome bar over a dark body.
|
|
104
|
+
if (variant === "terminal") {
|
|
105
|
+
const label = language ?? filename ?? "bash";
|
|
106
|
+
return (
|
|
107
|
+
<View style={[s.terminalOuter(tokens), style]}>
|
|
108
|
+
{/* Chrome bar: three traffic-light dots and a faint label. */}
|
|
109
|
+
<View style={s.terminalChrome}>
|
|
110
|
+
<View style={s.trafficDot("red")} />
|
|
111
|
+
<View style={s.trafficDot("amber")} />
|
|
112
|
+
<View style={s.trafficDot("green")} />
|
|
113
|
+
<Text style={[s.terminalLabel, s.MONO]}>{label}</Text>
|
|
114
|
+
</View>
|
|
115
|
+
{/* Body: each line gets a non-selectable "$ " prompt. */}
|
|
116
|
+
<View style={s.terminalBody}>
|
|
117
|
+
{lines.map((line, i) => (
|
|
118
|
+
<View key={i} style={s.terminalRow}>
|
|
119
|
+
<Text style={[s.terminalPrompt, s.MONO]}>{"$ "}</Text>
|
|
120
|
+
<Text style={[s.terminalLine, s.MONO]} numberOfLines={wrap ? undefined : 1}>
|
|
121
|
+
{line}
|
|
122
|
+
</Text>
|
|
123
|
+
</View>
|
|
124
|
+
))}
|
|
125
|
+
</View>
|
|
126
|
+
{copy ? <CopyButton text={code} onCopy={onCopy} dark /> : null}
|
|
127
|
+
</View>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Numbered: the plain surface with a right-aligned line-number gutter.
|
|
132
|
+
if (variant === "numbered") {
|
|
133
|
+
return (
|
|
134
|
+
<View style={[s.relative, style]}>
|
|
135
|
+
<View style={[s.surface(tokens), s.numberedSurface]}>
|
|
136
|
+
{/* Gutter: right-aligned, dimmed line numbers. */}
|
|
137
|
+
<View style={s.numberedGutter}>
|
|
138
|
+
{lines.map((_, i) => (
|
|
139
|
+
<Text key={i} style={[s.codeType, s.gutterText(tokens), s.MONO]}>
|
|
140
|
+
{String(i + 1)}
|
|
141
|
+
</Text>
|
|
142
|
+
))}
|
|
143
|
+
</View>
|
|
144
|
+
{/* Code column: one Text per line so the gutter stays aligned. */}
|
|
145
|
+
<View style={s.numberedCodeCol}>
|
|
146
|
+
{lines.map((line, i) => (
|
|
147
|
+
<Text
|
|
148
|
+
key={i}
|
|
149
|
+
style={[s.codeType, s.codeText(tokens), s.MONO]}
|
|
150
|
+
numberOfLines={wrap ? undefined : 1}
|
|
151
|
+
>
|
|
152
|
+
{line}
|
|
153
|
+
</Text>
|
|
154
|
+
))}
|
|
155
|
+
</View>
|
|
156
|
+
</View>
|
|
157
|
+
{copy ? <CopyButton text={code} onCopy={onCopy} /> : null}
|
|
158
|
+
</View>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Plain (default): the muted code surface, padded, monospace.
|
|
163
|
+
return (
|
|
164
|
+
<View style={[s.relative, style]}>
|
|
165
|
+
<View style={[s.surface(tokens), s.surfacePad]}>
|
|
166
|
+
<Text
|
|
167
|
+
style={[s.codeType, s.codeText(tokens), s.MONO]}
|
|
168
|
+
numberOfLines={wrap ? undefined : lines.length}
|
|
169
|
+
>
|
|
170
|
+
{code}
|
|
171
|
+
</Text>
|
|
172
|
+
</View>
|
|
173
|
+
{copy ? <CopyButton text={code} onCopy={onCopy} /> : null}
|
|
174
|
+
</View>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Description Lists
|
|
2
|
+
|
|
3
|
+
Key-value pairs in stacked, two-column, or inline-edit layouts. Used for detail panels, settings, and profile views.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<DescriptionList
|
|
9
|
+
card
|
|
10
|
+
twoColumn
|
|
11
|
+
divided
|
|
12
|
+
title="Application details"
|
|
13
|
+
subtitle="Personal information and credentials."
|
|
14
|
+
items={[
|
|
15
|
+
{ term: "Full name", value: "Rachel Chen" },
|
|
16
|
+
{ term: "Email", value: "rachel.chen@example.com" },
|
|
17
|
+
{ term: "Role", value: "admin", badge: true },
|
|
18
|
+
{ term: "Status", value: "Active", status: true }
|
|
19
|
+
]}
|
|
20
|
+
/>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Variants
|
|
24
|
+
|
|
25
|
+
### Layout - inline-edit
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<DescriptionList
|
|
29
|
+
card
|
|
30
|
+
twoColumn
|
|
31
|
+
divided
|
|
32
|
+
title="Profile"
|
|
33
|
+
items={[
|
|
34
|
+
{ term: "Name", value: "Rachel Chen", update: true },
|
|
35
|
+
{ term: "Email", value: "rachel.chen@example.com", update: true },
|
|
36
|
+
{ term: "Title", value: "Senior Engineer", update: true }
|
|
37
|
+
]}
|
|
38
|
+
/>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Layout - stacked
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<DescriptionList
|
|
45
|
+
card
|
|
46
|
+
stacked
|
|
47
|
+
items={[
|
|
48
|
+
{ term: "Full name", value: "Rachel Chen" },
|
|
49
|
+
{ term: "Email", value: "rachel.chen@example.com" },
|
|
50
|
+
{ term: "Client ID", value: "clnt_01H2X8K9P3Q7VN4W6R5T0JYMZF", mono: true }
|
|
51
|
+
]}
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Do & Don't
|
|
56
|
+
|
|
57
|
+
### Two-column
|
|
58
|
+
|
|
59
|
+
**Do** — Fix the label column wide enough for the longest term so every value lines up on one edge.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<DescriptionList twoColumn divided style={{ maxWidth: 420 }} items={[
|
|
63
|
+
{ term: "Client identifier", value: "clnt_01H2X8K9P3Q7VN4W6R5T0JYMZF", mono: true },
|
|
64
|
+
{ term: "Status", value: "Active", status: true }
|
|
65
|
+
]} />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Don't** — A too-narrow label column wraps the longest term and knocks the two columns out of alignment.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<View style={{ maxWidth: 420 }}>
|
|
72
|
+
<View style={{ flexDirection: "row", alignItems: "baseline", gap: 16, borderBottomWidth: 1, borderColor: tokens.border, paddingVertical: 12 }}>
|
|
73
|
+
<Text style={{ width: 64, fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Client identifier</Text>
|
|
74
|
+
<Text style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", fontSize: 12.5, fontWeight: "500", color: tokens.foreground, fontFamily: "monospace" }}>clnt_01H2X8K9P3Q7VN4W6R5T0JYMZF</Text>
|
|
75
|
+
</View>
|
|
76
|
+
<View style={{ flexDirection: "row", alignItems: "baseline", gap: 16, paddingVertical: 12 }}>
|
|
77
|
+
<Text style={{ width: 64, fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Status</Text>
|
|
78
|
+
<View style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%" }}>
|
|
79
|
+
<Badge status success>Active</Badge>
|
|
80
|
+
</View>
|
|
81
|
+
</View>
|
|
82
|
+
</View>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Inline-edit
|
|
86
|
+
|
|
87
|
+
**Do** — Give every editable row a trailing Update affordance so the inline editor is discoverable.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<DescriptionList twoColumn style={{ maxWidth: 420 }} items={[
|
|
91
|
+
{ term: "Name", value: "Rachel Chen", update: true },
|
|
92
|
+
{ term: "Email", value: "rachel.chen@example.com", update: true }
|
|
93
|
+
]} />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Don't** — Editable rows that look identical to read-only ones give no hint a value can be changed.
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<DescriptionList twoColumn style={{ maxWidth: 420 }} items={[
|
|
100
|
+
{ term: "Name", value: "Rachel Chen" },
|
|
101
|
+
{ term: "Email", value: "rachel.chen@example.com" }
|
|
102
|
+
]} />
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Stacked
|
|
106
|
+
|
|
107
|
+
**Do** — Keep the label small, uppercase, and muted above a full-weight value so the data stays primary.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<DescriptionList stacked style={{ maxWidth: 320 }} items={[
|
|
111
|
+
{ term: "Full name", value: "Rachel Chen" },
|
|
112
|
+
{ term: "Email", value: "rachel.chen@example.com" }
|
|
113
|
+
]} />
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Don't** — Muting the value and bolding nothing inverts the hierarchy; the label outweighs the data it describes.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<View style={{ maxWidth: 320, gap: 16 }}>
|
|
120
|
+
<View style={{ gap: 4 }}>
|
|
121
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>Full name</Text>
|
|
122
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>Rachel Chen</Text>
|
|
123
|
+
</View>
|
|
124
|
+
<View style={{ gap: 4 }}>
|
|
125
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>Email</Text>
|
|
126
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>rachel.chen@example.com</Text>
|
|
127
|
+
</View>
|
|
128
|
+
</View>
|
|
129
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, shadow } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located DescriptionList styles. Layout-only fragments are static objects;
|
|
5
|
+
// anything that reads a color is a function of the active tokens (so the card
|
|
6
|
+
// surface follows light/dark and reads as glass when the ThemeProvider's surface
|
|
7
|
+
// is "glass", since tokens.card is swapped translucent at the theming level).
|
|
8
|
+
|
|
9
|
+
export type Layout = "inline" | "twoColumn" | "stacked";
|
|
10
|
+
|
|
11
|
+
// Card surface: rounded, bordered, card fill, soft shadow. When a header band is
|
|
12
|
+
// present the card carries no padding (the header and rows supply their own
|
|
13
|
+
// px-6) so the header rule spans the full width; otherwise the card pads itself.
|
|
14
|
+
export function cardSurface(tokens: ColorTokens): ViewStyle {
|
|
15
|
+
return { borderRadius: 8, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card, ...shadow("sm") };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The self-padding applied to a card with no header band (p-6).
|
|
19
|
+
export const cardPad: ViewStyle = { padding: 24 };
|
|
20
|
+
|
|
21
|
+
// gap-3 between rows when there is no header band wrapping them.
|
|
22
|
+
export const stackGap: ViewStyle = { gap: 12 };
|
|
23
|
+
|
|
24
|
+
// With a header, rows sit in their own px-6 group beneath the bordered band.
|
|
25
|
+
export const rowsWrap: ViewStyle = { paddingHorizontal: 24, gap: 12 };
|
|
26
|
+
|
|
27
|
+
// --- term / value text ------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
// Term: small, muted label.
|
|
30
|
+
export function termLabel(tokens: ColorTokens): TextStyle {
|
|
31
|
+
return { fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Stacked term: uppercased and tracked so the label reads as secondary above a
|
|
35
|
+
// full-weight value.
|
|
36
|
+
export function termStacked(tokens: ColorTokens): TextStyle {
|
|
37
|
+
return {
|
|
38
|
+
fontSize: 12,
|
|
39
|
+
lineHeight: 16,
|
|
40
|
+
fontWeight: "500",
|
|
41
|
+
textTransform: "uppercase",
|
|
42
|
+
letterSpacing: 0.4,
|
|
43
|
+
color: tokens["muted-foreground"],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The two-column term's fixed label column (w-40). Lands on a Text.
|
|
48
|
+
export const termColumn: TextStyle = { width: 160 };
|
|
49
|
+
|
|
50
|
+
// Value: full-weight data it describes.
|
|
51
|
+
export function valueLabel(tokens: ColorTokens): TextStyle {
|
|
52
|
+
return { fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The inline value is right-aligned in its row.
|
|
56
|
+
export const valueAlignRight: TextStyle = { textAlign: "right" };
|
|
57
|
+
|
|
58
|
+
// Monospace value face, for tokens, scopes, identifiers.
|
|
59
|
+
export const valueMono: TextStyle = { fontFamily: "monospace" };
|
|
60
|
+
|
|
61
|
+
// --- rows -------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
// Row layout per axis.
|
|
64
|
+
export const rowLayout: Record<Layout, ViewStyle> = {
|
|
65
|
+
inline: { flexDirection: "row", alignItems: "baseline", justifyContent: "space-between", gap: 16 },
|
|
66
|
+
twoColumn: { flexDirection: "row", alignItems: "baseline", gap: 16 },
|
|
67
|
+
stacked: { gap: 4 },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// A divided list pads each row vertically so the rule sits clear of the text.
|
|
71
|
+
export const rowDividedPad: ViewStyle = { paddingBottom: 12 };
|
|
72
|
+
|
|
73
|
+
// The hairline beneath every divided row except the last.
|
|
74
|
+
export function rowDivider(tokens: ColorTokens): ViewStyle {
|
|
75
|
+
return { borderBottomWidth: 1, borderColor: tokens.border };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The two-column value cell: grows to fill, with the value and trailing
|
|
79
|
+
// affordance pushed to opposite ends.
|
|
80
|
+
export const twoColumnValueCell: ViewStyle = {
|
|
81
|
+
flexGrow: 1,
|
|
82
|
+
flexShrink: 1,
|
|
83
|
+
flexBasis: "0%",
|
|
84
|
+
flexDirection: "row",
|
|
85
|
+
alignItems: "baseline",
|
|
86
|
+
justifyContent: "space-between",
|
|
87
|
+
gap: 16,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// --- header band ------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
export function headerBand(tokens: ColorTokens): ViewStyle {
|
|
93
|
+
return { borderBottomWidth: 1, borderColor: tokens.border, paddingHorizontal: 24, paddingVertical: 16, gap: 2 };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function headerTitle(tokens: ColorTokens): TextStyle {
|
|
97
|
+
return { fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens.foreground };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function headerSubtitle(tokens: ColorTokens): TextStyle {
|
|
101
|
+
return { fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] };
|
|
102
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { View, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
|
|
2
|
+
import { Badge } from "../../atoms/badge/badge.js";
|
|
3
|
+
import { Button } from "../../atoms/button/button.js";
|
|
4
|
+
import * as s from "./description-lists.styles.js";
|
|
5
|
+
import { type Layout } from "./description-lists.styles.js";
|
|
6
|
+
|
|
7
|
+
// DescriptionList: term/value pairs for detail panels, settings, and profile
|
|
8
|
+
// views. Each item is a { term, value } pair. The term is the small muted
|
|
9
|
+
// label; the value is the full-weight data it describes, so the data always
|
|
10
|
+
// outranks its label.
|
|
11
|
+
//
|
|
12
|
+
// Boolean-prop API: one boolean per option, grouped by axis, first-match
|
|
13
|
+
// precedence within an axis (mirrors Button's intentOf / Card's surfaceOf).
|
|
14
|
+
//
|
|
15
|
+
// - Layout (pick one; precedence inline > twoColumn > stacked):
|
|
16
|
+
// `inline` lays the term on the left and the value on the right of a single
|
|
17
|
+
// row; `twoColumn` puts the term in a fixed 160px label column with the value
|
|
18
|
+
// beside it (the read-only detail look); the default `stacked` puts a small
|
|
19
|
+
// uppercase muted label above a full-weight value.
|
|
20
|
+
// - Rows: `divided` draws a hairline (border-b border-border) under every row
|
|
21
|
+
// but the last, for the two-column detail look.
|
|
22
|
+
// - Surface: `card` wraps the whole list in a card surface (border, radius,
|
|
23
|
+
// padding) so it reads as a self-contained panel. `title`/`subtitle` add a
|
|
24
|
+
// bordered header band to the card.
|
|
25
|
+
//
|
|
26
|
+
// Per-item value affordances (all optional, render the value richly):
|
|
27
|
+
// `badge` renders the value as a secondary metadata Badge; `status` renders it
|
|
28
|
+
// as a success status Badge with a leading dot (live state); `mono` sets a
|
|
29
|
+
// monospace face for tokens/IDs; `update` appends a trailing "Update" link
|
|
30
|
+
// button so an editable row is discoverable (the inline-edit affordance).
|
|
31
|
+
//
|
|
32
|
+
// Layout, rows, and surface are orthogonal axes and combine freely.
|
|
33
|
+
|
|
34
|
+
export interface DescriptionListItem {
|
|
35
|
+
term: string;
|
|
36
|
+
value: string;
|
|
37
|
+
// Render the value as a secondary metadata badge (e.g. a role).
|
|
38
|
+
badge?: boolean;
|
|
39
|
+
// Render the value as a success status badge with a leading dot (live state).
|
|
40
|
+
status?: boolean;
|
|
41
|
+
// Monospace value face, for tokens, scopes, identifiers.
|
|
42
|
+
mono?: boolean;
|
|
43
|
+
// Append a trailing "Update" link affordance, marking the row editable.
|
|
44
|
+
update?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DescriptionListProps {
|
|
48
|
+
items: DescriptionListItem[];
|
|
49
|
+
// Card header band (only shown when `card`): a title and optional subtitle.
|
|
50
|
+
title?: string;
|
|
51
|
+
subtitle?: string;
|
|
52
|
+
// Layout (pick one; precedence inline > twoColumn > stacked).
|
|
53
|
+
inline?: boolean;
|
|
54
|
+
twoColumn?: boolean;
|
|
55
|
+
stacked?: boolean;
|
|
56
|
+
// Rows: hairline divider beneath every row but the last.
|
|
57
|
+
divided?: boolean;
|
|
58
|
+
// Surface: wrap the list in a card surface.
|
|
59
|
+
card?: boolean;
|
|
60
|
+
/** Escape hatch for layout/positioning composition (mainly width). */
|
|
61
|
+
style?: StyleProp<ViewStyle>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Layout precedence when more than one is passed: first match wins.
|
|
65
|
+
function layoutOf(p: DescriptionListProps): Layout {
|
|
66
|
+
if (p.inline) return "inline";
|
|
67
|
+
if (p.twoColumn) return "twoColumn";
|
|
68
|
+
return "stacked";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Render a value cell: a badge family, a monospace token, or plain text.
|
|
72
|
+
function Value({ item, align }: { item: DescriptionListItem; align?: boolean }) {
|
|
73
|
+
const { tokens } = useTheme();
|
|
74
|
+
if (item.status) return <Badge status success>{item.value}</Badge>;
|
|
75
|
+
if (item.badge) return <Badge secondary>{item.value}</Badge>;
|
|
76
|
+
if (item.mono) {
|
|
77
|
+
return <Text style={[s.valueLabel(tokens), s.valueMono]}>{item.value}</Text>;
|
|
78
|
+
}
|
|
79
|
+
return <Text style={[s.valueLabel(tokens), align ? s.valueAlignRight : null]}>{item.value}</Text>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function DescriptionList(props: DescriptionListProps) {
|
|
83
|
+
const { items, title, subtitle, divided, card, style } = props;
|
|
84
|
+
const { tokens } = useTheme();
|
|
85
|
+
const layout = layoutOf(props);
|
|
86
|
+
const hasHeader = card && !!title;
|
|
87
|
+
|
|
88
|
+
const container: StyleProp<ViewStyle> = [
|
|
89
|
+
card ? s.cardSurface(tokens) : null,
|
|
90
|
+
// No global padding when a header band supplies its own px-6 per section.
|
|
91
|
+
card && !hasHeader ? s.cardPad : null,
|
|
92
|
+
!hasHeader ? s.stackGap : null,
|
|
93
|
+
style,
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const rows = items.map((item, index) => {
|
|
97
|
+
const last = index === items.length - 1;
|
|
98
|
+
// A divided list draws a hairline under each row except the last, and pads
|
|
99
|
+
// the row vertically so the rule sits clear of the text.
|
|
100
|
+
const row: StyleProp<ViewStyle> = [
|
|
101
|
+
s.rowLayout[layout],
|
|
102
|
+
divided ? s.rowDividedPad : null,
|
|
103
|
+
divided && !last ? s.rowDivider(tokens) : null,
|
|
104
|
+
];
|
|
105
|
+
return (
|
|
106
|
+
<View key={`${item.term}-${index}`} style={row}>
|
|
107
|
+
<Text style={[layout === "stacked" ? s.termStacked(tokens) : s.termLabel(tokens), layout === "twoColumn" ? s.termColumn : null]}>
|
|
108
|
+
{item.term}
|
|
109
|
+
</Text>
|
|
110
|
+
<View style={layout === "twoColumn" ? s.twoColumnValueCell : null}>
|
|
111
|
+
<Value item={item} align={layout === "inline"} />
|
|
112
|
+
{item.update ? (
|
|
113
|
+
<Button link small>
|
|
114
|
+
Update
|
|
115
|
+
</Button>
|
|
116
|
+
) : null}
|
|
117
|
+
</View>
|
|
118
|
+
</View>
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<View style={container}>
|
|
124
|
+
{hasHeader ? (
|
|
125
|
+
<View style={s.headerBand(tokens)}>
|
|
126
|
+
<Text style={s.headerTitle(tokens)}>{title}</Text>
|
|
127
|
+
{subtitle ? <Text style={s.headerSubtitle(tokens)}>{subtitle}</Text> : null}
|
|
128
|
+
</View>
|
|
129
|
+
) : null}
|
|
130
|
+
{hasHeader ? <View style={s.rowsWrap}>{rows}</View> : rows}
|
|
131
|
+
</View>
|
|
132
|
+
);
|
|
133
|
+
}
|