@ecohouse/ui 0.1.56 → 0.1.58

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.
Files changed (41) hide show
  1. package/dist/index.cjs +961 -504
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +53 -3
  4. package/dist/index.d.ts +53 -3
  5. package/dist/index.js +857 -414
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/NotificationStatus/NotificationStatus.stories.tsx +86 -0
  9. package/src/components/NotificationStatus/NotificationStatus.tsx +255 -0
  10. package/src/components/NotificationStatus/index.ts +9 -0
  11. package/src/components/StatusIndicator/StatusIndicator.stories.tsx +36 -0
  12. package/src/components/StatusIndicator/StatusIndicator.tsx +105 -0
  13. package/src/components/StatusIndicator/index.ts +2 -0
  14. package/src/components/index.ts +13 -0
  15. package/src/icons/ChevronRight/ChevronRightIcon.tsx +25 -0
  16. package/src/icons/ChevronRight/ChevronRightIcon.web.tsx +31 -0
  17. package/src/icons/ChevronRight/chevronRightPath.ts +2 -0
  18. package/src/icons/ChevronRight/index.ts +1 -0
  19. package/src/icons/DoubleCheck/DoubleCheckIcon.tsx +25 -0
  20. package/src/icons/DoubleCheck/DoubleCheckIcon.web.tsx +31 -0
  21. package/src/icons/DoubleCheck/doubleCheckPath.ts +3 -0
  22. package/src/icons/DoubleCheck/index.ts +1 -0
  23. package/src/icons/NotificationCancelled/NotificationCancelledIcon.tsx +22 -0
  24. package/src/icons/NotificationCancelled/NotificationCancelledIcon.web.tsx +21 -0
  25. package/src/icons/NotificationCancelled/index.ts +1 -0
  26. package/src/icons/NotificationCheckout/NotificationCheckoutIcon.tsx +22 -0
  27. package/src/icons/NotificationCheckout/NotificationCheckoutIcon.web.tsx +21 -0
  28. package/src/icons/NotificationCheckout/index.ts +1 -0
  29. package/src/icons/NotificationIcons.stories.tsx +99 -0
  30. package/src/icons/NotificationQrCode/NotificationQrCodeIcon.tsx +25 -0
  31. package/src/icons/NotificationQrCode/NotificationQrCodeIcon.web.tsx +24 -0
  32. package/src/icons/NotificationQrCode/index.ts +1 -0
  33. package/src/icons/NotificationSuccess/NotificationSuccessIcon.tsx +26 -0
  34. package/src/icons/NotificationSuccess/NotificationSuccessIcon.web.tsx +25 -0
  35. package/src/icons/NotificationSuccess/index.ts +1 -0
  36. package/src/icons/NotificationWarning/NotificationWarningIcon.tsx +22 -0
  37. package/src/icons/NotificationWarning/NotificationWarningIcon.web.tsx +21 -0
  38. package/src/icons/NotificationWarning/index.ts +1 -0
  39. package/src/icons/index.ts +7 -0
  40. package/src/icons/registry.ts +23 -0
  41. package/src/index.ts +17 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecohouse/ui",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Cross-platform presentation-only UI components for EcoHouse (React Native + React Native Web)",
@@ -0,0 +1,86 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { colors } from "../../theme";
4
+ import {
5
+ BookingWarningNotification,
6
+ CancelledBookingNotification,
7
+ GuestCheckInNotification,
8
+ NewBookingNotification,
9
+ NotificationStatusSkeleton,
10
+ QrKeyNotification,
11
+ } from "./NotificationStatus";
12
+
13
+ const meta = {
14
+ title: "Components/Notification Statuses",
15
+ component: NewBookingNotification,
16
+ args: {
17
+ title: "Նոր ամրագրում",
18
+ description: "Արմեն Գրիգորյանը ամրագրեց «Լեռնային Ռեզիդենցիա» (Օգս 28 - Օգս 30):",
19
+ timestamp: "2 ժ առաջ",
20
+ unread: true,
21
+ onPress: () => undefined,
22
+ },
23
+ decorators: [
24
+ (Story) => (
25
+ <View style={styles.frame}>
26
+ <Story />
27
+ </View>
28
+ ),
29
+ ],
30
+ parameters: {
31
+ backgrounds: { default: "dark" },
32
+ },
33
+ } satisfies Meta<typeof NewBookingNotification>;
34
+
35
+ export default meta;
36
+ type Story = StoryObj<typeof meta>;
37
+
38
+ export const NewBooking: Story = {};
39
+
40
+ export const CancelledBooking: Story = {
41
+ render: (args) => <CancelledBookingNotification {...args} title="Ամրագրումը չեղարկվել է" />,
42
+ };
43
+
44
+ export const GuestCheckIn: Story = {
45
+ render: (args) => <GuestCheckInNotification {...args} title="Հյուրը մուտք գործեց" />,
46
+ };
47
+
48
+ export const QrKey: Story = {
49
+ name: "QR Key",
50
+ render: (args) => <QrKeyNotification {...args} title="QR բանալին ակտիվացված է" />,
51
+ };
52
+
53
+ export const BookingWarning: Story = {
54
+ render: (args) => (
55
+ <BookingWarningNotification {...args} title="Քոթեջն ուշադրություն է պահանջում" />
56
+ ),
57
+ };
58
+
59
+ export const Loading: Story = {
60
+ render: () => <NotificationStatusSkeleton />,
61
+ };
62
+
63
+ export const AllStatuses: Story = {
64
+ render: (args) => (
65
+ <View>
66
+ <NewBookingNotification {...args} />
67
+ <CancelledBookingNotification {...args} title="Ամրագրումը չեղարկվել է" />
68
+ <GuestCheckInNotification {...args} title="Հյուրը մուտք գործեց" />
69
+ <QrKeyNotification {...args} title="QR բանալին ակտիվացված է" unread={false} />
70
+ <BookingWarningNotification
71
+ {...args}
72
+ title="Քոթեջն ուշադրություն է պահանջում"
73
+ unread={false}
74
+ />
75
+ </View>
76
+ ),
77
+ };
78
+
79
+ const styles = StyleSheet.create({
80
+ frame: {
81
+ width: 460,
82
+ overflow: "hidden",
83
+ borderRadius: 16,
84
+ backgroundColor: colors.grey750,
85
+ },
86
+ });
@@ -0,0 +1,255 @@
1
+ import { useState, type ReactNode } from "react";
2
+ import {
3
+ Pressable,
4
+ StyleSheet,
5
+ Text,
6
+ View,
7
+ type PressableProps,
8
+ type StyleProp,
9
+ type ViewStyle,
10
+ } from "react-native";
11
+ import {
12
+ ChevronRightIcon,
13
+ NotificationCancelledIcon,
14
+ NotificationCheckoutIcon,
15
+ NotificationQrCodeIcon,
16
+ NotificationSuccessIcon,
17
+ NotificationWarningIcon,
18
+ } from "../../icons";
19
+ import { colors, fonts } from "../../theme";
20
+
21
+ export interface NotificationStatusProps extends Omit<
22
+ PressableProps,
23
+ "children" | "disabled" | "onPress" | "style"
24
+ > {
25
+ title: string;
26
+ description: string;
27
+ timestamp: string;
28
+ unread?: boolean;
29
+ disabled?: boolean;
30
+ showChevron?: boolean;
31
+ onPress?: PressableProps["onPress"];
32
+ style?: StyleProp<ViewStyle>;
33
+ }
34
+
35
+ export function NewBookingNotification(props: NotificationStatusProps) {
36
+ return <NotificationStatus icon={<NotificationSuccessIcon />} {...props} />;
37
+ }
38
+
39
+ export function CancelledBookingNotification(props: NotificationStatusProps) {
40
+ return <NotificationStatus icon={<NotificationCancelledIcon />} {...props} />;
41
+ }
42
+
43
+ export function GuestCheckInNotification(props: NotificationStatusProps) {
44
+ return <NotificationStatus icon={<NotificationCheckoutIcon />} {...props} />;
45
+ }
46
+
47
+ export function QrKeyNotification(props: NotificationStatusProps) {
48
+ return <NotificationStatus icon={<NotificationQrCodeIcon />} {...props} />;
49
+ }
50
+
51
+ export function BookingWarningNotification(props: NotificationStatusProps) {
52
+ return <NotificationStatus icon={<NotificationWarningIcon />} {...props} />;
53
+ }
54
+
55
+ export function NotificationStatusSkeleton() {
56
+ return (
57
+ <View
58
+ accessibilityElementsHidden
59
+ importantForAccessibility="no-hide-descendants"
60
+ style={styles.root}
61
+ >
62
+ <View style={[styles.icon, styles.skeletonIcon]} />
63
+ <View style={[styles.content, styles.skeletonContent]}>
64
+ <View style={[styles.skeletonLine, styles.skeletonTitle]} />
65
+ <View style={styles.skeletonDescription}>
66
+ <View style={[styles.skeletonLine, styles.skeletonDescriptionFirst]} />
67
+ <View style={[styles.skeletonLine, styles.skeletonDescriptionSecond]} />
68
+ </View>
69
+ <View style={[styles.skeletonLine, styles.skeletonTimestamp]} />
70
+ </View>
71
+ <View style={[styles.chevron, styles.skeletonChevron]} />
72
+ </View>
73
+ );
74
+ }
75
+
76
+ function NotificationStatus({
77
+ icon,
78
+ title,
79
+ description,
80
+ timestamp,
81
+ unread = false,
82
+ disabled = false,
83
+ showChevron,
84
+ onPress,
85
+ style,
86
+ accessibilityLabel,
87
+ onHoverIn,
88
+ onHoverOut,
89
+ onPressIn,
90
+ onPressOut,
91
+ ...rest
92
+ }: NotificationStatusProps & { icon: ReactNode }) {
93
+ const [active, setActive] = useState(false);
94
+ const interactive = onPress !== undefined;
95
+
96
+ return (
97
+ <Pressable
98
+ {...rest}
99
+ accessibilityLabel={accessibilityLabel ?? `${title}. ${description}. ${timestamp}`}
100
+ accessibilityRole={interactive ? "button" : undefined}
101
+ accessibilityState={{ disabled }}
102
+ disabled={disabled}
103
+ onPress={onPress}
104
+ onHoverIn={(event) => {
105
+ setActive(true);
106
+ onHoverIn?.(event);
107
+ }}
108
+ onHoverOut={(event) => {
109
+ setActive(false);
110
+ onHoverOut?.(event);
111
+ }}
112
+ onPressIn={(event) => {
113
+ setActive(true);
114
+ onPressIn?.(event);
115
+ }}
116
+ onPressOut={(event) => {
117
+ setActive(false);
118
+ onPressOut?.(event);
119
+ }}
120
+ style={[styles.root, active && styles.rootActive, disabled && styles.disabled, style]}
121
+ >
122
+ <View style={styles.icon}>{icon}</View>
123
+ <View style={styles.content}>
124
+ <View style={styles.titleRow}>
125
+ {unread ? <View style={styles.unreadIndicator} /> : null}
126
+ <Text numberOfLines={1} style={styles.title}>
127
+ {title}
128
+ </Text>
129
+ </View>
130
+ <Text style={styles.description}>{description}</Text>
131
+ <Text numberOfLines={1} style={styles.timestamp}>
132
+ {timestamp}
133
+ </Text>
134
+ </View>
135
+ {(showChevron ?? interactive) ? (
136
+ <View
137
+ accessibilityElementsHidden
138
+ importantForAccessibility="no-hide-descendants"
139
+ style={styles.chevron}
140
+ >
141
+ <ChevronRightIcon color={colors.grey300} size={16} strokeWidth={1.5} />
142
+ </View>
143
+ ) : null}
144
+ </Pressable>
145
+ );
146
+ }
147
+
148
+ const styles = StyleSheet.create({
149
+ root: {
150
+ width: "100%",
151
+ minHeight: 111,
152
+ flexDirection: "row",
153
+ alignItems: "flex-start",
154
+ gap: 16,
155
+ padding: 16,
156
+ backgroundColor: "transparent",
157
+ },
158
+ rootActive: {
159
+ backgroundColor: colors.whiteAlpha1,
160
+ },
161
+ disabled: {
162
+ opacity: 0.4,
163
+ },
164
+ icon: {
165
+ width: 44,
166
+ height: 44,
167
+ flexShrink: 0,
168
+ },
169
+ content: {
170
+ minWidth: 0,
171
+ flex: 1,
172
+ alignSelf: "stretch",
173
+ justifyContent: "space-between",
174
+ },
175
+ titleRow: {
176
+ minWidth: 0,
177
+ flexDirection: "row",
178
+ alignItems: "center",
179
+ gap: 10,
180
+ },
181
+ unreadIndicator: {
182
+ width: 8,
183
+ height: 8,
184
+ flexShrink: 0,
185
+ borderRadius: 4,
186
+ backgroundColor: colors.primaryLight,
187
+ },
188
+ title: {
189
+ minWidth: 0,
190
+ flex: 1,
191
+ color: colors.white,
192
+ fontFamily: fonts.sans,
193
+ fontSize: 14,
194
+ fontWeight: "600",
195
+ lineHeight: 14,
196
+ letterSpacing: 0,
197
+ },
198
+ description: {
199
+ color: colors.lightGrey,
200
+ fontFamily: fonts.sans,
201
+ fontSize: 12,
202
+ fontWeight: "400",
203
+ lineHeight: 16,
204
+ letterSpacing: 0,
205
+ },
206
+ timestamp: {
207
+ color: colors.grey50,
208
+ fontFamily: fonts.sans,
209
+ fontSize: 12,
210
+ fontWeight: "400",
211
+ lineHeight: 16,
212
+ letterSpacing: 0,
213
+ },
214
+ chevron: {
215
+ alignSelf: "center",
216
+ flexShrink: 0,
217
+ },
218
+ skeletonIcon: {
219
+ borderRadius: 22,
220
+ backgroundColor: colors.grey600,
221
+ },
222
+ skeletonContent: {
223
+ paddingTop: 1,
224
+ paddingBottom: 2,
225
+ },
226
+ skeletonLine: {
227
+ borderRadius: 999,
228
+ backgroundColor: colors.grey500,
229
+ },
230
+ skeletonTitle: {
231
+ width: "42%",
232
+ height: 12,
233
+ },
234
+ skeletonDescription: {
235
+ gap: 5,
236
+ },
237
+ skeletonDescriptionFirst: {
238
+ width: "92%",
239
+ height: 10,
240
+ },
241
+ skeletonDescriptionSecond: {
242
+ width: "68%",
243
+ height: 10,
244
+ },
245
+ skeletonTimestamp: {
246
+ width: 58,
247
+ height: 10,
248
+ },
249
+ skeletonChevron: {
250
+ width: 7,
251
+ height: 12,
252
+ borderRadius: 4,
253
+ backgroundColor: colors.grey500,
254
+ },
255
+ });
@@ -0,0 +1,9 @@
1
+ export {
2
+ BookingWarningNotification,
3
+ CancelledBookingNotification,
4
+ GuestCheckInNotification,
5
+ NewBookingNotification,
6
+ NotificationStatusSkeleton,
7
+ QrKeyNotification,
8
+ } from "./NotificationStatus";
9
+ export type { NotificationStatusProps } from "./NotificationStatus";
@@ -0,0 +1,36 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { StatusIndicator } from "./StatusIndicator";
4
+
5
+ const meta = {
6
+ title: "Components/StatusIndicator",
7
+ component: StatusIndicator,
8
+ args: { label: "Ակտիվ է", status: "active" },
9
+ parameters: { backgrounds: { default: "black" } },
10
+ } satisfies Meta<typeof StatusIndicator>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Active: Story = {};
16
+
17
+ export const AllStatuses: Story = {
18
+ render: () => (
19
+ <View style={styles.column}>
20
+ <StatusIndicator label="Ակտիվ է" status="active" />
21
+ <StatusIndicator label="Սպասվող" status="pending" />
22
+ <StatusIndicator label="Ավարտված" status="completed" />
23
+ <StatusIndicator label="Չեղարկված" status="cancelled" />
24
+ <StatusIndicator label="Ապաակտիվ է" status="inactive" />
25
+ <StatusIndicator label="Հասանելի չէ" status="unavailable" />
26
+ </View>
27
+ ),
28
+ };
29
+
30
+ const styles = StyleSheet.create({
31
+ column: {
32
+ gap: 16,
33
+ padding: 24,
34
+ backgroundColor: "#090909",
35
+ },
36
+ });
@@ -0,0 +1,105 @@
1
+ import { forwardRef, useMemo, useRef, type ComponentRef } from "react";
2
+ import {
3
+ Platform,
4
+ StyleSheet,
5
+ Text,
6
+ View,
7
+ type StyleProp,
8
+ type ViewProps,
9
+ type ViewStyle,
10
+ } from "react-native";
11
+ import { colors, fonts } from "../../theme";
12
+ import { mergeRefs } from "../../utils/mergeRefs";
13
+ import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
14
+
15
+ export type StatusIndicatorStatus =
16
+ "active" | "pending" | "completed" | "cancelled" | "inactive" | "unavailable";
17
+
18
+ export interface StatusIndicatorProps extends Omit<ViewProps, "style" | "children"> {
19
+ status: StatusIndicatorStatus;
20
+ label: string;
21
+ style?: StyleProp<ViewStyle>;
22
+ className?: string;
23
+ }
24
+
25
+ const statusColors: Record<Exclude<StatusIndicatorStatus, "unavailable">, string> = {
26
+ active: colors.green,
27
+ pending: colors.primary,
28
+ completed: colors.grey0,
29
+ cancelled: colors.red,
30
+ inactive: colors.primary,
31
+ };
32
+
33
+ export const StatusIndicator = forwardRef<ComponentRef<typeof View>, StatusIndicatorProps>(
34
+ function StatusIndicator(
35
+ { status, label, style, className, accessibilityLabel, ...rest },
36
+ forwardedRef,
37
+ ) {
38
+ const containerRef = useRef<ComponentRef<typeof View>>(null);
39
+ const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
40
+ const indicatorColor = status === "unavailable" ? null : statusColors[status];
41
+
42
+ useApplyWebClassName(containerRef, className);
43
+
44
+ return (
45
+ <View
46
+ {...rest}
47
+ ref={setContainerRef}
48
+ accessibilityRole="text"
49
+ accessibilityLabel={accessibilityLabel ?? label}
50
+ style={[styles.container, style]}
51
+ >
52
+ {indicatorColor ? (
53
+ <View style={[styles.indicatorOuter, { backgroundColor: `${indicatorColor}1F` }]}>
54
+ <View style={[styles.indicatorMiddle, { backgroundColor: `${indicatorColor}52` }]}>
55
+ <View style={[styles.indicatorInner, { backgroundColor: indicatorColor }]} />
56
+ </View>
57
+ </View>
58
+ ) : null}
59
+ <Text style={[styles.label, Platform.OS === "android" ? styles.labelAndroid : null]}>
60
+ {label}
61
+ </Text>
62
+ </View>
63
+ );
64
+ },
65
+ );
66
+
67
+ const styles = StyleSheet.create({
68
+ container: {
69
+ flexDirection: "row",
70
+ alignItems: "center",
71
+ alignSelf: "flex-start",
72
+ gap: 8,
73
+ },
74
+ indicatorOuter: {
75
+ width: 14,
76
+ height: 14,
77
+ flexShrink: 0,
78
+ alignItems: "center",
79
+ justifyContent: "center",
80
+ borderRadius: 7,
81
+ },
82
+ indicatorMiddle: {
83
+ width: 10,
84
+ height: 10,
85
+ alignItems: "center",
86
+ justifyContent: "center",
87
+ borderRadius: 5,
88
+ },
89
+ indicatorInner: {
90
+ width: 6,
91
+ height: 6,
92
+ borderRadius: 3,
93
+ },
94
+ label: {
95
+ color: colors.white,
96
+ fontFamily: fonts.sans,
97
+ fontSize: 12,
98
+ fontWeight: "500",
99
+ lineHeight: 12,
100
+ letterSpacing: 0,
101
+ },
102
+ labelAndroid: {
103
+ includeFontPadding: false,
104
+ },
105
+ });
@@ -0,0 +1,2 @@
1
+ export { StatusIndicator } from "./StatusIndicator";
2
+ export type { StatusIndicatorProps, StatusIndicatorStatus } from "./StatusIndicator";
@@ -13,6 +13,9 @@ export type { BadgeProps, BadgeVariant } from "./Badge";
13
13
  export { StatusBadge } from "./StatusBadge";
14
14
  export type { StatusBadgeProps, StatusBadgeTone } from "./StatusBadge";
15
15
 
16
+ export { StatusIndicator } from "./StatusIndicator";
17
+ export type { StatusIndicatorProps, StatusIndicatorStatus } from "./StatusIndicator";
18
+
16
19
  export { PaginationDots } from "./PaginationDots";
17
20
  export type { PaginationDotsProps } from "./PaginationDots";
18
21
 
@@ -116,6 +119,16 @@ export type { RatingInputProps } from "./RatingInput";
116
119
  export { CommentCard } from "./CommentCard";
117
120
  export type { CommentCardProps } from "./CommentCard";
118
121
 
122
+ export {
123
+ BookingWarningNotification,
124
+ CancelledBookingNotification,
125
+ GuestCheckInNotification,
126
+ NewBookingNotification,
127
+ NotificationStatusSkeleton,
128
+ QrKeyNotification,
129
+ } from "./NotificationStatus";
130
+ export type { NotificationStatusProps } from "./NotificationStatus";
131
+
119
132
  export { Range } from "./Range";
120
133
  export type { RangeProps, RangeValue } from "./Range";
121
134
 
@@ -0,0 +1,25 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
5
+
6
+ export { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function ChevronRightIcon({
10
+ size = 16,
11
+ color = colors.grey100,
12
+ strokeWidth = 1.5,
13
+ }: IconProps) {
14
+ return (
15
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
16
+ <Path
17
+ d={CHEVRON_RIGHT_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </Svg>
24
+ );
25
+ }
@@ -0,0 +1,31 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function ChevronRightIcon({
7
+ size = 16,
8
+ color = colors.grey100,
9
+ strokeWidth = 1.5,
10
+ }: IconProps) {
11
+ return (
12
+ <svg
13
+ width={size}
14
+ height={size}
15
+ viewBox="0 0 16 16"
16
+ fill="none"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ aria-hidden
19
+ >
20
+ <path
21
+ d={CHEVRON_RIGHT_PATH}
22
+ stroke={color}
23
+ strokeWidth={strokeWidth}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ />
27
+ </svg>
28
+ );
29
+ }
30
+
31
+ export { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
@@ -0,0 +1,2 @@
1
+ /** Chevron right path — 16×16 viewBox. */
2
+ export const CHEVRON_RIGHT_PATH = "M6 12L10 8L6 4";
@@ -0,0 +1 @@
1
+ export { ChevronRightIcon, CHEVRON_RIGHT_PATH } from "./ChevronRightIcon";
@@ -0,0 +1,25 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { DOUBLE_CHECK_PATH } from "./doubleCheckPath";
5
+
6
+ export { DOUBLE_CHECK_PATH } from "./doubleCheckPath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function DoubleCheckIcon({
10
+ size = 16,
11
+ color = colors.primary,
12
+ strokeWidth = 1.5,
13
+ }: IconProps) {
14
+ return (
15
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
16
+ <Path
17
+ d={DOUBLE_CHECK_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </Svg>
24
+ );
25
+ }
@@ -0,0 +1,31 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { DOUBLE_CHECK_PATH } from "./doubleCheckPath";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function DoubleCheckIcon({
7
+ size = 16,
8
+ color = colors.primary,
9
+ strokeWidth = 1.5,
10
+ }: IconProps) {
11
+ return (
12
+ <svg
13
+ width={size}
14
+ height={size}
15
+ viewBox="0 0 16 16"
16
+ fill="none"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ aria-hidden
19
+ >
20
+ <path
21
+ d={DOUBLE_CHECK_PATH}
22
+ stroke={color}
23
+ strokeWidth={strokeWidth}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ />
27
+ </svg>
28
+ );
29
+ }
30
+
31
+ export { DOUBLE_CHECK_PATH } from "./doubleCheckPath";
@@ -0,0 +1,3 @@
1
+ /** Double check path — 16×16 viewBox. */
2
+ export const DOUBLE_CHECK_PATH =
3
+ "M4.66683 8L7.96666 11.2998L15.0383 4.22884M1.3667 8.03353L4.66653 11.3334M11.7375 4.26237L8.20199 7.79791";
@@ -0,0 +1 @@
1
+ export { DoubleCheckIcon, DOUBLE_CHECK_PATH } from "./DoubleCheckIcon";
@@ -0,0 +1,22 @@
1
+ import Svg, { Path, Rect } from "react-native-svg";
2
+ import type { IconProps } from "../types";
3
+
4
+ export function NotificationCancelledIcon({
5
+ size = 44,
6
+ color = "#A63E3E",
7
+ strokeWidth = 2,
8
+ fillOpacity = 0.08,
9
+ }: IconProps) {
10
+ return (
11
+ <Svg width={size} height={size} viewBox="0 0 44 44" fill="none">
12
+ <Rect width="44" height="44" rx="22" fill={color} fillOpacity={fillOpacity} />
13
+ <Path
14
+ d="M27 17L17 27M17 17L27 27"
15
+ stroke={color}
16
+ strokeWidth={strokeWidth}
17
+ strokeLinecap="round"
18
+ strokeLinejoin="round"
19
+ />
20
+ </Svg>
21
+ );
22
+ }