@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
package/src/components/Alert.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { View, Text, Pressable, type ViewProps, type PressableProps } from "react-native";
|
|
8
8
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
9
|
import { CloseMd } from "@medway-ui/icons/native";
|
|
10
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
10
11
|
import { cn } from "@/lib/utils";
|
|
11
12
|
|
|
12
13
|
type AlertVariant = "regular" | "emphasis" | "success" | "warning" | "error";
|
|
@@ -80,6 +81,7 @@ function useAlertContext() {
|
|
|
80
81
|
|
|
81
82
|
function Alert({ className, variant = "regular", onClose, children, ...props }: AlertProps) {
|
|
82
83
|
const ctx = useMemo(() => ({ variant, onClose }), [variant, onClose]);
|
|
84
|
+
const closeIconColor = useThemeColor("gray-foreground");
|
|
83
85
|
|
|
84
86
|
return (
|
|
85
87
|
<AlertContext.Provider value={ctx}>
|
|
@@ -95,7 +97,7 @@ function Alert({ className, variant = "regular", onClose, children, ...props }:
|
|
|
95
97
|
accessibilityLabel="Close alert"
|
|
96
98
|
className="absolute right-4 top-2"
|
|
97
99
|
>
|
|
98
|
-
<CloseMd
|
|
100
|
+
{closeIconColor && <CloseMd width={16} height={16} color={closeIconColor} />}
|
|
99
101
|
</Pressable>
|
|
100
102
|
)}
|
|
101
103
|
</View>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
2
2
|
import { View, Text, type ViewProps } from "react-native";
|
|
3
3
|
import * as AlertDialogPrimitive from "@rn-primitives/alert-dialog";
|
|
4
|
-
import { CloseMd } from "@medway-ui/icons/native";
|
|
5
4
|
import { cva } from "class-variance-authority";
|
|
5
|
+
import { CloseMd } from "@medway-ui/icons/native";
|
|
6
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
6
7
|
import { cn } from "@/lib/utils";
|
|
7
8
|
import { Button, type ButtonProps } from "@/components/Button";
|
|
8
9
|
import { Tag, type TagProps } from "@/components/Tag";
|
|
@@ -136,26 +137,31 @@ function AlertDialogContent({
|
|
|
136
137
|
showCloseButton = false,
|
|
137
138
|
...props
|
|
138
139
|
}: AlertDialogContentProps) {
|
|
140
|
+
const closeIconColor = useThemeColor("gray-foreground");
|
|
141
|
+
|
|
139
142
|
return (
|
|
140
143
|
<AlertDialogPrimitive.Portal>
|
|
141
144
|
<AlertDialogPrimitive.Overlay
|
|
142
|
-
className={cn("absolute inset-0
|
|
145
|
+
className={cn("absolute inset-0 bg-black/50", overlayClassName)}
|
|
146
|
+
/>
|
|
147
|
+
<AlertDialogPrimitive.Content
|
|
148
|
+
className="absolute inset-0 items-center justify-center px-4"
|
|
149
|
+
{...props}
|
|
143
150
|
>
|
|
144
|
-
<
|
|
151
|
+
<View
|
|
145
152
|
className={cn(
|
|
146
|
-
"relative flex-col w-full
|
|
153
|
+
"relative flex-col w-full rounded-xl bg-background shadow-lg overflow-hidden",
|
|
147
154
|
className
|
|
148
155
|
)}
|
|
149
|
-
{...props}
|
|
150
156
|
>
|
|
151
157
|
{children}
|
|
152
158
|
{showCloseButton && (
|
|
153
159
|
<AlertDialogPrimitive.Cancel className="absolute right-3 top-3 rounded-pill p-1.5 active:bg-gray-background-soft">
|
|
154
|
-
<CloseMd
|
|
160
|
+
{closeIconColor && <CloseMd width={16} height={16} color={closeIconColor} />}
|
|
155
161
|
</AlertDialogPrimitive.Cancel>
|
|
156
162
|
)}
|
|
157
|
-
</
|
|
158
|
-
</AlertDialogPrimitive.
|
|
163
|
+
</View>
|
|
164
|
+
</AlertDialogPrimitive.Content>
|
|
159
165
|
</AlertDialogPrimitive.Portal>
|
|
160
166
|
);
|
|
161
167
|
}
|
|
@@ -225,29 +231,67 @@ function AlertDialogFooter({ className, ...props }: ViewProps) {
|
|
|
225
231
|
);
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
function AlertDialogAction({
|
|
234
|
+
function AlertDialogAction({
|
|
235
|
+
className,
|
|
236
|
+
variant,
|
|
237
|
+
size = "lg",
|
|
238
|
+
children,
|
|
239
|
+
disabled,
|
|
240
|
+
onPress,
|
|
241
|
+
...props
|
|
242
|
+
}: AlertDialogActionProps) {
|
|
229
243
|
const ctx = useContext(AlertDialogContext);
|
|
244
|
+
const primitiveCtx = AlertDialogPrimitive.useRootContext();
|
|
230
245
|
const resolvedVariant = variant ?? actionVariantMap[ctx?.variant ?? DEFAULT_VARIANT];
|
|
231
246
|
|
|
232
247
|
return (
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
<View className={cn("flex-1", className)}>
|
|
249
|
+
<Button
|
|
250
|
+
variant={resolvedVariant}
|
|
251
|
+
size={size}
|
|
252
|
+
className="w-full"
|
|
253
|
+
disabled={disabled}
|
|
254
|
+
onPress={(event) => {
|
|
255
|
+
if (disabled) return;
|
|
256
|
+
primitiveCtx.onOpenChange(false);
|
|
257
|
+
onPress?.(event);
|
|
258
|
+
}}
|
|
259
|
+
{...props}
|
|
260
|
+
>
|
|
238
261
|
{children as ReactNode}
|
|
239
262
|
</Button>
|
|
240
|
-
</
|
|
263
|
+
</View>
|
|
241
264
|
);
|
|
242
265
|
}
|
|
243
266
|
|
|
244
|
-
function AlertDialogCancel({
|
|
267
|
+
function AlertDialogCancel({
|
|
268
|
+
className,
|
|
269
|
+
variant = "regular-soft",
|
|
270
|
+
size = "lg",
|
|
271
|
+
children,
|
|
272
|
+
disabled,
|
|
273
|
+
onPress,
|
|
274
|
+
...props
|
|
275
|
+
}: AlertDialogCancelProps) {
|
|
276
|
+
const primitiveCtx = AlertDialogPrimitive.useRootContext();
|
|
277
|
+
|
|
245
278
|
return (
|
|
246
|
-
<
|
|
247
|
-
<Button
|
|
279
|
+
<View className={cn("flex-1", className)}>
|
|
280
|
+
<Button
|
|
281
|
+
variant={variant}
|
|
282
|
+
size={size}
|
|
283
|
+
className="w-full"
|
|
284
|
+
disabled={disabled}
|
|
285
|
+
onPress={(event) => {
|
|
286
|
+
if (disabled) return;
|
|
287
|
+
primitiveCtx.onOpenChange(false);
|
|
288
|
+
onPress?.(event);
|
|
289
|
+
}}
|
|
290
|
+
{...props}
|
|
291
|
+
>
|
|
248
292
|
{children as ReactNode}
|
|
249
293
|
</Button>
|
|
250
|
-
</
|
|
294
|
+
</View>
|
|
251
295
|
);
|
|
252
296
|
}
|
|
253
297
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { View, Text, type ViewProps } from "react-native";
|
|
3
|
+
import * as AvatarPrimitive from "@rn-primitives/avatar";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { User } from "@medway-ui/icons/native";
|
|
6
|
+
|
|
7
|
+
import { Badge as BadgeComponent } from "@/components/Badge";
|
|
8
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
9
|
+
import { cn } from "@/lib/utils";
|
|
10
|
+
|
|
11
|
+
const badgeSizeMap: Record<
|
|
12
|
+
"sm" | "md" | "lg" | "xl",
|
|
13
|
+
"sm" | "md" | "lg" | "xl"
|
|
14
|
+
> = {
|
|
15
|
+
sm: "sm",
|
|
16
|
+
md: "md",
|
|
17
|
+
lg: "md",
|
|
18
|
+
xl: "lg",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const avatarVariants = cva(
|
|
22
|
+
"relative flex shrink-0 overflow-hidden rounded-full",
|
|
23
|
+
{
|
|
24
|
+
variants: {
|
|
25
|
+
size: {
|
|
26
|
+
sm: "size-8",
|
|
27
|
+
md: "size-10",
|
|
28
|
+
lg: "size-12",
|
|
29
|
+
xl: "size-14",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
size: "md",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface AvatarProps
|
|
39
|
+
extends ViewProps,
|
|
40
|
+
VariantProps<typeof avatarVariants> {
|
|
41
|
+
alt?: string;
|
|
42
|
+
fallback?: ReactNode;
|
|
43
|
+
badge?: number | string;
|
|
44
|
+
children?: ReactNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface AvatarImageProps extends Omit<AvatarPrimitive.ImageProps, "source"> {
|
|
48
|
+
src?: string;
|
|
49
|
+
source?: AvatarPrimitive.ImageProps["source"];
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface AvatarFallbackProps extends ViewProps {
|
|
54
|
+
className?: string;
|
|
55
|
+
children?: ReactNode;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Avatar({
|
|
59
|
+
className,
|
|
60
|
+
size,
|
|
61
|
+
badge,
|
|
62
|
+
children,
|
|
63
|
+
alt,
|
|
64
|
+
fallback,
|
|
65
|
+
...props
|
|
66
|
+
}: AvatarProps) {
|
|
67
|
+
const content =
|
|
68
|
+
children ?? <AvatarFallback>{fallback}</AvatarFallback>;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View className="relative">
|
|
72
|
+
{badge ? (
|
|
73
|
+
<BadgeComponent
|
|
74
|
+
size={badgeSizeMap[size || "md"]}
|
|
75
|
+
className="absolute -right-2 -top-1 z-10"
|
|
76
|
+
>
|
|
77
|
+
{String(badge)}
|
|
78
|
+
</BadgeComponent>
|
|
79
|
+
) : null}
|
|
80
|
+
<AvatarPrimitive.Root
|
|
81
|
+
alt={alt ?? ""}
|
|
82
|
+
className={cn(avatarVariants({ size, className }))}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
{content}
|
|
86
|
+
</AvatarPrimitive.Root>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function AvatarImage({ className, src, source, ...props }: AvatarImageProps) {
|
|
92
|
+
const resolvedSource = source ?? (src ? { uri: src } : undefined);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<AvatarPrimitive.Image
|
|
96
|
+
source={resolvedSource}
|
|
97
|
+
className={cn("size-full object-cover", className)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function AvatarFallback({ className, children, ...props }: AvatarFallbackProps) {
|
|
104
|
+
const iconColor = useThemeColor("primary-foreground");
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<AvatarPrimitive.Fallback
|
|
108
|
+
className={cn(
|
|
109
|
+
"flex size-full items-center justify-center rounded-full bg-gray-background-soft",
|
|
110
|
+
className
|
|
111
|
+
)}
|
|
112
|
+
{...props}
|
|
113
|
+
>
|
|
114
|
+
{children ? (
|
|
115
|
+
typeof children === "string" ? (
|
|
116
|
+
<Text className="text-sm font-semibold text-primary-foreground">
|
|
117
|
+
{children}
|
|
118
|
+
</Text>
|
|
119
|
+
) : (
|
|
120
|
+
children
|
|
121
|
+
)
|
|
122
|
+
) : iconColor ? (
|
|
123
|
+
<User width={20} height={20} color={iconColor} />
|
|
124
|
+
) : null}
|
|
125
|
+
</AvatarPrimitive.Fallback>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { Avatar, AvatarImage, AvatarFallback, avatarVariants };
|
|
130
|
+
export type { AvatarProps, AvatarImageProps, AvatarFallbackProps };
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { Calendar } from "@medway-ui/icons/native";
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useState,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from "react";
|
|
9
|
+
import {
|
|
10
|
+
Image,
|
|
11
|
+
Text,
|
|
12
|
+
View,
|
|
13
|
+
useWindowDimensions,
|
|
14
|
+
type ImageProps,
|
|
15
|
+
type ViewProps,
|
|
16
|
+
} from "react-native";
|
|
17
|
+
|
|
18
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/Avatar";
|
|
19
|
+
import { Button, type ButtonProps } from "@/components/Button";
|
|
20
|
+
import { FallbackImage } from "@/components/FallbackImage";
|
|
21
|
+
import { Skeleton } from "@/components/Skeleton";
|
|
22
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
23
|
+
import { cn } from "@/lib/utils";
|
|
24
|
+
|
|
25
|
+
interface CardNewsResourcesProps extends ViewProps {
|
|
26
|
+
label: string;
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface CardNewsContextValue {
|
|
31
|
+
orientation: "vertical" | "horizontal";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CardNewsContext = createContext<CardNewsContextValue>({
|
|
35
|
+
orientation: "vertical",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const HORIZONTAL_IMAGE_MIN_WIDTH = 1280;
|
|
39
|
+
|
|
40
|
+
interface CardNewsDateProps extends ViewProps {
|
|
41
|
+
icon?: ReactNode;
|
|
42
|
+
children?: ReactNode;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface CardNewsImageProps extends ViewProps {
|
|
46
|
+
src?: string;
|
|
47
|
+
alt?: string;
|
|
48
|
+
imageProps?: ImageProps;
|
|
49
|
+
children?: ReactNode;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface CardNewsAuthorProps extends Omit<ViewProps, "role"> {
|
|
53
|
+
src?: string;
|
|
54
|
+
alt?: string;
|
|
55
|
+
name?: string;
|
|
56
|
+
role?: string;
|
|
57
|
+
children?: ReactNode;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface CardNewsProps
|
|
61
|
+
extends ViewProps,
|
|
62
|
+
VariantProps<typeof cardNewsVariants> {
|
|
63
|
+
isRead?: boolean;
|
|
64
|
+
children?: ReactNode;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const cardNewsVariants = cva("flex border", {
|
|
68
|
+
variants: {
|
|
69
|
+
variant: {
|
|
70
|
+
default:
|
|
71
|
+
"rounded-lg border-secondary-stroke bg-secondary-background-soft-muted p-6",
|
|
72
|
+
read: "rounded-lg border-gray-stroke-muted bg-surface-card p-6",
|
|
73
|
+
compact: "rounded-xl border-gray-stroke-muted bg-surface-card p-4",
|
|
74
|
+
},
|
|
75
|
+
orientation: {
|
|
76
|
+
vertical: "flex-col gap-6",
|
|
77
|
+
horizontal: "flex-row gap-10",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
defaultVariants: {
|
|
81
|
+
variant: "default",
|
|
82
|
+
orientation: "vertical",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
function CardNews({
|
|
87
|
+
className,
|
|
88
|
+
isRead,
|
|
89
|
+
variant,
|
|
90
|
+
orientation = "vertical",
|
|
91
|
+
...props
|
|
92
|
+
}: CardNewsProps) {
|
|
93
|
+
const resolvedVariant = isRead ? "read" : (variant ?? "default");
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<CardNewsContext.Provider
|
|
97
|
+
value={{ orientation: orientation ?? "vertical" }}
|
|
98
|
+
>
|
|
99
|
+
<View
|
|
100
|
+
className={cn(
|
|
101
|
+
cardNewsVariants({ variant: resolvedVariant, orientation }),
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
106
|
+
</CardNewsContext.Provider>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function CardNewsBody({ className, style, ...props }: ViewProps) {
|
|
111
|
+
return (
|
|
112
|
+
<View
|
|
113
|
+
className={cn("flex min-w-0 flex-1 flex-col gap-6", className)}
|
|
114
|
+
style={[{ minWidth: 0 }, style]}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function CardNewsHeader({ className, ...props }: ViewProps) {
|
|
121
|
+
return (
|
|
122
|
+
<View
|
|
123
|
+
className={cn(
|
|
124
|
+
"flex w-full flex-row flex-wrap items-start justify-between gap-4",
|
|
125
|
+
className
|
|
126
|
+
)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function CardNewsTags({ className, ...props }: ViewProps) {
|
|
133
|
+
return (
|
|
134
|
+
<View
|
|
135
|
+
className={cn("flex min-w-0 flex-1 flex-row flex-wrap items-center gap-x-4 gap-y-3", className)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function CardNewsDate({
|
|
142
|
+
className,
|
|
143
|
+
icon,
|
|
144
|
+
children,
|
|
145
|
+
...props
|
|
146
|
+
}: CardNewsDateProps) {
|
|
147
|
+
const iconColor = useThemeColor("primary-foreground");
|
|
148
|
+
const resolvedIcon =
|
|
149
|
+
icon ??
|
|
150
|
+
(iconColor ? (
|
|
151
|
+
<Calendar width={16} height={16} color={iconColor} />
|
|
152
|
+
) : null);
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<View
|
|
156
|
+
className={cn("shrink-0 flex-row items-center gap-1.5", className)}
|
|
157
|
+
{...props}
|
|
158
|
+
>
|
|
159
|
+
{resolvedIcon}
|
|
160
|
+
{children ? (
|
|
161
|
+
<Text className="text-xs font-semibold leading-xs text-primary-foreground">
|
|
162
|
+
{children}
|
|
163
|
+
</Text>
|
|
164
|
+
) : null}
|
|
165
|
+
</View>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function CardNewsImage({
|
|
170
|
+
className,
|
|
171
|
+
src,
|
|
172
|
+
alt = "",
|
|
173
|
+
imageProps,
|
|
174
|
+
children,
|
|
175
|
+
...props
|
|
176
|
+
}: CardNewsImageProps) {
|
|
177
|
+
const { orientation } = useContext(CardNewsContext);
|
|
178
|
+
const { width } = useWindowDimensions();
|
|
179
|
+
const [hasError, setHasError] = useState(false);
|
|
180
|
+
const showImage = !!src && !hasError;
|
|
181
|
+
const showHorizontalImage =
|
|
182
|
+
orientation !== "horizontal" || width >= HORIZONTAL_IMAGE_MIN_WIDTH;
|
|
183
|
+
|
|
184
|
+
if (!showHorizontalImage) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<View
|
|
190
|
+
className={cn(
|
|
191
|
+
"relative overflow-hidden bg-gray-background-soft",
|
|
192
|
+
orientation === "vertical"
|
|
193
|
+
? "h-[300px] w-full rounded-lg"
|
|
194
|
+
: "w-[482px] rounded-xl",
|
|
195
|
+
className
|
|
196
|
+
)}
|
|
197
|
+
{...props}
|
|
198
|
+
>
|
|
199
|
+
{showImage ? (
|
|
200
|
+
<Image
|
|
201
|
+
source={{ uri: src }}
|
|
202
|
+
accessibilityLabel={alt}
|
|
203
|
+
className="absolute inset-0 size-full"
|
|
204
|
+
resizeMode="cover"
|
|
205
|
+
{...imageProps}
|
|
206
|
+
onError={(event) => {
|
|
207
|
+
imageProps?.onError?.(event);
|
|
208
|
+
setHasError(true);
|
|
209
|
+
}}
|
|
210
|
+
/>
|
|
211
|
+
) : (
|
|
212
|
+
children ?? <FallbackImage className="absolute inset-0" />
|
|
213
|
+
)}
|
|
214
|
+
</View>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function CardNewsContent({ className, ...props }: ViewProps) {
|
|
219
|
+
return (
|
|
220
|
+
<View
|
|
221
|
+
className={cn("flex w-full flex-col gap-6", className)}
|
|
222
|
+
{...props}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function CardNewsTitle({
|
|
228
|
+
className,
|
|
229
|
+
children,
|
|
230
|
+
...props
|
|
231
|
+
}: ViewProps & { children?: ReactNode }) {
|
|
232
|
+
return (
|
|
233
|
+
<Text
|
|
234
|
+
numberOfLines={2}
|
|
235
|
+
className={cn(
|
|
236
|
+
"-mb-2 text-title-xs font-semibold leading-title-xs text-primary-foreground",
|
|
237
|
+
className
|
|
238
|
+
)}
|
|
239
|
+
{...props}
|
|
240
|
+
>
|
|
241
|
+
{children}
|
|
242
|
+
</Text>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function CardNewsDescription({
|
|
247
|
+
className,
|
|
248
|
+
children,
|
|
249
|
+
...props
|
|
250
|
+
}: ViewProps & { children?: ReactNode }) {
|
|
251
|
+
return (
|
|
252
|
+
<Text
|
|
253
|
+
numberOfLines={3}
|
|
254
|
+
className={cn("text-md leading-md text-primary-foreground", className)}
|
|
255
|
+
{...props}
|
|
256
|
+
>
|
|
257
|
+
{children}
|
|
258
|
+
</Text>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function CardNewsResources({
|
|
263
|
+
className,
|
|
264
|
+
label,
|
|
265
|
+
children,
|
|
266
|
+
...props
|
|
267
|
+
}: CardNewsResourcesProps) {
|
|
268
|
+
return (
|
|
269
|
+
<View className={cn("flex flex-col gap-2", className)} {...props}>
|
|
270
|
+
<Text className="text-sm font-semibold leading-sm text-primary-foreground-muted">
|
|
271
|
+
{label}
|
|
272
|
+
</Text>
|
|
273
|
+
<View className="flex-row flex-wrap gap-2">{children}</View>
|
|
274
|
+
</View>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function CardNewsResourceItem({
|
|
279
|
+
className,
|
|
280
|
+
children,
|
|
281
|
+
iconLeft,
|
|
282
|
+
...props
|
|
283
|
+
}: ButtonProps) {
|
|
284
|
+
return (
|
|
285
|
+
<Button
|
|
286
|
+
variant="outlined"
|
|
287
|
+
size="sm"
|
|
288
|
+
className={cn("bg-surface-card", className)}
|
|
289
|
+
iconLeft={iconLeft}
|
|
290
|
+
{...props}
|
|
291
|
+
>
|
|
292
|
+
{children}
|
|
293
|
+
</Button>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getInitials(name: string): string {
|
|
298
|
+
return name
|
|
299
|
+
.trim()
|
|
300
|
+
.split(/\s+/)
|
|
301
|
+
.slice(0, 2)
|
|
302
|
+
.map((word) => word[0] ?? "")
|
|
303
|
+
.join("")
|
|
304
|
+
.toUpperCase();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function CardNewsAuthor({
|
|
308
|
+
className,
|
|
309
|
+
src,
|
|
310
|
+
alt,
|
|
311
|
+
name,
|
|
312
|
+
role,
|
|
313
|
+
children,
|
|
314
|
+
...props
|
|
315
|
+
}: CardNewsAuthorProps) {
|
|
316
|
+
return (
|
|
317
|
+
<View
|
|
318
|
+
className={cn("flex-row items-center gap-2", className)}
|
|
319
|
+
{...props}
|
|
320
|
+
>
|
|
321
|
+
{children ?? (
|
|
322
|
+
<>
|
|
323
|
+
<Avatar size="md" alt={alt ?? name ?? ""}>
|
|
324
|
+
{src ? <AvatarImage src={src} /> : null}
|
|
325
|
+
<AvatarFallback>
|
|
326
|
+
{name ? getInitials(name) : undefined}
|
|
327
|
+
</AvatarFallback>
|
|
328
|
+
</Avatar>
|
|
329
|
+
<View className="flex flex-col gap-0.5">
|
|
330
|
+
{role ? (
|
|
331
|
+
<Text className="text-xs font-medium leading-xs text-secondary-foreground">
|
|
332
|
+
{role}
|
|
333
|
+
</Text>
|
|
334
|
+
) : null}
|
|
335
|
+
{name ? (
|
|
336
|
+
<Text className="text-sm font-semibold leading-sm text-primary-foreground">
|
|
337
|
+
{name}
|
|
338
|
+
</Text>
|
|
339
|
+
) : null}
|
|
340
|
+
</View>
|
|
341
|
+
</>
|
|
342
|
+
)}
|
|
343
|
+
</View>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function CardNewsFooter({ className, ...props }: ViewProps) {
|
|
348
|
+
return (
|
|
349
|
+
<View
|
|
350
|
+
className={cn(
|
|
351
|
+
"flex w-full flex-row flex-wrap items-center gap-6 gap-y-3",
|
|
352
|
+
className
|
|
353
|
+
)}
|
|
354
|
+
{...props}
|
|
355
|
+
/>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function CardNewsSkeleton({ className, ...props }: ViewProps) {
|
|
360
|
+
return (
|
|
361
|
+
<View
|
|
362
|
+
className={cn(
|
|
363
|
+
"gap-6 rounded-lg border border-gray-stroke-muted bg-surface-card p-6",
|
|
364
|
+
className
|
|
365
|
+
)}
|
|
366
|
+
{...props}
|
|
367
|
+
>
|
|
368
|
+
<View className="flex-row items-start justify-between gap-4">
|
|
369
|
+
<View className="flex-1 flex-row flex-wrap gap-2">
|
|
370
|
+
<Skeleton className="h-7 w-[88px] rounded-full" />
|
|
371
|
+
<Skeleton className="h-7 w-[72px] rounded-full" />
|
|
372
|
+
</View>
|
|
373
|
+
<Skeleton className="h-4 w-[72px]" />
|
|
374
|
+
</View>
|
|
375
|
+
<Skeleton className="h-[150px] w-full rounded-lg" />
|
|
376
|
+
<View className="gap-2">
|
|
377
|
+
<Skeleton className="h-7 w-24 rounded-full" />
|
|
378
|
+
<Skeleton className="h-5 w-full" />
|
|
379
|
+
<Skeleton className="h-4 w-full" />
|
|
380
|
+
<Skeleton className="h-4 w-[85%]" />
|
|
381
|
+
</View>
|
|
382
|
+
<View className="gap-4">
|
|
383
|
+
<Skeleton className="h-12 w-full rounded-lg" />
|
|
384
|
+
<Skeleton className="h-12 w-full rounded-lg" />
|
|
385
|
+
</View>
|
|
386
|
+
</View>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export {
|
|
391
|
+
CardNews,
|
|
392
|
+
CardNewsAuthor,
|
|
393
|
+
CardNewsBody,
|
|
394
|
+
CardNewsContent,
|
|
395
|
+
CardNewsDate,
|
|
396
|
+
CardNewsDescription,
|
|
397
|
+
CardNewsFooter,
|
|
398
|
+
CardNewsHeader,
|
|
399
|
+
CardNewsImage,
|
|
400
|
+
CardNewsResourceItem,
|
|
401
|
+
CardNewsResources,
|
|
402
|
+
CardNewsSkeleton,
|
|
403
|
+
CardNewsTags,
|
|
404
|
+
CardNewsTitle,
|
|
405
|
+
};
|
|
406
|
+
export type {
|
|
407
|
+
CardNewsAuthorProps,
|
|
408
|
+
CardNewsDateProps,
|
|
409
|
+
CardNewsImageProps,
|
|
410
|
+
CardNewsProps,
|
|
411
|
+
CardNewsResourcesProps,
|
|
412
|
+
};
|