@ponti-studios/ui 1.0.1 → 1.2.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/package.json +7 -2
- package/src/components/feedback/update-guard.tsx +5 -1
- package/src/components/filters/active-filters-bar.stories.tsx +34 -36
- package/src/components/filters/filter-select.stories.tsx +26 -26
- package/src/components/filters/filter-select.tsx +1 -6
- package/src/components/filters/filters.stories.tsx +21 -21
- package/src/components/filters/filters.tsx +1 -7
- package/src/components/filters/search-filter-bar.stories.tsx +8 -10
- package/src/components/filters/search-filter-bar.tsx +6 -8
- package/src/components/navigation/app-navigation.stories.tsx +5 -1
- package/src/components/navigation/app-navigation.tsx +19 -4
- package/src/components/overlays/sheet.tsx +22 -7
- package/src/hooks/use-derived-filter-state.ts +1 -3
- package/src/hooks/use-filter-state.ts +1 -2
- package/src/native/card.tsx +36 -0
- package/src/native/icon-button.tsx +41 -0
- package/src/native/index.ts +27 -0
- package/src/native/list-row.tsx +67 -0
- package/src/native/text-field.tsx +34 -0
- package/src/native/uniwind.d.ts +1 -0
- package/src/styles/animations.css +91 -0
- package/src/styles/tokens/generated/foundations.css +1 -0
- package/src/styles/tokens/generated/foundations.generated.ts +1 -0
- package/src/styles/tokens/generated/native.generated.ts +266 -0
- package/src/styles/tokens/index.ts +2 -0
- package/src/styles/tokens/normalize-native.ts +88 -0
- package/src/styles/tokens/source/foundations.tokens.json +2 -1
- package/src/styles/tokens/source/native.tokens.json +204 -0
- package/src/styles/tokens/style-dictionary.native.mjs +26 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { useCSSVariable } from "uniwind";
|
|
4
|
+
|
|
5
|
+
interface IconButtonProps {
|
|
6
|
+
accessibilityLabel?: string;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onPress?: () => void;
|
|
10
|
+
testID?: string;
|
|
11
|
+
variant?: "bordered" | "plain";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function IconButton({
|
|
15
|
+
accessibilityLabel,
|
|
16
|
+
children,
|
|
17
|
+
disabled = false,
|
|
18
|
+
onPress,
|
|
19
|
+
testID,
|
|
20
|
+
variant = "bordered",
|
|
21
|
+
}: IconButtonProps) {
|
|
22
|
+
const [border] = useCSSVariable(["--color-border"]) as string[];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Pressable
|
|
26
|
+
accessibilityLabel={accessibilityLabel}
|
|
27
|
+
accessibilityRole="button"
|
|
28
|
+
disabled={disabled}
|
|
29
|
+
onPress={onPress}
|
|
30
|
+
testID={testID}
|
|
31
|
+
className={`w-8 h-8 rounded-full border items-center justify-center ${variant === "plain" ? "border-0" : ""}`}
|
|
32
|
+
style={({ pressed }) => [
|
|
33
|
+
{ borderColor: variant === "plain" ? "transparent" : border },
|
|
34
|
+
pressed && { opacity: 0.7 },
|
|
35
|
+
disabled && { opacity: 0.4 },
|
|
36
|
+
]}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</Pressable>
|
|
40
|
+
);
|
|
41
|
+
}
|
package/src/native/index.ts
CHANGED
|
@@ -6,6 +6,33 @@
|
|
|
6
6
|
* code, so web consumers never pull React Native into their graph and native
|
|
7
7
|
* consumers never pull in react-dom.
|
|
8
8
|
*/
|
|
9
|
+
export {
|
|
10
|
+
Card,
|
|
11
|
+
CardAction,
|
|
12
|
+
CardContent,
|
|
13
|
+
CardDescription,
|
|
14
|
+
CardFooter,
|
|
15
|
+
CardHeader,
|
|
16
|
+
CardTitle,
|
|
17
|
+
} from "./card";
|
|
18
|
+
export { IconButton } from "./icon-button";
|
|
19
|
+
export { ListRow } from "./list-row";
|
|
9
20
|
export { createMakeStyles, type NamedStyles } from "./make-styles";
|
|
10
21
|
export { nativeShadows, type NativeShadowLayer } from "./shadows";
|
|
22
|
+
export { TextField } from "./text-field";
|
|
11
23
|
export { useColorMode, useThemeColors } from "./use-theme-colors";
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
borderRadius as nativeBorderRadius,
|
|
27
|
+
componentSize as nativeComponentSize,
|
|
28
|
+
fontFamily as nativeFontFamily,
|
|
29
|
+
screenMargin as nativeScreenMargin,
|
|
30
|
+
spacing as nativeSpacing,
|
|
31
|
+
streamItem as nativeStreamItem,
|
|
32
|
+
typography as nativeTypography,
|
|
33
|
+
type BorderRadiusToken,
|
|
34
|
+
type ComponentSizeToken,
|
|
35
|
+
type SpacingToken,
|
|
36
|
+
type TypographyVariant,
|
|
37
|
+
} from "../styles/tokens/normalize-native";
|
|
38
|
+
export { motion as nativeMotion } from "../styles/tokens";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { Pressable, type StyleProp, Text, type TextStyle, View } from "react-native";
|
|
3
|
+
import { useCSSVariable } from "uniwind";
|
|
4
|
+
|
|
5
|
+
interface ListRowProps {
|
|
6
|
+
accessibilityLabel: string;
|
|
7
|
+
actionTestID?: string;
|
|
8
|
+
eyebrow?: string;
|
|
9
|
+
leading?: ReactNode;
|
|
10
|
+
onLongPress?: () => void;
|
|
11
|
+
onPress: () => void;
|
|
12
|
+
subtitle?: string | null;
|
|
13
|
+
testID?: string;
|
|
14
|
+
title: string;
|
|
15
|
+
titleStyle?: StyleProp<TextStyle>;
|
|
16
|
+
trailing?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ListRow({
|
|
20
|
+
accessibilityLabel,
|
|
21
|
+
actionTestID,
|
|
22
|
+
eyebrow,
|
|
23
|
+
leading,
|
|
24
|
+
onLongPress,
|
|
25
|
+
onPress,
|
|
26
|
+
subtitle,
|
|
27
|
+
testID,
|
|
28
|
+
title,
|
|
29
|
+
titleStyle,
|
|
30
|
+
trailing,
|
|
31
|
+
}: ListRowProps) {
|
|
32
|
+
const [muted] = useCSSVariable(["--color-muted"]) as string[];
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Pressable
|
|
36
|
+
accessibilityLabel={accessibilityLabel}
|
|
37
|
+
accessibilityRole="button"
|
|
38
|
+
onLongPress={onLongPress}
|
|
39
|
+
onPress={onPress}
|
|
40
|
+
className="flex-row items-center gap-3 min-h-14 border-b border-border px-4 py-2"
|
|
41
|
+
style={({ pressed }) => pressed && { backgroundColor: muted }}
|
|
42
|
+
testID={testID ?? actionTestID}
|
|
43
|
+
>
|
|
44
|
+
{leading ? <View className="items-center justify-center w-6">{leading}</View> : null}
|
|
45
|
+
<View className="flex-1 gap-0.5 min-w-0">
|
|
46
|
+
{eyebrow ? (
|
|
47
|
+
<Text className="text-caption2 text-muted-foreground tracking-[0.2]">{eyebrow}</Text>
|
|
48
|
+
) : null}
|
|
49
|
+
<Text numberOfLines={2} className="text-body text-foreground" style={titleStyle}>
|
|
50
|
+
{title}
|
|
51
|
+
</Text>
|
|
52
|
+
{subtitle ? (
|
|
53
|
+
<Text
|
|
54
|
+
ellipsizeMode="tail"
|
|
55
|
+
numberOfLines={1}
|
|
56
|
+
className="text-caption1 text-muted-foreground"
|
|
57
|
+
>
|
|
58
|
+
{subtitle}
|
|
59
|
+
</Text>
|
|
60
|
+
) : null}
|
|
61
|
+
</View>
|
|
62
|
+
{trailing ? <View className="items-center justify-center">{trailing}</View> : null}
|
|
63
|
+
</Pressable>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { ListRow };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { forwardRef, useState } from "react";
|
|
2
|
+
import { TextInput, type TextInputProps } from "react-native";
|
|
3
|
+
import { useCSSVariable } from "uniwind";
|
|
4
|
+
|
|
5
|
+
export const TextField = forwardRef<TextInput, TextInputProps>(function TextField(
|
|
6
|
+
{ onBlur, onFocus, style, ...props },
|
|
7
|
+
ref,
|
|
8
|
+
) {
|
|
9
|
+
const [ring, borderInput, foreground, mutedForeground] = useCSSVariable([
|
|
10
|
+
"--color-ring",
|
|
11
|
+
"--color-input",
|
|
12
|
+
"--color-foreground",
|
|
13
|
+
"--color-muted-foreground",
|
|
14
|
+
]) as string[];
|
|
15
|
+
const [focused, setFocused] = useState(false);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<TextInput
|
|
19
|
+
{...props}
|
|
20
|
+
ref={ref}
|
|
21
|
+
onBlur={(event) => {
|
|
22
|
+
setFocused(false);
|
|
23
|
+
onBlur?.(event);
|
|
24
|
+
}}
|
|
25
|
+
onFocus={(event) => {
|
|
26
|
+
setFocused(true);
|
|
27
|
+
onFocus?.(event);
|
|
28
|
+
}}
|
|
29
|
+
placeholderTextColor={props.placeholderTextColor ?? mutedForeground}
|
|
30
|
+
className="rounded-md border min-h-11 px-4 py-3 text-base"
|
|
31
|
+
style={[{ borderColor: focused ? ring : borderInput, color: foreground }, style]}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="uniwind/types" />
|
|
@@ -50,6 +50,46 @@
|
|
|
50
50
|
transform: scale(0.95);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
@keyframes sheet-slide-in-bottom {
|
|
54
|
+
from {
|
|
55
|
+
transform: translateY(100%);
|
|
56
|
+
opacity: 0;
|
|
57
|
+
}
|
|
58
|
+
to {
|
|
59
|
+
transform: translateY(0);
|
|
60
|
+
opacity: 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
@keyframes sheet-slide-out-bottom {
|
|
64
|
+
from {
|
|
65
|
+
transform: translateY(0);
|
|
66
|
+
opacity: 1;
|
|
67
|
+
}
|
|
68
|
+
to {
|
|
69
|
+
transform: translateY(100%);
|
|
70
|
+
opacity: 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
@keyframes sheet-slide-in-right {
|
|
74
|
+
from {
|
|
75
|
+
transform: translateX(100%);
|
|
76
|
+
opacity: 0;
|
|
77
|
+
}
|
|
78
|
+
to {
|
|
79
|
+
transform: translateX(0);
|
|
80
|
+
opacity: 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
@keyframes sheet-slide-out-right {
|
|
84
|
+
from {
|
|
85
|
+
transform: translateX(0);
|
|
86
|
+
opacity: 1;
|
|
87
|
+
}
|
|
88
|
+
to {
|
|
89
|
+
transform: translateX(100%);
|
|
90
|
+
opacity: 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
53
93
|
|
|
54
94
|
.data-open\:animate-in[data-open],
|
|
55
95
|
.data-open\:animate-in[data-state="open"] {
|
|
@@ -64,6 +104,38 @@
|
|
|
64
104
|
zoom-out 120ms ease-in;
|
|
65
105
|
}
|
|
66
106
|
|
|
107
|
+
[data-slot="sheet-overlay"][data-open],
|
|
108
|
+
[data-slot="sheet-overlay"][data-state="open"] {
|
|
109
|
+
animation: fade-in 400ms var(--ease-out);
|
|
110
|
+
}
|
|
111
|
+
[data-slot="sheet-overlay"][data-closed],
|
|
112
|
+
[data-slot="sheet-overlay"][data-state="closed"] {
|
|
113
|
+
/* Must stay in lockstep with [data-slot="sheet-content"]'s close duration below (both 200ms) —
|
|
114
|
+
Base UI gates the whole portal's unmount on this element's own close-animation duration, not
|
|
115
|
+
the content panel's, so a mismatch here yanks the sliding panel off mid-animation. */
|
|
116
|
+
animation: fade-out 200ms var(--ease-in);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-slot="sheet-content"][data-open],
|
|
120
|
+
[data-slot="sheet-content"][data-state="open"] {
|
|
121
|
+
animation: sheet-slide-in-bottom 300ms var(--ease-drawer);
|
|
122
|
+
}
|
|
123
|
+
[data-slot="sheet-content"][data-closed],
|
|
124
|
+
[data-slot="sheet-content"][data-state="closed"] {
|
|
125
|
+
animation: sheet-slide-out-bottom 200ms var(--ease-drawer);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (min-width: 768px) {
|
|
129
|
+
[data-slot="sheet-content"][data-open],
|
|
130
|
+
[data-slot="sheet-content"][data-state="open"] {
|
|
131
|
+
animation: sheet-slide-in-right 300ms var(--ease-drawer);
|
|
132
|
+
}
|
|
133
|
+
[data-slot="sheet-content"][data-closed],
|
|
134
|
+
[data-slot="sheet-content"][data-state="closed"] {
|
|
135
|
+
animation: sheet-slide-out-right 200ms var(--ease-drawer);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
67
139
|
@media (prefers-reduced-motion: reduce) {
|
|
68
140
|
.animate-in,
|
|
69
141
|
.animate-out,
|
|
@@ -72,3 +144,22 @@
|
|
|
72
144
|
animation: none !important;
|
|
73
145
|
}
|
|
74
146
|
}
|
|
147
|
+
|
|
148
|
+
@media (prefers-reduced-motion: reduce) {
|
|
149
|
+
[data-slot="sheet-overlay"][data-open],
|
|
150
|
+
[data-slot="sheet-overlay"][data-state="open"] {
|
|
151
|
+
animation: fade-in 120ms var(--ease-out) !important;
|
|
152
|
+
}
|
|
153
|
+
[data-slot="sheet-overlay"][data-closed],
|
|
154
|
+
[data-slot="sheet-overlay"][data-state="closed"] {
|
|
155
|
+
animation: fade-out 120ms var(--ease-in) !important;
|
|
156
|
+
}
|
|
157
|
+
[data-slot="sheet-content"][data-open],
|
|
158
|
+
[data-slot="sheet-content"][data-state="open"] {
|
|
159
|
+
animation: fade-in var(--duration-150) var(--ease-out) !important;
|
|
160
|
+
}
|
|
161
|
+
[data-slot="sheet-content"][data-closed],
|
|
162
|
+
[data-slot="sheet-content"][data-state="closed"] {
|
|
163
|
+
animation: fade-out 120ms var(--ease-in) !important;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -133,6 +133,7 @@
|
|
|
133
133
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
134
134
|
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
135
135
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
136
|
+
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
|
|
136
137
|
--duration-75: 75ms;
|
|
137
138
|
--duration-100: 100ms;
|
|
138
139
|
--duration-150: 150ms;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
text: {
|
|
7
|
+
"large-title": {
|
|
8
|
+
fontSize: {
|
|
9
|
+
value: 34,
|
|
10
|
+
unit: "px",
|
|
11
|
+
},
|
|
12
|
+
lineHeight: {
|
|
13
|
+
value: 41,
|
|
14
|
+
unit: "px",
|
|
15
|
+
},
|
|
16
|
+
fontWeight: 700,
|
|
17
|
+
letterSpacing: {
|
|
18
|
+
value: -0.6,
|
|
19
|
+
unit: "px",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
title1: {
|
|
23
|
+
fontSize: {
|
|
24
|
+
value: 28,
|
|
25
|
+
unit: "px",
|
|
26
|
+
},
|
|
27
|
+
lineHeight: {
|
|
28
|
+
value: 34,
|
|
29
|
+
unit: "px",
|
|
30
|
+
},
|
|
31
|
+
fontWeight: 700,
|
|
32
|
+
letterSpacing: {
|
|
33
|
+
value: -0.4,
|
|
34
|
+
unit: "px",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
title2: {
|
|
38
|
+
fontSize: {
|
|
39
|
+
value: 22,
|
|
40
|
+
unit: "px",
|
|
41
|
+
},
|
|
42
|
+
lineHeight: {
|
|
43
|
+
value: 28,
|
|
44
|
+
unit: "px",
|
|
45
|
+
},
|
|
46
|
+
fontWeight: 600,
|
|
47
|
+
letterSpacing: {
|
|
48
|
+
value: -0.2,
|
|
49
|
+
unit: "px",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
headline: {
|
|
53
|
+
fontSize: {
|
|
54
|
+
value: 17,
|
|
55
|
+
unit: "px",
|
|
56
|
+
},
|
|
57
|
+
lineHeight: {
|
|
58
|
+
value: 22,
|
|
59
|
+
unit: "px",
|
|
60
|
+
},
|
|
61
|
+
fontWeight: 600,
|
|
62
|
+
letterSpacing: {
|
|
63
|
+
value: -0.1,
|
|
64
|
+
unit: "px",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
body: {
|
|
68
|
+
fontSize: {
|
|
69
|
+
value: 17,
|
|
70
|
+
unit: "px",
|
|
71
|
+
},
|
|
72
|
+
lineHeight: {
|
|
73
|
+
value: 24,
|
|
74
|
+
unit: "px",
|
|
75
|
+
},
|
|
76
|
+
fontWeight: 400,
|
|
77
|
+
letterSpacing: {
|
|
78
|
+
value: 0,
|
|
79
|
+
unit: "px",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
callout: {
|
|
83
|
+
fontSize: {
|
|
84
|
+
value: 16,
|
|
85
|
+
unit: "px",
|
|
86
|
+
},
|
|
87
|
+
lineHeight: {
|
|
88
|
+
value: 22,
|
|
89
|
+
unit: "px",
|
|
90
|
+
},
|
|
91
|
+
fontWeight: 400,
|
|
92
|
+
letterSpacing: {
|
|
93
|
+
value: -0.1,
|
|
94
|
+
unit: "px",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
subhead: {
|
|
98
|
+
fontSize: {
|
|
99
|
+
value: 15,
|
|
100
|
+
unit: "px",
|
|
101
|
+
},
|
|
102
|
+
lineHeight: {
|
|
103
|
+
value: 20,
|
|
104
|
+
unit: "px",
|
|
105
|
+
},
|
|
106
|
+
fontWeight: 400,
|
|
107
|
+
letterSpacing: {
|
|
108
|
+
value: 0,
|
|
109
|
+
unit: "px",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
footnote: {
|
|
113
|
+
fontSize: {
|
|
114
|
+
value: 13,
|
|
115
|
+
unit: "px",
|
|
116
|
+
},
|
|
117
|
+
lineHeight: {
|
|
118
|
+
value: 18,
|
|
119
|
+
unit: "px",
|
|
120
|
+
},
|
|
121
|
+
fontWeight: 400,
|
|
122
|
+
letterSpacing: {
|
|
123
|
+
value: 0,
|
|
124
|
+
unit: "px",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
caption1: {
|
|
128
|
+
fontSize: {
|
|
129
|
+
value: 12,
|
|
130
|
+
unit: "px",
|
|
131
|
+
},
|
|
132
|
+
lineHeight: {
|
|
133
|
+
value: 16,
|
|
134
|
+
unit: "px",
|
|
135
|
+
},
|
|
136
|
+
fontWeight: 400,
|
|
137
|
+
letterSpacing: {
|
|
138
|
+
value: 0,
|
|
139
|
+
unit: "px",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
caption2: {
|
|
143
|
+
fontSize: {
|
|
144
|
+
value: 11,
|
|
145
|
+
unit: "px",
|
|
146
|
+
},
|
|
147
|
+
lineHeight: {
|
|
148
|
+
value: 14,
|
|
149
|
+
unit: "px",
|
|
150
|
+
},
|
|
151
|
+
fontWeight: 500,
|
|
152
|
+
letterSpacing: {
|
|
153
|
+
value: 0.2,
|
|
154
|
+
unit: "px",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
display: {
|
|
158
|
+
fontSize: {
|
|
159
|
+
value: 40,
|
|
160
|
+
unit: "px",
|
|
161
|
+
},
|
|
162
|
+
lineHeight: {
|
|
163
|
+
value: 44,
|
|
164
|
+
unit: "px",
|
|
165
|
+
},
|
|
166
|
+
fontWeight: 700,
|
|
167
|
+
letterSpacing: {
|
|
168
|
+
value: -1.2,
|
|
169
|
+
unit: "px",
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
overline: {
|
|
173
|
+
fontSize: {
|
|
174
|
+
value: 11,
|
|
175
|
+
unit: "px",
|
|
176
|
+
},
|
|
177
|
+
lineHeight: {
|
|
178
|
+
value: 16,
|
|
179
|
+
unit: "px",
|
|
180
|
+
},
|
|
181
|
+
fontWeight: 500,
|
|
182
|
+
letterSpacing: {
|
|
183
|
+
value: 0.8,
|
|
184
|
+
unit: "px",
|
|
185
|
+
},
|
|
186
|
+
textTransform: "uppercase",
|
|
187
|
+
},
|
|
188
|
+
mono: {
|
|
189
|
+
fontFamily: "Menlo",
|
|
190
|
+
fontSize: {
|
|
191
|
+
value: 12,
|
|
192
|
+
unit: "px",
|
|
193
|
+
},
|
|
194
|
+
lineHeight: {
|
|
195
|
+
value: 16,
|
|
196
|
+
unit: "px",
|
|
197
|
+
},
|
|
198
|
+
fontWeight: 400,
|
|
199
|
+
letterSpacing: {
|
|
200
|
+
value: 0,
|
|
201
|
+
unit: "px",
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
space: {
|
|
206
|
+
sm: "0.5rem",
|
|
207
|
+
md: "0.75rem",
|
|
208
|
+
lg: "1rem",
|
|
209
|
+
xl: "1.5rem",
|
|
210
|
+
},
|
|
211
|
+
"screen-margin": "1rem",
|
|
212
|
+
"component-size": {
|
|
213
|
+
sm: "1rem",
|
|
214
|
+
md: "1.5rem",
|
|
215
|
+
lg: "2rem",
|
|
216
|
+
xl: "2.75rem",
|
|
217
|
+
icon: "1.25rem",
|
|
218
|
+
},
|
|
219
|
+
"border-radius": {
|
|
220
|
+
sm: "0.25rem",
|
|
221
|
+
md: "0.375rem",
|
|
222
|
+
lg: "0.5rem",
|
|
223
|
+
icon: "0.375rem",
|
|
224
|
+
},
|
|
225
|
+
"stream-item": {
|
|
226
|
+
"border-radius": "0rem",
|
|
227
|
+
"border-width": "1px",
|
|
228
|
+
gap: "0.75rem",
|
|
229
|
+
"icon-size": "1.25rem",
|
|
230
|
+
"min-height": "3.5rem",
|
|
231
|
+
"padding-horizontal": "1rem",
|
|
232
|
+
"padding-vertical": "0.5rem",
|
|
233
|
+
},
|
|
234
|
+
"font-family": {
|
|
235
|
+
primary: "System",
|
|
236
|
+
mono: "Menlo",
|
|
237
|
+
},
|
|
238
|
+
motion: {
|
|
239
|
+
duration: {
|
|
240
|
+
instant: "0ms",
|
|
241
|
+
quick: "150ms",
|
|
242
|
+
standard: "300ms",
|
|
243
|
+
deliberate: "500ms",
|
|
244
|
+
},
|
|
245
|
+
easing: {
|
|
246
|
+
enter: [0, 0, 0.2, 1],
|
|
247
|
+
exit: [0.4, 0, 1, 1],
|
|
248
|
+
inOut: [0.4, 0, 0.2, 1],
|
|
249
|
+
},
|
|
250
|
+
distance: {
|
|
251
|
+
rowEnter: "0.5rem",
|
|
252
|
+
sceneReveal: "2rem",
|
|
253
|
+
},
|
|
254
|
+
opacity: {
|
|
255
|
+
hidden: 0,
|
|
256
|
+
visible: 1,
|
|
257
|
+
disabled: 0.5,
|
|
258
|
+
},
|
|
259
|
+
interruption: {
|
|
260
|
+
policy: "cancel-and-settle",
|
|
261
|
+
},
|
|
262
|
+
reducedMotion: {
|
|
263
|
+
policy: "replace-transform-with-opacity",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import darkTokens from "./generated/dark.colors.generated";
|
|
2
2
|
import foundations from "./generated/foundations.generated";
|
|
3
3
|
import lightTokens from "./generated/light.colors.generated";
|
|
4
|
+
import nativeTokens from "./generated/native.generated";
|
|
4
5
|
|
|
5
6
|
const toPixels = (value: string) => {
|
|
6
7
|
if (value.endsWith("rem")) return Number.parseFloat(value) * 16;
|
|
@@ -62,6 +63,7 @@ export const transitionDurations = Object.fromEntries(
|
|
|
62
63
|
Object.entries(foundations.duration).map(([key, value]) => [key, toMilliseconds(value)]),
|
|
63
64
|
) as Record<keyof typeof foundations.duration, number>;
|
|
64
65
|
export const animations = foundations.animate;
|
|
66
|
+
export const motion = nativeTokens.motion;
|
|
65
67
|
|
|
66
68
|
export type RadiusToken = keyof typeof radii;
|
|
67
69
|
export type TextSizeToken = keyof typeof textSizes;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import nativeGenerated from "./generated/native.generated";
|
|
2
|
+
|
|
3
|
+
const toPixels = (value: string) => {
|
|
4
|
+
if (value.endsWith("rem")) return Number.parseFloat(value) * 16;
|
|
5
|
+
if (value.endsWith("px")) return Number.parseFloat(value);
|
|
6
|
+
throw new Error(`Unsupported dimension: ${value}`);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const unwrap = (v: { value: number; unit?: string } | number) =>
|
|
10
|
+
typeof v === "number" ? v : v.value;
|
|
11
|
+
|
|
12
|
+
type DimensionValue = number | { value: number; unit?: string };
|
|
13
|
+
|
|
14
|
+
const makeFlat = (raw: Record<string, unknown>) => ({
|
|
15
|
+
fontFamily: raw.fontFamily as string | undefined,
|
|
16
|
+
fontSize: unwrap(raw.fontSize as DimensionValue),
|
|
17
|
+
fontWeight: raw.fontWeight as number,
|
|
18
|
+
lineHeight: unwrap(raw.lineHeight as DimensionValue),
|
|
19
|
+
letterSpacing: unwrap(raw.letterSpacing as DimensionValue),
|
|
20
|
+
...(raw.textTransform
|
|
21
|
+
? { textTransform: raw.textTransform as "uppercase" | "lowercase" | "capitalize" | "none" }
|
|
22
|
+
: {}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const generated = nativeGenerated as unknown as {
|
|
26
|
+
text: Record<string, Record<string, unknown>>;
|
|
27
|
+
space: Record<string, string>;
|
|
28
|
+
"screen-margin": string;
|
|
29
|
+
"component-size": Record<string, string>;
|
|
30
|
+
"border-radius": Record<string, string>;
|
|
31
|
+
"stream-item": Record<string, string>;
|
|
32
|
+
"font-family": { primary: string; mono: string };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const primaryFont = generated["font-family"].primary;
|
|
36
|
+
|
|
37
|
+
const typographyEntries = Object.entries(generated.text).map(([key, raw]) => {
|
|
38
|
+
const flat = makeFlat(raw);
|
|
39
|
+
if (!flat.fontFamily) flat.fontFamily = primaryFont;
|
|
40
|
+
return [key, flat] as const;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const typography = Object.fromEntries(typographyEntries) as Record<
|
|
44
|
+
string,
|
|
45
|
+
{
|
|
46
|
+
fontFamily: string;
|
|
47
|
+
fontSize: number;
|
|
48
|
+
fontWeight: number;
|
|
49
|
+
lineHeight: number;
|
|
50
|
+
letterSpacing: number;
|
|
51
|
+
textTransform?: "uppercase" | "lowercase" | "capitalize" | "none";
|
|
52
|
+
}
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
export type TypographyVariant = keyof typeof typography;
|
|
56
|
+
|
|
57
|
+
export const spacing = Object.fromEntries(
|
|
58
|
+
Object.entries(generated.space).map(([key, value]) => [key, toPixels(value)]),
|
|
59
|
+
) as Record<keyof typeof generated.space, number>;
|
|
60
|
+
|
|
61
|
+
export type SpacingToken = keyof typeof spacing;
|
|
62
|
+
|
|
63
|
+
export const screenMargin = toPixels(generated["screen-margin"]);
|
|
64
|
+
|
|
65
|
+
export const componentSize = Object.fromEntries(
|
|
66
|
+
Object.entries(generated["component-size"]).map(([key, value]) => [key, toPixels(value)]),
|
|
67
|
+
) as Record<keyof (typeof generated)["component-size"], number>;
|
|
68
|
+
|
|
69
|
+
export type ComponentSizeToken = keyof typeof componentSize;
|
|
70
|
+
|
|
71
|
+
export const borderRadius = Object.fromEntries(
|
|
72
|
+
Object.entries(generated["border-radius"]).map(([key, value]) => [key, toPixels(value)]),
|
|
73
|
+
) as Record<keyof (typeof generated)["border-radius"], number>;
|
|
74
|
+
|
|
75
|
+
export type BorderRadiusToken = keyof typeof borderRadius;
|
|
76
|
+
|
|
77
|
+
const rawStreamItem = generated["stream-item"];
|
|
78
|
+
export const streamItem = {
|
|
79
|
+
borderRadius: toPixels(rawStreamItem["border-radius"]),
|
|
80
|
+
borderWidth: toPixels(rawStreamItem["border-width"]),
|
|
81
|
+
gap: toPixels(rawStreamItem.gap),
|
|
82
|
+
iconSize: toPixels(rawStreamItem["icon-size"]),
|
|
83
|
+
minHeight: toPixels(rawStreamItem["min-height"]),
|
|
84
|
+
paddingHorizontal: toPixels(rawStreamItem["padding-horizontal"]),
|
|
85
|
+
paddingVertical: toPixels(rawStreamItem["padding-vertical"]),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const fontFamily = generated["font-family"];
|
|
@@ -318,7 +318,8 @@
|
|
|
318
318
|
"$type": "cubicBezier",
|
|
319
319
|
"in": { "$value": [0.4, 0, 1, 1] },
|
|
320
320
|
"out": { "$value": [0, 0, 0.2, 1] },
|
|
321
|
-
"in-out": { "$value": [0.4, 0, 0.2, 1] }
|
|
321
|
+
"in-out": { "$value": [0.4, 0, 0.2, 1] },
|
|
322
|
+
"drawer": { "$value": [0.32, 0.72, 0, 1] }
|
|
322
323
|
},
|
|
323
324
|
"duration": {
|
|
324
325
|
"$type": "duration",
|