@sublime-ui/library 0.1.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 +21 -0
- package/README.md +42 -0
- package/dist/components/AppBar/AppBar.d.ts +6 -0
- package/dist/components/AppBar/AppBar.js +55 -0
- package/dist/components/AppBar/AppBar.types.d.ts +11 -0
- package/dist/components/AppBar/AppBar.types.js +0 -0
- package/dist/components/AppBar/index.d.ts +3 -0
- package/dist/components/AppBar/index.js +4 -0
- package/dist/components/Avatar/Avatar.d.ts +7 -0
- package/dist/components/Avatar/Avatar.js +30 -0
- package/dist/components/Avatar/Avatar.types.d.ts +12 -0
- package/dist/components/Avatar/Avatar.types.js +0 -0
- package/dist/components/Avatar/index.d.ts +4 -0
- package/dist/components/Avatar/index.js +4 -0
- package/dist/components/Badge/Badge.d.ts +7 -0
- package/dist/components/Badge/Badge.js +75 -0
- package/dist/components/Badge/Badge.types.d.ts +12 -0
- package/dist/components/Badge/Badge.types.js +0 -0
- package/dist/components/Badge/index.d.ts +4 -0
- package/dist/components/Badge/index.js +4 -0
- package/dist/components/Banner/Banner.d.ts +7 -0
- package/dist/components/Banner/Banner.js +68 -0
- package/dist/components/Banner/Banner.types.d.ts +13 -0
- package/dist/components/Banner/Banner.types.js +0 -0
- package/dist/components/Banner/index.d.ts +4 -0
- package/dist/components/Banner/index.js +4 -0
- package/dist/components/BottomNav/BottomNav.d.ts +7 -0
- package/dist/components/BottomNav/BottomNav.js +9 -0
- package/dist/components/BottomNav/BottomNav.types.d.ts +10 -0
- package/dist/components/BottomNav/BottomNav.types.js +0 -0
- package/dist/components/BottomNav/index.d.ts +3 -0
- package/dist/components/BottomNav/index.js +4 -0
- package/dist/components/Button/Button.d.ts +7 -0
- package/dist/components/Button/Button.js +42 -0
- package/dist/components/Button/Button.types.d.ts +17 -0
- package/dist/components/Button/Button.types.js +0 -0
- package/dist/components/Button/index.d.ts +4 -0
- package/dist/components/Button/index.js +4 -0
- package/dist/components/Card/Card.d.ts +6 -0
- package/dist/components/Card/Card.js +26 -0
- package/dist/components/Card/Card.types.d.ts +10 -0
- package/dist/components/Card/Card.types.js +0 -0
- package/dist/components/Card/index.d.ts +3 -0
- package/dist/components/Card/index.js +4 -0
- package/dist/components/Checkbox/Checkbox.d.ts +7 -0
- package/dist/components/Checkbox/Checkbox.js +34 -0
- package/dist/components/Checkbox/Checkbox.types.d.ts +17 -0
- package/dist/components/Checkbox/Checkbox.types.js +0 -0
- package/dist/components/Checkbox/index.d.ts +4 -0
- package/dist/components/Checkbox/index.js +4 -0
- package/dist/components/Dialog/Dialog.d.ts +6 -0
- package/dist/components/Dialog/Dialog.js +51 -0
- package/dist/components/Dialog/Dialog.types.d.ts +12 -0
- package/dist/components/Dialog/Dialog.types.js +0 -0
- package/dist/components/Dialog/index.d.ts +3 -0
- package/dist/components/Dialog/index.js +4 -0
- package/dist/components/Divider/Divider.d.ts +6 -0
- package/dist/components/Divider/Divider.js +19 -0
- package/dist/components/Divider/Divider.types.d.ts +7 -0
- package/dist/components/Divider/Divider.types.js +0 -0
- package/dist/components/Divider/index.d.ts +3 -0
- package/dist/components/Divider/index.js +4 -0
- package/dist/components/Drawer/Drawer.d.ts +8 -0
- package/dist/components/Drawer/Drawer.js +9 -0
- package/dist/components/Drawer/Drawer.types.d.ts +13 -0
- package/dist/components/Drawer/Drawer.types.js +0 -0
- package/dist/components/Drawer/index.d.ts +4 -0
- package/dist/components/Drawer/index.js +4 -0
- package/dist/components/Fab/Fab.d.ts +7 -0
- package/dist/components/Fab/Fab.js +33 -0
- package/dist/components/Fab/Fab.types.d.ts +11 -0
- package/dist/components/Fab/Fab.types.js +0 -0
- package/dist/components/Fab/index.d.ts +4 -0
- package/dist/components/Fab/index.js +4 -0
- package/dist/components/GlassAppBar/GlassAppBar.d.ts +6 -0
- package/dist/components/GlassAppBar/GlassAppBar.js +57 -0
- package/dist/components/GlassAppBar/GlassAppBar.types.d.ts +12 -0
- package/dist/components/GlassAppBar/GlassAppBar.types.js +0 -0
- package/dist/components/GlassAppBar/index.d.ts +3 -0
- package/dist/components/GlassAppBar/index.js +4 -0
- package/dist/components/Icon/Icon.d.ts +8 -0
- package/dist/components/Icon/Icon.js +46 -0
- package/dist/components/Icon/Icon.types.d.ts +14 -0
- package/dist/components/Icon/Icon.types.js +0 -0
- package/dist/components/Icon/index.d.ts +5 -0
- package/dist/components/Icon/index.js +4 -0
- package/dist/components/Input/Input.d.ts +6 -0
- package/dist/components/Input/Input.js +43 -0
- package/dist/components/Input/Input.types.d.ts +13 -0
- package/dist/components/Input/Input.types.js +0 -0
- package/dist/components/Input/index.d.ts +3 -0
- package/dist/components/Input/index.js +4 -0
- package/dist/components/Select/Select.d.ts +6 -0
- package/dist/components/Select/Select.js +50 -0
- package/dist/components/Select/Select.types.d.ts +15 -0
- package/dist/components/Select/Select.types.js +0 -0
- package/dist/components/Select/index.d.ts +3 -0
- package/dist/components/Select/index.js +4 -0
- package/dist/components/Spinner/Spinner.d.ts +7 -0
- package/dist/components/Spinner/Spinner.js +35 -0
- package/dist/components/Spinner/Spinner.types.d.ts +9 -0
- package/dist/components/Spinner/Spinner.types.js +0 -0
- package/dist/components/Spinner/index.d.ts +4 -0
- package/dist/components/Spinner/index.js +4 -0
- package/dist/components/Surface/Surface.d.ts +6 -0
- package/dist/components/Surface/Surface.js +29 -0
- package/dist/components/Surface/Surface.types.d.ts +11 -0
- package/dist/components/Surface/Surface.types.js +0 -0
- package/dist/components/Surface/index.d.ts +3 -0
- package/dist/components/Surface/index.js +4 -0
- package/dist/components/Switch/Switch.d.ts +7 -0
- package/dist/components/Switch/Switch.js +34 -0
- package/dist/components/Switch/Switch.types.d.ts +17 -0
- package/dist/components/Switch/Switch.types.js +0 -0
- package/dist/components/Switch/index.d.ts +4 -0
- package/dist/components/Switch/index.js +4 -0
- package/dist/components/Text/Text.d.ts +7 -0
- package/dist/components/Text/Text.js +34 -0
- package/dist/components/Text/Text.types.d.ts +13 -0
- package/dist/components/Text/Text.types.js +0 -0
- package/dist/components/Text/index.d.ts +4 -0
- package/dist/components/Text/index.js +4 -0
- package/dist/components/Tooltip/Tooltip.d.ts +6 -0
- package/dist/components/Tooltip/Tooltip.js +8 -0
- package/dist/components/Tooltip/Tooltip.types.d.ts +11 -0
- package/dist/components/Tooltip/Tooltip.types.js +0 -0
- package/dist/components/Tooltip/index.d.ts +3 -0
- package/dist/components/Tooltip/index.js +4 -0
- package/dist/components/common.d.ts +11 -0
- package/dist/components/common.js +0 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +54 -0
- package/dist/notifications/NotificationContext.d.ts +33 -0
- package/dist/notifications/NotificationContext.js +34 -0
- package/dist/notifications/NotificationHost.d.ts +5 -0
- package/dist/notifications/NotificationHost.js +22 -0
- package/dist/notifications/useNotify.d.ts +13 -0
- package/dist/notifications/useNotify.js +19 -0
- package/dist/provider/SublimeProvider.d.ts +12 -0
- package/dist/provider/SublimeProvider.js +23 -0
- package/dist/provider/TokenContext.d.ts +14 -0
- package/dist/provider/TokenContext.js +5 -0
- package/dist/provider/resolveTokens.d.ts +7 -0
- package/dist/provider/resolveTokens.js +13 -0
- package/dist/provider/useTokens.d.ts +7 -0
- package/dist/provider/useTokens.js +12 -0
- package/dist/tokens/generateThemes.d.ts +10 -0
- package/dist/tokens/generateThemes.js +44 -0
- package/dist/tokens/tokens.d.ts +74 -0
- package/dist/tokens/tokens.js +78 -0
- package/package.json +53 -0
- package/src/components/AppBar/AppBar.native.tsx +28 -0
- package/src/components/AppBar/AppBar.tsx +51 -0
- package/src/components/AppBar/AppBar.types.ts +9 -0
- package/src/components/AppBar/index.ts +2 -0
- package/src/components/Avatar/Avatar.native.tsx +38 -0
- package/src/components/Avatar/Avatar.tsx +38 -0
- package/src/components/Avatar/Avatar.types.ts +10 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Badge/Badge.native.tsx +97 -0
- package/src/components/Badge/Badge.tsx +89 -0
- package/src/components/Badge/Badge.types.ts +11 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Banner/Banner.native.tsx +89 -0
- package/src/components/Banner/Banner.tsx +78 -0
- package/src/components/Banner/Banner.types.ts +11 -0
- package/src/components/Banner/index.ts +2 -0
- package/src/components/BottomNav/BottomNav.native.tsx +87 -0
- package/src/components/BottomNav/BottomNav.tsx +9 -0
- package/src/components/BottomNav/BottomNav.types.ts +8 -0
- package/src/components/BottomNav/index.ts +2 -0
- package/src/components/Button/Button.native.tsx +27 -0
- package/src/components/Button/Button.tsx +37 -0
- package/src/components/Button/Button.types.ts +17 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Card/Card.native.tsx +24 -0
- package/src/components/Card/Card.tsx +30 -0
- package/src/components/Card/Card.types.ts +8 -0
- package/src/components/Card/index.ts +2 -0
- package/src/components/Checkbox/Checkbox.native.tsx +58 -0
- package/src/components/Checkbox/Checkbox.tsx +35 -0
- package/src/components/Checkbox/Checkbox.types.ts +15 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/Dialog/Dialog.native.tsx +28 -0
- package/src/components/Dialog/Dialog.tsx +49 -0
- package/src/components/Dialog/Dialog.types.ts +10 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Divider/Divider.native.tsx +30 -0
- package/src/components/Divider/Divider.tsx +16 -0
- package/src/components/Divider/Divider.types.ts +5 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Drawer/Drawer.native.tsx +113 -0
- package/src/components/Drawer/Drawer.tsx +9 -0
- package/src/components/Drawer/Drawer.types.ts +11 -0
- package/src/components/Drawer/index.ts +2 -0
- package/src/components/Fab/Fab.native.tsx +41 -0
- package/src/components/Fab/Fab.tsx +36 -0
- package/src/components/Fab/Fab.types.ts +9 -0
- package/src/components/Fab/index.ts +2 -0
- package/src/components/GlassAppBar/GlassAppBar.native.tsx +29 -0
- package/src/components/GlassAppBar/GlassAppBar.tsx +53 -0
- package/src/components/GlassAppBar/GlassAppBar.types.ts +10 -0
- package/src/components/GlassAppBar/index.ts +2 -0
- package/src/components/Icon/Icon.native.tsx +39 -0
- package/src/components/Icon/Icon.tsx +57 -0
- package/src/components/Icon/Icon.types.ts +13 -0
- package/src/components/Icon/index.ts +2 -0
- package/src/components/Input/Input.native.tsx +34 -0
- package/src/components/Input/Input.tsx +33 -0
- package/src/components/Input/Input.types.ts +11 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Select/Select.native.tsx +63 -0
- package/src/components/Select/Select.tsx +48 -0
- package/src/components/Select/Select.types.ts +14 -0
- package/src/components/Select/index.ts +2 -0
- package/src/components/Spinner/Spinner.native.tsx +38 -0
- package/src/components/Spinner/Spinner.tsx +37 -0
- package/src/components/Spinner/Spinner.types.ts +7 -0
- package/src/components/Spinner/index.ts +2 -0
- package/src/components/Surface/Surface.native.tsx +32 -0
- package/src/components/Surface/Surface.tsx +31 -0
- package/src/components/Surface/Surface.types.ts +10 -0
- package/src/components/Surface/index.ts +2 -0
- package/src/components/Switch/Switch.native.tsx +58 -0
- package/src/components/Switch/Switch.tsx +35 -0
- package/src/components/Switch/Switch.types.ts +15 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Text/Text.native.tsx +31 -0
- package/src/components/Text/Text.tsx +34 -0
- package/src/components/Text/Text.types.ts +12 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.native.tsx +6 -0
- package/src/components/Tooltip/Tooltip.tsx +10 -0
- package/src/components/Tooltip/Tooltip.types.ts +9 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/common.ts +10 -0
- package/src/index.ts +33 -0
- package/src/notifications/NotificationContext.tsx +57 -0
- package/src/notifications/NotificationHost.native.tsx +20 -0
- package/src/notifications/NotificationHost.tsx +27 -0
- package/src/notifications/useNotify.ts +18 -0
- package/src/provider/SublimeProvider.native.tsx +29 -0
- package/src/provider/SublimeProvider.tsx +30 -0
- package/src/provider/TokenContext.ts +13 -0
- package/src/provider/resolveTokens.ts +16 -0
- package/src/provider/useTokens.ts +10 -0
- package/src/test-utils/renderWeb.tsx +8 -0
- package/src/tokens/generateThemes.ts +49 -0
- package/src/tokens/tokens.ts +71 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Portal, Dialog as PaperDialog, Text } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { DialogProps } from './Dialog.types.js';
|
|
4
|
+
|
|
5
|
+
export function Dialog({ open, onClose, title, children, actions, testID }: DialogProps) {
|
|
6
|
+
const tokens = useTokens();
|
|
7
|
+
return (
|
|
8
|
+
<Portal>
|
|
9
|
+
<PaperDialog
|
|
10
|
+
visible={open}
|
|
11
|
+
onDismiss={onClose}
|
|
12
|
+
style={{
|
|
13
|
+
backgroundColor: tokens.color.glassBg,
|
|
14
|
+
borderColor: tokens.color.glassBorder,
|
|
15
|
+
borderWidth: 1,
|
|
16
|
+
borderRadius: tokens.radii.lg,
|
|
17
|
+
}}
|
|
18
|
+
{...(testID ? { testID } : {})}
|
|
19
|
+
>
|
|
20
|
+
{title ? <PaperDialog.Title>{title}</PaperDialog.Title> : null}
|
|
21
|
+
<PaperDialog.Content>
|
|
22
|
+
{typeof children === 'string' ? <Text>{children}</Text> : children}
|
|
23
|
+
</PaperDialog.Content>
|
|
24
|
+
{actions ? <PaperDialog.Actions>{actions}</PaperDialog.Actions> : null}
|
|
25
|
+
</PaperDialog>
|
|
26
|
+
</Portal>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Dialog as MuiDialog,
|
|
3
|
+
DialogTitle,
|
|
4
|
+
DialogContent,
|
|
5
|
+
DialogActions,
|
|
6
|
+
IconButton,
|
|
7
|
+
} from '@mui/material';
|
|
8
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
9
|
+
import type { DialogProps } from './Dialog.types.js';
|
|
10
|
+
|
|
11
|
+
export function Dialog({ open, onClose, title, children, actions, testID }: DialogProps) {
|
|
12
|
+
const tokens = useTokens();
|
|
13
|
+
return (
|
|
14
|
+
<MuiDialog
|
|
15
|
+
open={open}
|
|
16
|
+
onClose={onClose}
|
|
17
|
+
data-testid={testID}
|
|
18
|
+
PaperProps={{
|
|
19
|
+
sx: {
|
|
20
|
+
backgroundColor: tokens.color.glassBg,
|
|
21
|
+
border: `1px solid ${tokens.color.glassBorder}`,
|
|
22
|
+
borderRadius: `${tokens.radii.lg}px`,
|
|
23
|
+
backgroundImage: 'none',
|
|
24
|
+
},
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
{title ? (
|
|
28
|
+
<DialogTitle
|
|
29
|
+
sx={{
|
|
30
|
+
display: 'flex',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
justifyContent: 'space-between',
|
|
33
|
+
fontWeight: tokens.typography.weights.semibold,
|
|
34
|
+
color: tokens.color.foreground,
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
{title}
|
|
38
|
+
<IconButton aria-label="close" onClick={onClose} size="small" edge="end">
|
|
39
|
+
<span className="material-icons" aria-hidden>
|
|
40
|
+
close
|
|
41
|
+
</span>
|
|
42
|
+
</IconButton>
|
|
43
|
+
</DialogTitle>
|
|
44
|
+
) : null}
|
|
45
|
+
<DialogContent sx={{ color: tokens.color.foreground }}>{children}</DialogContent>
|
|
46
|
+
{actions ? <DialogActions>{actions}</DialogActions> : null}
|
|
47
|
+
</MuiDialog>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import { Divider as PaperDivider } from 'react-native-paper';
|
|
3
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
4
|
+
import type { DividerProps } from './Divider.types.js';
|
|
5
|
+
|
|
6
|
+
export function Divider({ vertical, inset, testID }: DividerProps) {
|
|
7
|
+
const tokens = useTokens();
|
|
8
|
+
if (vertical) {
|
|
9
|
+
return (
|
|
10
|
+
<View
|
|
11
|
+
accessibilityRole="none"
|
|
12
|
+
style={{
|
|
13
|
+
alignSelf: 'stretch',
|
|
14
|
+
width: 1,
|
|
15
|
+
backgroundColor: tokens.color.divider,
|
|
16
|
+
}}
|
|
17
|
+
{...(testID ? { testID } : {})}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return (
|
|
22
|
+
<PaperDivider
|
|
23
|
+
style={{
|
|
24
|
+
backgroundColor: tokens.color.divider,
|
|
25
|
+
marginLeft: inset ? tokens.spacing.lg : 0,
|
|
26
|
+
}}
|
|
27
|
+
{...(testID ? { testID } : {})}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Divider as MuiDivider } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { DividerProps } from './Divider.types.js';
|
|
4
|
+
|
|
5
|
+
export function Divider({ vertical, inset, testID }: DividerProps) {
|
|
6
|
+
const tokens = useTokens();
|
|
7
|
+
return (
|
|
8
|
+
<MuiDivider
|
|
9
|
+
orientation={vertical ? 'vertical' : 'horizontal'}
|
|
10
|
+
flexItem={vertical === true}
|
|
11
|
+
{...(inset ? { variant: 'inset' as const } : {})}
|
|
12
|
+
data-testid={testID}
|
|
13
|
+
sx={{ borderColor: tokens.color.divider }}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { View, ScrollView, Pressable } from 'react-native';
|
|
2
|
+
import { Surface, Text, Icon } from 'react-native-paper';
|
|
3
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
4
|
+
import type { NavItem } from '../common.js';
|
|
5
|
+
import type { DrawerProps } from './Drawer.types.js';
|
|
6
|
+
|
|
7
|
+
function DrawerBadge({ value, fg, bg }: { value: string | number; fg: string; bg: string }) {
|
|
8
|
+
return (
|
|
9
|
+
<View
|
|
10
|
+
style={{
|
|
11
|
+
minWidth: 18,
|
|
12
|
+
height: 18,
|
|
13
|
+
paddingHorizontal: 5,
|
|
14
|
+
borderRadius: 9,
|
|
15
|
+
backgroundColor: bg,
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<Text style={{ color: fg, fontSize: 10, fontWeight: '600' as '600' }}>{String(value)}</Text>
|
|
21
|
+
</View>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Drawer({ items, activeKey, onSelect, header, footer, testID }: DrawerProps) {
|
|
26
|
+
const tokens = useTokens();
|
|
27
|
+
return (
|
|
28
|
+
<Surface
|
|
29
|
+
elevation={1}
|
|
30
|
+
{...(testID ? { testID } : {})}
|
|
31
|
+
style={{
|
|
32
|
+
flex: 1,
|
|
33
|
+
backgroundColor: tokens.color.glassBg,
|
|
34
|
+
borderRightColor: tokens.color.glassBorder,
|
|
35
|
+
borderRightWidth: 1,
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{header != null ? (
|
|
39
|
+
<View
|
|
40
|
+
style={{
|
|
41
|
+
padding: tokens.spacing.lg,
|
|
42
|
+
borderBottomColor: tokens.color.divider,
|
|
43
|
+
borderBottomWidth: 1,
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{header}
|
|
47
|
+
</View>
|
|
48
|
+
) : null}
|
|
49
|
+
<ScrollView
|
|
50
|
+
contentContainerStyle={{
|
|
51
|
+
paddingVertical: tokens.spacing.sm,
|
|
52
|
+
paddingHorizontal: tokens.spacing.sm,
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{items.map((item: NavItem) => {
|
|
56
|
+
const active = item.key === activeKey;
|
|
57
|
+
const fg = active ? tokens.color.primarySoftFg : tokens.color.foreground;
|
|
58
|
+
return (
|
|
59
|
+
<Pressable
|
|
60
|
+
key={item.key}
|
|
61
|
+
onPress={() => onSelect(item.key)}
|
|
62
|
+
accessibilityRole="button"
|
|
63
|
+
accessibilityState={{ selected: active }}
|
|
64
|
+
testID={`${testID ?? 'drawer'}-${item.key}`}
|
|
65
|
+
style={{
|
|
66
|
+
flexDirection: 'row',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
paddingVertical: tokens.spacing.sm,
|
|
69
|
+
paddingHorizontal: tokens.spacing.md,
|
|
70
|
+
marginVertical: tokens.spacing.xs / 2,
|
|
71
|
+
borderRadius: tokens.radii.md,
|
|
72
|
+
backgroundColor: active ? tokens.color.primarySoftBg : 'transparent',
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<Icon source={item.icon} size={tokens.typography.sizes.lg} color={fg} />
|
|
76
|
+
<Text
|
|
77
|
+
style={{
|
|
78
|
+
flex: 1,
|
|
79
|
+
marginLeft: tokens.spacing.md,
|
|
80
|
+
color: fg,
|
|
81
|
+
fontSize: tokens.typography.sizes.md,
|
|
82
|
+
fontWeight: String(
|
|
83
|
+
active ? tokens.typography.weights.semibold : tokens.typography.weights.medium,
|
|
84
|
+
) as '600',
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{item.label}
|
|
88
|
+
</Text>
|
|
89
|
+
{item.badge != null ? (
|
|
90
|
+
<DrawerBadge
|
|
91
|
+
value={item.badge}
|
|
92
|
+
bg={tokens.color.danger}
|
|
93
|
+
fg={tokens.color.primaryFg}
|
|
94
|
+
/>
|
|
95
|
+
) : null}
|
|
96
|
+
</Pressable>
|
|
97
|
+
);
|
|
98
|
+
})}
|
|
99
|
+
</ScrollView>
|
|
100
|
+
{footer != null ? (
|
|
101
|
+
<View
|
|
102
|
+
style={{
|
|
103
|
+
padding: tokens.spacing.lg,
|
|
104
|
+
borderTopColor: tokens.color.divider,
|
|
105
|
+
borderTopWidth: 1,
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
{footer}
|
|
109
|
+
</View>
|
|
110
|
+
) : null}
|
|
111
|
+
</Surface>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DrawerProps } from './Drawer.types.js';
|
|
2
|
+
|
|
3
|
+
/** Web stub: Drawer is a mobile-only component. */
|
|
4
|
+
export function Drawer(_props: DrawerProps): null {
|
|
5
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
6
|
+
console.warn('Drawer is mobile-only');
|
|
7
|
+
}
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { NavItem } from '../common.js';
|
|
3
|
+
|
|
4
|
+
export interface DrawerProps {
|
|
5
|
+
items: NavItem[];
|
|
6
|
+
activeKey: string;
|
|
7
|
+
onSelect: (key: string) => void;
|
|
8
|
+
header?: ReactNode;
|
|
9
|
+
footer?: ReactNode;
|
|
10
|
+
testID?: string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FAB } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { Tone } from '../common.js';
|
|
4
|
+
import type { FabProps } from './Fab.types.js';
|
|
5
|
+
|
|
6
|
+
const paperColor = (c: ReturnType<typeof useTokens>['color'], tone: Tone): string => {
|
|
7
|
+
switch (tone) {
|
|
8
|
+
case 'success':
|
|
9
|
+
return c.success;
|
|
10
|
+
case 'danger':
|
|
11
|
+
return c.danger;
|
|
12
|
+
case 'warning':
|
|
13
|
+
return c.warning;
|
|
14
|
+
case 'info':
|
|
15
|
+
return c.info;
|
|
16
|
+
case 'neutral':
|
|
17
|
+
return c.foreground;
|
|
18
|
+
case 'primary':
|
|
19
|
+
default:
|
|
20
|
+
return c.primary;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function Fab({ icon, onPress, tone = 'primary', label, testID }: FabProps) {
|
|
25
|
+
const tokens = useTokens();
|
|
26
|
+
return (
|
|
27
|
+
<FAB
|
|
28
|
+
icon={icon}
|
|
29
|
+
color={paperColor(tokens.color, tone)}
|
|
30
|
+
onPress={onPress ?? (() => {})}
|
|
31
|
+
style={{
|
|
32
|
+
backgroundColor: tokens.color.surface,
|
|
33
|
+
borderColor: tokens.color.surfaceBorder,
|
|
34
|
+
borderWidth: 1,
|
|
35
|
+
borderRadius: tokens.radii.full,
|
|
36
|
+
}}
|
|
37
|
+
{...(label !== undefined ? { label } : {})}
|
|
38
|
+
{...(testID !== undefined ? { testID } : {})}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fab as MuiFab } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { Tone } from '../common.js';
|
|
4
|
+
import type { FabProps } from './Fab.types.js';
|
|
5
|
+
|
|
6
|
+
const muiColor = (
|
|
7
|
+
t: Tone,
|
|
8
|
+
): 'primary' | 'success' | 'error' | 'warning' | 'info' | 'inherit' =>
|
|
9
|
+
t === 'danger' ? 'error' : t === 'neutral' ? 'inherit' : t;
|
|
10
|
+
|
|
11
|
+
export function Fab({ icon, onPress, tone = 'primary', label, testID }: FabProps) {
|
|
12
|
+
const tokens = useTokens();
|
|
13
|
+
const extended = label !== undefined;
|
|
14
|
+
return (
|
|
15
|
+
<MuiFab
|
|
16
|
+
color={muiColor(tone)}
|
|
17
|
+
variant={extended ? 'extended' : 'circular'}
|
|
18
|
+
onClick={onPress}
|
|
19
|
+
data-testid={testID}
|
|
20
|
+
sx={{
|
|
21
|
+
backgroundColor: tokens.color.surface,
|
|
22
|
+
color: tokens.color.foreground,
|
|
23
|
+
border: `1px solid ${tokens.color.surfaceBorder}`,
|
|
24
|
+
backdropFilter: 'blur(12px)',
|
|
25
|
+
textTransform: 'none',
|
|
26
|
+
fontWeight: tokens.typography.weights.semibold,
|
|
27
|
+
'&:hover': { backgroundColor: tokens.color.surfaceHover },
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
<span className="material-icons" aria-hidden {...(extended ? { style: { marginRight: tokens.spacing.sm } } : {})}>
|
|
31
|
+
{icon}
|
|
32
|
+
</span>
|
|
33
|
+
{label}
|
|
34
|
+
</MuiFab>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Appbar } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { GlassAppBarProps } from './GlassAppBar.types.js';
|
|
4
|
+
|
|
5
|
+
export function GlassAppBar({ title, subtitle, onBack, actions, transparent, testID }: GlassAppBarProps) {
|
|
6
|
+
const tokens = useTokens();
|
|
7
|
+
return (
|
|
8
|
+
<Appbar.Header
|
|
9
|
+
testID={testID}
|
|
10
|
+
style={{
|
|
11
|
+
backgroundColor: transparent ? 'transparent' : tokens.color.glassBg,
|
|
12
|
+
borderBottomColor: tokens.color.glassBorder,
|
|
13
|
+
borderBottomWidth: 1,
|
|
14
|
+
elevation: 0,
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
{onBack ? <Appbar.BackAction onPress={onBack} accessibilityLabel="back" /> : null}
|
|
18
|
+
<Appbar.Content
|
|
19
|
+
title={title}
|
|
20
|
+
{...(subtitle ? { subtitle } : {})}
|
|
21
|
+
titleStyle={{
|
|
22
|
+
color: tokens.color.foreground,
|
|
23
|
+
fontWeight: String(tokens.typography.weights.semibold) as '600',
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
{actions ?? null}
|
|
27
|
+
</Appbar.Header>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AppBar as MuiAppBar, Toolbar, Typography, IconButton, Box } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { GlassAppBarProps } from './GlassAppBar.types.js';
|
|
4
|
+
|
|
5
|
+
export function GlassAppBar({ title, subtitle, onBack, actions, transparent, testID }: GlassAppBarProps) {
|
|
6
|
+
const tokens = useTokens();
|
|
7
|
+
return (
|
|
8
|
+
<MuiAppBar
|
|
9
|
+
position="static"
|
|
10
|
+
elevation={0}
|
|
11
|
+
data-testid={testID}
|
|
12
|
+
sx={{
|
|
13
|
+
backgroundColor: transparent ? 'transparent' : tokens.color.glassBg,
|
|
14
|
+
backdropFilter: 'blur(12px)',
|
|
15
|
+
color: tokens.color.foreground,
|
|
16
|
+
borderBottom: `1px solid ${tokens.color.glassBorder}`,
|
|
17
|
+
boxShadow: 'none',
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<Toolbar sx={{ gap: `${tokens.spacing.sm}px` }}>
|
|
21
|
+
{onBack ? (
|
|
22
|
+
<IconButton
|
|
23
|
+
edge="start"
|
|
24
|
+
aria-label="back"
|
|
25
|
+
onClick={onBack}
|
|
26
|
+
sx={{ color: tokens.color.foreground }}
|
|
27
|
+
>
|
|
28
|
+
<span aria-hidden>{'←'}</span>
|
|
29
|
+
</IconButton>
|
|
30
|
+
) : null}
|
|
31
|
+
<Box sx={{ flex: 1, minWidth: 0 }}>
|
|
32
|
+
<Typography
|
|
33
|
+
variant="h6"
|
|
34
|
+
noWrap
|
|
35
|
+
sx={{ fontWeight: tokens.typography.weights.semibold }}
|
|
36
|
+
>
|
|
37
|
+
{title}
|
|
38
|
+
</Typography>
|
|
39
|
+
{subtitle ? (
|
|
40
|
+
<Typography
|
|
41
|
+
variant="caption"
|
|
42
|
+
noWrap
|
|
43
|
+
sx={{ display: 'block', color: tokens.color.mutedFg }}
|
|
44
|
+
>
|
|
45
|
+
{subtitle}
|
|
46
|
+
</Typography>
|
|
47
|
+
) : null}
|
|
48
|
+
</Box>
|
|
49
|
+
{actions ?? null}
|
|
50
|
+
</Toolbar>
|
|
51
|
+
</MuiAppBar>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Icon as PaperIcon } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { ResolvedTokens } from '../../provider/TokenContext.js';
|
|
4
|
+
import type { ColorTokens } from '../../tokens/tokens.js';
|
|
5
|
+
import type { IconColor, IconProps } from './Icon.types.js';
|
|
6
|
+
import type { Size } from '../common.js';
|
|
7
|
+
|
|
8
|
+
const resolveSize = (
|
|
9
|
+
t: ResolvedTokens,
|
|
10
|
+
size: number | Size | undefined,
|
|
11
|
+
): number => {
|
|
12
|
+
if (typeof size === 'number') return size;
|
|
13
|
+
const key: Size = size ?? 'md';
|
|
14
|
+
return t.typography.sizes[key];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const resolveColor = (
|
|
18
|
+
c: ColorTokens,
|
|
19
|
+
color: IconColor | undefined,
|
|
20
|
+
): string | undefined => {
|
|
21
|
+
if (color === undefined) return undefined;
|
|
22
|
+
if (color in c) return c[color as keyof ColorTokens];
|
|
23
|
+
return color;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function Icon({ name, size, color, testID }: IconProps) {
|
|
27
|
+
const tokens = useTokens();
|
|
28
|
+
const px = resolveSize(tokens, size);
|
|
29
|
+
const resolved = resolveColor(tokens.color, color);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<PaperIcon
|
|
33
|
+
source={name}
|
|
34
|
+
size={px}
|
|
35
|
+
{...(resolved ? { color: resolved } : {})}
|
|
36
|
+
{...(testID ? { testID } : {})}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
2
|
+
import type { ResolvedTokens } from '../../provider/TokenContext.js';
|
|
3
|
+
import type { ColorTokens } from '../../tokens/tokens.js';
|
|
4
|
+
import type { IconColor, IconProps } from './Icon.types.js';
|
|
5
|
+
import type { Size } from '../common.js';
|
|
6
|
+
|
|
7
|
+
const resolveSize = (
|
|
8
|
+
t: ResolvedTokens,
|
|
9
|
+
size: number | Size | undefined,
|
|
10
|
+
): number => {
|
|
11
|
+
if (typeof size === 'number') return size;
|
|
12
|
+
const key: Size = size ?? 'md';
|
|
13
|
+
return t.typography.sizes[key];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const resolveColor = (
|
|
17
|
+
c: ColorTokens,
|
|
18
|
+
color: IconColor | undefined,
|
|
19
|
+
): string | undefined => {
|
|
20
|
+
if (color === undefined) return undefined;
|
|
21
|
+
if (color in c) return c[color as keyof ColorTokens];
|
|
22
|
+
return color;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function Icon({ name, node, size, color, testID }: IconProps) {
|
|
26
|
+
const tokens = useTokens();
|
|
27
|
+
const px = resolveSize(tokens, size);
|
|
28
|
+
const resolved = resolveColor(tokens.color, color);
|
|
29
|
+
|
|
30
|
+
if (node !== undefined) {
|
|
31
|
+
return (
|
|
32
|
+
<span
|
|
33
|
+
data-testid={testID}
|
|
34
|
+
style={{
|
|
35
|
+
display: 'inline-flex',
|
|
36
|
+
fontSize: `${px}px`,
|
|
37
|
+
...(resolved ? { color: resolved } : {}),
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{node}
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<span
|
|
47
|
+
className="material-icons"
|
|
48
|
+
data-testid={testID}
|
|
49
|
+
style={{
|
|
50
|
+
fontSize: `${px}px`,
|
|
51
|
+
...(resolved ? { color: resolved } : {}),
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{name}
|
|
55
|
+
</span>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Size } from '../common.js';
|
|
3
|
+
import type { ColorTokens } from '../../tokens/tokens.js';
|
|
4
|
+
|
|
5
|
+
export type IconColor = keyof ColorTokens | (string & {});
|
|
6
|
+
|
|
7
|
+
export interface IconProps {
|
|
8
|
+
name: string;
|
|
9
|
+
node?: ReactNode;
|
|
10
|
+
size?: number | Size;
|
|
11
|
+
color?: IconColor;
|
|
12
|
+
testID?: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import { TextInput, HelperText } from 'react-native-paper';
|
|
3
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
4
|
+
import type { InputProps } from './Input.types.js';
|
|
5
|
+
|
|
6
|
+
export function Input({
|
|
7
|
+
value, onChangeText, label, placeholder, error,
|
|
8
|
+
disabled, secureTextEntry, multiline, testID,
|
|
9
|
+
}: InputProps) {
|
|
10
|
+
const tokens = useTokens();
|
|
11
|
+
return (
|
|
12
|
+
<View>
|
|
13
|
+
<TextInput
|
|
14
|
+
mode="outlined"
|
|
15
|
+
value={value}
|
|
16
|
+
onChangeText={onChangeText}
|
|
17
|
+
{...(label !== undefined ? { label } : {})}
|
|
18
|
+
{...(placeholder !== undefined ? { placeholder } : {})}
|
|
19
|
+
{...(testID !== undefined ? { testID } : {})}
|
|
20
|
+
error={!!error}
|
|
21
|
+
disabled={!!disabled}
|
|
22
|
+
secureTextEntry={!!secureTextEntry}
|
|
23
|
+
multiline={!!multiline}
|
|
24
|
+
outlineColor={tokens.color.surfaceBorder}
|
|
25
|
+
style={{ borderRadius: tokens.radii.md }}
|
|
26
|
+
/>
|
|
27
|
+
{error !== undefined ? (
|
|
28
|
+
<HelperText type="error" visible={!!error}>
|
|
29
|
+
{error}
|
|
30
|
+
</HelperText>
|
|
31
|
+
) : null}
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TextField } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { InputProps } from './Input.types.js';
|
|
4
|
+
|
|
5
|
+
export function Input({
|
|
6
|
+
value, onChangeText, label, placeholder, error,
|
|
7
|
+
disabled, secureTextEntry, multiline, testID,
|
|
8
|
+
}: InputProps) {
|
|
9
|
+
const tokens = useTokens();
|
|
10
|
+
return (
|
|
11
|
+
<TextField
|
|
12
|
+
value={value}
|
|
13
|
+
onChange={(e) => onChangeText(e.target.value)}
|
|
14
|
+
{...(label !== undefined ? { label } : {})}
|
|
15
|
+
{...(placeholder !== undefined ? { placeholder } : {})}
|
|
16
|
+
{...(error !== undefined ? { helperText: error } : {})}
|
|
17
|
+
{...(testID !== undefined ? { 'data-testid': testID } : {})}
|
|
18
|
+
error={!!error}
|
|
19
|
+
disabled={!!disabled}
|
|
20
|
+
type={secureTextEntry ? 'password' : 'text'}
|
|
21
|
+
multiline={!!multiline}
|
|
22
|
+
fullWidth
|
|
23
|
+
variant="outlined"
|
|
24
|
+
size="small"
|
|
25
|
+
sx={{
|
|
26
|
+
'& .MuiOutlinedInput-root': {
|
|
27
|
+
borderRadius: `${tokens.radii.md}px`,
|
|
28
|
+
'& fieldset': { borderColor: tokens.color.surfaceBorder },
|
|
29
|
+
},
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|