@medway-ui/native 1.0.3 → 1.1.1
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 +76 -3
- package/lib/commonjs/components/Alert.js +6 -3
- package/lib/commonjs/components/Alert.js.map +1 -1
- package/lib/commonjs/components/AlertDialog.js +38 -15
- package/lib/commonjs/components/AlertDialog.js.map +1 -1
- package/lib/commonjs/components/Avatar.js +101 -0
- package/lib/commonjs/components/Avatar.js.map +1 -0
- package/lib/commonjs/components/CardNews.js +319 -0
- package/lib/commonjs/components/CardNews.js.map +1 -0
- package/lib/commonjs/components/Checkbox.js +36 -13
- package/lib/commonjs/components/Checkbox.js.map +1 -1
- package/lib/commonjs/components/Dialog.js +15 -10
- package/lib/commonjs/components/Dialog.js.map +1 -1
- package/lib/commonjs/components/EmptyState.js +42 -0
- package/lib/commonjs/components/EmptyState.js.map +1 -0
- package/lib/commonjs/components/FallbackImage.js +55 -0
- package/lib/commonjs/components/FallbackImage.js.map +1 -0
- package/lib/commonjs/components/Input.js +55 -16
- package/lib/commonjs/components/Input.js.map +1 -1
- package/lib/commonjs/components/Label.js +43 -14
- package/lib/commonjs/components/Label.js.map +1 -1
- package/lib/commonjs/components/Loading.js +7 -3
- package/lib/commonjs/components/Loading.js.map +1 -1
- package/lib/commonjs/components/PageHeader.js +39 -0
- package/lib/commonjs/components/PageHeader.js.map +1 -0
- package/lib/commonjs/components/Select.js +32 -13
- package/lib/commonjs/components/Select.js.map +1 -1
- package/lib/commonjs/components/Sheet.js +14 -9
- package/lib/commonjs/components/Sheet.js.map +1 -1
- package/lib/commonjs/components/Skeleton.js +34 -0
- package/lib/commonjs/components/Skeleton.js.map +1 -0
- package/lib/commonjs/components/Switch.js +43 -14
- package/lib/commonjs/components/Switch.js.map +1 -1
- package/lib/commonjs/components/ThemedIcon.js +29 -0
- package/lib/commonjs/components/ThemedIcon.js.map +1 -0
- package/lib/commonjs/components/Toast.js +24 -12
- package/lib/commonjs/components/Toast.js.map +1 -1
- package/lib/commonjs/components/Tooltip.js +5 -3
- package/lib/commonjs/components/Tooltip.js.map +1 -1
- package/lib/commonjs/hooks/useThemeColor.js +71 -0
- package/lib/commonjs/hooks/useThemeColor.js.map +1 -0
- package/lib/commonjs/index.js +152 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/Alert.js +6 -3
- package/lib/module/components/Alert.js.map +1 -1
- package/lib/module/components/AlertDialog.js +38 -15
- package/lib/module/components/AlertDialog.js.map +1 -1
- package/lib/module/components/Avatar.js +94 -0
- package/lib/module/components/Avatar.js.map +1 -0
- package/lib/module/components/CardNews.js +303 -0
- package/lib/module/components/CardNews.js.map +1 -0
- package/lib/module/components/Checkbox.js +37 -14
- package/lib/module/components/Checkbox.js.map +1 -1
- package/lib/module/components/Dialog.js +15 -10
- package/lib/module/components/Dialog.js.map +1 -1
- package/lib/module/components/EmptyState.js +39 -0
- package/lib/module/components/EmptyState.js.map +1 -0
- package/lib/module/components/FallbackImage.js +51 -0
- package/lib/module/components/FallbackImage.js.map +1 -0
- package/lib/module/components/Input.js +55 -16
- package/lib/module/components/Input.js.map +1 -1
- package/lib/module/components/Label.js +44 -15
- package/lib/module/components/Label.js.map +1 -1
- package/lib/module/components/Loading.js +7 -3
- package/lib/module/components/Loading.js.map +1 -1
- package/lib/module/components/PageHeader.js +36 -0
- package/lib/module/components/PageHeader.js.map +1 -0
- package/lib/module/components/Select.js +32 -13
- package/lib/module/components/Select.js.map +1 -1
- package/lib/module/components/Sheet.js +14 -9
- package/lib/module/components/Sheet.js.map +1 -1
- package/lib/module/components/Skeleton.js +30 -0
- package/lib/module/components/Skeleton.js.map +1 -0
- package/lib/module/components/Switch.js +44 -15
- package/lib/module/components/Switch.js.map +1 -1
- package/lib/module/components/ThemedIcon.js +26 -0
- package/lib/module/components/ThemedIcon.js.map +1 -0
- package/lib/module/components/Toast.js +24 -12
- package/lib/module/components/Toast.js.map +1 -1
- package/lib/module/components/Tooltip.js +6 -4
- package/lib/module/components/Tooltip.js.map +1 -1
- package/lib/module/hooks/useThemeColor.js +68 -0
- package/lib/module/hooks/useThemeColor.js.map +1 -0
- package/lib/module/index.js +15 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/Alert.d.ts.map +1 -1
- package/lib/typescript/components/AlertDialog.d.ts +2 -2
- package/lib/typescript/components/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/components/Avatar.d.ts +28 -0
- package/lib/typescript/components/Avatar.d.ts.map +1 -0
- package/lib/typescript/components/CardNews.d.ts +54 -0
- package/lib/typescript/components/CardNews.d.ts.map +1 -0
- package/lib/typescript/components/Checkbox.d.ts +5 -5
- package/lib/typescript/components/Checkbox.d.ts.map +1 -1
- package/lib/typescript/components/Dialog.d.ts.map +1 -1
- package/lib/typescript/components/EmptyState.d.ts +13 -0
- package/lib/typescript/components/EmptyState.d.ts.map +1 -0
- package/lib/typescript/components/FallbackImage.d.ts +9 -0
- package/lib/typescript/components/FallbackImage.d.ts.map +1 -0
- package/lib/typescript/components/Input.d.ts +4 -5
- package/lib/typescript/components/Input.d.ts.map +1 -1
- package/lib/typescript/components/Label.d.ts.map +1 -1
- package/lib/typescript/components/Loading.d.ts +5 -1
- package/lib/typescript/components/Loading.d.ts.map +1 -1
- package/lib/typescript/components/PageHeader.d.ts +12 -0
- package/lib/typescript/components/PageHeader.d.ts.map +1 -0
- package/lib/typescript/components/Select.d.ts +3 -1
- package/lib/typescript/components/Select.d.ts.map +1 -1
- package/lib/typescript/components/Sheet.d.ts.map +1 -1
- package/lib/typescript/components/Skeleton.d.ts +8 -0
- package/lib/typescript/components/Skeleton.d.ts.map +1 -0
- package/lib/typescript/components/Switch.d.ts +2 -2
- package/lib/typescript/components/Switch.d.ts.map +1 -1
- package/lib/typescript/components/ThemedIcon.d.ts +24 -0
- package/lib/typescript/components/ThemedIcon.d.ts.map +1 -0
- package/lib/typescript/components/Toast.d.ts +1 -1
- package/lib/typescript/components/Toast.d.ts.map +1 -1
- package/lib/typescript/components/Tooltip.d.ts.map +1 -1
- package/lib/typescript/hooks/useThemeColor.d.ts +11 -0
- package/lib/typescript/hooks/useThemeColor.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +16 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +28 -5
- package/setup/MedwayUiProvider.tsx +65 -0
- package/setup/babel-brownfield.js +50 -0
- package/setup/createMedwayUiNavigator.tsx +94 -0
- package/setup/global.css +5 -0
- package/setup/index.ts +12 -0
- package/setup/index.tsx +42 -0
- package/setup/lazyScreen.tsx +14 -0
- package/setup/metro.js +195 -0
- package/setup/tailwind.js +64 -0
- package/setup/tokens.native.css +873 -0
- package/src/components/Alert.tsx +3 -1
- package/src/components/AlertDialog.tsx +63 -19
- package/src/components/Avatar.tsx +130 -0
- package/src/components/CardNews.tsx +412 -0
- package/src/components/Checkbox.tsx +51 -23
- package/src/components/Dialog.tsx +13 -7
- package/src/components/EmptyState.tsx +57 -0
- package/src/components/FallbackImage.tsx +58 -0
- package/src/components/Input.tsx +65 -19
- package/src/components/Label.tsx +49 -14
- package/src/components/Loading.tsx +13 -2
- package/src/components/PageHeader.tsx +50 -0
- package/src/components/Select.tsx +22 -10
- package/src/components/Sheet.tsx +30 -24
- package/src/components/Skeleton.tsx +45 -0
- package/src/components/Switch.tsx +64 -32
- package/src/components/ThemedIcon.tsx +46 -0
- package/src/components/Toast.tsx +26 -13
- package/src/components/Tooltip.tsx +1 -0
- package/src/hooks/useThemeColor.ts +79 -0
- package/src/index.ts +60 -0
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useState, type ReactNode } from "react";
|
|
2
|
+
import { type ViewStyle } from "react-native";
|
|
2
3
|
import * as CheckboxPrimitive from "@rn-primitives/checkbox";
|
|
3
|
-
import {
|
|
4
|
-
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
import { cn } from "@/lib/utils";
|
|
6
|
+
import { Check, RemoveMinus } from "@medway-ui/icons/native";
|
|
7
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
6
8
|
|
|
7
|
-
interface CheckboxProps
|
|
9
|
+
interface CheckboxProps
|
|
10
|
+
extends Omit<
|
|
11
|
+
CheckboxPrimitive.RootProps,
|
|
12
|
+
"checked" | "onCheckedChange" | "children"
|
|
13
|
+
>,
|
|
14
|
+
Omit<VariantProps<typeof checkboxVariants>, "checked" | "disabled"> {
|
|
15
|
+
children?: ReactNode;
|
|
8
16
|
checked?: boolean;
|
|
9
17
|
defaultChecked?: boolean;
|
|
10
18
|
onCheckedChange?: (checked: boolean) => void;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
size?: "sm" | "md" | "lg";
|
|
13
|
-
variant?: "regular";
|
|
14
19
|
uncheckIcon?: boolean;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -20,6 +25,12 @@ const ICON_SIZES: Record<"sm" | "md" | "lg", number> = {
|
|
|
20
25
|
lg: 20,
|
|
21
26
|
};
|
|
22
27
|
|
|
28
|
+
const CHECKBOX_DIMENSIONS: Record<"sm" | "md" | "lg", number> = {
|
|
29
|
+
sm: 16,
|
|
30
|
+
md: 20,
|
|
31
|
+
lg: 24,
|
|
32
|
+
};
|
|
33
|
+
|
|
23
34
|
const checkboxVariants = cva(
|
|
24
35
|
"items-center justify-center rounded-sm border-2 shrink-0",
|
|
25
36
|
{
|
|
@@ -59,17 +70,32 @@ function Checkbox({
|
|
|
59
70
|
defaultChecked = false,
|
|
60
71
|
onCheckedChange,
|
|
61
72
|
disabled,
|
|
73
|
+
style,
|
|
74
|
+
children,
|
|
62
75
|
...props
|
|
63
76
|
}: CheckboxProps) {
|
|
64
77
|
const resolvedSize = size ?? "md";
|
|
65
78
|
const iconSize = ICON_SIZES[resolvedSize];
|
|
79
|
+
const dimension = CHECKBOX_DIMENSIONS[resolvedSize];
|
|
66
80
|
const [internalChecked, setInternalChecked] = useState(defaultChecked);
|
|
67
81
|
const isControlled = checked !== undefined;
|
|
68
82
|
const isChecked = isControlled ? checked : internalChecked;
|
|
83
|
+
const iconColor = useThemeColor("primary-foreground-onfill");
|
|
84
|
+
|
|
85
|
+
const handleCheckedChange = (nextChecked: boolean) => {
|
|
86
|
+
if (!isControlled) setInternalChecked(nextChecked);
|
|
87
|
+
onCheckedChange?.(nextChecked);
|
|
88
|
+
};
|
|
69
89
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
const rootStyle: ViewStyle = {
|
|
91
|
+
width: dimension,
|
|
92
|
+
height: dimension,
|
|
93
|
+
minWidth: dimension,
|
|
94
|
+
minHeight: dimension,
|
|
95
|
+
borderWidth: 2,
|
|
96
|
+
borderRadius: 4,
|
|
97
|
+
alignItems: "center",
|
|
98
|
+
justifyContent: "center",
|
|
73
99
|
};
|
|
74
100
|
|
|
75
101
|
return (
|
|
@@ -77,27 +103,29 @@ function Checkbox({
|
|
|
77
103
|
checked={isChecked}
|
|
78
104
|
onCheckedChange={handleCheckedChange}
|
|
79
105
|
disabled={disabled}
|
|
106
|
+
style={(state) => [
|
|
107
|
+
rootStyle,
|
|
108
|
+
typeof style === "function" ? style(state) : style,
|
|
109
|
+
]}
|
|
80
110
|
className={cn(
|
|
81
|
-
checkboxVariants({
|
|
111
|
+
checkboxVariants({
|
|
112
|
+
size,
|
|
113
|
+
variant,
|
|
114
|
+
checked: isChecked,
|
|
115
|
+
disabled: disabled ?? false,
|
|
116
|
+
}),
|
|
82
117
|
className
|
|
83
118
|
)}
|
|
84
119
|
{...props}
|
|
85
120
|
>
|
|
86
121
|
<CheckboxPrimitive.Indicator className="items-center justify-center">
|
|
87
|
-
{uncheckIcon ? (
|
|
88
|
-
<RemoveMinus
|
|
89
|
-
size={iconSize}
|
|
90
|
-
strokeWidth={2.6}
|
|
91
|
-
className="text-primary-foreground-onfill"
|
|
92
|
-
/>
|
|
122
|
+
{iconColor && (uncheckIcon ? (
|
|
123
|
+
<RemoveMinus width={iconSize} height={iconSize} strokeWidth={2.6} color={iconColor} />
|
|
93
124
|
) : (
|
|
94
|
-
<Check
|
|
95
|
-
|
|
96
|
-
strokeWidth={2.6}
|
|
97
|
-
className="text-primary-foreground-onfill"
|
|
98
|
-
/>
|
|
99
|
-
)}
|
|
125
|
+
<Check width={iconSize} height={iconSize} strokeWidth={2.6} color={iconColor} />
|
|
126
|
+
))}
|
|
100
127
|
</CheckboxPrimitive.Indicator>
|
|
128
|
+
{children}
|
|
101
129
|
</CheckboxPrimitive.Root>
|
|
102
130
|
);
|
|
103
131
|
}
|
|
@@ -2,6 +2,7 @@ import { type ReactNode, isValidElement, cloneElement, type ReactElement } from
|
|
|
2
2
|
import { View, Text, ScrollView, type ViewProps } from "react-native";
|
|
3
3
|
import * as DialogPrimitive from "@rn-primitives/dialog";
|
|
4
4
|
import { CloseMd } from "@medway-ui/icons/native";
|
|
5
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
5
6
|
import { cn } from "@/lib/utils";
|
|
6
7
|
import { Separator } from "@/components/Separator";
|
|
7
8
|
|
|
@@ -55,26 +56,31 @@ function DialogContainer({
|
|
|
55
56
|
showCloseButton = true,
|
|
56
57
|
...props
|
|
57
58
|
}: DialogContainerProps) {
|
|
59
|
+
const closeIconColor = useThemeColor("gray-foreground");
|
|
60
|
+
|
|
58
61
|
return (
|
|
59
62
|
<DialogPrimitive.Portal>
|
|
60
63
|
<DialogPrimitive.Overlay
|
|
61
|
-
className={cn("absolute inset-0
|
|
64
|
+
className={cn("absolute inset-0 bg-black/50", overlayClassName)}
|
|
65
|
+
/>
|
|
66
|
+
<DialogPrimitive.Content
|
|
67
|
+
className="absolute inset-0 items-center justify-center px-4"
|
|
68
|
+
{...props}
|
|
62
69
|
>
|
|
63
|
-
<
|
|
70
|
+
<View
|
|
64
71
|
className={cn(
|
|
65
|
-
"relative flex-col max-h-[90%] w-full
|
|
72
|
+
"relative flex-col max-h-[90%] w-full rounded-xl bg-background shadow-lg overflow-hidden",
|
|
66
73
|
className
|
|
67
74
|
)}
|
|
68
|
-
{...props}
|
|
69
75
|
>
|
|
70
76
|
{children}
|
|
71
77
|
{showCloseButton && (
|
|
72
78
|
<DialogPrimitive.Close className="absolute right-3 top-3 rounded-pill p-1.5 active:bg-gray-background-soft">
|
|
73
|
-
<CloseMd
|
|
79
|
+
{closeIconColor && <CloseMd width={16} height={16} color={closeIconColor} />}
|
|
74
80
|
</DialogPrimitive.Close>
|
|
75
81
|
)}
|
|
76
|
-
</
|
|
77
|
-
</DialogPrimitive.
|
|
82
|
+
</View>
|
|
83
|
+
</DialogPrimitive.Content>
|
|
78
84
|
</DialogPrimitive.Portal>
|
|
79
85
|
);
|
|
80
86
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Alert,
|
|
6
|
+
AlertActions,
|
|
7
|
+
AlertContainer,
|
|
8
|
+
AlertContent,
|
|
9
|
+
AlertDescription,
|
|
10
|
+
AlertIcon,
|
|
11
|
+
AlertTitle,
|
|
12
|
+
} from "@/components/Alert";
|
|
13
|
+
import { cn } from "@/lib/utils";
|
|
14
|
+
|
|
15
|
+
interface EmptyStateProps extends ViewProps {
|
|
16
|
+
icon?: ReactNode;
|
|
17
|
+
title: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
action?: ReactNode;
|
|
20
|
+
variant?: "regular" | "emphasis" | "success" | "warning" | "error";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function EmptyState({
|
|
24
|
+
className,
|
|
25
|
+
icon,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
action,
|
|
29
|
+
variant = "regular",
|
|
30
|
+
...props
|
|
31
|
+
}: EmptyStateProps) {
|
|
32
|
+
return (
|
|
33
|
+
<View
|
|
34
|
+
className={cn(
|
|
35
|
+
"items-center gap-6 rounded-lg border border-gray-stroke-muted bg-surface-card p-8",
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
>
|
|
40
|
+
<Alert variant={variant} className="border-0 bg-transparent p-0">
|
|
41
|
+
<AlertContainer>
|
|
42
|
+
{icon ? <AlertIcon icon={icon} /> : null}
|
|
43
|
+
<AlertContent>
|
|
44
|
+
<AlertTitle>{title}</AlertTitle>
|
|
45
|
+
{description ? (
|
|
46
|
+
<AlertDescription>{description}</AlertDescription>
|
|
47
|
+
) : null}
|
|
48
|
+
</AlertContent>
|
|
49
|
+
</AlertContainer>
|
|
50
|
+
{action ? <AlertActions>{action}</AlertActions> : null}
|
|
51
|
+
</Alert>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { EmptyState };
|
|
57
|
+
export type { EmptyStateProps };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Svg, {
|
|
2
|
+
Defs,
|
|
3
|
+
RadialGradient,
|
|
4
|
+
Stop,
|
|
5
|
+
Rect,
|
|
6
|
+
Path,
|
|
7
|
+
G,
|
|
8
|
+
type SvgProps,
|
|
9
|
+
} from "react-native-svg";
|
|
10
|
+
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
|
|
13
|
+
const GRADIENT_ID = "medway-fallback-gradient";
|
|
14
|
+
|
|
15
|
+
const LOGO_PATH =
|
|
16
|
+
"M104.229 3.90299H107.408C107.836 3.90299 108.122 4.34302 107.948 4.73464L100.413 21.6491C100.318 21.8625 100.106 22 99.8724 22H96.6935C96.2653 22 95.9791 21.56 96.153 21.1684L97.89 17.2687L92.3798 4.90074C92.1706 4.43102 92.5141 3.90189 93.0281 3.90189H95.8779C96.1585 3.90189 96.4128 4.0669 96.5262 4.32322L100.09 12.325L103.686 4.25391C103.781 4.0405 103.993 3.90299 104.227 3.90299H104.229ZM91.9043 11.0005V17.3875C91.9043 17.7791 91.5862 18.097 91.1943 18.097H88.9455C88.5537 18.097 88.2356 17.7791 88.2356 17.3875V17.2841C87.1547 17.8957 85.8999 18.2367 84.5647 18.2147C80.7496 18.152 77.6159 15.0928 77.4706 11.2822C77.3132 7.16906 80.6043 3.78419 84.6847 3.78419C88.765 3.78419 91.9043 7.01505 91.9043 10.9994V11.0005ZM88.0573 11.0005C88.0573 9.13926 86.5471 7.62888 84.6836 7.62888C82.8201 7.62888 81.3099 9.13816 81.3099 11.0005C81.3099 12.8629 82.8201 14.3722 84.6836 14.3722C86.5471 14.3722 88.0573 12.8629 88.0573 11.0005ZM75.4519 12.1578V4.61363C75.4519 4.22201 75.1338 3.9041 74.7419 3.9041H72.3159C71.9241 3.9041 71.606 4.22201 71.606 4.61363V12.2282C71.606 13.4273 70.6153 14.3997 69.4089 14.3711C68.2378 14.3436 67.3154 13.358 67.3154 12.1875V4.61363C67.3154 4.22201 66.9973 3.9041 66.6054 3.9041H64.1794C63.7876 3.9041 63.4695 4.22201 63.4695 4.61363V12.2282C63.4695 13.4273 62.4788 14.3997 61.2724 14.3711C60.1013 14.3436 59.1789 13.358 59.1789 12.1875V4.61363C59.1789 4.22201 58.8608 3.9041 58.4689 3.9041H56.0429C55.6511 3.9041 55.333 4.22201 55.333 4.61363V12.1578C55.333 15.4778 58.0507 18.2422 61.3726 18.2158C62.9235 18.2037 64.3346 17.5998 65.3935 16.6196C66.4524 17.5998 67.8635 18.2037 69.4144 18.2158C72.7364 18.2422 75.4541 15.4767 75.4541 12.1578H75.4519ZM49.7094 0H51.9582C52.35 0 52.6682 0.317916 52.6682 0.709535V17.3875C52.6682 17.7791 52.35 18.097 51.9582 18.097H49.7094C49.3176 18.097 48.9995 17.7791 48.9995 17.3875V17.2841C47.9505 17.877 46.7397 18.2158 45.4486 18.2158C41.3671 18.2158 38.0771 14.832 38.2345 10.7178C38.3798 6.90834 41.5135 3.84799 45.3286 3.78529C46.6648 3.76329 47.9186 4.1043 48.9995 4.71594V0.709535C48.9995 0.317916 49.3176 0 49.7094 0ZM48.8223 11.0005C48.8223 9.13926 47.3121 7.62888 45.4486 7.62888C43.585 7.62888 42.0749 9.13816 42.0749 11.0005C42.0749 12.8629 43.585 14.3722 45.4486 14.3722C47.3121 14.3722 48.8223 12.8629 48.8223 11.0005ZM26.4139 12.2425C26.1508 12.2425 25.9769 12.5186 26.0947 12.7529C26.6979 13.9608 27.9031 14.7847 29.2945 14.7847C30.2576 14.7847 31.1326 14.3887 31.7799 13.7452C31.9109 13.6143 32.0903 13.5428 32.2752 13.5428H34.8806C35.4265 13.5428 35.7689 14.1324 35.497 14.6054C34.2587 16.7626 31.945 18.2147 29.2945 18.2147C25.3396 18.2147 22.1343 14.9838 22.1343 10.9994C22.1343 7.01505 25.3407 3.78419 29.2945 3.78419C33.2482 3.78419 36.4546 7.01505 36.4546 10.9994C36.4546 11.1997 36.4469 11.3966 36.4304 11.5924C36.4007 11.9598 36.0914 12.2414 35.7226 12.2414H26.4139V12.2425ZM26.2356 8.98855C26.0947 9.22286 26.2686 9.52208 26.5427 9.52208H32.0451C32.3192 9.52208 32.4931 9.22396 32.3522 8.98855C31.7138 7.9237 30.5823 7.21526 29.2945 7.21526C28.0066 7.21526 26.8751 7.9237 26.2367 8.98855H26.2356ZM14.0815 3.78529C12.5306 3.79739 11.1194 4.40132 10.0605 5.38147C9.00165 4.40132 7.59054 3.79739 6.03963 3.78529C2.71767 3.75889 0 6.52443 0 9.84329V17.3875C0 17.7791 0.318107 18.097 0.709962 18.097H3.13594C3.52779 18.097 3.8459 17.7791 3.8459 17.3875V9.81359C3.8459 8.64313 4.7683 7.65748 5.93946 7.62998C7.14585 7.60138 8.13649 8.57383 8.13649 9.77289V17.3875C8.13649 17.7791 8.4546 18.097 8.84645 18.097H11.2724C11.6643 18.097 11.9824 17.7791 11.9824 17.3875V9.81359C11.9824 8.64313 12.9048 7.65748 14.076 7.62998C15.2823 7.60138 16.273 8.57383 16.273 9.77289V17.3875C16.273 17.7791 16.5911 18.097 16.9829 18.097H19.4089C19.8008 18.097 20.1189 17.7791 20.1189 17.3875V9.84329C20.1189 6.52333 17.4012 3.75889 14.0793 3.78529H14.0815Z";
|
|
17
|
+
|
|
18
|
+
interface FallbackImageProps extends SvgProps {
|
|
19
|
+
label?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function FallbackImage({
|
|
24
|
+
className,
|
|
25
|
+
label = "Medway",
|
|
26
|
+
...props
|
|
27
|
+
}: FallbackImageProps) {
|
|
28
|
+
return (
|
|
29
|
+
<Svg
|
|
30
|
+
accessibilityRole="image"
|
|
31
|
+
accessibilityLabel={label}
|
|
32
|
+
viewBox="0 0 320 167"
|
|
33
|
+
preserveAspectRatio="xMidYMid slice"
|
|
34
|
+
className={cn("size-full", className)}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<Defs>
|
|
38
|
+
<RadialGradient
|
|
39
|
+
id={GRADIENT_ID}
|
|
40
|
+
cx="50%"
|
|
41
|
+
cy="42%"
|
|
42
|
+
r="78%"
|
|
43
|
+
>
|
|
44
|
+
<Stop offset="0%" stopColor="#15397A" />
|
|
45
|
+
<Stop offset="55%" stopColor="#06245E" />
|
|
46
|
+
<Stop offset="100%" stopColor="#021536" />
|
|
47
|
+
</RadialGradient>
|
|
48
|
+
</Defs>
|
|
49
|
+
<Rect width="320" height="167" fill={`url(#${GRADIENT_ID})`} />
|
|
50
|
+
<G transform="translate(85.5 62.7) scale(1.38)" fill="#FFFFFF">
|
|
51
|
+
<Path d={LOGO_PATH} />
|
|
52
|
+
</G>
|
|
53
|
+
</Svg>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { FallbackImage };
|
|
58
|
+
export type { FallbackImageProps };
|
package/src/components/Input.tsx
CHANGED
|
@@ -32,11 +32,17 @@ import {
|
|
|
32
32
|
Show,
|
|
33
33
|
TriangleWarning,
|
|
34
34
|
} from "@medway-ui/icons/native";
|
|
35
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
35
36
|
import { cn } from "@/lib/utils";
|
|
36
37
|
|
|
37
38
|
export type InputType = "text" | "email" | "numeric" | "password";
|
|
38
39
|
|
|
39
|
-
type IconElement = ReactElement<{
|
|
40
|
+
type IconElement = ReactElement<{
|
|
41
|
+
width?: number;
|
|
42
|
+
height?: number;
|
|
43
|
+
color?: string;
|
|
44
|
+
strokeWidth?: number;
|
|
45
|
+
}>;
|
|
40
46
|
|
|
41
47
|
export interface InputProps
|
|
42
48
|
extends Omit<TextInputProps, "size">,
|
|
@@ -193,11 +199,17 @@ function InputEndIcon({ className, ...props }: ViewProps) {
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
function applyInputIconSize(
|
|
196
|
-
icon:
|
|
197
|
-
size: VariantProps<typeof inputVariants>["size"] = "lg"
|
|
198
|
-
|
|
202
|
+
icon: IconElement,
|
|
203
|
+
size: VariantProps<typeof inputVariants>["size"] = "lg",
|
|
204
|
+
color?: string,
|
|
205
|
+
): IconElement {
|
|
199
206
|
const iconSize = size === "md" ? INPUT_ICON_SIZE_MD : INPUT_ICON_SIZE_LG;
|
|
200
|
-
return cloneElement(icon, {
|
|
207
|
+
return cloneElement(icon, {
|
|
208
|
+
width: iconSize,
|
|
209
|
+
height: iconSize,
|
|
210
|
+
color,
|
|
211
|
+
strokeWidth: INPUT_ICON_STROKE_WIDTH,
|
|
212
|
+
});
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
function InputPassword({ className, size = "lg", invalid = false, label, ...props }: InputPasswordProps) {
|
|
@@ -205,11 +217,20 @@ function InputPassword({ className, size = "lg", invalid = false, label, ...prop
|
|
|
205
217
|
const [focused, setFocused] = useState(false);
|
|
206
218
|
const hasValue = Boolean(props.value || props.defaultValue);
|
|
207
219
|
const isFloated = focused || hasValue;
|
|
220
|
+
const iconColor = useThemeColor("gray-foreground");
|
|
221
|
+
const iconSize = size === "md" ? INPUT_ICON_SIZE_MD : INPUT_ICON_SIZE_LG;
|
|
208
222
|
|
|
209
223
|
return (
|
|
210
224
|
<InputAdornment>
|
|
211
225
|
<InputStartIcon>
|
|
212
|
-
{
|
|
226
|
+
{iconColor && (
|
|
227
|
+
<Lock
|
|
228
|
+
width={iconSize}
|
|
229
|
+
height={iconSize}
|
|
230
|
+
strokeWidth={INPUT_ICON_STROKE_WIDTH}
|
|
231
|
+
color={iconColor}
|
|
232
|
+
/>
|
|
233
|
+
)}
|
|
213
234
|
</InputStartIcon>
|
|
214
235
|
<View className="relative flex-1">
|
|
215
236
|
{label && <InputLabel floated={isFloated} disabled={props.editable === false}>{label}</InputLabel>}
|
|
@@ -232,7 +253,11 @@ function InputPassword({ className, size = "lg", invalid = false, label, ...prop
|
|
|
232
253
|
disabled={props.editable === false}
|
|
233
254
|
accessibilityLabel={visible ? "Hide password" : "Show password"}
|
|
234
255
|
>
|
|
235
|
-
{
|
|
256
|
+
{iconColor && (visible ? (
|
|
257
|
+
<Hide width={iconSize} height={iconSize} strokeWidth={INPUT_ICON_STROKE_WIDTH} color={iconColor} />
|
|
258
|
+
) : (
|
|
259
|
+
<Show width={iconSize} height={iconSize} strokeWidth={INPUT_ICON_STROKE_WIDTH} color={iconColor} />
|
|
260
|
+
))}
|
|
236
261
|
</Pressable>
|
|
237
262
|
</InputEndIcon>
|
|
238
263
|
</InputAdornment>
|
|
@@ -258,6 +283,7 @@ function InputNumeric({
|
|
|
258
283
|
const displayValue = isControlled ? String(value ?? "") : internalValue;
|
|
259
284
|
const [focused, setFocused] = useState(false);
|
|
260
285
|
const isFloated = focused || displayValue !== "";
|
|
286
|
+
const iconColor = useThemeColor("gray-foreground");
|
|
261
287
|
|
|
262
288
|
const numericValue = parseFloat(displayValue.replace(",", ".")) || 0;
|
|
263
289
|
|
|
@@ -278,7 +304,7 @@ function InputNumeric({
|
|
|
278
304
|
<InputAdornment>
|
|
279
305
|
{iconLeft && (
|
|
280
306
|
<InputStartIcon>
|
|
281
|
-
{applyInputIconSize(iconLeft, size)}
|
|
307
|
+
{applyInputIconSize(iconLeft, size, iconColor)}
|
|
282
308
|
</InputStartIcon>
|
|
283
309
|
)}
|
|
284
310
|
<View className="relative flex-1">
|
|
@@ -314,11 +340,15 @@ function InputNumeric({
|
|
|
314
340
|
className="items-center justify-center p-1"
|
|
315
341
|
accessibilityLabel="Increase value"
|
|
316
342
|
>
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
343
|
+
{iconColor && (
|
|
344
|
+
<ChevronDown
|
|
345
|
+
width={chevronSize}
|
|
346
|
+
height={chevronSize}
|
|
347
|
+
strokeWidth={3}
|
|
348
|
+
color={iconColor}
|
|
349
|
+
style={{ transform: [{ rotate: "180deg" }] }}
|
|
350
|
+
/>
|
|
351
|
+
)}
|
|
322
352
|
</Pressable>
|
|
323
353
|
<Pressable
|
|
324
354
|
disabled={!editable}
|
|
@@ -326,7 +356,9 @@ function InputNumeric({
|
|
|
326
356
|
className="items-center justify-center p-1"
|
|
327
357
|
accessibilityLabel="Decrease value"
|
|
328
358
|
>
|
|
329
|
-
|
|
359
|
+
{iconColor && (
|
|
360
|
+
<ChevronDown width={chevronSize} height={chevronSize} strokeWidth={3} color={iconColor} />
|
|
361
|
+
)}
|
|
330
362
|
</Pressable>
|
|
331
363
|
</View>
|
|
332
364
|
</InputEndIcon>
|
|
@@ -350,6 +382,8 @@ function Input({
|
|
|
350
382
|
const [focused, setFocused] = useState(false);
|
|
351
383
|
const hasValue = Boolean(value || defaultValue);
|
|
352
384
|
const isFloated = focused || hasValue;
|
|
385
|
+
const iconColor = useThemeColor("gray-foreground");
|
|
386
|
+
const errorIconColor = useThemeColor("feedback-error-foreground");
|
|
353
387
|
|
|
354
388
|
if (type === "numeric") {
|
|
355
389
|
return (
|
|
@@ -382,17 +416,29 @@ function Input({
|
|
|
382
416
|
);
|
|
383
417
|
}
|
|
384
418
|
|
|
385
|
-
const
|
|
419
|
+
const iconSize = size === "md" ? INPUT_ICON_SIZE_MD : INPUT_ICON_SIZE_LG;
|
|
420
|
+
const defaultIconLeft =
|
|
421
|
+
type === "email" && iconColor ? (
|
|
422
|
+
<Mail width={iconSize} height={iconSize} strokeWidth={INPUT_ICON_STROKE_WIDTH} color={iconColor} />
|
|
423
|
+
) : undefined;
|
|
424
|
+
const resolvedIconLeft = iconLeft
|
|
425
|
+
? applyInputIconSize(iconLeft, size, iconColor)
|
|
426
|
+
: defaultIconLeft;
|
|
427
|
+
|
|
386
428
|
const showInvalidRightIcon = invalid && (type === "text" || type === "email");
|
|
387
429
|
const resolvedIconRight = showInvalidRightIcon
|
|
388
|
-
?
|
|
389
|
-
|
|
430
|
+
? errorIconColor && (
|
|
431
|
+
<TriangleWarning width={iconSize} height={iconSize} strokeWidth={INPUT_ICON_STROKE_WIDTH} color={errorIconColor} />
|
|
432
|
+
)
|
|
433
|
+
: iconRight
|
|
434
|
+
? applyInputIconSize(iconRight, size, iconColor)
|
|
435
|
+
: undefined;
|
|
390
436
|
|
|
391
437
|
return (
|
|
392
438
|
<InputAdornment>
|
|
393
439
|
{resolvedIconLeft && (
|
|
394
440
|
<InputStartIcon>
|
|
395
|
-
{
|
|
441
|
+
{resolvedIconLeft}
|
|
396
442
|
</InputStartIcon>
|
|
397
443
|
)}
|
|
398
444
|
<View className="relative flex-1">
|
|
@@ -416,7 +462,7 @@ function Input({
|
|
|
416
462
|
</View>
|
|
417
463
|
{resolvedIconRight && (
|
|
418
464
|
<InputEndIcon>
|
|
419
|
-
{
|
|
465
|
+
{resolvedIconRight}
|
|
420
466
|
</InputEndIcon>
|
|
421
467
|
)}
|
|
422
468
|
</InputAdornment>
|
package/src/components/Label.tsx
CHANGED
|
@@ -9,6 +9,49 @@ interface LabelProps extends PressableProps, VariantProps<typeof labelVariants>
|
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function getLabelText(children: ReactNode): string | null {
|
|
13
|
+
if (typeof children === "string" || typeof children === "number") {
|
|
14
|
+
return String(children);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (Array.isArray(children)) {
|
|
18
|
+
const parts = children.filter(
|
|
19
|
+
(child): child is string | number =>
|
|
20
|
+
typeof child === "string" || typeof child === "number",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (parts.length === children.length && parts.length > 0) {
|
|
24
|
+
return parts.join("");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function LabelText({
|
|
32
|
+
children,
|
|
33
|
+
variant,
|
|
34
|
+
size,
|
|
35
|
+
disabled,
|
|
36
|
+
}: {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
variant?: LabelProps["variant"];
|
|
39
|
+
size?: LabelProps["size"];
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
}) {
|
|
42
|
+
const text = getLabelText(children);
|
|
43
|
+
|
|
44
|
+
if (text !== null) {
|
|
45
|
+
return (
|
|
46
|
+
<Text className={cn(labelTextVariants({ variant, size, disabled }))}>
|
|
47
|
+
{text}
|
|
48
|
+
</Text>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return <>{children}</>;
|
|
53
|
+
}
|
|
54
|
+
|
|
12
55
|
const labelVariants = cva(
|
|
13
56
|
"flex-row items-center gap-3 shrink-0",
|
|
14
57
|
{
|
|
@@ -63,13 +106,9 @@ function Label({ className, variant, size, block, disabled = false, children, ..
|
|
|
63
106
|
)}
|
|
64
107
|
{...props}
|
|
65
108
|
>
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
</Text>
|
|
70
|
-
) : (
|
|
71
|
-
children
|
|
72
|
-
)}
|
|
109
|
+
<LabelText variant={variant} size={size} disabled={disabled}>
|
|
110
|
+
{children}
|
|
111
|
+
</LabelText>
|
|
73
112
|
</Pressable>
|
|
74
113
|
);
|
|
75
114
|
}
|
|
@@ -80,13 +119,9 @@ function Label({ className, variant, size, block, disabled = false, children, ..
|
|
|
80
119
|
className={cn(labelVariants({ variant, size }), className)}
|
|
81
120
|
{...props}
|
|
82
121
|
>
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</Text>
|
|
87
|
-
) : (
|
|
88
|
-
children
|
|
89
|
-
)}
|
|
122
|
+
<LabelText variant={variant} size={size} disabled={disabled}>
|
|
123
|
+
{children}
|
|
124
|
+
</LabelText>
|
|
90
125
|
</Pressable>
|
|
91
126
|
);
|
|
92
127
|
}
|
|
@@ -9,14 +9,25 @@ import Animated, {
|
|
|
9
9
|
Easing,
|
|
10
10
|
} from "react-native-reanimated";
|
|
11
11
|
import { cn } from "@/lib/utils";
|
|
12
|
+
import { useThemeColor, type ThemeColorToken } from "@/hooks/useThemeColor";
|
|
12
13
|
|
|
13
14
|
interface LoadingProps {
|
|
14
15
|
size?: number;
|
|
15
16
|
className?: string;
|
|
17
|
+
/** Cor explícita (opcional). Preferir `colorToken` para cores do theme. */
|
|
16
18
|
color?: string;
|
|
19
|
+
/** Token semântico do design system (default: primary-background-fill). */
|
|
20
|
+
colorToken?: ThemeColorToken;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
function Loading({
|
|
23
|
+
function Loading({
|
|
24
|
+
size = 24,
|
|
25
|
+
colorToken = "primary-background-fill",
|
|
26
|
+
className,
|
|
27
|
+
color,
|
|
28
|
+
}: LoadingProps) {
|
|
29
|
+
const themeColor = useThemeColor(colorToken);
|
|
30
|
+
const stroke = color ?? themeColor;
|
|
20
31
|
const rotation = useSharedValue(0);
|
|
21
32
|
|
|
22
33
|
useEffect(() => {
|
|
@@ -39,7 +50,7 @@ function Loading({ size = 24, color = "currentColor", className }: LoadingProps)
|
|
|
39
50
|
height={size}
|
|
40
51
|
viewBox="0 0 24 24"
|
|
41
52
|
fill="none"
|
|
42
|
-
stroke={
|
|
53
|
+
stroke={stroke}
|
|
43
54
|
strokeWidth={2}
|
|
44
55
|
strokeLinecap="round"
|
|
45
56
|
strokeLinejoin="round"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { Text, View, type ViewProps } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
interface PageHeaderProps extends ViewProps {
|
|
7
|
+
icon?: ReactNode;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
badge?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function PageHeader({
|
|
14
|
+
className,
|
|
15
|
+
icon,
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
badge,
|
|
19
|
+
...props
|
|
20
|
+
}: PageHeaderProps) {
|
|
21
|
+
return (
|
|
22
|
+
<View
|
|
23
|
+
className={cn(
|
|
24
|
+
"overflow-hidden rounded-t-xl rounded-b-2xl bg-secondary-background-soft-muted pb-4",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
<View className="gap-4 px-4 pt-8">
|
|
30
|
+
<View className="flex-row items-start gap-3">
|
|
31
|
+
{icon}
|
|
32
|
+
<View className="flex-1 gap-1">
|
|
33
|
+
<Text className="text-title-xs font-semibold text-primary-foreground">
|
|
34
|
+
{title}
|
|
35
|
+
</Text>
|
|
36
|
+
{description ? (
|
|
37
|
+
<Text className="text-sm leading-sm text-gray-foreground-muted">
|
|
38
|
+
{description}
|
|
39
|
+
</Text>
|
|
40
|
+
) : null}
|
|
41
|
+
</View>
|
|
42
|
+
</View>
|
|
43
|
+
{badge}
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { PageHeader };
|
|
50
|
+
export type { PageHeaderProps };
|