@medway-ui/native 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/lib/commonjs/components/Badge.js +159 -36
- package/lib/commonjs/components/Badge.js.map +1 -1
- package/lib/commonjs/components/Choice.js +35 -37
- package/lib/commonjs/components/Choice.js.map +1 -1
- package/lib/commonjs/components/SegmentControl.js +197 -0
- package/lib/commonjs/components/SegmentControl.js.map +1 -0
- package/lib/commonjs/editor/editorHtml.generated.js +1 -1
- package/lib/commonjs/editor/editorHtml.generated.js.map +1 -1
- package/lib/commonjs/hooks/useThemeColor.js +42 -42
- package/lib/commonjs/hooks/useThemeColor.js.map +1 -1
- package/lib/commonjs/index.js +31 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/lib/raised-surface.js +24 -0
- package/lib/commonjs/lib/raised-surface.js.map +1 -0
- package/lib/module/components/Badge.js +159 -36
- package/lib/module/components/Badge.js.map +1 -1
- package/lib/module/components/Choice.js +35 -37
- package/lib/module/components/Choice.js.map +1 -1
- package/lib/module/components/SegmentControl.js +189 -0
- package/lib/module/components/SegmentControl.js.map +1 -0
- package/lib/module/editor/editorHtml.generated.js +1 -1
- package/lib/module/editor/editorHtml.generated.js.map +1 -1
- package/lib/module/hooks/useThemeColor.js +42 -42
- package/lib/module/hooks/useThemeColor.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/lib/raised-surface.js +22 -0
- package/lib/module/lib/raised-surface.js.map +1 -0
- package/lib/typescript/components/Badge.d.ts +14 -4
- package/lib/typescript/components/Badge.d.ts.map +1 -1
- package/lib/typescript/components/Chip.d.ts +2 -2
- package/lib/typescript/components/Choice.d.ts.map +1 -1
- package/lib/typescript/components/SegmentControl.d.ts +37 -0
- package/lib/typescript/components/SegmentControl.d.ts.map +1 -0
- package/lib/typescript/components/Tag.d.ts +2 -2
- package/lib/typescript/editor/editorHtml.generated.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/lib/raised-surface.d.ts +3 -0
- package/lib/typescript/lib/raised-surface.d.ts.map +1 -0
- package/package.json +1 -1
- package/setup/tokens.native.css +49 -22
- package/src/components/Badge.tsx +207 -61
- package/src/components/Choice.tsx +66 -45
- package/src/components/SegmentControl.tsx +265 -0
- package/src/editor/editorHtml.generated.ts +1 -1
- package/src/hooks/useThemeColor.ts +42 -42
- package/src/index.ts +21 -1
- package/src/lib/raised-surface.tsx +21 -0
package/setup/tokens.native.css
CHANGED
|
@@ -247,12 +247,12 @@
|
|
|
247
247
|
|
|
248
248
|
/* === Gold colors === */
|
|
249
249
|
--color-gold: rgba(217, 172, 104, 1);
|
|
250
|
-
--color-gold-darken-1: rgba(
|
|
251
|
-
--color-gold-darken-2: rgba(
|
|
252
|
-
--color-gold-darken-3: rgba(
|
|
253
|
-
--color-gold-darken-4: rgba(
|
|
254
|
-
--color-gold-darken-5: rgba(
|
|
255
|
-
--color-gold-darken-6: rgba(
|
|
250
|
+
--color-gold-darken-1: rgba(200, 152, 81, 1);
|
|
251
|
+
--color-gold-darken-2: rgba(181, 132, 59, 1);
|
|
252
|
+
--color-gold-darken-3: rgba(151, 110, 48, 1);
|
|
253
|
+
--color-gold-darken-4: rgba(119, 87, 39, 1);
|
|
254
|
+
--color-gold-darken-5: rgba(89, 65, 29, 1);
|
|
255
|
+
--color-gold-darken-6: rgba(57, 42, 19, 1);
|
|
256
256
|
--color-gold-lighten-1: rgba(221, 181, 121, 1);
|
|
257
257
|
--color-gold-lighten-2: rgba(229, 199, 153, 1);
|
|
258
258
|
--color-gold-lighten-3: rgba(233, 208, 170, 1);
|
|
@@ -366,8 +366,8 @@
|
|
|
366
366
|
--color-secondary-0: rgba(1, 208, 180, 0);
|
|
367
367
|
|
|
368
368
|
/* === Base drop-shadow colors === */
|
|
369
|
-
--color-drop-shadow-layer-a: rgba(33, 38, 49, 0.
|
|
370
|
-
--color-drop-shadow-layer-b: rgba(73, 85, 110, 0.
|
|
369
|
+
--color-drop-shadow-layer-a: rgba(33, 38, 49, 0.04);
|
|
370
|
+
--color-drop-shadow-layer-b: rgba(73, 85, 110, 0.08);
|
|
371
371
|
--color-drop-shadow-subtle-layer-a: rgba(33, 38, 49, 0.04);
|
|
372
372
|
--color-drop-shadow-subtle-layer-b: rgba(73, 85, 110, 0.08);
|
|
373
373
|
|
|
@@ -535,29 +535,29 @@
|
|
|
535
535
|
/* === Base gold colors === */
|
|
536
536
|
--color-gold-foreground: var(--color-gold-darken-4);
|
|
537
537
|
--color-gold-foreground-emphasis: var(--color-gold-darken-5);
|
|
538
|
-
--color-gold-foreground-onfill: var(--color-gold-darken-
|
|
539
|
-
--color-gold-background-fill: var(--color-gold);
|
|
538
|
+
--color-gold-foreground-onfill: var(--color-gold-darken-6);
|
|
539
|
+
--color-gold-background-fill: var(--color-gold-lighten-1);
|
|
540
540
|
--color-gold-background-fill-emphasis: var(--color-gold-darken-1);
|
|
541
|
-
--color-gold-background-soft: var(--color-gold-
|
|
542
|
-
--color-gold-background-soft-emphasis: var(--color-gold-
|
|
541
|
+
--color-gold-background-soft: var(--color-gold-16);
|
|
542
|
+
--color-gold-background-soft-emphasis: var(--color-gold-8);
|
|
543
543
|
|
|
544
544
|
/* === Base silver colors === */
|
|
545
|
-
--color-silver-foreground: var(--color-silver-darken-
|
|
545
|
+
--color-silver-foreground: var(--color-silver-darken-3);
|
|
546
546
|
--color-silver-foreground-emphasis: var(--color-silver-darken-6);
|
|
547
547
|
--color-silver-foreground-onfill: var(--color-white);
|
|
548
|
-
--color-silver-background-fill: var(--color-silver);
|
|
549
|
-
--color-silver-background-fill-emphasis: var(--color-silver-darken-
|
|
550
|
-
--color-silver-background-soft: var(--color-silver-
|
|
551
|
-
--color-silver-background-soft-emphasis: var(--color-silver-
|
|
548
|
+
--color-silver-background-fill: var(--color-silver-darken-1);
|
|
549
|
+
--color-silver-background-fill-emphasis: var(--color-silver-darken-3);
|
|
550
|
+
--color-silver-background-soft: var(--color-silver-16);
|
|
551
|
+
--color-silver-background-soft-emphasis: var(--color-silver-8);
|
|
552
552
|
|
|
553
553
|
/* === Base bronze colors === */
|
|
554
|
-
--color-bronze-foreground: var(--color-bronze-darken-
|
|
555
|
-
--color-bronze-foreground-emphasis: var(--color-bronze-darken-
|
|
554
|
+
--color-bronze-foreground: var(--color-bronze-darken-2);
|
|
555
|
+
--color-bronze-foreground-emphasis: var(--color-bronze-darken-3);
|
|
556
556
|
--color-bronze-foreground-onfill: var(--color-white);
|
|
557
557
|
--color-bronze-background-fill: var(--color-bronze);
|
|
558
558
|
--color-bronze-background-fill-emphasis: var(--color-bronze-darken-1);
|
|
559
|
-
--color-bronze-background-soft: var(--color-bronze-
|
|
560
|
-
--color-bronze-background-soft-emphasis: var(--color-bronze-
|
|
559
|
+
--color-bronze-background-soft: var(--color-bronze-16);
|
|
560
|
+
--color-bronze-background-soft-emphasis: var(--color-bronze-8);
|
|
561
561
|
|
|
562
562
|
/* === Base primary colors === */
|
|
563
563
|
--color-primary-foreground: var(--color-primary);
|
|
@@ -795,7 +795,7 @@
|
|
|
795
795
|
--color-surface-elevated: var(--color-gray-800);
|
|
796
796
|
--color-surface-card: var(--color-gray-850);
|
|
797
797
|
--color-surface-navigation: var(--color-gray-850);
|
|
798
|
-
--color-surface-section: var(--color-gray-
|
|
798
|
+
--color-surface-section: var(--color-gray-850);
|
|
799
799
|
--color-surface-page: var(--color-gray-950);
|
|
800
800
|
--color-surface-disable: var(--color-black-8);
|
|
801
801
|
--color-surface-settings-on: var(--color-black-48);
|
|
@@ -903,6 +903,33 @@
|
|
|
903
903
|
--color-prev-background-soft: var(--color-prev-24);
|
|
904
904
|
--color-prev-background-soft-emphasis: var(--color-prev-48);
|
|
905
905
|
|
|
906
|
+
/* === Dark gold colors === */
|
|
907
|
+
--color-gold-foreground: var(--color-gold-lighten-1);
|
|
908
|
+
--color-gold-foreground-emphasis: var(--color-gold-lighten-2);
|
|
909
|
+
--color-gold-foreground-onfill: var(--color-gold-darken-6);
|
|
910
|
+
--color-gold-background-fill: var(--color-gold-darken-1);
|
|
911
|
+
--color-gold-background-fill-emphasis: var(--color-gold);
|
|
912
|
+
--color-gold-background-soft: var(--color-gold-8);
|
|
913
|
+
--color-gold-background-soft-emphasis: var(--color-gold-16);
|
|
914
|
+
|
|
915
|
+
/* === Dark silver colors === */
|
|
916
|
+
--color-silver-foreground: var(--color-silver-lighten-3);
|
|
917
|
+
--color-silver-foreground-emphasis: var(--color-silver-lighten-5);
|
|
918
|
+
--color-silver-foreground-onfill: var(--color-white);
|
|
919
|
+
--color-silver-background-fill: var(--color-silver-darken-1);
|
|
920
|
+
--color-silver-background-fill-emphasis: var(--color-silver);
|
|
921
|
+
--color-silver-background-soft: var(--color-silver-8);
|
|
922
|
+
--color-silver-background-soft-emphasis: var(--color-silver-16);
|
|
923
|
+
|
|
924
|
+
/* === Dark bronze colors === */
|
|
925
|
+
--color-bronze-foreground: var(--color-bronze-lighten-3);
|
|
926
|
+
--color-bronze-foreground-emphasis: var(--color-bronze-lighten-4);
|
|
927
|
+
--color-bronze-foreground-onfill: var(--color-bronze-darken-5);
|
|
928
|
+
--color-bronze-background-fill: var(--color-bronze-lighten-1);
|
|
929
|
+
--color-bronze-background-fill-emphasis: var(--color-bronze);
|
|
930
|
+
--color-bronze-background-soft: var(--color-bronze-8);
|
|
931
|
+
--color-bronze-background-soft-emphasis: var(--color-bronze-16);
|
|
932
|
+
|
|
906
933
|
/* === Dark primary colors === */
|
|
907
934
|
--color-primary-foreground: var(--color-gray-100);
|
|
908
935
|
--color-primary-foreground-emphasis: var(--color-white);
|
package/src/components/Badge.tsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useEffect, type ReactNode } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
type LayoutChangeEvent,
|
|
5
|
+
type ViewProps,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
} from "react-native";
|
|
3
8
|
import Animated, {
|
|
4
9
|
Easing,
|
|
5
10
|
cancelAnimation,
|
|
@@ -16,71 +21,180 @@ import { cn } from "@/lib/utils";
|
|
|
16
21
|
|
|
17
22
|
type BadgeAnimation = "shine";
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
type BadgeLayout = "tag" | "dot";
|
|
25
|
+
|
|
26
|
+
type BadgeBaseProps = ViewProps & VariantProps<typeof badgeVariants>;
|
|
27
|
+
|
|
28
|
+
interface BadgeTagProps extends BadgeBaseProps {
|
|
29
|
+
layout?: "tag";
|
|
20
30
|
icon?: ReactNode;
|
|
21
31
|
animation?: BadgeAnimation;
|
|
22
32
|
children?: ReactNode;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
// A dot has no content, so the types flag what it would drop.
|
|
36
|
+
interface BadgeDotProps extends BadgeBaseProps {
|
|
37
|
+
layout: "dot";
|
|
38
|
+
icon?: never;
|
|
39
|
+
animation?: never;
|
|
40
|
+
children?: never;
|
|
41
|
+
}
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
sm: "px-1",
|
|
34
|
-
md: "px-1",
|
|
35
|
-
lg: "px-1",
|
|
36
|
-
xl: "px-1.5",
|
|
37
|
-
};
|
|
43
|
+
type BadgeProps = BadgeTagProps | BadgeDotProps;
|
|
38
44
|
|
|
39
|
-
const badgeVariants = cva(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
const badgeVariants = cva("flex-row items-center justify-center rounded-pill", {
|
|
46
|
+
variants: {
|
|
47
|
+
variant: {
|
|
48
|
+
primary: "bg-primary-background-fill",
|
|
49
|
+
"primary-soft": "bg-surface-card",
|
|
50
|
+
secondary: "bg-secondary-background-fill",
|
|
51
|
+
"secondary-soft": "bg-surface-card",
|
|
52
|
+
regular: "bg-secondary-background-fill",
|
|
53
|
+
cm: "bg-cm-background-fill",
|
|
54
|
+
"cm-soft": "bg-surface-card",
|
|
55
|
+
go: "bg-go-background-fill",
|
|
56
|
+
"go-soft": "bg-surface-card",
|
|
57
|
+
cir: "bg-cir-background-fill",
|
|
58
|
+
"cir-soft": "bg-surface-card",
|
|
59
|
+
// The design paints pediatrics with the warning tokens, all but the filled label.
|
|
60
|
+
ped: "bg-yellow-background-fill",
|
|
61
|
+
"ped-soft": "bg-surface-card",
|
|
62
|
+
prev: "bg-prev-background-fill",
|
|
63
|
+
"prev-soft": "bg-surface-card",
|
|
64
|
+
freemium: "bg-primary",
|
|
65
|
+
error: "bg-red-background-soft",
|
|
66
|
+
warning: "bg-yellow-background-soft",
|
|
67
|
+
neutral: "bg-gray-background-soft",
|
|
55
68
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
size: {
|
|
70
|
+
sm: "min-h-4 min-w-4 gap-1 px-1 py-0.5",
|
|
71
|
+
md: "min-h-5 min-w-5 gap-1 px-1 py-0.5",
|
|
72
|
+
lg: "min-h-6 min-w-6 gap-1.5 px-1.5 py-1",
|
|
73
|
+
xl: "min-h-7 min-w-7 gap-2 px-2 py-1",
|
|
59
74
|
},
|
|
60
|
-
}
|
|
61
|
-
|
|
75
|
+
},
|
|
76
|
+
compoundVariants: [
|
|
77
|
+
{ variant: "freemium", size: "md", className: "px-1.5 py-1" },
|
|
78
|
+
{ variant: "freemium", size: "lg", className: "px-2" },
|
|
79
|
+
{ variant: "freemium", size: "xl", className: "px-2.5" },
|
|
80
|
+
],
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
variant: "secondary",
|
|
83
|
+
size: "md",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
62
86
|
|
|
63
|
-
|
|
87
|
+
type BadgeVariant = NonNullable<VariantProps<typeof badgeVariants>["variant"]>;
|
|
88
|
+
|
|
89
|
+
// Laid over `surface-card`, as in the design, so a soft badge stays opaque on any surface.
|
|
90
|
+
const SOFT_TINT: Partial<Record<BadgeVariant, string>> = {
|
|
91
|
+
"primary-soft": "bg-primary-background-soft",
|
|
92
|
+
"secondary-soft": "bg-secondary-background-soft",
|
|
93
|
+
"cm-soft": "bg-cm-background-soft",
|
|
94
|
+
"go-soft": "bg-go-background-soft",
|
|
95
|
+
"cir-soft": "bg-cir-background-soft",
|
|
96
|
+
"ped-soft": "bg-yellow-background-soft",
|
|
97
|
+
"prev-soft": "bg-prev-background-soft",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const badgeTextVariants = cva("font-bold", {
|
|
64
101
|
variants: {
|
|
65
102
|
variant: {
|
|
103
|
+
primary: "text-primary-foreground-onfill",
|
|
104
|
+
"primary-soft": "text-primary-foreground",
|
|
105
|
+
secondary: "text-secondary-foreground-onfill",
|
|
106
|
+
"secondary-soft": "text-secondary-foreground",
|
|
66
107
|
regular: "text-secondary-foreground-onfill",
|
|
108
|
+
cm: "text-cm-foreground-onfill",
|
|
109
|
+
"cm-soft": "text-cm-foreground",
|
|
110
|
+
go: "text-go-foreground-onfill",
|
|
111
|
+
"go-soft": "text-go-foreground",
|
|
112
|
+
cir: "text-cir-foreground-onfill",
|
|
113
|
+
"cir-soft": "text-cir-foreground",
|
|
114
|
+
ped: "text-ped-foreground-onfill",
|
|
115
|
+
"ped-soft": "text-yellow-foreground",
|
|
116
|
+
prev: "text-prev-foreground-onfill",
|
|
117
|
+
"prev-soft": "text-prev-foreground",
|
|
118
|
+
// White, not the design's onfill token: that one turns dark in dark mode, on a fill that stays navy.
|
|
119
|
+
freemium: "font-semibold text-white",
|
|
67
120
|
error: "text-red-stroke-emphasis",
|
|
68
121
|
warning: "text-yellow-foreground",
|
|
69
|
-
|
|
122
|
+
neutral: "text-primary-foreground",
|
|
70
123
|
},
|
|
71
124
|
size: {
|
|
72
|
-
sm: "text-
|
|
73
|
-
md: "text-xs",
|
|
74
|
-
lg: "text-xs",
|
|
75
|
-
xl: "text-sm",
|
|
125
|
+
sm: "text-xxs leading-xxs",
|
|
126
|
+
md: "text-xs leading-xs",
|
|
127
|
+
lg: "text-xs leading-xs",
|
|
128
|
+
xl: "text-sm leading-sm",
|
|
76
129
|
},
|
|
77
130
|
},
|
|
131
|
+
compoundVariants: [
|
|
132
|
+
{ variant: "freemium", size: "md", className: "text-xxs leading-xxs" },
|
|
133
|
+
],
|
|
78
134
|
defaultVariants: {
|
|
79
|
-
variant: "
|
|
135
|
+
variant: "secondary",
|
|
80
136
|
size: "md",
|
|
81
137
|
},
|
|
82
138
|
});
|
|
83
139
|
|
|
140
|
+
const badgeDotVariants = cva("rounded-circular", {
|
|
141
|
+
variants: {
|
|
142
|
+
variant: {
|
|
143
|
+
primary: "bg-primary-background-fill",
|
|
144
|
+
"primary-soft": "bg-primary-background-fill",
|
|
145
|
+
secondary: "bg-secondary-background-fill",
|
|
146
|
+
"secondary-soft": "bg-secondary-background-fill",
|
|
147
|
+
regular: "bg-secondary-background-fill",
|
|
148
|
+
cm: "bg-cm-background-fill",
|
|
149
|
+
"cm-soft": "bg-cm-background-fill",
|
|
150
|
+
go: "bg-go-background-fill",
|
|
151
|
+
"go-soft": "bg-go-background-fill",
|
|
152
|
+
cir: "bg-cir-background-fill",
|
|
153
|
+
"cir-soft": "bg-cir-background-fill",
|
|
154
|
+
ped: "bg-yellow-background-fill",
|
|
155
|
+
"ped-soft": "bg-yellow-background-fill",
|
|
156
|
+
prev: "bg-prev-background-fill",
|
|
157
|
+
"prev-soft": "bg-prev-background-fill",
|
|
158
|
+
freemium: "bg-primary",
|
|
159
|
+
error: "bg-red-background-fill",
|
|
160
|
+
warning: "bg-yellow-background-fill",
|
|
161
|
+
neutral: "bg-gray-background-fill",
|
|
162
|
+
},
|
|
163
|
+
size: {
|
|
164
|
+
sm: "size-1",
|
|
165
|
+
md: "size-2",
|
|
166
|
+
lg: "size-3",
|
|
167
|
+
xl: "size-4",
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
defaultVariants: {
|
|
171
|
+
variant: "secondary",
|
|
172
|
+
size: "md",
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// The design's 10, 12, 16 and 20px as a minimum: a bigger icon grows the badge instead of
|
|
177
|
+
// spilling over its gap and padding.
|
|
178
|
+
const ICON_BOX: Record<"sm" | "md" | "lg" | "xl", string> = {
|
|
179
|
+
sm: "min-h-2.5 min-w-2.5",
|
|
180
|
+
md: "min-h-3 min-w-3",
|
|
181
|
+
lg: "min-h-4 min-w-4",
|
|
182
|
+
xl: "min-h-5 min-w-5",
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const ICON_ONLY_PADDING: Record<"sm" | "md" | "lg" | "xl", string> = {
|
|
186
|
+
sm: "p-0.5",
|
|
187
|
+
md: "px-1 py-0.5",
|
|
188
|
+
lg: "p-1",
|
|
189
|
+
xl: "p-1",
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Corner to corner like the design's at any width, unlike the web's fixed 46deg.
|
|
193
|
+
const FREEMIUM_GRADIENT: ViewStyle = {
|
|
194
|
+
experimental_backgroundImage:
|
|
195
|
+
"radial-gradient(135.09% 111.8% at 0% 50%, rgba(24, 98, 188, 0.02) 0%, rgba(0, 32, 91, 0) 100%), linear-gradient(to top right, rgba(62, 218, 199, 0.56) 6.54%, rgba(24, 98, 188, 0.17) 85.21%)",
|
|
196
|
+
};
|
|
197
|
+
|
|
84
198
|
const SHINE_DURATION_MS = 2000;
|
|
85
199
|
|
|
86
200
|
function BadgeShine() {
|
|
@@ -94,7 +208,13 @@ function BadgeShine() {
|
|
|
94
208
|
const bandStyle = useAnimatedStyle(() => ({
|
|
95
209
|
width: width.value / 2,
|
|
96
210
|
transform: [
|
|
97
|
-
{
|
|
211
|
+
{
|
|
212
|
+
translateX: interpolate(
|
|
213
|
+
progress.value,
|
|
214
|
+
[0, 1],
|
|
215
|
+
[width.value, -width.value / 2]
|
|
216
|
+
),
|
|
217
|
+
},
|
|
98
218
|
{ skewX: "-20deg" },
|
|
99
219
|
],
|
|
100
220
|
}));
|
|
@@ -111,10 +231,13 @@ function BadgeShine() {
|
|
|
111
231
|
return (
|
|
112
232
|
<View
|
|
113
233
|
pointerEvents="none"
|
|
114
|
-
className="absolute inset-0 overflow-hidden rounded-
|
|
234
|
+
className="absolute inset-0 overflow-hidden rounded-pill"
|
|
115
235
|
onLayout={handleLayout}
|
|
116
236
|
>
|
|
117
|
-
<Animated.View
|
|
237
|
+
<Animated.View
|
|
238
|
+
className="absolute bottom-0 top-0 bg-white-24"
|
|
239
|
+
style={bandStyle}
|
|
240
|
+
/>
|
|
118
241
|
</View>
|
|
119
242
|
);
|
|
120
243
|
}
|
|
@@ -125,39 +248,62 @@ function Badge({
|
|
|
125
248
|
size,
|
|
126
249
|
icon,
|
|
127
250
|
animation,
|
|
251
|
+
layout = "tag",
|
|
252
|
+
style,
|
|
128
253
|
children,
|
|
129
254
|
...props
|
|
130
255
|
}: BadgeProps) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
256
|
+
if (layout === "dot") {
|
|
257
|
+
return (
|
|
258
|
+
<View
|
|
259
|
+
className={cn(badgeDotVariants({ variant, size }), className)}
|
|
260
|
+
style={style}
|
|
261
|
+
{...props}
|
|
262
|
+
/>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const resolvedSize = size ?? "md";
|
|
267
|
+
const tint = SOFT_TINT[variant ?? "secondary"];
|
|
268
|
+
const hasContent =
|
|
269
|
+
children != null && typeof children !== "boolean" && children !== "";
|
|
270
|
+
const isIconOnly = !!icon && !hasContent;
|
|
140
271
|
|
|
141
272
|
return (
|
|
142
273
|
<View
|
|
143
274
|
className={cn(
|
|
144
275
|
badgeVariants({ variant, size }),
|
|
145
|
-
|
|
146
|
-
!isIconOnly && isShortUpTo3 && CHAR_PADDING[size ?? "md"],
|
|
276
|
+
isIconOnly && ICON_ONLY_PADDING[resolvedSize],
|
|
147
277
|
className
|
|
148
278
|
)}
|
|
279
|
+
style={variant === "freemium" ? [FREEMIUM_GRADIENT, style] : style}
|
|
149
280
|
{...props}
|
|
150
281
|
>
|
|
282
|
+
{tint ? (
|
|
283
|
+
<View
|
|
284
|
+
pointerEvents="none"
|
|
285
|
+
className={cn("absolute inset-0 rounded-pill", tint)}
|
|
286
|
+
/>
|
|
287
|
+
) : null}
|
|
151
288
|
{animation === "shine" ? <BadgeShine /> : null}
|
|
152
|
-
{icon
|
|
153
|
-
|
|
154
|
-
|
|
289
|
+
{icon ? (
|
|
290
|
+
<View
|
|
291
|
+
className={cn("items-center justify-center", ICON_BOX[resolvedSize])}
|
|
292
|
+
>
|
|
293
|
+
{icon}
|
|
294
|
+
</View>
|
|
295
|
+
) : null}
|
|
296
|
+
{hasContent ? (
|
|
297
|
+
<Text
|
|
298
|
+
numberOfLines={1}
|
|
299
|
+
className={cn(badgeTextVariants({ variant, size }))}
|
|
300
|
+
>
|
|
155
301
|
{children}
|
|
156
302
|
</Text>
|
|
157
|
-
)}
|
|
303
|
+
) : null}
|
|
158
304
|
</View>
|
|
159
305
|
);
|
|
160
306
|
}
|
|
161
307
|
|
|
162
308
|
export { Badge, badgeVariants };
|
|
163
|
-
export type { BadgeAnimation, BadgeProps };
|
|
309
|
+
export type { BadgeAnimation, BadgeLayout, BadgeProps };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import { Pressable, View } from "react-native";
|
|
2
|
+
import { Pressable, View, type Insets } from "react-native";
|
|
3
3
|
import * as MedwayIcons from "@medway-ui/icons/native";
|
|
4
4
|
|
|
5
5
|
import { Text } from "@/components/Text";
|
|
6
6
|
import { ThemedIcon } from "@/components/ThemedIcon";
|
|
7
|
-
import {
|
|
7
|
+
import { RaisedSurface } from "@/lib/raised-surface";
|
|
8
8
|
import { cn } from "@/lib/utils";
|
|
9
9
|
|
|
10
10
|
type ChoiceSize = "sm" | "md" | "lg";
|
|
@@ -33,13 +33,29 @@ interface ChoiceProps {
|
|
|
33
33
|
className?: string;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// The design's padding minus the 2px ring (`p-0.5`) around the inner card. The vertical hit slop
|
|
37
|
+
// takes the 32, 40 and 48pt chips to the package's 44pt touch target (`MIN_TOUCH_TARGET`).
|
|
36
38
|
const SIZE_MAP: Record<
|
|
37
39
|
ChoiceSize,
|
|
38
|
-
{ container: string; text: string; icon: number }
|
|
40
|
+
{ container: string; text: string; icon: number; hitSlop?: Insets }
|
|
39
41
|
> = {
|
|
40
|
-
sm: {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
sm: {
|
|
43
|
+
container: "gap-2 px-2.5 py-1.5",
|
|
44
|
+
text: "text-xs leading-xs",
|
|
45
|
+
icon: 16,
|
|
46
|
+
hitSlop: { top: 6, bottom: 6 },
|
|
47
|
+
},
|
|
48
|
+
md: {
|
|
49
|
+
container: "gap-2.5 px-3 py-2",
|
|
50
|
+
text: "text-sm leading-sm",
|
|
51
|
+
icon: 20,
|
|
52
|
+
hitSlop: { top: 2, bottom: 2 },
|
|
53
|
+
},
|
|
54
|
+
lg: {
|
|
55
|
+
container: "gap-3 px-3.5 py-2.5",
|
|
56
|
+
text: "text-md leading-md",
|
|
57
|
+
icon: 24,
|
|
58
|
+
},
|
|
43
59
|
};
|
|
44
60
|
|
|
45
61
|
const THEME_SELECTED_OUTER_BG: Record<ChoiceTheme, string> = {
|
|
@@ -70,13 +86,6 @@ const THEME_CHECK_COLOR_TOKEN: Record<ChoiceTheme, string> = {
|
|
|
70
86
|
prev: "prev-foreground",
|
|
71
87
|
};
|
|
72
88
|
|
|
73
|
-
const SHADOW_STYLE = {
|
|
74
|
-
shadowOffset: { width: 0, height: 2 },
|
|
75
|
-
shadowOpacity: 0.08,
|
|
76
|
-
shadowRadius: 4,
|
|
77
|
-
elevation: 2,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
89
|
function Choice({
|
|
81
90
|
label,
|
|
82
91
|
selected = false,
|
|
@@ -88,49 +97,61 @@ function Choice({
|
|
|
88
97
|
className,
|
|
89
98
|
}: ChoiceProps) {
|
|
90
99
|
const sz = SIZE_MAP[size] ?? SIZE_MAP.md;
|
|
91
|
-
const
|
|
100
|
+
const isRaised = selected && !disabled;
|
|
92
101
|
|
|
93
102
|
const outerBg = disabled
|
|
94
103
|
? "bg-gray-background-soft-muted"
|
|
95
104
|
: selected
|
|
96
105
|
? THEME_SELECTED_OUTER_BG[theme]
|
|
97
|
-
: "bg-gray-background-soft";
|
|
98
|
-
|
|
99
|
-
const textClass = disabled
|
|
100
|
-
? "font-medium text-gray-foreground-muted"
|
|
101
|
-
: selected
|
|
102
|
-
? "font-semibold text-primary-foreground"
|
|
103
|
-
: "font-medium text-gray-foreground";
|
|
106
|
+
: "bg-gray-background-soft active:bg-gray-background-soft-emphasis";
|
|
104
107
|
|
|
105
108
|
return (
|
|
106
109
|
<Pressable
|
|
107
110
|
onPress={disabled ? undefined : onPress}
|
|
108
111
|
disabled={disabled}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
hitSlop={sz.hitSlop}
|
|
113
|
+
accessibilityRole="togglebutton"
|
|
114
|
+
accessibilityLabel={label}
|
|
115
|
+
accessibilityState={{ checked: selected, disabled }}
|
|
116
|
+
className={cn("rounded-md p-0.5", outerBg, className)}
|
|
113
117
|
>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"flex-row items-center justify-center
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
118
|
+
{({ pressed }) => (
|
|
119
|
+
<View
|
|
120
|
+
className={cn("flex-row items-center justify-center", sz.container)}
|
|
121
|
+
>
|
|
122
|
+
{isRaised ? <RaisedSurface /> : null}
|
|
123
|
+
{icon ? <View className="shrink-0">{icon}</View> : null}
|
|
124
|
+
<Text
|
|
125
|
+
numberOfLines={1}
|
|
126
|
+
className={cn(
|
|
127
|
+
"shrink text-center",
|
|
128
|
+
sz.text,
|
|
129
|
+
disabled
|
|
130
|
+
? "font-medium text-gray-foreground-muted"
|
|
131
|
+
: selected
|
|
132
|
+
? "font-semibold text-primary-foreground"
|
|
133
|
+
: pressed
|
|
134
|
+
? "font-medium text-gray-foreground-emphasis"
|
|
135
|
+
: "font-medium text-gray-foreground"
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
{label}
|
|
139
|
+
</Text>
|
|
140
|
+
{selected ? (
|
|
141
|
+
<View className="shrink-0">
|
|
142
|
+
<ThemedIcon
|
|
143
|
+
icon={MedwayIcons.CheckMedium}
|
|
144
|
+
size={sz.icon}
|
|
145
|
+
colorToken={
|
|
146
|
+
disabled
|
|
147
|
+
? "gray-foreground-muted"
|
|
148
|
+
: THEME_CHECK_COLOR_TOKEN[theme]
|
|
149
|
+
}
|
|
150
|
+
/>
|
|
151
|
+
</View>
|
|
152
|
+
) : null}
|
|
153
|
+
</View>
|
|
154
|
+
)}
|
|
134
155
|
</Pressable>
|
|
135
156
|
);
|
|
136
157
|
}
|