@munchi_oy/native-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +568 -0
- package/dist/index.d.ts +568 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/global.css +53 -0
- package/nativewind-env.d.ts +2 -0
- package/package.json +88 -0
- package/src/MAlert.tsx +38 -0
- package/src/MAnimation.tsx +55 -0
- package/src/MAvatar.tsx +111 -0
- package/src/MBadge.tsx +72 -0
- package/src/MButton.tsx +90 -0
- package/src/MCard.tsx +15 -0
- package/src/MChevron.tsx +47 -0
- package/src/MConfirmation.tsx +68 -0
- package/src/MCountDown.tsx +120 -0
- package/src/MDateTimePicker.tsx +124 -0
- package/src/MDivider.tsx +69 -0
- package/src/MDrawerRightPanel.tsx +187 -0
- package/src/MDropdown.tsx +277 -0
- package/src/MInput.tsx +162 -0
- package/src/MLabel.tsx +3 -0
- package/src/MLucideIcon.tsx +21 -0
- package/src/MModal.tsx +287 -0
- package/src/MNativeAlert.tsx +33 -0
- package/src/MNumpad.tsx +520 -0
- package/src/MPicker.tsx +150 -0
- package/src/MPinPadKeys.tsx +104 -0
- package/src/MPortal.tsx +4 -0
- package/src/MProgressBar.tsx +74 -0
- package/src/MRadioGroup.tsx +4 -0
- package/src/MRequiredLabel.tsx +21 -0
- package/src/MResponsiveContainer.tsx +74 -0
- package/src/MSearch.tsx +138 -0
- package/src/MSelector.tsx +48 -0
- package/src/MSkeleton.tsx +3 -0
- package/src/MSwitch.tsx +13 -0
- package/src/MTable.tsx +17 -0
- package/src/MTabs.tsx +198 -0
- package/src/MText.tsx +51 -0
- package/src/MTimerUp.tsx +88 -0
- package/src/MToggle.tsx +51 -0
- package/src/constants.ts +19 -0
- package/src/hooks/useColorScheme.tsx +12 -0
- package/src/hooks/useIconColors.ts +19 -0
- package/src/index.ts +124 -0
- package/src/primitives/accordion.tsx +143 -0
- package/src/primitives/alert-dialog.tsx +181 -0
- package/src/primitives/alert.tsx +94 -0
- package/src/primitives/aspect-ratio.tsx +5 -0
- package/src/primitives/avatar.tsx +47 -0
- package/src/primitives/badge.tsx +57 -0
- package/src/primitives/button.tsx +92 -0
- package/src/primitives/card.tsx +86 -0
- package/src/primitives/checkbox.tsx +35 -0
- package/src/primitives/collapsible.tsx +9 -0
- package/src/primitives/context-menu.tsx +255 -0
- package/src/primitives/dialog.tsx +166 -0
- package/src/primitives/dropdown-menu.tsx +264 -0
- package/src/primitives/hover-card.tsx +45 -0
- package/src/primitives/input.tsx +25 -0
- package/src/primitives/label.tsx +33 -0
- package/src/primitives/menubar.tsx +266 -0
- package/src/primitives/navigation-menu.tsx +192 -0
- package/src/primitives/popover.tsx +46 -0
- package/src/primitives/progress.tsx +82 -0
- package/src/primitives/radio-group.tsx +42 -0
- package/src/primitives/select.tsx +192 -0
- package/src/primitives/separator.tsx +28 -0
- package/src/primitives/skeleton.tsx +39 -0
- package/src/primitives/switch.tsx +102 -0
- package/src/primitives/table.tsx +107 -0
- package/src/primitives/tabs.tsx +66 -0
- package/src/primitives/text.tsx +28 -0
- package/src/primitives/textarea.tsx +39 -0
- package/src/primitives/toggle-group.tsx +89 -0
- package/src/primitives/toggle.tsx +91 -0
- package/src/primitives/tooltip.tsx +40 -0
- package/src/primitives/typography.tsx +214 -0
- package/src/theme.ts +43 -0
- package/src/tokens.ts +7 -0
- package/src/utils.ts +14 -0
- package/tailwind.config.ts +112 -0
package/src/MText.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type VariantProps, cva } from "class-variance-authority";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Text, type TextProps } from "react-native";
|
|
4
|
+
import { cn } from "./utils";
|
|
5
|
+
|
|
6
|
+
const mTextVariants = cva("text-base text-foreground", {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "font-munchi"
|
|
10
|
+
},
|
|
11
|
+
size: {
|
|
12
|
+
xs: "text-xs",
|
|
13
|
+
sm: "text-sm",
|
|
14
|
+
base: "text-base",
|
|
15
|
+
lg: "text-lg",
|
|
16
|
+
xl: "text-xl",
|
|
17
|
+
"2xl": "text-2xl",
|
|
18
|
+
"3xl": "text-3xl",
|
|
19
|
+
"4xl": "text-4xl"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "base"
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export interface MTextProps
|
|
29
|
+
extends Omit<TextProps, "allowFontScaling">,
|
|
30
|
+
VariantProps<typeof mTextVariants> {
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const MText = React.forwardRef<Text, MTextProps>(
|
|
35
|
+
({ className, variant, size, children, ...props }, ref) => {
|
|
36
|
+
return (
|
|
37
|
+
<Text
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(mTextVariants({ variant, size }), className)}
|
|
40
|
+
{...props}
|
|
41
|
+
allowFontScaling={false}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
</Text>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
MText.displayName = "MText";
|
|
50
|
+
|
|
51
|
+
export { MText };
|
package/src/MTimerUp.tsx
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { Text, type TextProps, View, type ViewProps } from "react-native";
|
|
5
|
+
import { cn } from "./utils";
|
|
6
|
+
|
|
7
|
+
export interface MTimerUpProps extends Pick<ViewProps, "className"> {
|
|
8
|
+
date: string | number | Date;
|
|
9
|
+
textClassName?: TextProps["className"];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const formatElapsedTime = (
|
|
13
|
+
now: dayjs.Dayjs,
|
|
14
|
+
targetDate: dayjs.Dayjs
|
|
15
|
+
): string => {
|
|
16
|
+
const totalMinutes = now.diff(targetDate, "minute");
|
|
17
|
+
|
|
18
|
+
if (totalMinutes < 1) {
|
|
19
|
+
return "0m";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const days = Math.floor(totalMinutes / 1440);
|
|
23
|
+
const remainingMinutes = totalMinutes % 1440;
|
|
24
|
+
const hours = Math.floor(remainingMinutes / 60);
|
|
25
|
+
const minutes = remainingMinutes % 60;
|
|
26
|
+
|
|
27
|
+
let result = "";
|
|
28
|
+
if (days > 0) result += `${days}d `;
|
|
29
|
+
if (hours > 0 || days > 0) result += `${hours}h `;
|
|
30
|
+
result += `${minutes}m`;
|
|
31
|
+
|
|
32
|
+
return result.trim();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const MTimerUp: React.FC<MTimerUpProps> = ({
|
|
36
|
+
date,
|
|
37
|
+
className = "",
|
|
38
|
+
textClassName = ""
|
|
39
|
+
}) => {
|
|
40
|
+
const targetDate = useMemo(() => dayjs(date), [date]);
|
|
41
|
+
const [now, setNow] = useState(() => dayjs());
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const nextTick = () => {
|
|
45
|
+
const currentTime = dayjs();
|
|
46
|
+
const nextMinute = currentTime.add(1, "minute").startOf("minute");
|
|
47
|
+
const delay = nextMinute.diff(currentTime);
|
|
48
|
+
|
|
49
|
+
return setTimeout(
|
|
50
|
+
() => {
|
|
51
|
+
setNow(dayjs());
|
|
52
|
+
},
|
|
53
|
+
delay > 0 ? delay : 60000
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const timerId = nextTick();
|
|
58
|
+
return () => clearTimeout(timerId);
|
|
59
|
+
}, [now]);
|
|
60
|
+
|
|
61
|
+
const formattedDuration = useMemo(
|
|
62
|
+
() => formatElapsedTime(now, targetDate),
|
|
63
|
+
[now, targetDate]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<View
|
|
68
|
+
className={cn(
|
|
69
|
+
"rounded-md flex items-center justify-center bg-muted border border-border/60",
|
|
70
|
+
"dark:border-white/20 dark:bg-white/12",
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
<Text
|
|
75
|
+
className={cn(
|
|
76
|
+
"font-munchi-semibold text-foreground dark:text-neutral-200",
|
|
77
|
+
textClassName
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{formattedDuration}
|
|
81
|
+
</Text>
|
|
82
|
+
</View>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
MTimerUp.displayName = "MTimerUp";
|
|
87
|
+
|
|
88
|
+
export default MTimerUp;
|
package/src/MToggle.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Pressable, Text, View } from "react-native";
|
|
2
|
+
import { cn } from "./utils";
|
|
3
|
+
|
|
4
|
+
export interface MToggleOption<T> {
|
|
5
|
+
key: T;
|
|
6
|
+
label: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MToggleProps<T> {
|
|
11
|
+
options: MToggleOption<T>[];
|
|
12
|
+
selectedKey: T;
|
|
13
|
+
onSelect: (key: T) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const MToggle = <T,>({
|
|
17
|
+
options,
|
|
18
|
+
selectedKey,
|
|
19
|
+
onSelect
|
|
20
|
+
}: MToggleProps<T>) => {
|
|
21
|
+
return (
|
|
22
|
+
<View className="flex flex-row gap-4 w-full items-center justify-between">
|
|
23
|
+
{options.map((option) => (
|
|
24
|
+
<Pressable
|
|
25
|
+
key={String(option.key)}
|
|
26
|
+
className={cn(
|
|
27
|
+
"flex-1 rounded-lg py-4",
|
|
28
|
+
selectedKey === option.key ? "bg-primary" : "bg-muted"
|
|
29
|
+
)}
|
|
30
|
+
onPress={() => onSelect(option.key)}
|
|
31
|
+
disabled={option.disabled}
|
|
32
|
+
>
|
|
33
|
+
<Text
|
|
34
|
+
className={cn(
|
|
35
|
+
"text-center font-munchi",
|
|
36
|
+
selectedKey === option.key
|
|
37
|
+
? "text-primary-foreground"
|
|
38
|
+
: "text-muted-foreground"
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
{option.label}
|
|
42
|
+
</Text>
|
|
43
|
+
</Pressable>
|
|
44
|
+
))}
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
MToggle.displayName = "MToggle";
|
|
50
|
+
|
|
51
|
+
export default MToggle;
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { darkVars, lightVars } from "./theme";
|
|
2
|
+
|
|
3
|
+
type CssVars = Record<string, string>;
|
|
4
|
+
|
|
5
|
+
const hsl = (vars: CssVars, key: string) => `hsl(${vars[key]})`;
|
|
6
|
+
|
|
7
|
+
const makeNavTheme = (vars: CssVars) => ({
|
|
8
|
+
background: hsl(vars, "--background"),
|
|
9
|
+
border: hsl(vars, "--border"),
|
|
10
|
+
card: hsl(vars, "--card"),
|
|
11
|
+
notification: hsl(vars, "--destructive"),
|
|
12
|
+
primary: hsl(vars, "--primary"),
|
|
13
|
+
text: hsl(vars, "--foreground")
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const NAV_THEME = {
|
|
17
|
+
light: makeNavTheme(lightVars),
|
|
18
|
+
dark: makeNavTheme(darkVars)
|
|
19
|
+
} as const;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useColorScheme as useNativewindColorScheme } from "nativewind";
|
|
2
|
+
|
|
3
|
+
export function useColorScheme() {
|
|
4
|
+
const { colorScheme, setColorScheme, toggleColorScheme } =
|
|
5
|
+
useNativewindColorScheme();
|
|
6
|
+
return {
|
|
7
|
+
colorScheme: colorScheme ?? "light",
|
|
8
|
+
isDarkColorScheme: colorScheme === "dark",
|
|
9
|
+
setColorScheme,
|
|
10
|
+
toggleColorScheme
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NAV_THEME } from "../constants";
|
|
2
|
+
import { useColorScheme } from "./useColorScheme";
|
|
3
|
+
|
|
4
|
+
/** Icon `color` values aligned with `NAV_THEME` + semantic muted text (matches `global.css` muted-foreground). */
|
|
5
|
+
export function useIconColors() {
|
|
6
|
+
const { colorScheme } = useColorScheme();
|
|
7
|
+
const scheme = colorScheme === "dark" ? "dark" : "light";
|
|
8
|
+
const t = NAV_THEME[scheme];
|
|
9
|
+
return {
|
|
10
|
+
foreground: t.text,
|
|
11
|
+
muted: scheme === "dark" ? "hsl(240 5% 64.9%)" : "hsl(240 3.8% 46.1%)",
|
|
12
|
+
primary: t.primary,
|
|
13
|
+
destructive: t.notification,
|
|
14
|
+
/** For icons/spinners on `bg-destructive` (matches `--destructive-foreground`). */
|
|
15
|
+
onDestructive: "hsl(0 0% 100%)",
|
|
16
|
+
/** For icons/spinners on `bg-primary` (matches `--primary-foreground`). */
|
|
17
|
+
onPrimary: scheme === "dark" ? "hsl(240 5.9% 10%)" : "hsl(0 0% 98%)"
|
|
18
|
+
};
|
|
19
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export { cn } from "./utils";
|
|
2
|
+
export { useColorScheme } from "./hooks/useColorScheme";
|
|
3
|
+
export { useIconColors } from "./hooks/useIconColors";
|
|
4
|
+
export { NAV_THEME } from "./constants";
|
|
5
|
+
export { typography } from "./tokens";
|
|
6
|
+
export { lightVars, darkVars } from "./theme";
|
|
7
|
+
export type { TypographyToken } from "./tokens";
|
|
8
|
+
|
|
9
|
+
export { MAlert } from "./MAlert";
|
|
10
|
+
export type { MAlertProps } from "./MAlert";
|
|
11
|
+
|
|
12
|
+
export { MAnimation } from "./MAnimation";
|
|
13
|
+
export type { MAnimationProps } from "./MAnimation";
|
|
14
|
+
|
|
15
|
+
export { MAvatar } from "./MAvatar";
|
|
16
|
+
export type { MAvatarProps } from "./MAvatar";
|
|
17
|
+
|
|
18
|
+
export { MBadge } from "./MBadge";
|
|
19
|
+
export type { MBadgeProps } from "./MBadge";
|
|
20
|
+
|
|
21
|
+
export { MButton, buttonVariants } from "./MButton";
|
|
22
|
+
export type { MButtonProps } from "./MButton";
|
|
23
|
+
|
|
24
|
+
export { MChevron } from "./MChevron";
|
|
25
|
+
export type { MChevronProps } from "./MChevron";
|
|
26
|
+
|
|
27
|
+
export { MDateTimePicker } from "./MDateTimePicker";
|
|
28
|
+
export type { CalendarProps } from "./MDateTimePicker";
|
|
29
|
+
|
|
30
|
+
export { MDivider } from "./MDivider";
|
|
31
|
+
export type { MDividerProps } from "./MDivider";
|
|
32
|
+
|
|
33
|
+
export { MLucideIcon } from "./MLucideIcon";
|
|
34
|
+
export type { MLucideIconProps, IconName } from "./MLucideIcon";
|
|
35
|
+
|
|
36
|
+
export { MPicker } from "./MPicker";
|
|
37
|
+
export type { MPickerProps, PickerItem } from "./MPicker";
|
|
38
|
+
|
|
39
|
+
export { MProgressBar } from "./MProgressBar";
|
|
40
|
+
export type { MProgressBarProps } from "./MProgressBar";
|
|
41
|
+
|
|
42
|
+
export { MResponsiveContainer } from "./MResponsiveContainer";
|
|
43
|
+
|
|
44
|
+
export { MText } from "./MText";
|
|
45
|
+
export type { MTextProps } from "./MText";
|
|
46
|
+
|
|
47
|
+
export { MRequiredLabel } from "./MRequiredLabel";
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
MModal,
|
|
51
|
+
ModalHeight,
|
|
52
|
+
ModalScrollContext,
|
|
53
|
+
ModalContent
|
|
54
|
+
} from "./MModal";
|
|
55
|
+
export type { MModalProps } from "./MModal";
|
|
56
|
+
|
|
57
|
+
export { MDrawerRightPanel } from "./MDrawerRightPanel";
|
|
58
|
+
export type { MDrawerRightPanelProps } from "./MDrawerRightPanel";
|
|
59
|
+
|
|
60
|
+
export { MNativeAlert } from "./MNativeAlert";
|
|
61
|
+
export type { MNativeAlertProps } from "./MNativeAlert";
|
|
62
|
+
|
|
63
|
+
export { MCountDown } from "./MCountDown";
|
|
64
|
+
export type { MCountDownProps } from "./MCountDown";
|
|
65
|
+
|
|
66
|
+
export { MDropdown } from "./MDropdown";
|
|
67
|
+
export type { MDropdownProps } from "./MDropdown";
|
|
68
|
+
|
|
69
|
+
export { MSearch } from "./MSearch";
|
|
70
|
+
export type { MSearchProps } from "./MSearch";
|
|
71
|
+
|
|
72
|
+
export { MSelector } from "./MSelector";
|
|
73
|
+
export type { MSelectorProps } from "./MSelector";
|
|
74
|
+
|
|
75
|
+
export { MToggle } from "./MToggle";
|
|
76
|
+
export type { MToggleProps, MToggleOption } from "./MToggle";
|
|
77
|
+
|
|
78
|
+
export { MConfirmation } from "./MConfirmation";
|
|
79
|
+
export type { MConfirmationProps } from "./MConfirmation";
|
|
80
|
+
|
|
81
|
+
export { MPinPadKeys } from "./MPinPadKeys";
|
|
82
|
+
export type { MPinPadKeysProps } from "./MPinPadKeys";
|
|
83
|
+
|
|
84
|
+
export { MNumpad, NumpadMode, getDecimalSeparator } from "./MNumpad";
|
|
85
|
+
export type { MNumpadProps } from "./MNumpad";
|
|
86
|
+
|
|
87
|
+
export { MTabs } from "./MTabs";
|
|
88
|
+
export type { MTabsProps, Tab } from "./MTabs";
|
|
89
|
+
|
|
90
|
+
export { MTimerUp } from "./MTimerUp";
|
|
91
|
+
export type { MTimerUpProps } from "./MTimerUp";
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
MCard,
|
|
95
|
+
MCardContent,
|
|
96
|
+
MCardDescription,
|
|
97
|
+
MCardFooter,
|
|
98
|
+
MCardHeader,
|
|
99
|
+
MCardTitle
|
|
100
|
+
} from "./MCard";
|
|
101
|
+
|
|
102
|
+
export { MSkeleton } from "./MSkeleton";
|
|
103
|
+
|
|
104
|
+
export { MSwitch } from "./MSwitch";
|
|
105
|
+
export type { MSwitchProps } from "./MSwitch";
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
MTable,
|
|
109
|
+
MTableBody,
|
|
110
|
+
MTableCell,
|
|
111
|
+
MTableFooter,
|
|
112
|
+
MTableHead,
|
|
113
|
+
MTableHeader,
|
|
114
|
+
MTableRow
|
|
115
|
+
} from "./MTable";
|
|
116
|
+
|
|
117
|
+
export { MPortal, MPortalHost } from "./MPortal";
|
|
118
|
+
|
|
119
|
+
export { MInput } from "./MInput";
|
|
120
|
+
export type { MInputProps } from "./MInput";
|
|
121
|
+
|
|
122
|
+
export { MLabel } from "./MLabel";
|
|
123
|
+
|
|
124
|
+
export { MRadioGroup, MRadioGroupItem } from "./MRadioGroup";
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as AccordionPrimitive from "@rn-primitives/accordion";
|
|
2
|
+
import { ChevronDown } from "lucide-react-native";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Platform, Pressable, View, type ViewProps } from "react-native";
|
|
5
|
+
import Animated, {
|
|
6
|
+
Extrapolation,
|
|
7
|
+
FadeIn,
|
|
8
|
+
FadeOutUp,
|
|
9
|
+
LayoutAnimationConfig,
|
|
10
|
+
LinearTransition,
|
|
11
|
+
interpolate,
|
|
12
|
+
useAnimatedStyle,
|
|
13
|
+
useDerivedValue,
|
|
14
|
+
withTiming
|
|
15
|
+
} from "react-native-reanimated";
|
|
16
|
+
import { cn } from "../utils";
|
|
17
|
+
import { TextClassContext } from "./text";
|
|
18
|
+
|
|
19
|
+
const Accordion = React.forwardRef<
|
|
20
|
+
AccordionPrimitive.RootRef,
|
|
21
|
+
AccordionPrimitive.RootProps
|
|
22
|
+
>(({ children, ...props }, ref) => {
|
|
23
|
+
return (
|
|
24
|
+
<LayoutAnimationConfig skipEntering>
|
|
25
|
+
<AccordionPrimitive.Root
|
|
26
|
+
ref={ref}
|
|
27
|
+
{...props}
|
|
28
|
+
asChild={Platform.OS !== "web"}
|
|
29
|
+
>
|
|
30
|
+
<Animated.View layout={LinearTransition.duration(200)}>
|
|
31
|
+
{children}
|
|
32
|
+
</Animated.View>
|
|
33
|
+
</AccordionPrimitive.Root>
|
|
34
|
+
</LayoutAnimationConfig>
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
Accordion.displayName = AccordionPrimitive.Root.displayName;
|
|
39
|
+
|
|
40
|
+
const AccordionItem = React.forwardRef<
|
|
41
|
+
AccordionPrimitive.ItemRef,
|
|
42
|
+
AccordionPrimitive.ItemProps
|
|
43
|
+
>(({ className, value, ...props }, ref) => {
|
|
44
|
+
return (
|
|
45
|
+
<Animated.View
|
|
46
|
+
className={"overflow-hidden"}
|
|
47
|
+
layout={LinearTransition.duration(200)}
|
|
48
|
+
>
|
|
49
|
+
<AccordionPrimitive.Item
|
|
50
|
+
ref={ref}
|
|
51
|
+
className={cn("border-b border-border", className)}
|
|
52
|
+
value={value}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
</Animated.View>
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
AccordionItem.displayName = AccordionPrimitive.Item.displayName;
|
|
59
|
+
|
|
60
|
+
const Trigger = Platform.OS === "web" ? View : Pressable;
|
|
61
|
+
|
|
62
|
+
const AccordionTrigger = React.forwardRef<
|
|
63
|
+
AccordionPrimitive.TriggerRef,
|
|
64
|
+
AccordionPrimitive.TriggerProps
|
|
65
|
+
>(({ className, children, ...props }, ref) => {
|
|
66
|
+
const { isExpanded } = AccordionPrimitive.useItemContext();
|
|
67
|
+
|
|
68
|
+
const progress = useDerivedValue(() =>
|
|
69
|
+
isExpanded
|
|
70
|
+
? withTiming(1, { duration: 250 })
|
|
71
|
+
: withTiming(0, { duration: 200 })
|
|
72
|
+
);
|
|
73
|
+
const chevronStyle = useAnimatedStyle(() => ({
|
|
74
|
+
transform: [{ rotate: `${progress.value * 180}deg` }],
|
|
75
|
+
opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP)
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<TextClassContext.Provider value="native:text-lg font-medium web:group-hover:underline">
|
|
80
|
+
<AccordionPrimitive.Header className="flex">
|
|
81
|
+
<AccordionPrimitive.Trigger ref={ref} {...props} asChild>
|
|
82
|
+
<Trigger
|
|
83
|
+
className={cn(
|
|
84
|
+
"flex flex-row web:flex-1 items-center justify-between py-4 web:transition-all group web:focus-visible:outline-none web:focus-visible:ring-1 web:focus-visible:ring-muted-foreground",
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
<>{children}</>
|
|
89
|
+
<Animated.View style={chevronStyle}>
|
|
90
|
+
<ChevronDown size={18} className={"text-foreground shrink-0"} />
|
|
91
|
+
</Animated.View>
|
|
92
|
+
</Trigger>
|
|
93
|
+
</AccordionPrimitive.Trigger>
|
|
94
|
+
</AccordionPrimitive.Header>
|
|
95
|
+
</TextClassContext.Provider>
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
99
|
+
|
|
100
|
+
const AccordionContent = React.forwardRef<
|
|
101
|
+
AccordionPrimitive.ContentRef,
|
|
102
|
+
AccordionPrimitive.ContentProps
|
|
103
|
+
>(({ className, children, ...props }, ref) => {
|
|
104
|
+
const { isExpanded } = AccordionPrimitive.useItemContext();
|
|
105
|
+
return (
|
|
106
|
+
<TextClassContext.Provider value="native:text-lg">
|
|
107
|
+
<AccordionPrimitive.Content
|
|
108
|
+
className={cn(
|
|
109
|
+
"overflow-hidden text-sm web:transition-all",
|
|
110
|
+
isExpanded ? "web:animate-accordion-down" : "web:animate-accordion-up"
|
|
111
|
+
)}
|
|
112
|
+
ref={ref}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
<InnerContent className={cn("pb-4", className)}>
|
|
116
|
+
{children}
|
|
117
|
+
</InnerContent>
|
|
118
|
+
</AccordionPrimitive.Content>
|
|
119
|
+
</TextClassContext.Provider>
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
function InnerContent({
|
|
124
|
+
children,
|
|
125
|
+
className
|
|
126
|
+
}: React.PropsWithChildren<Pick<ViewProps, "className">>) {
|
|
127
|
+
if (Platform.OS === "web") {
|
|
128
|
+
return <View className={cn("pb-4", className)}>{children}</View>;
|
|
129
|
+
}
|
|
130
|
+
return (
|
|
131
|
+
<Animated.View
|
|
132
|
+
entering={FadeIn}
|
|
133
|
+
exiting={FadeOutUp.duration(200)}
|
|
134
|
+
className={cn("pb-4", className)}
|
|
135
|
+
>
|
|
136
|
+
{children}
|
|
137
|
+
</Animated.View>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
142
|
+
|
|
143
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as AlertDialogPrimitive from "@rn-primitives/alert-dialog";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Platform, StyleSheet, View, type ViewProps } from "react-native";
|
|
4
|
+
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
|
5
|
+
import { cn } from "../utils";
|
|
6
|
+
import { buttonTextVariants, buttonVariants } from "./button";
|
|
7
|
+
import { TextClassContext } from "./text";
|
|
8
|
+
|
|
9
|
+
const AlertDialog = AlertDialogPrimitive.Root;
|
|
10
|
+
|
|
11
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
12
|
+
|
|
13
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
14
|
+
|
|
15
|
+
const AlertDialogOverlayWeb = React.forwardRef<
|
|
16
|
+
AlertDialogPrimitive.OverlayRef,
|
|
17
|
+
AlertDialogPrimitive.OverlayProps
|
|
18
|
+
>(({ className, ...props }, ref) => {
|
|
19
|
+
const { open } = AlertDialogPrimitive.useRootContext();
|
|
20
|
+
return (
|
|
21
|
+
<AlertDialogPrimitive.Overlay
|
|
22
|
+
className={cn(
|
|
23
|
+
"z-50 bg-black/80 flex justify-center items-center p-2 absolute top-0 right-0 bottom-0 left-0",
|
|
24
|
+
open
|
|
25
|
+
? "web:animate-in web:fade-in-0"
|
|
26
|
+
: "web:animate-out web:fade-out-0",
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
ref={ref}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
AlertDialogOverlayWeb.displayName = "AlertDialogOverlayWeb";
|
|
36
|
+
|
|
37
|
+
const AlertDialogOverlayNative = React.forwardRef<
|
|
38
|
+
AlertDialogPrimitive.OverlayRef,
|
|
39
|
+
AlertDialogPrimitive.OverlayProps
|
|
40
|
+
>(({ className, children, ...props }, ref) => {
|
|
41
|
+
return (
|
|
42
|
+
<AlertDialogPrimitive.Overlay
|
|
43
|
+
style={StyleSheet.absoluteFill}
|
|
44
|
+
className={cn(
|
|
45
|
+
"z-50 bg-black/80 flex justify-center items-center p-2",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
ref={ref}
|
|
50
|
+
asChild
|
|
51
|
+
>
|
|
52
|
+
<Animated.View
|
|
53
|
+
entering={FadeIn.duration(150)}
|
|
54
|
+
exiting={FadeOut.duration(150)}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</Animated.View>
|
|
58
|
+
</AlertDialogPrimitive.Overlay>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
AlertDialogOverlayNative.displayName = "AlertDialogOverlayNative";
|
|
63
|
+
|
|
64
|
+
const AlertDialogOverlay = Platform.select({
|
|
65
|
+
web: AlertDialogOverlayWeb,
|
|
66
|
+
default: AlertDialogOverlayNative
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const AlertDialogContent = React.forwardRef<
|
|
70
|
+
AlertDialogPrimitive.ContentRef,
|
|
71
|
+
AlertDialogPrimitive.ContentProps & { portalHost?: string }
|
|
72
|
+
>(({ className, portalHost, ...props }, ref) => {
|
|
73
|
+
const { open } = AlertDialogPrimitive.useRootContext();
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<AlertDialogPortal hostName={portalHost}>
|
|
77
|
+
<AlertDialogOverlay>
|
|
78
|
+
<AlertDialogPrimitive.Content
|
|
79
|
+
ref={ref}
|
|
80
|
+
className={cn(
|
|
81
|
+
"z-50 max-w-lg gap-4 border border-border bg-background p-6 shadow-lg shadow-foreground/10 web:duration-200 rounded-lg",
|
|
82
|
+
open
|
|
83
|
+
? "web:animate-in web:fade-in-0 web:zoom-in-95"
|
|
84
|
+
: "web:animate-out web:fade-out-0 web:zoom-out-95",
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
</AlertDialogOverlay>
|
|
90
|
+
</AlertDialogPortal>
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
94
|
+
|
|
95
|
+
const AlertDialogHeader = ({ className, ...props }: ViewProps) => (
|
|
96
|
+
<View className={cn("flex flex-col gap-2", className)} {...props} />
|
|
97
|
+
);
|
|
98
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
99
|
+
|
|
100
|
+
const AlertDialogFooter = ({ className, ...props }: ViewProps) => (
|
|
101
|
+
<View
|
|
102
|
+
className={cn(
|
|
103
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end gap-2",
|
|
104
|
+
className
|
|
105
|
+
)}
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
110
|
+
|
|
111
|
+
const AlertDialogTitle = React.forwardRef<
|
|
112
|
+
AlertDialogPrimitive.TitleRef,
|
|
113
|
+
AlertDialogPrimitive.TitleProps
|
|
114
|
+
>(({ className, ...props }, ref) => (
|
|
115
|
+
<AlertDialogPrimitive.Title
|
|
116
|
+
ref={ref}
|
|
117
|
+
className={cn(
|
|
118
|
+
"text-lg native:text-xl text-foreground font-semibold",
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
));
|
|
124
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
125
|
+
|
|
126
|
+
const AlertDialogDescription = React.forwardRef<
|
|
127
|
+
AlertDialogPrimitive.DescriptionRef,
|
|
128
|
+
AlertDialogPrimitive.DescriptionProps
|
|
129
|
+
>(({ className, ...props }, ref) => (
|
|
130
|
+
<AlertDialogPrimitive.Description
|
|
131
|
+
ref={ref}
|
|
132
|
+
className={cn("text-sm native:text-base text-muted-foreground", className)}
|
|
133
|
+
{...props}
|
|
134
|
+
/>
|
|
135
|
+
));
|
|
136
|
+
AlertDialogDescription.displayName =
|
|
137
|
+
AlertDialogPrimitive.Description.displayName;
|
|
138
|
+
|
|
139
|
+
const AlertDialogAction = React.forwardRef<
|
|
140
|
+
AlertDialogPrimitive.ActionRef,
|
|
141
|
+
AlertDialogPrimitive.ActionProps
|
|
142
|
+
>(({ className, ...props }, ref) => (
|
|
143
|
+
<TextClassContext.Provider value={buttonTextVariants({ className })}>
|
|
144
|
+
<AlertDialogPrimitive.Action
|
|
145
|
+
ref={ref}
|
|
146
|
+
className={cn(buttonVariants(), className)}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
</TextClassContext.Provider>
|
|
150
|
+
));
|
|
151
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
152
|
+
|
|
153
|
+
const AlertDialogCancel = React.forwardRef<
|
|
154
|
+
AlertDialogPrimitive.CancelRef,
|
|
155
|
+
AlertDialogPrimitive.CancelProps
|
|
156
|
+
>(({ className, ...props }, ref) => (
|
|
157
|
+
<TextClassContext.Provider
|
|
158
|
+
value={buttonTextVariants({ className, variant: "outline" })}
|
|
159
|
+
>
|
|
160
|
+
<AlertDialogPrimitive.Cancel
|
|
161
|
+
ref={ref}
|
|
162
|
+
className={cn(buttonVariants({ variant: "outline", className }))}
|
|
163
|
+
{...props}
|
|
164
|
+
/>
|
|
165
|
+
</TextClassContext.Provider>
|
|
166
|
+
));
|
|
167
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
AlertDialog,
|
|
171
|
+
AlertDialogAction,
|
|
172
|
+
AlertDialogCancel,
|
|
173
|
+
AlertDialogContent,
|
|
174
|
+
AlertDialogDescription,
|
|
175
|
+
AlertDialogFooter,
|
|
176
|
+
AlertDialogHeader,
|
|
177
|
+
AlertDialogOverlay,
|
|
178
|
+
AlertDialogPortal,
|
|
179
|
+
AlertDialogTitle,
|
|
180
|
+
AlertDialogTrigger
|
|
181
|
+
};
|