@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,63 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Pressable, View } from 'react-native';
|
|
3
|
+
import { Menu, TextInput } from 'react-native-paper';
|
|
4
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
5
|
+
import type { SelectProps } from './Select.types.js';
|
|
6
|
+
|
|
7
|
+
export function Select({
|
|
8
|
+
value, onChange, options, label, placeholder, disabled, testID,
|
|
9
|
+
}: SelectProps) {
|
|
10
|
+
const tokens = useTokens();
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
const selected = options.find((o) => o.value === value);
|
|
13
|
+
const display = selected?.label ?? placeholder ?? '';
|
|
14
|
+
return (
|
|
15
|
+
<View {...(testID !== undefined ? { testID } : {})}>
|
|
16
|
+
<Menu
|
|
17
|
+
visible={open}
|
|
18
|
+
onDismiss={() => setOpen(false)}
|
|
19
|
+
contentStyle={{
|
|
20
|
+
backgroundColor: tokens.color.glassBg,
|
|
21
|
+
borderColor: tokens.color.glassBorder,
|
|
22
|
+
borderWidth: 1,
|
|
23
|
+
borderRadius: tokens.radii.md,
|
|
24
|
+
}}
|
|
25
|
+
anchor={
|
|
26
|
+
<Pressable
|
|
27
|
+
onPress={() => {
|
|
28
|
+
if (!disabled) setOpen(true);
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<TextInput
|
|
32
|
+
mode="outlined"
|
|
33
|
+
editable={false}
|
|
34
|
+
pointerEvents="none"
|
|
35
|
+
disabled={!!disabled}
|
|
36
|
+
value={display}
|
|
37
|
+
{...(label !== undefined ? { label } : {})}
|
|
38
|
+
{...(placeholder !== undefined ? { placeholder } : {})}
|
|
39
|
+
outlineColor={tokens.color.surfaceBorder}
|
|
40
|
+
right={<TextInput.Icon icon="menu-down" />}
|
|
41
|
+
style={{ borderRadius: tokens.radii.md }}
|
|
42
|
+
/>
|
|
43
|
+
</Pressable>
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
{options.length === 0 ? (
|
|
47
|
+
<Menu.Item disabled title="No options" />
|
|
48
|
+
) : (
|
|
49
|
+
options.map((opt) => (
|
|
50
|
+
<Menu.Item
|
|
51
|
+
key={opt.value}
|
|
52
|
+
title={opt.label}
|
|
53
|
+
onPress={() => {
|
|
54
|
+
onChange(opt.value);
|
|
55
|
+
setOpen(false);
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
))
|
|
59
|
+
)}
|
|
60
|
+
</Menu>
|
|
61
|
+
</View>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { FormControl, InputLabel, MenuItem, Select as MuiSelect } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { SelectProps } from './Select.types.js';
|
|
4
|
+
|
|
5
|
+
export function Select({
|
|
6
|
+
value, onChange, options, label, placeholder, disabled, testID,
|
|
7
|
+
}: SelectProps) {
|
|
8
|
+
const tokens = useTokens();
|
|
9
|
+
const labelId = label !== undefined ? `${label}-select-label` : undefined;
|
|
10
|
+
return (
|
|
11
|
+
<FormControl fullWidth size="small" disabled={!!disabled}>
|
|
12
|
+
{label !== undefined ? <InputLabel id={labelId}>{label}</InputLabel> : null}
|
|
13
|
+
<MuiSelect
|
|
14
|
+
{...(labelId !== undefined ? { labelId } : {})}
|
|
15
|
+
{...(label !== undefined ? { label } : {})}
|
|
16
|
+
{...(placeholder !== undefined ? { displayEmpty: true } : {})}
|
|
17
|
+
{...(testID !== undefined ? { 'data-testid': testID } : {})}
|
|
18
|
+
value={value}
|
|
19
|
+
onChange={(e) => onChange(e.target.value)}
|
|
20
|
+
MenuProps={{
|
|
21
|
+
PaperProps: {
|
|
22
|
+
sx: {
|
|
23
|
+
backgroundColor: tokens.color.glassBg,
|
|
24
|
+
border: `1px solid ${tokens.color.glassBorder}`,
|
|
25
|
+
backdropFilter: 'blur(12px)',
|
|
26
|
+
borderRadius: `${tokens.radii.md}px`,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}}
|
|
30
|
+
sx={{
|
|
31
|
+
borderRadius: `${tokens.radii.md}px`,
|
|
32
|
+
'& fieldset': { borderColor: tokens.color.surfaceBorder },
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{placeholder !== undefined ? (
|
|
36
|
+
<MenuItem value="" disabled>
|
|
37
|
+
{placeholder}
|
|
38
|
+
</MenuItem>
|
|
39
|
+
) : null}
|
|
40
|
+
{options.map((opt) => (
|
|
41
|
+
<MenuItem key={opt.value} value={opt.value}>
|
|
42
|
+
{opt.label}
|
|
43
|
+
</MenuItem>
|
|
44
|
+
))}
|
|
45
|
+
</MuiSelect>
|
|
46
|
+
</FormControl>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SelectOption {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SelectProps {
|
|
7
|
+
value: string;
|
|
8
|
+
onChange: (value: string) => void;
|
|
9
|
+
options: SelectOption[];
|
|
10
|
+
label?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
testID?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ActivityIndicator } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { ResolvedTokens } from '../../provider/TokenContext.js';
|
|
4
|
+
import type { Tone, Size } from '../common.js';
|
|
5
|
+
import type { SpinnerProps } from './Spinner.types.js';
|
|
6
|
+
|
|
7
|
+
const paperSize = (size: Size): 'small' | 'large' =>
|
|
8
|
+
size === 'lg' ? 'large' : 'small';
|
|
9
|
+
|
|
10
|
+
const toneColor = (c: ResolvedTokens['color'], tone: Tone): string => {
|
|
11
|
+
switch (tone) {
|
|
12
|
+
case 'primary':
|
|
13
|
+
return c.primary;
|
|
14
|
+
case 'success':
|
|
15
|
+
return c.success;
|
|
16
|
+
case 'danger':
|
|
17
|
+
return c.danger;
|
|
18
|
+
case 'warning':
|
|
19
|
+
return c.warning;
|
|
20
|
+
case 'info':
|
|
21
|
+
return c.info;
|
|
22
|
+
case 'neutral':
|
|
23
|
+
default:
|
|
24
|
+
return c.mutedFg;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function Spinner({ size = 'md', tone = 'primary', testID }: SpinnerProps) {
|
|
29
|
+
const tokens = useTokens();
|
|
30
|
+
return (
|
|
31
|
+
<ActivityIndicator
|
|
32
|
+
animating
|
|
33
|
+
size={paperSize(size)}
|
|
34
|
+
color={toneColor(tokens.color, tone)}
|
|
35
|
+
{...(testID ? { testID } : {})}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CircularProgress } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { ResolvedTokens } from '../../provider/TokenContext.js';
|
|
4
|
+
import type { Tone, Size } from '../common.js';
|
|
5
|
+
import type { SpinnerProps } from './Spinner.types.js';
|
|
6
|
+
|
|
7
|
+
const sizePx = (size: Size): number =>
|
|
8
|
+
size === 'sm' ? 16 : size === 'lg' ? 40 : 24;
|
|
9
|
+
|
|
10
|
+
const toneColor = (c: ResolvedTokens['color'], tone: Tone): string => {
|
|
11
|
+
switch (tone) {
|
|
12
|
+
case 'primary':
|
|
13
|
+
return c.primary;
|
|
14
|
+
case 'success':
|
|
15
|
+
return c.success;
|
|
16
|
+
case 'danger':
|
|
17
|
+
return c.danger;
|
|
18
|
+
case 'warning':
|
|
19
|
+
return c.warning;
|
|
20
|
+
case 'info':
|
|
21
|
+
return c.info;
|
|
22
|
+
case 'neutral':
|
|
23
|
+
default:
|
|
24
|
+
return c.mutedFg;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function Spinner({ size = 'md', tone = 'primary', testID }: SpinnerProps) {
|
|
29
|
+
const tokens = useTokens();
|
|
30
|
+
return (
|
|
31
|
+
<CircularProgress
|
|
32
|
+
size={sizePx(size)}
|
|
33
|
+
data-testid={testID}
|
|
34
|
+
sx={{ color: toneColor(tokens.color, tone) }}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Surface as PaperSurface } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { SurfaceProps, SurfaceElevation } from './Surface.types.js';
|
|
4
|
+
|
|
5
|
+
type PaperElevation = 0 | 1 | 2 | 3 | 4 | 5;
|
|
6
|
+
|
|
7
|
+
const paperElevation = (e: SurfaceElevation): PaperElevation =>
|
|
8
|
+
e === 'none' ? 0 : e === 'sm' ? 1 : e === 'md' ? 2 : 4;
|
|
9
|
+
|
|
10
|
+
export function Surface({
|
|
11
|
+
children,
|
|
12
|
+
elevation = 'sm',
|
|
13
|
+
padded = true,
|
|
14
|
+
testID,
|
|
15
|
+
}: SurfaceProps) {
|
|
16
|
+
const tokens = useTokens();
|
|
17
|
+
return (
|
|
18
|
+
<PaperSurface
|
|
19
|
+
elevation={paperElevation(elevation)}
|
|
20
|
+
style={{
|
|
21
|
+
backgroundColor: tokens.color.surface,
|
|
22
|
+
borderColor: tokens.color.surfaceBorder,
|
|
23
|
+
borderWidth: 1,
|
|
24
|
+
borderRadius: tokens.radii.lg,
|
|
25
|
+
padding: padded ? tokens.spacing.lg : 0,
|
|
26
|
+
}}
|
|
27
|
+
{...(testID ? { testID } : {})}
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</PaperSurface>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Box } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { SurfaceProps, SurfaceElevation } from './Surface.types.js';
|
|
4
|
+
|
|
5
|
+
const boxShadow = (
|
|
6
|
+
e: SurfaceElevation,
|
|
7
|
+
shadows: { sm: string; md: string; lg: string },
|
|
8
|
+
): string => (e === 'none' ? 'none' : shadows[e]);
|
|
9
|
+
|
|
10
|
+
export function Surface({
|
|
11
|
+
children,
|
|
12
|
+
elevation = 'sm',
|
|
13
|
+
padded = true,
|
|
14
|
+
testID,
|
|
15
|
+
}: SurfaceProps) {
|
|
16
|
+
const tokens = useTokens();
|
|
17
|
+
return (
|
|
18
|
+
<Box
|
|
19
|
+
data-testid={testID}
|
|
20
|
+
sx={{
|
|
21
|
+
backgroundColor: tokens.color.surface,
|
|
22
|
+
border: `1px solid ${tokens.color.surfaceBorder}`,
|
|
23
|
+
borderRadius: `${tokens.radii.lg}px`,
|
|
24
|
+
boxShadow: boxShadow(elevation, tokens.shadows),
|
|
25
|
+
padding: padded ? `${tokens.spacing.lg}px` : 0,
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</Box>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import { Switch as PaperSwitch, Text } from 'react-native-paper';
|
|
3
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
4
|
+
import type { ResolvedTokens } from '../../provider/TokenContext.js';
|
|
5
|
+
import type { Tone } from '../common.js';
|
|
6
|
+
import type { SwitchProps } from './Switch.types.js';
|
|
7
|
+
|
|
8
|
+
const toneColor = (c: ResolvedTokens['color'], tone: Tone): string => {
|
|
9
|
+
switch (tone) {
|
|
10
|
+
case 'success':
|
|
11
|
+
return c.success;
|
|
12
|
+
case 'danger':
|
|
13
|
+
return c.danger;
|
|
14
|
+
case 'warning':
|
|
15
|
+
return c.warning;
|
|
16
|
+
case 'info':
|
|
17
|
+
return c.info;
|
|
18
|
+
case 'neutral':
|
|
19
|
+
return c.secondary;
|
|
20
|
+
case 'primary':
|
|
21
|
+
default:
|
|
22
|
+
return c.primary;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function Switch({
|
|
27
|
+
value,
|
|
28
|
+
onValueChange,
|
|
29
|
+
label,
|
|
30
|
+
disabled,
|
|
31
|
+
tone = 'primary',
|
|
32
|
+
testID,
|
|
33
|
+
}: SwitchProps) {
|
|
34
|
+
const tokens = useTokens();
|
|
35
|
+
const color = toneColor(tokens.color, tone);
|
|
36
|
+
const control = (
|
|
37
|
+
<PaperSwitch
|
|
38
|
+
value={value}
|
|
39
|
+
onValueChange={onValueChange}
|
|
40
|
+
disabled={disabled ?? false}
|
|
41
|
+
color={color}
|
|
42
|
+
{...(testID === undefined ? {} : { testID })}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
if (label === undefined) return control;
|
|
46
|
+
return (
|
|
47
|
+
<View
|
|
48
|
+
style={{
|
|
49
|
+
flexDirection: 'row',
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
gap: tokens.spacing.sm,
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{control}
|
|
55
|
+
<Text style={{ color: tokens.color.foreground }}>{label}</Text>
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Switch as MuiSwitch, FormControlLabel } from '@mui/material';
|
|
2
|
+
import type { Tone } from '../common.js';
|
|
3
|
+
import type { SwitchProps } from './Switch.types.js';
|
|
4
|
+
|
|
5
|
+
const muiColor = (
|
|
6
|
+
t: Tone,
|
|
7
|
+
): 'primary' | 'success' | 'error' | 'warning' | 'info' | 'default' =>
|
|
8
|
+
t === 'danger' ? 'error' : t === 'neutral' ? 'default' : t;
|
|
9
|
+
|
|
10
|
+
export function Switch({
|
|
11
|
+
value,
|
|
12
|
+
onValueChange,
|
|
13
|
+
label,
|
|
14
|
+
disabled,
|
|
15
|
+
tone = 'primary',
|
|
16
|
+
testID,
|
|
17
|
+
}: SwitchProps) {
|
|
18
|
+
const control = (
|
|
19
|
+
<MuiSwitch
|
|
20
|
+
checked={value}
|
|
21
|
+
onChange={(e) => onValueChange(e.target.checked)}
|
|
22
|
+
color={muiColor(tone)}
|
|
23
|
+
data-testid={testID}
|
|
24
|
+
{...(disabled === undefined ? {} : { disabled })}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
if (label === undefined) return control;
|
|
28
|
+
return (
|
|
29
|
+
<FormControlLabel
|
|
30
|
+
control={control}
|
|
31
|
+
label={label}
|
|
32
|
+
{...(disabled === undefined ? {} : { disabled })}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Tone } from '../common.js';
|
|
2
|
+
|
|
3
|
+
export interface SwitchProps {
|
|
4
|
+
/** Whether the switch is on. */
|
|
5
|
+
value: boolean;
|
|
6
|
+
/** Called with the new value when toggled. */
|
|
7
|
+
onValueChange: (value: boolean) => void;
|
|
8
|
+
/** Optional label rendered beside the switch. */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Disables interaction. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Color tone applied to the on state. */
|
|
13
|
+
tone?: Tone;
|
|
14
|
+
testID?: string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Text as PaperText } from 'react-native-paper';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { TextProps, TextVariant } from './Text.types.js';
|
|
4
|
+
|
|
5
|
+
const paperVariant = (
|
|
6
|
+
v: TextVariant,
|
|
7
|
+
): 'titleMedium' | 'titleSmall' | 'bodyMedium' | 'bodySmall' =>
|
|
8
|
+
v === 'title'
|
|
9
|
+
? 'titleMedium'
|
|
10
|
+
: v === 'subtitle'
|
|
11
|
+
? 'titleSmall'
|
|
12
|
+
: v === 'caption'
|
|
13
|
+
? 'bodySmall'
|
|
14
|
+
: 'bodyMedium';
|
|
15
|
+
|
|
16
|
+
export function Text({
|
|
17
|
+
children, variant = 'body', numberOfLines, testID,
|
|
18
|
+
}: TextProps) {
|
|
19
|
+
const tokens = useTokens();
|
|
20
|
+
const muted = variant === 'caption' || variant === 'subtitle';
|
|
21
|
+
return (
|
|
22
|
+
<PaperText
|
|
23
|
+
variant={paperVariant(variant)}
|
|
24
|
+
numberOfLines={numberOfLines}
|
|
25
|
+
testID={testID}
|
|
26
|
+
style={{ color: muted ? tokens.color.mutedFg : tokens.color.foreground }}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</PaperText>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Typography } from '@mui/material';
|
|
2
|
+
import { useTokens } from '../../provider/useTokens.js';
|
|
3
|
+
import type { TextProps, TextVariant } from './Text.types.js';
|
|
4
|
+
|
|
5
|
+
const muiVariant = (v: TextVariant): 'h6' | 'subtitle1' | 'body1' | 'caption' =>
|
|
6
|
+
v === 'title' ? 'h6' : v === 'subtitle' ? 'subtitle1' : v === 'caption' ? 'caption' : 'body1';
|
|
7
|
+
|
|
8
|
+
export function Text({
|
|
9
|
+
children, variant = 'body', numberOfLines, testID,
|
|
10
|
+
}: TextProps) {
|
|
11
|
+
const tokens = useTokens();
|
|
12
|
+
const muted = variant === 'caption' || variant === 'subtitle';
|
|
13
|
+
const clamp =
|
|
14
|
+
numberOfLines !== undefined
|
|
15
|
+
? {
|
|
16
|
+
display: '-webkit-box',
|
|
17
|
+
WebkitLineClamp: numberOfLines,
|
|
18
|
+
WebkitBoxOrient: 'vertical' as const,
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
}
|
|
21
|
+
: {};
|
|
22
|
+
return (
|
|
23
|
+
<Typography
|
|
24
|
+
variant={muiVariant(variant)}
|
|
25
|
+
data-testid={testID}
|
|
26
|
+
sx={{
|
|
27
|
+
color: muted ? tokens.color.mutedFg : tokens.color.foreground,
|
|
28
|
+
...clamp,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</Typography>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Tone } from '../common.js';
|
|
3
|
+
|
|
4
|
+
export type TextVariant = 'title' | 'subtitle' | 'body' | 'caption';
|
|
5
|
+
|
|
6
|
+
export interface TextProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
variant?: TextVariant;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
numberOfLines?: number;
|
|
11
|
+
testID?: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Tooltip as MuiTooltip } from '@mui/material';
|
|
2
|
+
import type { TooltipProps } from './Tooltip.types.js';
|
|
3
|
+
|
|
4
|
+
export function Tooltip({ label, children, testID }: TooltipProps) {
|
|
5
|
+
return (
|
|
6
|
+
<MuiTooltip title={label} data-testid={testID}>
|
|
7
|
+
{children}
|
|
8
|
+
</MuiTooltip>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Variant = 'solid' | 'soft' | 'outline' | 'ghost';
|
|
2
|
+
export type Tone = 'primary' | 'success' | 'danger' | 'warning' | 'info' | 'neutral';
|
|
3
|
+
export type Size = 'sm' | 'md' | 'lg';
|
|
4
|
+
|
|
5
|
+
export interface NavItem {
|
|
6
|
+
key: string;
|
|
7
|
+
label: string;
|
|
8
|
+
icon: string;
|
|
9
|
+
badge?: string | number;
|
|
10
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// theming
|
|
2
|
+
export { SublimeProvider } from './provider/SublimeProvider.js';
|
|
3
|
+
export type { SublimeProviderProps } from './provider/SublimeProvider.js';
|
|
4
|
+
export { useTokens } from './provider/useTokens.js';
|
|
5
|
+
export { generateThemes } from './tokens/generateThemes.js';
|
|
6
|
+
export { defaultTokens } from './tokens/tokens.js';
|
|
7
|
+
export type { SublimeTokens, ColorTokens } from './tokens/tokens.js';
|
|
8
|
+
// notifications
|
|
9
|
+
export { useNotify } from './notifications/useNotify.js';
|
|
10
|
+
// components
|
|
11
|
+
export { Button } from './components/Button/index.js';
|
|
12
|
+
export type { ButtonProps } from './components/Button/index.js';
|
|
13
|
+
export { Text } from './components/Text/index.js';
|
|
14
|
+
export { Input } from './components/Input/index.js';
|
|
15
|
+
export { Card } from './components/Card/index.js';
|
|
16
|
+
export { Badge } from './components/Badge/index.js';
|
|
17
|
+
export { Icon } from './components/Icon/index.js';
|
|
18
|
+
export { Surface } from './components/Surface/index.js';
|
|
19
|
+
export { Divider } from './components/Divider/index.js';
|
|
20
|
+
export { Spinner } from './components/Spinner/index.js';
|
|
21
|
+
export { Dialog } from './components/Dialog/index.js';
|
|
22
|
+
export { Banner } from './components/Banner/index.js';
|
|
23
|
+
export { Fab } from './components/Fab/index.js';
|
|
24
|
+
export { AppBar } from './components/AppBar/index.js';
|
|
25
|
+
export { Select } from './components/Select/index.js';
|
|
26
|
+
export { Avatar } from './components/Avatar/index.js';
|
|
27
|
+
export { Tooltip } from './components/Tooltip/index.js';
|
|
28
|
+
export { Checkbox } from './components/Checkbox/index.js';
|
|
29
|
+
export { Switch } from './components/Switch/index.js';
|
|
30
|
+
export { GlassAppBar } from './components/GlassAppBar/index.js';
|
|
31
|
+
export { BottomNav } from './components/BottomNav/index.js';
|
|
32
|
+
export { Drawer } from './components/Drawer/index.js';
|
|
33
|
+
export type { NavItem } from './components/common.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Tone = 'success' | 'error' | 'warning' | 'info' | 'neutral';
|
|
4
|
+
|
|
5
|
+
export interface Notification {
|
|
6
|
+
id: string;
|
|
7
|
+
message: string;
|
|
8
|
+
tone: Tone;
|
|
9
|
+
duration: number;
|
|
10
|
+
action?: { label: string; onPress: () => void };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface NotifyOptions {
|
|
14
|
+
tone?: Tone;
|
|
15
|
+
duration?: number;
|
|
16
|
+
action?: { label: string; onPress: () => void };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface QueueApi {
|
|
20
|
+
queue: Notification[];
|
|
21
|
+
notify: (message: string, opts?: NotifyOptions) => string;
|
|
22
|
+
dismiss: (id: string) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Ctx = createContext<QueueApi | null>(null);
|
|
26
|
+
|
|
27
|
+
export function NotificationProvider({ children }: { children: ReactNode }) {
|
|
28
|
+
const [queue, setQueue] = useState<Notification[]>([]);
|
|
29
|
+
const counter = useRef(0);
|
|
30
|
+
|
|
31
|
+
const dismiss = useCallback((id: string) => {
|
|
32
|
+
setQueue((q) => q.filter((n) => n.id !== id));
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const notify = useCallback((message: string, opts: NotifyOptions = {}) => {
|
|
36
|
+
counter.current += 1;
|
|
37
|
+
const id = `n${counter.current}`;
|
|
38
|
+
const n: Notification = {
|
|
39
|
+
id,
|
|
40
|
+
message,
|
|
41
|
+
tone: opts.tone ?? 'neutral',
|
|
42
|
+
duration: opts.duration ?? 4000,
|
|
43
|
+
...(opts.action ? { action: opts.action } : {}),
|
|
44
|
+
};
|
|
45
|
+
setQueue((q) => [...q, n]);
|
|
46
|
+
return id;
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
const value = useMemo<QueueApi>(() => ({ queue, notify, dismiss }), [queue, notify, dismiss]);
|
|
50
|
+
return <Ctx.Provider value={value}>{children}</Ctx.Provider>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useNotificationQueue(): QueueApi {
|
|
54
|
+
const api = useContext(Ctx);
|
|
55
|
+
if (api === null) throw new Error('Notifications require a <SublimeProvider>.');
|
|
56
|
+
return api;
|
|
57
|
+
}
|