@agimon-ai/po-native-components 0.0.0-alpha.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/LICENSE +22 -0
- package/README.md +71 -0
- package/dist/components/Badge.d.mts +17 -0
- package/dist/components/Badge.d.mts.map +1 -0
- package/dist/components/Badge.mjs +106 -0
- package/dist/components/Badge.mjs.map +1 -0
- package/dist/components/Button.d.mts +20 -0
- package/dist/components/Button.d.mts.map +1 -0
- package/dist/components/Button.mjs +278 -0
- package/dist/components/Button.mjs.map +1 -0
- package/dist/components/CodeBlock.d.mts +10 -0
- package/dist/components/CodeBlock.d.mts.map +1 -0
- package/dist/components/CodeBlock.mjs +88 -0
- package/dist/components/CodeBlock.mjs.map +1 -0
- package/dist/components/Dialog.d.mts +52 -0
- package/dist/components/Dialog.d.mts.map +1 -0
- package/dist/components/Dialog.mjs +257 -0
- package/dist/components/Dialog.mjs.map +1 -0
- package/dist/components/Dot.d.mts +17 -0
- package/dist/components/Dot.d.mts.map +1 -0
- package/dist/components/Dot.mjs +94 -0
- package/dist/components/Dot.mjs.map +1 -0
- package/dist/components/DropdownMenu.d.mts +63 -0
- package/dist/components/DropdownMenu.d.mts.map +1 -0
- package/dist/components/DropdownMenu.mjs +256 -0
- package/dist/components/DropdownMenu.mjs.map +1 -0
- package/dist/components/EmptyState.d.mts +14 -0
- package/dist/components/EmptyState.d.mts.map +1 -0
- package/dist/components/EmptyState.mjs +64 -0
- package/dist/components/EmptyState.mjs.map +1 -0
- package/dist/components/Input.d.mts +19 -0
- package/dist/components/Input.d.mts.map +1 -0
- package/dist/components/Input.mjs +123 -0
- package/dist/components/Input.mjs.map +1 -0
- package/dist/components/Label.d.mts +13 -0
- package/dist/components/Label.d.mts.map +1 -0
- package/dist/components/Label.mjs +36 -0
- package/dist/components/Label.mjs.map +1 -0
- package/dist/components/List.d.mts +48 -0
- package/dist/components/List.d.mts.map +1 -0
- package/dist/components/List.mjs +156 -0
- package/dist/components/List.mjs.map +1 -0
- package/dist/components/Markdown.d.mts +15 -0
- package/dist/components/Markdown.d.mts.map +1 -0
- package/dist/components/Markdown.mjs +219 -0
- package/dist/components/Markdown.mjs.map +1 -0
- package/dist/components/MessageItem.d.mts +87 -0
- package/dist/components/MessageItem.d.mts.map +1 -0
- package/dist/components/MessageItem.mjs +361 -0
- package/dist/components/MessageItem.mjs.map +1 -0
- package/dist/components/Panel.d.mts +15 -0
- package/dist/components/Panel.d.mts.map +1 -0
- package/dist/components/Panel.mjs +77 -0
- package/dist/components/Panel.mjs.map +1 -0
- package/dist/components/PoNativeProvider.d.mts +15 -0
- package/dist/components/PoNativeProvider.d.mts.map +1 -0
- package/dist/components/PoNativeProvider.mjs +22 -0
- package/dist/components/PoNativeProvider.mjs.map +1 -0
- package/dist/components/SectionLabel.d.mts +6 -0
- package/dist/components/SectionLabel.d.mts.map +1 -0
- package/dist/components/SectionLabel.mjs +40 -0
- package/dist/components/SectionLabel.mjs.map +1 -0
- package/dist/components/SegmentedControl.d.mts +26 -0
- package/dist/components/SegmentedControl.d.mts.map +1 -0
- package/dist/components/SegmentedControl.mjs +56 -0
- package/dist/components/SegmentedControl.mjs.map +1 -0
- package/dist/components/Sheet.d.mts +39 -0
- package/dist/components/Sheet.d.mts.map +1 -0
- package/dist/components/Sheet.mjs +166 -0
- package/dist/components/Sheet.mjs.map +1 -0
- package/dist/components/SheetFrame.mjs +84 -0
- package/dist/components/SheetFrame.mjs.map +1 -0
- package/dist/components/Spinner.d.mts +19 -0
- package/dist/components/Spinner.d.mts.map +1 -0
- package/dist/components/Spinner.mjs +34 -0
- package/dist/components/Spinner.mjs.map +1 -0
- package/dist/components/StatusBadge.d.mts +23 -0
- package/dist/components/StatusBadge.d.mts.map +1 -0
- package/dist/components/StatusBadge.mjs +116 -0
- package/dist/components/StatusBadge.mjs.map +1 -0
- package/dist/components/Tabs.d.mts +44 -0
- package/dist/components/Tabs.d.mts.map +1 -0
- package/dist/components/Tabs.mjs +197 -0
- package/dist/components/Tabs.mjs.map +1 -0
- package/dist/components/Textarea.d.mts +10 -0
- package/dist/components/Textarea.d.mts.map +1 -0
- package/dist/components/Textarea.mjs +29 -0
- package/dist/components/Textarea.mjs.map +1 -0
- package/dist/icons/IconColorContext.d.mts +10 -0
- package/dist/icons/IconColorContext.d.mts.map +1 -0
- package/dist/icons/IconColorContext.mjs +13 -0
- package/dist/icons/IconColorContext.mjs.map +1 -0
- package/dist/icons/iconGlyphs.d.mts +120 -0
- package/dist/icons/iconGlyphs.d.mts.map +1 -0
- package/dist/icons/iconGlyphs.mjs +121 -0
- package/dist/icons/iconGlyphs.mjs.map +1 -0
- package/dist/icons/icons.d.mts +122 -0
- package/dist/icons/icons.d.mts.map +1 -0
- package/dist/icons/icons.mjs +79 -0
- package/dist/icons/icons.mjs.map +1 -0
- package/dist/index.d.mts +27 -0
- package/dist/index.mjs +27 -0
- package/dist/lib/fonts.d.mts +13 -0
- package/dist/lib/fonts.d.mts.map +1 -0
- package/dist/lib/fonts.mjs +22 -0
- package/dist/lib/fonts.mjs.map +1 -0
- package/dist/lib/haptics.d.mts +7 -0
- package/dist/lib/haptics.d.mts.map +1 -0
- package/dist/lib/haptics.mjs +25 -0
- package/dist/lib/haptics.mjs.map +1 -0
- package/dist/lib/insideSheet.mjs +16 -0
- package/dist/lib/insideSheet.mjs.map +1 -0
- package/dist/lib/markdown.mjs +271 -0
- package/dist/lib/markdown.mjs.map +1 -0
- package/dist/lib/tone.mjs +50 -0
- package/dist/lib/tone.mjs.map +1 -0
- package/dist/lib/useControllable.mjs +22 -0
- package/dist/lib/useControllable.mjs.map +1 -0
- package/dist/lib/wrapText.mjs +20 -0
- package/dist/lib/wrapText.mjs.map +1 -0
- package/llms.txt +8 -0
- package/package.json +104 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
Copyright (c) 2026 pulseaiclub
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @agimon-ai/po-native-components
|
|
2
|
+
|
|
3
|
+
The React Native component library for Po's native client and its native plugins. It gives native
|
|
4
|
+
code the web library's look and API (`@agimon-ai/po-web-components`) built on native parts: the
|
|
5
|
+
platform activity indicator, Pressable with an Android ripple and 44pt tap targets, SF Symbols on
|
|
6
|
+
iOS and Material icons elsewhere, and a small haptics policy.
|
|
7
|
+
|
|
8
|
+
## Use
|
|
9
|
+
|
|
10
|
+
Mount `PoNativeProvider` once at the app root, inside `SafeAreaProvider`. It supplies the gesture
|
|
11
|
+
handler root and the bottom sheet host that sheets, dialogs and menus need. Register Po's native
|
|
12
|
+
themes with Unistyles (`NATIVE_THEMES` from `@agimon-ai/po-web-components/nativeTheme`) before this
|
|
13
|
+
package is imported.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Button, PoNativeProvider } from '@agimon-ai/po-native-components';
|
|
17
|
+
|
|
18
|
+
<PoNativeProvider>
|
|
19
|
+
<Button variant="primary" onPress={save}>
|
|
20
|
+
Save
|
|
21
|
+
</Button>
|
|
22
|
+
</PoNativeProvider>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Plugins reach this package only through the native host module list. Its API is part of that host
|
|
26
|
+
contract, so it only ever grows: props and exports are added, never removed or renamed.
|
|
27
|
+
|
|
28
|
+
## Catalogue
|
|
29
|
+
|
|
30
|
+
| Export | Web counterpart | Native behaviour |
|
|
31
|
+
| ----------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
32
|
+
| `Button` | Button (9 variants, 7 sizes) | Pressed state for hover, Android ripple, hitSlop to 44pt, light haptic on primary, danger and success |
|
|
33
|
+
| `Label`, `Input`, `Textarea` | Label, Input, Textarea | Placeholder in `faint`, jade cursor, autocorrect off, focus edge at 60% green |
|
|
34
|
+
| `Spinner` | Spinner | `ActivityIndicator` in a theme token; a labelled spinner is a live progress indicator |
|
|
35
|
+
| `Badge`, `StatusBadge`, `STATUS_EDGE`, `Dot` | Same | Accent edges at 40% alpha; `Dot` pulses with RN `Animated` |
|
|
36
|
+
| `EmptyState`, `SectionLabel`, `Panel`, `PanelHeader`, `PanelBody` | Same | Headings carry the header role |
|
|
37
|
+
| `List`, `ListRow` | None (the web uses card buttons) | FlashList; hairline separators, iOS disclosure chevron, selected rows on a green tint, `startAtBottom` for a chat list |
|
|
38
|
+
| `Sheet` and its parts | Sheet (`side` and `width` dropped) | `BottomSheetModal`: drag handle, sized to content, drag down, backdrop tap or Android back closes; fields inside switch to the sheet's own input |
|
|
39
|
+
| `Dialog` and its parts | Dialog (`width`, footer `plain` or `bar`) | RN `Modal` with an `Animated` pop; centred card; Android back and a backdrop tap close it |
|
|
40
|
+
| `DropdownMenu` and its parts | DropdownMenu (no submenus yet) | An action list in a bottom sheet; selection haptic; destructive items in red |
|
|
41
|
+
| `Tabs` and its parts, `TabBadge` | Tabs | Horizontal `ScrollView`; tab roles; active tab on a green tint; selection haptic |
|
|
42
|
+
| `SegmentedControl` | Select (task status) | segmented-control through `withUnistyles`; native on iOS, the library's fallback elsewhere |
|
|
43
|
+
| `CodeBlock` | CodeBlock (`text`, `language`) | Horizontal scroll, selectable mono text, expo-clipboard copy; no colouring or mermaid yet |
|
|
44
|
+
| `Markdown` | Markdown (`text`, `onFileLink`) | The package's own parser; links open with `Linking`; no tables, images or blockquotes yet |
|
|
45
|
+
| `MessageItem` and its parts, `toolTone`, `STATUS_LABEL` | MessageItem | The header is one Pressable that expands the item; edge from `STATUS_EDGE`; outcome as a `StatusBadge` |
|
|
46
|
+
| Icons (`CheckIcon` ... `CloseIcon`, 14) | icons.ts, same names | SF Symbols on iOS, MaterialIcons on Android and web |
|
|
47
|
+
| `PoNativeProvider` | None | `GestureHandlerRootView` and `BottomSheetModalProvider` |
|
|
48
|
+
| `fontOf`, `impactLight`, `selectionTick` | None | Per-platform font family; the haptics policy |
|
|
49
|
+
|
|
50
|
+
`asChild` has no native equivalent and is dropped. Colours always come from the Unistyles theme;
|
|
51
|
+
alpha mixes are computed in render because Unistyles hands stylesheets CSS variables on web.
|
|
52
|
+
|
|
53
|
+
## Build
|
|
54
|
+
|
|
55
|
+
`tsdown` emits ESM only (`exports['.']` is `{types, default}`), unbundled, with every bare import
|
|
56
|
+
external, so each native library resolves from the app's single copy. The Unistyles Babel rewrite
|
|
57
|
+
runs at build time through `unistylesBabel` from `@agimon-ai/po-build/tsdown`.
|
|
58
|
+
|
|
59
|
+
## Stories
|
|
60
|
+
|
|
61
|
+
Each component has a colocated `*.stories.tsx` titled `Native/Components/<Name>`, rendered from the
|
|
62
|
+
repository root:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
pnpm exec style-system get-ui-component -c 'Native/Components/Button' -a packages/core/po-native-components -s Playground
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`src/unistyles.ts` registers the themes for story renders only and is never exported. Story renders
|
|
69
|
+
run on react-native-web, so they cannot show SF Symbols, haptics or native ripples; check those on
|
|
70
|
+
a device. The SegmentedControl story does not render: style-system's bundler cannot parse the Flow
|
|
71
|
+
source segmented-control ships for its non-iOS fallback.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ChipTone } from "@agimon-ai/po-web-components/nativeTheme";
|
|
2
|
+
import { StyleProp, ViewProps, ViewStyle } from "react-native";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
//#region src/components/Badge.d.ts
|
|
5
|
+
export type BadgeTone = ChipTone;
|
|
6
|
+
export type BadgeSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
7
|
+
export interface BadgeProps extends Omit<ViewProps, 'children' | 'style'> {
|
|
8
|
+
tone?: BadgeTone;
|
|
9
|
+
/** The same rungs Button names, so a chip can sit beside a button of any size. */
|
|
10
|
+
size?: BadgeSize;
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
13
|
+
}
|
|
14
|
+
/** An outlined chip in one of the accent tones. */
|
|
15
|
+
export declare function Badge({ tone, size, children, style, ...props }: BadgeProps): import("react").JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//# sourceMappingURL=Badge.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.d.mts","names":[],"sources":["../../src/components/Badge.tsx"],"mappings":";;;;YASY,YAAY;YACZ;iBAKK,mBAAmB,KAAK;EACvC,OAAO;;EAEP,OAAO;EACP,WAAW;EACX,QAAQ,UAAU;;;wBAIJ,QAAQ,MAAkB,MAAa,UAAU,UAAU,SAAS,6BAAU,IAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { fontOf } from "../lib/fonts.mjs";
|
|
2
|
+
import { CHIP_INK } from "../lib/tone.mjs";
|
|
3
|
+
import { wrapText } from "../lib/wrapText.mjs";
|
|
4
|
+
import { View } from "react-native-unistyles/components/native/View";
|
|
5
|
+
import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
|
|
6
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
|
|
9
|
+
//#region src/components/Badge.tsx
|
|
10
|
+
/** `border-doom-<accent>/40`. */
|
|
11
|
+
const EDGE_ALPHA_PERCENT = 40;
|
|
12
|
+
/** An outlined chip in one of the accent tones. */
|
|
13
|
+
function Badge({ tone = "neutral", size = "sm", children, style, ...props }) {
|
|
14
|
+
const { theme } = useUnistyles();
|
|
15
|
+
const _styles = styles;
|
|
16
|
+
{
|
|
17
|
+
const styles = _styles.useVariants({
|
|
18
|
+
tone,
|
|
19
|
+
size
|
|
20
|
+
});
|
|
21
|
+
const edge = tone === "neutral" ? void 0 : { borderColor: withAlpha(theme.colors[CHIP_INK[tone]], EDGE_ALPHA_PERCENT) };
|
|
22
|
+
return /* @__PURE__ */ jsx(View, {
|
|
23
|
+
style: [
|
|
24
|
+
styles.badge,
|
|
25
|
+
edge,
|
|
26
|
+
style
|
|
27
|
+
],
|
|
28
|
+
...props,
|
|
29
|
+
children: wrapText(children, styles.label)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const styles = StyleSheet.create((theme) => {
|
|
34
|
+
const ink = (tone) => ({ color: theme.colors[CHIP_INK[tone]] });
|
|
35
|
+
return {
|
|
36
|
+
badge: {
|
|
37
|
+
flexDirection: "row",
|
|
38
|
+
alignItems: "center",
|
|
39
|
+
alignSelf: "flex-start",
|
|
40
|
+
gap: theme.spacing(1.5),
|
|
41
|
+
borderRadius: theme.borderRadius.DEFAULT,
|
|
42
|
+
borderWidth: 1,
|
|
43
|
+
borderColor: theme.colors.border,
|
|
44
|
+
variants: { size: {
|
|
45
|
+
xs: {
|
|
46
|
+
paddingHorizontal: theme.spacing(1.5),
|
|
47
|
+
paddingVertical: 1
|
|
48
|
+
},
|
|
49
|
+
sm: {
|
|
50
|
+
paddingHorizontal: theme.spacing(2),
|
|
51
|
+
paddingVertical: theme.spacing(.5)
|
|
52
|
+
},
|
|
53
|
+
md: {
|
|
54
|
+
paddingHorizontal: theme.spacing(2.5),
|
|
55
|
+
paddingVertical: theme.spacing(1)
|
|
56
|
+
},
|
|
57
|
+
lg: {
|
|
58
|
+
paddingHorizontal: theme.spacing(3),
|
|
59
|
+
paddingVertical: theme.spacing(1.5)
|
|
60
|
+
}
|
|
61
|
+
} },
|
|
62
|
+
uni__dependencies: [0, 4]
|
|
63
|
+
},
|
|
64
|
+
label: {
|
|
65
|
+
fontFamily: fontOf(theme.fontFamily.sans),
|
|
66
|
+
variants: {
|
|
67
|
+
tone: {
|
|
68
|
+
neutral: ink("neutral"),
|
|
69
|
+
blue: ink("blue"),
|
|
70
|
+
green: ink("green"),
|
|
71
|
+
yellow: ink("yellow"),
|
|
72
|
+
red: ink("red"),
|
|
73
|
+
magenta: ink("magenta"),
|
|
74
|
+
violet: ink("violet"),
|
|
75
|
+
cyan: ink("cyan"),
|
|
76
|
+
orange: ink("orange"),
|
|
77
|
+
teal: ink("teal")
|
|
78
|
+
},
|
|
79
|
+
size: {
|
|
80
|
+
xs: {
|
|
81
|
+
fontSize: theme.fontSize.sm,
|
|
82
|
+
lineHeight: theme.lineHeight.sm,
|
|
83
|
+
fontWeight: theme.fontWeight.semibold
|
|
84
|
+
},
|
|
85
|
+
sm: {
|
|
86
|
+
fontSize: theme.fontSize.sm,
|
|
87
|
+
lineHeight: theme.lineHeight.sm
|
|
88
|
+
},
|
|
89
|
+
md: {
|
|
90
|
+
fontSize: theme.fontSize.sm,
|
|
91
|
+
lineHeight: theme.lineHeight.sm
|
|
92
|
+
},
|
|
93
|
+
lg: {
|
|
94
|
+
fontSize: theme.fontSize.base,
|
|
95
|
+
lineHeight: theme.lineHeight.base
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
uni__dependencies: [0, 4]
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { Badge };
|
|
106
|
+
//# sourceMappingURL=Badge.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.mjs","names":["ChipTone","withAlpha","ReactNode","StyleProp","ViewProps","ViewStyle","StyleSheet","useUnistyles","fontOf","CHIP_INK","wrapText","BadgeTone","BadgeSize","EDGE_ALPHA_PERCENT","BadgeProps","Omit","tone","size","children","style","Badge","props","theme","_styles","styles","useVariants","edge","undefined","borderColor","colors","badge","label","create","ink","color","flexDirection","alignItems","alignSelf","gap","spacing","borderRadius","DEFAULT","borderWidth","border","variants","xs","paddingHorizontal","paddingVertical","sm","md","lg","uni__dependencies","fontFamily","sans","neutral","blue","green","yellow","red","magenta","violet","cyan","orange","teal","fontSize","lineHeight","fontWeight","semibold","base"],"sources":["../../src/components/Badge.tsx"],"sourcesContent":["import { type ChipTone, withAlpha } from '@agimon-ai/po-web-components/nativeTheme';\nimport type { ReactNode } from 'react';\nimport { type StyleProp, View, type ViewProps, type ViewStyle } from 'react-native';\nimport { StyleSheet, useUnistyles } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { CHIP_INK } from '../lib/tone';\nimport { wrapText } from '../lib/wrapText';\n\nexport type BadgeTone = ChipTone;\nexport type BadgeSize = 'xs' | 'sm' | 'md' | 'lg';\n\n/** `border-doom-<accent>/40`. */\nconst EDGE_ALPHA_PERCENT = 40;\n\nexport interface BadgeProps extends Omit<ViewProps, 'children' | 'style'> {\n tone?: BadgeTone;\n /** The same rungs Button names, so a chip can sit beside a button of any size. */\n size?: BadgeSize;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** An outlined chip in one of the accent tones. */\nexport function Badge({ tone = 'neutral', size = 'sm', children, style, ...props }: BadgeProps) {\n const { theme } = useUnistyles();\n styles.useVariants({ tone, size });\n // Alpha mixes are computed in render from useUnistyles(), not in StyleSheet.create: on web Unistyles hands\n // the stylesheet CSS variables, which withAlpha cannot read.\n const edge =\n tone === 'neutral' ? undefined : { borderColor: withAlpha(theme.colors[CHIP_INK[tone]], EDGE_ALPHA_PERCENT) };\n return (\n <View style={[styles.badge, edge, style]} {...props}>\n {wrapText(children, styles.label)}\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => {\n const ink = (tone: ChipTone) => ({ color: theme.colors[CHIP_INK[tone]] });\n return {\n badge: {\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'flex-start',\n gap: theme.spacing(1.5),\n borderRadius: theme.borderRadius.DEFAULT,\n borderWidth: 1,\n borderColor: theme.colors.border,\n variants: {\n size: {\n xs: { paddingHorizontal: theme.spacing(1.5), paddingVertical: 1 },\n sm: { paddingHorizontal: theme.spacing(2), paddingVertical: theme.spacing(0.5) },\n md: { paddingHorizontal: theme.spacing(2.5), paddingVertical: theme.spacing(1) },\n lg: { paddingHorizontal: theme.spacing(3), paddingVertical: theme.spacing(1.5) },\n },\n },\n },\n label: {\n fontFamily: fontOf(theme.fontFamily.sans),\n variants: {\n tone: {\n neutral: ink('neutral'),\n blue: ink('blue'),\n green: ink('green'),\n yellow: ink('yellow'),\n red: ink('red'),\n magenta: ink('magenta'),\n violet: ink('violet'),\n cyan: ink('cyan'),\n orange: ink('orange'),\n teal: ink('teal'),\n },\n size: {\n xs: { fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.sm, fontWeight: theme.fontWeight.semibold },\n sm: { fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.sm },\n md: { fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.sm },\n lg: { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n },\n },\n },\n };\n});\n"],"mappings":";;;;;;;;;;AAaA,MAAMa,qBAAqB;;AAW3B,SAAgBO,MAAM,EAAEJ,OAAO,WAAWC,OAAO,MAAMC,UAAUC,OAAO,GAAGE,SAAqB;CAC9F,MAAM,EAAEC,UAAUf,aAAa;CAAE,MAAAgB,UAAAC;CAAA;EAAA,MAAAA,SAAAD,QAAAE,YACd;GAAET;GAAMC;EAAK,CAAC;EAGjC,MAAMS,OACJV,SAAS,YAAYW,SAAY,EAAEC,aAAa3B,UAAUqB,MAAMO,OAAOpB,SAASO,QAAQH,kBAAkB,EAAE;EAC9G,OACE,oBAAC,MAAD;GAAM,OAAO;IAACW,OAAOM;IAAOJ;IAAMP;GAAK;GAAG,GAAIE;aAC3CX,SAASQ,UAAUM,OAAOO,KAAK;EAC5B;CACN;AACJ;AAEA,MAAMP,SAASlB,WAAW0B,QAAQV,UAAU;CAC1C,MAAMW,OAAOjB,UAAoB,EAAEkB,OAAOZ,MAAMO,OAAOpB,SAASO,OAAO;CACvE,OAAO;EACLc,OAAO;GACLK,eAAe;GACfC,YAAY;GACZC,WAAW;GACXC,KAAKhB,MAAMiB,QAAQ,GAAG;GACtBC,cAAclB,MAAMkB,aAAaC;GACjCC,aAAa;GACbd,aAAaN,MAAMO,OAAOc;GAC1BC,UAAU,EACR3B,MAAM;IACJ4B,IAAI;KAAEC,mBAAmBxB,MAAMiB,QAAQ,GAAG;KAAGQ,iBAAiB;IAAE;IAChEC,IAAI;KAAEF,mBAAmBxB,MAAMiB,QAAQ,CAAC;KAAGQ,iBAAiBzB,MAAMiB,QAAQ,EAAG;IAAE;IAC/EU,IAAI;KAAEH,mBAAmBxB,MAAMiB,QAAQ,GAAG;KAAGQ,iBAAiBzB,MAAMiB,QAAQ,CAAC;IAAE;IAC/EW,IAAI;KAAEJ,mBAAmBxB,MAAMiB,QAAQ,CAAC;KAAGQ,iBAAiBzB,MAAMiB,QAAQ,GAAG;IAAE;GACjF,EACF;GAACY,mBAAA,CAAA,GAAA,CAAA;EACH;EACApB,OAAO;GACLqB,YAAY5C,OAAOc,MAAM8B,WAAWC,IAAI;GACxCT,UAAU;IACR5B,MAAM;KACJsC,SAASrB,IAAI,SAAS;KACtBsB,MAAMtB,IAAI,MAAM;KAChBuB,OAAOvB,IAAI,OAAO;KAClBwB,QAAQxB,IAAI,QAAQ;KACpByB,KAAKzB,IAAI,KAAK;KACd0B,SAAS1B,IAAI,SAAS;KACtB2B,QAAQ3B,IAAI,QAAQ;KACpB4B,MAAM5B,IAAI,MAAM;KAChB6B,QAAQ7B,IAAI,QAAQ;KACpB8B,MAAM9B,IAAI,MAAM;IAClB;IACAhB,MAAM;KACJ4B,IAAI;MAAEmB,UAAU1C,MAAM0C,SAAShB;MAAIiB,YAAY3C,MAAM2C,WAAWjB;MAAIkB,YAAY5C,MAAM4C,WAAWC;KAAS;KAC1GnB,IAAI;MAAEgB,UAAU1C,MAAM0C,SAAShB;MAAIiB,YAAY3C,MAAM2C,WAAWjB;KAAG;KACnEC,IAAI;MAAEe,UAAU1C,MAAM0C,SAAShB;MAAIiB,YAAY3C,MAAM2C,WAAWjB;KAAG;KACnEE,IAAI;MAAEc,UAAU1C,MAAM0C,SAASI;MAAMH,YAAY3C,MAAM2C,WAAWG;KAAK;IACzE;GACF;GAACjB,mBAAA,CAAA,GAAA,CAAA;EACH;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PressableProps, StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/components/Button.d.ts
|
|
4
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'subtle' | 'danger' | 'danger-outline' | 'success' | 'link';
|
|
5
|
+
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'icon' | 'icon-md' | 'card';
|
|
6
|
+
export interface ButtonProps extends Omit<PressableProps, 'children' | 'style'> {
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
/** Work is in flight: the button shows a spinner and stops accepting presses. */
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
/** What the spinner announces while loading; omit it and the wait is silent. */
|
|
12
|
+
loadingLabel?: string;
|
|
13
|
+
/** A bare string is drawn as the label; elements (an icon, a card's lines) render as given. */
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
}
|
|
17
|
+
/** The one button: every variant is a theme token, a press dims or lifts it, and committing presses buzz once. */
|
|
18
|
+
export declare function Button({ variant, size, loading, loadingLabel, disabled, onPress, children, style, hitSlop, accessibilityState, ...props }: ButtonProps): import("react").JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
//# sourceMappingURL=Button.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.mts","names":[],"sources":["../../src/components/Button.tsx"],"mappings":";;;YAWY;YAUA;iBAqCK,oBAAoB,KAAK;EACxC,UAAU;EACV,OAAO;;EAEP;;EAEA;;EAEA,WAAW;EACX,QAAQ,UAAU;;;wBAIJ,SACd,SACA,MACA,SACA,cACA,UACA,SACA,UACA,OACA,SACA,uBACG,SACF,8BAAW,IAAA"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { fontOf } from "../lib/fonts.mjs";
|
|
2
|
+
import { wrapText } from "../lib/wrapText.mjs";
|
|
3
|
+
import { IconColorContext } from "../icons/IconColorContext.mjs";
|
|
4
|
+
import { impactLight } from "../lib/haptics.mjs";
|
|
5
|
+
import { Spinner } from "./Spinner.mjs";
|
|
6
|
+
import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
|
|
7
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { Pressable } from "react-native-unistyles/components/native/Pressable";
|
|
10
|
+
|
|
11
|
+
//#region src/components/Button.tsx
|
|
12
|
+
/** Apple's and Google's minimum tap target; a smaller visual box makes up the rest with hitSlop. */
|
|
13
|
+
const MIN_TAP_TARGET = 44;
|
|
14
|
+
const DISABLED_OPACITY = .4;
|
|
15
|
+
const PRESSED_FILL_OPACITY = .85;
|
|
16
|
+
const RIPPLE_ALPHA_PERCENT = 20;
|
|
17
|
+
/** `hover:border-doom-green/50`. */
|
|
18
|
+
const PRESSED_EDGE_ALPHA_PERCENT = 50;
|
|
19
|
+
/** The visual height of each size, from which hitSlop pads the tap target to 44pt. */
|
|
20
|
+
const SIZE_HEIGHT = {
|
|
21
|
+
xs: 24,
|
|
22
|
+
sm: 28,
|
|
23
|
+
md: 32,
|
|
24
|
+
lg: 36,
|
|
25
|
+
icon: 24,
|
|
26
|
+
"icon-md": 32,
|
|
27
|
+
card: MIN_TAP_TARGET
|
|
28
|
+
};
|
|
29
|
+
/** The label ink of each variant, and the ink it lifts to while pressed (the web hover). */
|
|
30
|
+
const INK = {
|
|
31
|
+
primary: {
|
|
32
|
+
rest: "on-selected",
|
|
33
|
+
pressed: "on-selected"
|
|
34
|
+
},
|
|
35
|
+
secondary: {
|
|
36
|
+
rest: "magenta",
|
|
37
|
+
pressed: "magenta"
|
|
38
|
+
},
|
|
39
|
+
outline: {
|
|
40
|
+
rest: "dim",
|
|
41
|
+
pressed: "hi"
|
|
42
|
+
},
|
|
43
|
+
ghost: {
|
|
44
|
+
rest: "dim",
|
|
45
|
+
pressed: "hi"
|
|
46
|
+
},
|
|
47
|
+
subtle: {
|
|
48
|
+
rest: "dim",
|
|
49
|
+
pressed: "hi"
|
|
50
|
+
},
|
|
51
|
+
danger: {
|
|
52
|
+
rest: "rail",
|
|
53
|
+
pressed: "rail"
|
|
54
|
+
},
|
|
55
|
+
"danger-outline": {
|
|
56
|
+
rest: "red",
|
|
57
|
+
pressed: "red"
|
|
58
|
+
},
|
|
59
|
+
success: {
|
|
60
|
+
rest: "rail",
|
|
61
|
+
pressed: "rail"
|
|
62
|
+
},
|
|
63
|
+
link: {
|
|
64
|
+
rest: "blue",
|
|
65
|
+
pressed: "blue"
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/** Presses that commit something get a light impact; the rest stay quiet. */
|
|
69
|
+
const HAPTIC_VARIANTS = /* @__PURE__ */ new Set([
|
|
70
|
+
"primary",
|
|
71
|
+
"danger",
|
|
72
|
+
"success"
|
|
73
|
+
]);
|
|
74
|
+
/** The one button: every variant is a theme token, a press dims or lifts it, and committing presses buzz once. */
|
|
75
|
+
function Button({ variant = "outline", size = "md", loading = false, loadingLabel, disabled, onPress, children, style, hitSlop, accessibilityState, ...props }) {
|
|
76
|
+
const { theme } = useUnistyles();
|
|
77
|
+
const _styles = styles;
|
|
78
|
+
{
|
|
79
|
+
const styles = _styles.useVariants({
|
|
80
|
+
variant,
|
|
81
|
+
size
|
|
82
|
+
});
|
|
83
|
+
const inactive = (disabled ?? false) || loading;
|
|
84
|
+
const slop = Math.max(0, (MIN_TAP_TARGET - SIZE_HEIGHT[size]) / 2);
|
|
85
|
+
const ink = INK[variant];
|
|
86
|
+
return /* @__PURE__ */ jsx(Pressable, {
|
|
87
|
+
accessibilityRole: variant === "link" ? "link" : "button",
|
|
88
|
+
accessibilityState: {
|
|
89
|
+
...accessibilityState,
|
|
90
|
+
disabled: inactive,
|
|
91
|
+
busy: loading
|
|
92
|
+
},
|
|
93
|
+
accessibilityValue: loading && loadingLabel !== void 0 ? { text: loadingLabel } : void 0,
|
|
94
|
+
disabled: inactive,
|
|
95
|
+
hitSlop: hitSlop ?? slop,
|
|
96
|
+
android_ripple: {
|
|
97
|
+
color: withAlpha(theme.colors[ink.pressed], RIPPLE_ALPHA_PERCENT),
|
|
98
|
+
borderless: false
|
|
99
|
+
},
|
|
100
|
+
onPress: (event) => {
|
|
101
|
+
if (HAPTIC_VARIANTS.has(variant)) impactLight();
|
|
102
|
+
onPress?.(event);
|
|
103
|
+
},
|
|
104
|
+
style: ({ pressed }) => [
|
|
105
|
+
styles.button,
|
|
106
|
+
pressed && styles.pressed,
|
|
107
|
+
pressed && variant === "outline" && { borderColor: withAlpha(theme.colors.green, PRESSED_EDGE_ALPHA_PERCENT) },
|
|
108
|
+
inactive && styles.disabled,
|
|
109
|
+
style
|
|
110
|
+
],
|
|
111
|
+
...props,
|
|
112
|
+
children: ({ pressed }) => {
|
|
113
|
+
const token = pressed ? ink.pressed : ink.rest;
|
|
114
|
+
return /* @__PURE__ */ jsxs(IconColorContext.Provider, {
|
|
115
|
+
value: token,
|
|
116
|
+
children: [loading ? /* @__PURE__ */ jsx(Spinner, { color: token }) : null, wrapText(children, [
|
|
117
|
+
styles.label,
|
|
118
|
+
pressed && styles.labelPressed,
|
|
119
|
+
{ color: theme.colors[token] }
|
|
120
|
+
])]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const styles = StyleSheet.create((theme) => ({
|
|
127
|
+
button: {
|
|
128
|
+
flexDirection: "row",
|
|
129
|
+
alignItems: "center",
|
|
130
|
+
justifyContent: "center",
|
|
131
|
+
gap: theme.spacing(1.5),
|
|
132
|
+
borderRadius: theme.borderRadius.md,
|
|
133
|
+
borderWidth: 1,
|
|
134
|
+
borderColor: "transparent",
|
|
135
|
+
overflow: "hidden",
|
|
136
|
+
variants: {
|
|
137
|
+
variant: {
|
|
138
|
+
primary: { backgroundColor: theme.colors.selected },
|
|
139
|
+
secondary: {
|
|
140
|
+
borderColor: theme.colors["edge-magenta"],
|
|
141
|
+
backgroundColor: theme.colors["tint-magenta"]
|
|
142
|
+
},
|
|
143
|
+
outline: { borderColor: theme.colors.border },
|
|
144
|
+
ghost: {},
|
|
145
|
+
subtle: { backgroundColor: theme.colors.panel },
|
|
146
|
+
danger: { backgroundColor: theme.colors.red },
|
|
147
|
+
"danger-outline": {
|
|
148
|
+
borderColor: theme.colors["edge-red"],
|
|
149
|
+
backgroundColor: theme.colors["tint-red"]
|
|
150
|
+
},
|
|
151
|
+
success: { backgroundColor: theme.colors.green },
|
|
152
|
+
link: {}
|
|
153
|
+
},
|
|
154
|
+
size: {
|
|
155
|
+
xs: {
|
|
156
|
+
height: SIZE_HEIGHT.xs,
|
|
157
|
+
paddingHorizontal: theme.spacing(1.5),
|
|
158
|
+
borderRadius: theme.borderRadius.DEFAULT
|
|
159
|
+
},
|
|
160
|
+
sm: {
|
|
161
|
+
height: SIZE_HEIGHT.sm,
|
|
162
|
+
paddingHorizontal: theme.spacing(2.5)
|
|
163
|
+
},
|
|
164
|
+
md: {
|
|
165
|
+
height: SIZE_HEIGHT.md,
|
|
166
|
+
paddingHorizontal: theme.spacing(3)
|
|
167
|
+
},
|
|
168
|
+
lg: {
|
|
169
|
+
height: SIZE_HEIGHT.lg,
|
|
170
|
+
paddingHorizontal: theme.spacing(3.5)
|
|
171
|
+
},
|
|
172
|
+
icon: {
|
|
173
|
+
width: SIZE_HEIGHT.icon,
|
|
174
|
+
height: SIZE_HEIGHT.icon,
|
|
175
|
+
padding: 0,
|
|
176
|
+
borderRadius: theme.borderRadius.DEFAULT
|
|
177
|
+
},
|
|
178
|
+
"icon-md": {
|
|
179
|
+
width: SIZE_HEIGHT["icon-md"],
|
|
180
|
+
height: SIZE_HEIGHT["icon-md"],
|
|
181
|
+
padding: 0
|
|
182
|
+
},
|
|
183
|
+
card: {
|
|
184
|
+
alignSelf: "stretch",
|
|
185
|
+
flexDirection: "column",
|
|
186
|
+
alignItems: "stretch",
|
|
187
|
+
justifyContent: "flex-start",
|
|
188
|
+
gap: theme.spacing(1),
|
|
189
|
+
minHeight: SIZE_HEIGHT.card,
|
|
190
|
+
paddingHorizontal: 11,
|
|
191
|
+
paddingVertical: theme.spacing(2.5)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
uni__dependencies: [0, 4]
|
|
196
|
+
},
|
|
197
|
+
pressed: {
|
|
198
|
+
variants: { variant: {
|
|
199
|
+
primary: { opacity: PRESSED_FILL_OPACITY },
|
|
200
|
+
secondary: { opacity: PRESSED_FILL_OPACITY },
|
|
201
|
+
outline: {},
|
|
202
|
+
ghost: { backgroundColor: theme.colors.panel },
|
|
203
|
+
subtle: { backgroundColor: theme.colors.deep },
|
|
204
|
+
danger: { opacity: PRESSED_FILL_OPACITY },
|
|
205
|
+
"danger-outline": { opacity: PRESSED_FILL_OPACITY },
|
|
206
|
+
success: { opacity: PRESSED_FILL_OPACITY },
|
|
207
|
+
link: {}
|
|
208
|
+
} },
|
|
209
|
+
uni__dependencies: [0, 4]
|
|
210
|
+
},
|
|
211
|
+
disabled: { opacity: DISABLED_OPACITY },
|
|
212
|
+
label: {
|
|
213
|
+
fontFamily: fontOf(theme.fontFamily.sans),
|
|
214
|
+
fontWeight: theme.fontWeight.medium,
|
|
215
|
+
variants: {
|
|
216
|
+
variant: {
|
|
217
|
+
primary: { fontWeight: theme.fontWeight.semibold },
|
|
218
|
+
secondary: { fontWeight: theme.fontWeight.semibold },
|
|
219
|
+
outline: {},
|
|
220
|
+
ghost: {},
|
|
221
|
+
subtle: {},
|
|
222
|
+
danger: { fontWeight: theme.fontWeight.semibold },
|
|
223
|
+
"danger-outline": { fontWeight: theme.fontWeight.semibold },
|
|
224
|
+
success: { fontWeight: theme.fontWeight.semibold },
|
|
225
|
+
link: {}
|
|
226
|
+
},
|
|
227
|
+
size: {
|
|
228
|
+
xs: {
|
|
229
|
+
fontSize: theme.fontSize.sm,
|
|
230
|
+
lineHeight: theme.lineHeight.sm
|
|
231
|
+
},
|
|
232
|
+
sm: {
|
|
233
|
+
fontSize: theme.fontSize.sm,
|
|
234
|
+
lineHeight: theme.lineHeight.sm
|
|
235
|
+
},
|
|
236
|
+
md: {
|
|
237
|
+
fontSize: theme.fontSize.base,
|
|
238
|
+
lineHeight: theme.lineHeight.base
|
|
239
|
+
},
|
|
240
|
+
lg: {
|
|
241
|
+
fontSize: theme.fontSize.base,
|
|
242
|
+
lineHeight: theme.lineHeight.base
|
|
243
|
+
},
|
|
244
|
+
icon: {
|
|
245
|
+
fontSize: theme.fontSize.base,
|
|
246
|
+
lineHeight: theme.lineHeight.base
|
|
247
|
+
},
|
|
248
|
+
"icon-md": {
|
|
249
|
+
fontSize: theme.fontSize.base,
|
|
250
|
+
lineHeight: theme.lineHeight.base
|
|
251
|
+
},
|
|
252
|
+
card: {
|
|
253
|
+
fontSize: theme.fontSize.base,
|
|
254
|
+
lineHeight: theme.lineHeight.base
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
uni__dependencies: [0, 4]
|
|
259
|
+
},
|
|
260
|
+
labelPressed: {
|
|
261
|
+
variants: { variant: {
|
|
262
|
+
primary: {},
|
|
263
|
+
secondary: {},
|
|
264
|
+
outline: {},
|
|
265
|
+
ghost: {},
|
|
266
|
+
subtle: {},
|
|
267
|
+
danger: {},
|
|
268
|
+
"danger-outline": {},
|
|
269
|
+
success: {},
|
|
270
|
+
link: { textDecorationLine: "underline" }
|
|
271
|
+
} },
|
|
272
|
+
uni__dependencies: [4]
|
|
273
|
+
}
|
|
274
|
+
}));
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
export { Button };
|
|
278
|
+
//# sourceMappingURL=Button.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.mjs","names":["NativeColorToken","withAlpha","ReactNode","PressableProps","StyleProp","ViewStyle","StyleSheet","useUnistyles","IconColorContext","fontOf","impactLight","wrapText","Spinner","ButtonVariant","ButtonSize","MIN_TAP_TARGET","DISABLED_OPACITY","PRESSED_FILL_OPACITY","RIPPLE_ALPHA_PERCENT","PRESSED_EDGE_ALPHA_PERCENT","SIZE_HEIGHT","Readonly","Record","xs","sm","md","lg","icon","card","INK","rest","pressed","primary","secondary","outline","ghost","subtle","danger","success","link","HAPTIC_VARIANTS","ReadonlySet","Set","ButtonProps","Omit","variant","size","loading","loadingLabel","children","style","Button","disabled","onPress","hitSlop","accessibilityState","props","theme","_styles","styles","useVariants","inactive","slop","Math","max","ink","busy","undefined","text","color","colors","borderless","event","has","button","borderColor","green","token","label","labelPressed","create","flexDirection","alignItems","justifyContent","gap","spacing","borderRadius","borderWidth","overflow","variants","backgroundColor","selected","border","panel","red","height","paddingHorizontal","DEFAULT","width","padding","alignSelf","minHeight","paddingVertical","uni__dependencies","opacity","deep","fontFamily","sans","fontWeight","medium","semibold","fontSize","lineHeight","base","textDecorationLine"],"sources":["../../src/components/Button.tsx"],"sourcesContent":["import { type NativeColorToken, withAlpha } from '@agimon-ai/po-web-components/nativeTheme';\nimport type { ReactNode } from 'react';\nimport { Pressable, type PressableProps, type StyleProp, type ViewStyle } from 'react-native';\nimport { StyleSheet, useUnistyles } from 'react-native-unistyles';\n\nimport { IconColorContext } from '../icons/IconColorContext';\nimport { fontOf } from '../lib/fonts';\nimport { impactLight } from '../lib/haptics';\nimport { wrapText } from '../lib/wrapText';\nimport { Spinner } from './Spinner';\n\nexport type ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'outline'\n | 'ghost'\n | 'subtle'\n | 'danger'\n | 'danger-outline'\n | 'success'\n | 'link';\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'icon' | 'icon-md' | 'card';\n\n/** Apple's and Google's minimum tap target; a smaller visual box makes up the rest with hitSlop. */\nconst MIN_TAP_TARGET = 44;\nconst DISABLED_OPACITY = 0.4;\nconst PRESSED_FILL_OPACITY = 0.85;\nconst RIPPLE_ALPHA_PERCENT = 20;\n/** `hover:border-doom-green/50`. */\nconst PRESSED_EDGE_ALPHA_PERCENT = 50;\n\n/** The visual height of each size, from which hitSlop pads the tap target to 44pt. */\nconst SIZE_HEIGHT: Readonly<Record<ButtonSize, number>> = {\n xs: 24,\n sm: 28,\n md: 32,\n lg: 36,\n icon: 24,\n 'icon-md': 32,\n card: MIN_TAP_TARGET,\n};\n\n/** The label ink of each variant, and the ink it lifts to while pressed (the web hover). */\nconst INK: Readonly<Record<ButtonVariant, { rest: NativeColorToken; pressed: NativeColorToken }>> = {\n primary: { rest: 'on-selected', pressed: 'on-selected' },\n secondary: { rest: 'magenta', pressed: 'magenta' },\n outline: { rest: 'dim', pressed: 'hi' },\n ghost: { rest: 'dim', pressed: 'hi' },\n subtle: { rest: 'dim', pressed: 'hi' },\n danger: { rest: 'rail', pressed: 'rail' },\n 'danger-outline': { rest: 'red', pressed: 'red' },\n success: { rest: 'rail', pressed: 'rail' },\n link: { rest: 'blue', pressed: 'blue' },\n};\n\n/** Presses that commit something get a light impact; the rest stay quiet. */\nconst HAPTIC_VARIANTS: ReadonlySet<ButtonVariant> = new Set(['primary', 'danger', 'success']);\n\nexport interface ButtonProps extends Omit<PressableProps, 'children' | 'style'> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n /** Work is in flight: the button shows a spinner and stops accepting presses. */\n loading?: boolean;\n /** What the spinner announces while loading; omit it and the wait is silent. */\n loadingLabel?: string;\n /** A bare string is drawn as the label; elements (an icon, a card's lines) render as given. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** The one button: every variant is a theme token, a press dims or lifts it, and committing presses buzz once. */\nexport function Button({\n variant = 'outline',\n size = 'md',\n loading = false,\n loadingLabel,\n disabled,\n onPress,\n children,\n style,\n hitSlop,\n accessibilityState,\n ...props\n}: ButtonProps) {\n const { theme } = useUnistyles();\n styles.useVariants({ variant, size });\n const inactive = (disabled ?? false) || loading;\n const slop = Math.max(0, (MIN_TAP_TARGET - SIZE_HEIGHT[size]) / 2);\n const ink = INK[variant];\n return (\n <Pressable\n accessibilityRole={variant === 'link' ? 'link' : 'button'}\n accessibilityState={{ ...accessibilityState, disabled: inactive, busy: loading }}\n accessibilityValue={loading && loadingLabel !== undefined ? { text: loadingLabel } : undefined}\n disabled={inactive}\n hitSlop={hitSlop ?? slop}\n android_ripple={{ color: withAlpha(theme.colors[ink.pressed], RIPPLE_ALPHA_PERCENT), borderless: false }}\n onPress={(event) => {\n if (HAPTIC_VARIANTS.has(variant)) impactLight();\n onPress?.(event);\n }}\n style={({ pressed }) => [\n styles.button,\n pressed && styles.pressed,\n // Alpha mixes are computed in render from useUnistyles(), not in StyleSheet.create: on web Unistyles hands\n // the stylesheet CSS variables, which withAlpha cannot read.\n pressed && variant === 'outline' && { borderColor: withAlpha(theme.colors.green, PRESSED_EDGE_ALPHA_PERCENT) },\n inactive && styles.disabled,\n style,\n ]}\n {...props}\n >\n {({ pressed }) => {\n const token = pressed ? ink.pressed : ink.rest;\n return (\n <IconColorContext.Provider value={token}>\n {loading ? <Spinner color={token} /> : null}\n {wrapText(children, [styles.label, pressed && styles.labelPressed, { color: theme.colors[token] }])}\n </IconColorContext.Provider>\n );\n }}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n button: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.spacing(1.5),\n borderRadius: theme.borderRadius.md,\n borderWidth: 1,\n borderColor: 'transparent',\n overflow: 'hidden',\n variants: {\n variant: {\n primary: { backgroundColor: theme.colors.selected },\n secondary: { borderColor: theme.colors['edge-magenta'], backgroundColor: theme.colors['tint-magenta'] },\n outline: { borderColor: theme.colors.border },\n ghost: {},\n subtle: { backgroundColor: theme.colors.panel },\n danger: { backgroundColor: theme.colors.red },\n 'danger-outline': { borderColor: theme.colors['edge-red'], backgroundColor: theme.colors['tint-red'] },\n success: { backgroundColor: theme.colors.green },\n link: {},\n },\n size: {\n xs: { height: SIZE_HEIGHT.xs, paddingHorizontal: theme.spacing(1.5), borderRadius: theme.borderRadius.DEFAULT },\n sm: { height: SIZE_HEIGHT.sm, paddingHorizontal: theme.spacing(2.5) },\n md: { height: SIZE_HEIGHT.md, paddingHorizontal: theme.spacing(3) },\n lg: { height: SIZE_HEIGHT.lg, paddingHorizontal: theme.spacing(3.5) },\n icon: {\n width: SIZE_HEIGHT.icon,\n height: SIZE_HEIGHT.icon,\n padding: 0,\n borderRadius: theme.borderRadius.DEFAULT,\n },\n 'icon-md': { width: SIZE_HEIGHT['icon-md'], height: SIZE_HEIGHT['icon-md'], padding: 0 },\n card: {\n alignSelf: 'stretch',\n flexDirection: 'column',\n alignItems: 'stretch',\n justifyContent: 'flex-start',\n gap: theme.spacing(1),\n minHeight: SIZE_HEIGHT.card,\n paddingHorizontal: 11,\n paddingVertical: theme.spacing(2.5),\n },\n },\n },\n },\n pressed: {\n variants: {\n variant: {\n primary: { opacity: PRESSED_FILL_OPACITY },\n secondary: { opacity: PRESSED_FILL_OPACITY },\n outline: {},\n ghost: { backgroundColor: theme.colors.panel },\n subtle: { backgroundColor: theme.colors.deep },\n danger: { opacity: PRESSED_FILL_OPACITY },\n 'danger-outline': { opacity: PRESSED_FILL_OPACITY },\n success: { opacity: PRESSED_FILL_OPACITY },\n link: {},\n },\n },\n },\n disabled: { opacity: DISABLED_OPACITY },\n label: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontWeight: theme.fontWeight.medium,\n variants: {\n variant: {\n primary: { fontWeight: theme.fontWeight.semibold },\n secondary: { fontWeight: theme.fontWeight.semibold },\n outline: {},\n ghost: {},\n subtle: {},\n danger: { fontWeight: theme.fontWeight.semibold },\n 'danger-outline': { fontWeight: theme.fontWeight.semibold },\n success: { fontWeight: theme.fontWeight.semibold },\n link: {},\n },\n size: {\n xs: { fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.sm },\n sm: { fontSize: theme.fontSize.sm, lineHeight: theme.lineHeight.sm },\n md: { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n lg: { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n icon: { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n 'icon-md': { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n card: { fontSize: theme.fontSize.base, lineHeight: theme.lineHeight.base },\n },\n },\n },\n labelPressed: {\n variants: {\n variant: {\n primary: {},\n secondary: {},\n outline: {},\n ghost: {},\n subtle: {},\n danger: {},\n 'danger-outline': {},\n success: {},\n link: { textDecorationLine: 'underline' },\n },\n },\n },\n}));\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/components/CodeBlock.d.ts
|
|
2
|
+
export interface CodeBlockProps {
|
|
3
|
+
/** The block's source, without the fence lines. */
|
|
4
|
+
text: string;
|
|
5
|
+
/** The fence's language word, such as `ts`; the web reads it from a `language-*` class. */
|
|
6
|
+
language?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function CodeBlock({ text, language }: CodeBlockProps): import("react").JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
//# sourceMappingURL=CodeBlock.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.mts","names":[],"sources":["../../src/components/CodeBlock.tsx"],"mappings":";iBA4BiB;;EAEf;;EAEA;;wBAGc,YAAY,MAAM,YAAY,iCAAc,IAAA"}
|