@ecohouse/ui 0.1.4 → 0.1.6

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 (61) hide show
  1. package/README.md +66 -15
  2. package/dist/index.cjs +1293 -147
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +358 -154
  5. package/dist/index.d.ts +358 -154
  6. package/dist/index.js +1278 -149
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/Avatar/Avatar.tsx +19 -71
  10. package/src/components/Badge/Badge.stories.tsx +52 -0
  11. package/src/components/Badge/Badge.tsx +131 -0
  12. package/src/components/Badge/index.ts +2 -0
  13. package/src/components/Counter/Counter.stories.tsx +46 -0
  14. package/src/components/Counter/Counter.tsx +135 -0
  15. package/src/components/Counter/index.ts +2 -0
  16. package/src/components/DatePicker/DatePicker.stories.tsx +172 -0
  17. package/src/components/DatePicker/DatePicker.tsx +628 -0
  18. package/src/components/DatePicker/index.ts +7 -0
  19. package/src/components/SegmentedToggle/SegmentedToggle.stories.tsx +63 -0
  20. package/src/components/SegmentedToggle/SegmentedToggle.tsx +221 -0
  21. package/src/components/SegmentedToggle/index.ts +2 -0
  22. package/src/components/StatCard/StatCard.stories.tsx +20 -0
  23. package/src/components/StatCard/StatCard.tsx +61 -0
  24. package/src/components/StatCard/index.ts +2 -0
  25. package/src/components/index.ts +20 -0
  26. package/src/icons/CalendarOutline/CalendarOutlineIcon.tsx +25 -0
  27. package/src/icons/CalendarOutline/CalendarOutlineIcon.web.tsx +31 -0
  28. package/src/icons/CalendarOutline/calendarOutlinePath.ts +3 -0
  29. package/src/icons/CalendarOutline/index.ts +1 -0
  30. package/src/icons/ChevronLeft/ChevronLeftIcon.tsx +21 -0
  31. package/src/icons/ChevronLeft/ChevronLeftIcon.web.tsx +27 -0
  32. package/src/icons/ChevronLeft/chevronLeftPath.ts +2 -0
  33. package/src/icons/ChevronLeft/index.ts +1 -0
  34. package/src/icons/Minus/MinusIcon.tsx +20 -0
  35. package/src/icons/Minus/MinusIcon.web.tsx +19 -0
  36. package/src/icons/Minus/index.ts +1 -0
  37. package/src/icons/Minus/minusPath.ts +1 -0
  38. package/src/icons/Plus/PlusIcon.tsx +20 -0
  39. package/src/icons/Plus/PlusIcon.web.tsx +19 -0
  40. package/src/icons/Plus/index.ts +1 -0
  41. package/src/icons/Plus/plusPath.ts +1 -0
  42. package/src/icons/Sidebar/SidebarIcon.tsx +21 -0
  43. package/src/icons/Sidebar/SidebarIcon.web.tsx +27 -0
  44. package/src/icons/Sidebar/index.ts +1 -0
  45. package/src/icons/Sidebar/sidebarPath.ts +3 -0
  46. package/src/icons/UsersAlt/UsersAltIcon.tsx +14 -0
  47. package/src/icons/UsersAlt/UsersAltIcon.web.tsx +13 -0
  48. package/src/icons/UsersAlt/index.ts +1 -0
  49. package/src/icons/UsersAlt/usersAltPath.ts +2 -0
  50. package/src/icons/index.ts +6 -0
  51. package/src/icons/registry.ts +29 -4
  52. package/src/index.ts +38 -1
  53. package/src/primitives/MenuItem/MenuItem.stories.tsx +98 -0
  54. package/src/primitives/MenuItem/MenuItem.tsx +138 -0
  55. package/src/primitives/MenuItem/index.ts +2 -0
  56. package/src/primitives/index.ts +2 -0
  57. package/src/theme/colors.test.ts +6 -0
  58. package/src/theme/colors.ts +7 -0
  59. package/src/theme/index.ts +5 -0
  60. package/src/theme/typography.ts +76 -0
  61. package/src/utils/dateUtils.ts +86 -0
@@ -0,0 +1,221 @@
1
+ import { forwardRef, useEffect, useMemo, useRef, useState, type ComponentRef } from "react";
2
+ import {
3
+ Animated,
4
+ Easing,
5
+ Pressable,
6
+ StyleSheet,
7
+ Text,
8
+ View,
9
+ type PressableProps,
10
+ type StyleProp,
11
+ type ViewProps,
12
+ type ViewStyle,
13
+ } from "react-native";
14
+ import type { IconComponent } from "../../icons";
15
+ import { colors, segmentedToggleTypography } from "../../theme";
16
+ import { mergeRefs } from "../../utils/mergeRefs";
17
+ import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
18
+
19
+ export interface SegmentedToggleOption {
20
+ value: string;
21
+ label: string;
22
+ icon?: IconComponent;
23
+ }
24
+
25
+ export interface SegmentedToggleProps extends Omit<ViewProps, "style" | "children"> {
26
+ options: readonly [SegmentedToggleOption, SegmentedToggleOption];
27
+ value?: string;
28
+ defaultValue?: string;
29
+ onValueChange?: (value: string) => void;
30
+ fullWidth?: boolean;
31
+ disabled?: boolean;
32
+ style?: StyleProp<ViewStyle>;
33
+ className?: string;
34
+ hitSlop?: PressableProps["hitSlop"];
35
+ }
36
+
37
+ const ICON_SIZE = 20;
38
+ const ANIMATION_DURATION = 200;
39
+
40
+ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedToggleProps>(
41
+ function SegmentedToggle(
42
+ {
43
+ options,
44
+ value,
45
+ defaultValue = options[0].value,
46
+ onValueChange,
47
+ fullWidth = true,
48
+ disabled = false,
49
+ style,
50
+ className,
51
+ hitSlop,
52
+ onLayout,
53
+ ...rest
54
+ },
55
+ forwardedRef,
56
+ ) {
57
+ const containerRef = useRef<ComponentRef<typeof View>>(null);
58
+ const isControlled = value !== undefined;
59
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
60
+ const selectedValue = isControlled ? value : uncontrolledValue;
61
+ const selectedIndex = Math.max(
62
+ 0,
63
+ options.findIndex(({ value: optionValue }) => optionValue === selectedValue),
64
+ );
65
+ const resolvedClassName = className?.trim() || undefined;
66
+ const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
67
+ const progress = useRef(new Animated.Value(selectedIndex)).current;
68
+ const [contentWidths, setContentWidths] = useState<[number, number]>([0, 0]);
69
+ const [containerWidth, setContainerWidth] = useState(0);
70
+ const availableOptionWidth = Math.max(0, (containerWidth - 12) / 2);
71
+ const widestContentWidth = Math.max(...contentWidths);
72
+ const contentBasedOptionWidth = widestContentWidth > 0 ? widestContentWidth + 24 * 2 : 0;
73
+ const optionWidth = fullWidth ? availableOptionWidth : contentBasedOptionWidth;
74
+ const indicatorTranslateX = progress.interpolate({
75
+ inputRange: [0, 1],
76
+ outputRange: [0, optionWidth + 4],
77
+ });
78
+
79
+ useApplyWebClassName(containerRef, resolvedClassName);
80
+
81
+ useEffect(() => {
82
+ const animation = Animated.timing(progress, {
83
+ toValue: selectedIndex,
84
+ duration: ANIMATION_DURATION,
85
+ easing: Easing.out(Easing.cubic),
86
+ useNativeDriver: true,
87
+ });
88
+
89
+ animation.start();
90
+ return () => animation.stop();
91
+ }, [progress, selectedIndex]);
92
+
93
+ const selectOption = (nextValue: string) => {
94
+ if (disabled || nextValue === selectedValue) return;
95
+ if (!isControlled) setUncontrolledValue(nextValue);
96
+ onValueChange?.(nextValue);
97
+ };
98
+
99
+ return (
100
+ <View
101
+ {...rest}
102
+ ref={setContainerRef}
103
+ onLayout={(event) => {
104
+ const nextWidth = event.nativeEvent.layout.width;
105
+ setContainerWidth((currentWidth) =>
106
+ currentWidth === nextWidth ? currentWidth : nextWidth,
107
+ );
108
+ onLayout?.(event);
109
+ }}
110
+ style={[
111
+ styles.base,
112
+ fullWidth ? styles.fullWidth : styles.contentWidth,
113
+ disabled && styles.disabled,
114
+ style,
115
+ ]}
116
+ accessibilityRole="radiogroup"
117
+ >
118
+ {optionWidth > 0 ? (
119
+ <Animated.View
120
+ pointerEvents="none"
121
+ style={[
122
+ styles.activeIndicator,
123
+ { width: optionWidth, transform: [{ translateX: indicatorTranslateX }] },
124
+ ]}
125
+ />
126
+ ) : null}
127
+ {options.map(({ value: optionValue, label, icon: Icon }, index) => {
128
+ const selected = optionValue === selectedValue;
129
+ const iconColor = selected ? colors.primary : colors.whiteAlpha20;
130
+ const labelColor = selected ? colors.whiteAlpha86 : colors.whiteAlpha40;
131
+
132
+ return (
133
+ <Pressable
134
+ key={optionValue}
135
+ onPress={() => selectOption(optionValue)}
136
+ disabled={disabled}
137
+ hitSlop={hitSlop}
138
+ accessibilityRole="radio"
139
+ accessibilityLabel={label}
140
+ accessibilityState={{ selected, disabled }}
141
+ style={[
142
+ styles.option,
143
+ fullWidth ? styles.optionFullWidth : optionWidth > 0 && { minWidth: optionWidth },
144
+ ]}
145
+ >
146
+ <View
147
+ onLayout={(event) => {
148
+ const nextWidth = event.nativeEvent.layout.width;
149
+ setContentWidths((currentWidths) => {
150
+ if (currentWidths[index] === nextWidth) return currentWidths;
151
+ const nextWidths: [number, number] = [currentWidths[0], currentWidths[1]];
152
+ nextWidths[index] = nextWidth;
153
+ return nextWidths;
154
+ });
155
+ }}
156
+ style={styles.optionContent}
157
+ >
158
+ {Icon ? <Icon size={ICON_SIZE} color={iconColor} /> : null}
159
+ <Text numberOfLines={1} style={[styles.label, { color: labelColor }]}>
160
+ {label}
161
+ </Text>
162
+ </View>
163
+ </Pressable>
164
+ );
165
+ })}
166
+ </View>
167
+ );
168
+ },
169
+ );
170
+
171
+ const styles = StyleSheet.create({
172
+ base: {
173
+ height: 44,
174
+ flexDirection: "row",
175
+ padding: 4,
176
+ gap: 4,
177
+ borderRadius: 60,
178
+ backgroundColor: colors.grey700,
179
+ overflow: "hidden",
180
+ },
181
+ fullWidth: {
182
+ width: "100%",
183
+ alignSelf: "stretch",
184
+ },
185
+ contentWidth: {
186
+ alignSelf: "flex-start",
187
+ },
188
+ disabled: {
189
+ opacity: 0.6,
190
+ },
191
+ option: {
192
+ zIndex: 1,
193
+ flexDirection: "row",
194
+ alignItems: "center",
195
+ justifyContent: "center",
196
+ paddingHorizontal: 24,
197
+ borderRadius: 60,
198
+ },
199
+ optionContent: {
200
+ maxWidth: "100%",
201
+ flexDirection: "row",
202
+ alignItems: "center",
203
+ gap: 12,
204
+ },
205
+ optionFullWidth: {
206
+ flex: 1,
207
+ minWidth: 0,
208
+ },
209
+ activeIndicator: {
210
+ position: "absolute",
211
+ top: 4,
212
+ left: 4,
213
+ height: 36,
214
+ backgroundColor: colors.grey600,
215
+ borderRadius: 60,
216
+ },
217
+ label: {
218
+ ...segmentedToggleTypography,
219
+ flexShrink: 1,
220
+ },
221
+ });
@@ -0,0 +1,2 @@
1
+ export { SegmentedToggle } from "./SegmentedToggle";
2
+ export type { SegmentedToggleOption, SegmentedToggleProps } from "./SegmentedToggle";
@@ -0,0 +1,20 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { UsersAltIcon } from "../../icons";
3
+ import { StatCard } from "./StatCard";
4
+
5
+ const meta = {
6
+ title: "Components/StatCard",
7
+ component: StatCard,
8
+ args: {
9
+ label: "Label",
10
+ icon: UsersAltIcon,
11
+ },
12
+ parameters: {
13
+ backgrounds: { default: "black" },
14
+ },
15
+ } satisfies Meta<typeof StatCard>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Default: Story = {};
@@ -0,0 +1,61 @@
1
+ import { forwardRef, useMemo, useRef, type ComponentRef } from "react";
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ type StyleProp,
7
+ type ViewProps,
8
+ type ViewStyle,
9
+ } from "react-native";
10
+ import type { IconComponent } from "../../icons";
11
+ import { colors, statCardTypography } from "../../theme";
12
+ import { mergeRefs } from "../../utils/mergeRefs";
13
+ import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
14
+
15
+ export interface StatCardProps extends Omit<ViewProps, "style" | "children"> {
16
+ label: string;
17
+ icon: IconComponent;
18
+ style?: StyleProp<ViewStyle>;
19
+ className?: string;
20
+ }
21
+
22
+ const ICON_SIZE = 28;
23
+
24
+ export const StatCard = forwardRef<ComponentRef<typeof View>, StatCardProps>(function StatCard(
25
+ { label, icon: Icon, style, className, accessibilityLabel, ...rest },
26
+ forwardedRef,
27
+ ) {
28
+ const containerRef = useRef<ComponentRef<typeof View>>(null);
29
+ const resolvedClassName = className?.trim() || undefined;
30
+ const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
31
+
32
+ useApplyWebClassName(containerRef, resolvedClassName);
33
+
34
+ return (
35
+ <View
36
+ {...rest}
37
+ ref={setContainerRef}
38
+ style={[styles.base, style]}
39
+ accessibilityRole="text"
40
+ accessibilityLabel={accessibilityLabel ?? label}
41
+ >
42
+ <Icon size={ICON_SIZE} color={colors.white} />
43
+ <Text style={styles.label}>{label}</Text>
44
+ </View>
45
+ );
46
+ });
47
+
48
+ const styles = StyleSheet.create({
49
+ base: {
50
+ width: 204.5,
51
+ minHeight: 114,
52
+ padding: 24,
53
+ gap: 16,
54
+ borderRadius: 20,
55
+ backgroundColor: colors.grey750,
56
+ },
57
+ label: {
58
+ ...statCardTypography,
59
+ color: colors.whiteAlpha86,
60
+ },
61
+ });
@@ -0,0 +1,2 @@
1
+ export { StatCard } from "./StatCard";
2
+ export type { StatCardProps } from "./StatCard";
@@ -1,12 +1,32 @@
1
1
  export { Avatar } from "./Avatar";
2
2
  export type { AvatarProps, AvatarMenuItem } from "./Avatar";
3
3
 
4
+ export { Badge } from "./Badge";
5
+ export type { BadgeProps, BadgeVariant } from "./Badge";
6
+
7
+ export { Counter } from "./Counter";
8
+ export type { CounterProps } from "./Counter";
9
+
10
+ export { SegmentedToggle } from "./SegmentedToggle";
11
+ export type { SegmentedToggleOption, SegmentedToggleProps } from "./SegmentedToggle";
12
+
13
+ export { StatCard } from "./StatCard";
14
+ export type { StatCardProps } from "./StatCard";
15
+
4
16
  export { Button } from "./Button";
5
17
  export type { ButtonProps, ButtonSize, ButtonVariant } from "./Button";
6
18
 
7
19
  export { Checkbox } from "./Checkbox";
8
20
  export type { CheckboxProps, CheckboxVariant } from "./Checkbox";
9
21
 
22
+ export { DatePicker } from "./DatePicker";
23
+ export type {
24
+ DatePickerProps,
25
+ DatePickerSingleProps,
26
+ DatePickerRangeProps,
27
+ DateRange,
28
+ } from "./DatePicker";
29
+
10
30
  export { Input } from "./Input";
11
31
  export type { InputProps, InputSize } from "./Input";
12
32
 
@@ -0,0 +1,25 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { CALENDAR_OUTLINE_PATH } from "./calendarOutlinePath";
3
+ import { colors } from "../../theme";
4
+ import type { IconProps } from "../types";
5
+
6
+ export { CALENDAR_OUTLINE_PATH } from "./calendarOutlinePath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function CalendarOutlineIcon({
10
+ size = 20,
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={CALENDAR_OUTLINE_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </Svg>
24
+ );
25
+ }
@@ -0,0 +1,31 @@
1
+ import { CALENDAR_OUTLINE_PATH } from "./calendarOutlinePath";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function CalendarOutlineIcon({
7
+ size = 20,
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={CALENDAR_OUTLINE_PATH}
22
+ stroke={color}
23
+ strokeWidth={strokeWidth}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ />
27
+ </svg>
28
+ );
29
+ }
30
+
31
+ export { CALENDAR_OUTLINE_PATH } from "./calendarOutlinePath";
@@ -0,0 +1,3 @@
1
+ /** Plain calendar icon path (no checkmark badge) — 16×16 viewBox. */
2
+ export const CALENDAR_OUTLINE_PATH =
3
+ "M10.6667 1.33334V4.00001M5.33333 1.33334V4.00001M2 6.66668H14M5.2 2.66668H10.8C11.9201 2.66668 12.4802 2.66668 12.908 2.88466C13.2843 3.07641 13.5903 3.38237 13.782 3.7587C14 4.18652 14 4.74657 14 5.86668V11.4667C14 12.5868 14 13.1468 13.782 13.5747C13.5903 13.951 13.2843 14.2569 12.908 14.4487C12.4802 14.6667 11.9201 14.6667 10.8 14.6667H5.2C4.0799 14.6667 3.51984 14.6667 3.09202 14.4487C2.71569 14.2569 2.40973 13.951 2.21799 13.5747C2 13.1468 2 12.5868 2 11.4667V5.86668C2 4.74657 2 4.18652 2.21799 3.7587C2.40973 3.38237 2.71569 3.07641 3.09202 2.88466C3.51984 2.66668 4.0799 2.66668 5.2 2.66668Z";
@@ -0,0 +1 @@
1
+ export { CalendarOutlineIcon, CALENDAR_OUTLINE_PATH } from "./CalendarOutlineIcon";
@@ -0,0 +1,21 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { CHEVRON_LEFT_PATH } from "./chevronLeftPath";
3
+ import { colors } from "../../theme";
4
+ import type { IconProps } from "../types";
5
+
6
+ export { CHEVRON_LEFT_PATH } from "./chevronLeftPath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function ChevronLeftIcon({ size = 20, color = colors.primary, strokeWidth = 2 }: IconProps) {
10
+ return (
11
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
12
+ <Path
13
+ d={CHEVRON_LEFT_PATH}
14
+ stroke={color}
15
+ strokeWidth={strokeWidth}
16
+ strokeLinecap="round"
17
+ strokeLinejoin="round"
18
+ />
19
+ </Svg>
20
+ );
21
+ }
@@ -0,0 +1,27 @@
1
+ import { CHEVRON_LEFT_PATH } from "./chevronLeftPath";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function ChevronLeftIcon({ size = 20, color = colors.primary, strokeWidth = 2 }: IconProps) {
7
+ return (
8
+ <svg
9
+ width={size}
10
+ height={size}
11
+ viewBox="0 0 20 20"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ aria-hidden
15
+ >
16
+ <path
17
+ d={CHEVRON_LEFT_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ export { CHEVRON_LEFT_PATH } from "./chevronLeftPath";
@@ -0,0 +1,2 @@
1
+ /** Chevron left path — 20×20 viewBox. */
2
+ export const CHEVRON_LEFT_PATH = "M12.5 15L7.5 10L12.5 5";
@@ -0,0 +1 @@
1
+ export { ChevronLeftIcon, CHEVRON_LEFT_PATH } from "./ChevronLeftIcon";
@@ -0,0 +1,20 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { MINUS_PATH } from "./minusPath";
5
+
6
+ export { MINUS_PATH } from "./minusPath";
7
+
8
+ export function MinusIcon({ size = 16, color = colors.white, strokeWidth = 2 }: IconProps) {
9
+ return (
10
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
11
+ <Path
12
+ d={MINUS_PATH}
13
+ stroke={color}
14
+ strokeWidth={strokeWidth}
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ />
18
+ </Svg>
19
+ );
20
+ }
@@ -0,0 +1,19 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { MINUS_PATH } from "./minusPath";
4
+
5
+ export { MINUS_PATH } from "./minusPath";
6
+
7
+ export function MinusIcon({ size = 16, color = colors.white, strokeWidth = 2 }: IconProps) {
8
+ return (
9
+ <svg width={size} height={size} viewBox="0 0 16 16" fill="none" aria-hidden>
10
+ <path
11
+ d={MINUS_PATH}
12
+ stroke={color}
13
+ strokeWidth={strokeWidth}
14
+ strokeLinecap="round"
15
+ strokeLinejoin="round"
16
+ />
17
+ </svg>
18
+ );
19
+ }
@@ -0,0 +1 @@
1
+ export { MinusIcon, MINUS_PATH } from "./MinusIcon";
@@ -0,0 +1 @@
1
+ export const MINUS_PATH = "M3.33337 8H12.6667";
@@ -0,0 +1,20 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { PLUS_PATH } from "./plusPath";
5
+
6
+ export { PLUS_PATH } from "./plusPath";
7
+
8
+ export function PlusIcon({ size = 16, color = colors.white, strokeWidth = 2 }: IconProps) {
9
+ return (
10
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
11
+ <Path
12
+ d={PLUS_PATH}
13
+ stroke={color}
14
+ strokeWidth={strokeWidth}
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ />
18
+ </Svg>
19
+ );
20
+ }
@@ -0,0 +1,19 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { PLUS_PATH } from "./plusPath";
4
+
5
+ export { PLUS_PATH } from "./plusPath";
6
+
7
+ export function PlusIcon({ size = 16, color = colors.white, strokeWidth = 2 }: IconProps) {
8
+ return (
9
+ <svg width={size} height={size} viewBox="0 0 16 16" fill="none" aria-hidden>
10
+ <path
11
+ d={PLUS_PATH}
12
+ stroke={color}
13
+ strokeWidth={strokeWidth}
14
+ strokeLinecap="round"
15
+ strokeLinejoin="round"
16
+ />
17
+ </svg>
18
+ );
19
+ }
@@ -0,0 +1 @@
1
+ export { PlusIcon, PLUS_PATH } from "./PlusIcon";
@@ -0,0 +1 @@
1
+ export const PLUS_PATH = "M8.00004 3.33325V12.6666M3.33337 7.99992H12.6667";
@@ -0,0 +1,21 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { SIDEBAR_PATH } from "./sidebarPath";
3
+ import { colors } from "../../theme";
4
+ import type { IconProps } from "../types";
5
+
6
+ export { SIDEBAR_PATH } from "./sidebarPath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function SidebarIcon({ size = 20, color = colors.primary, strokeWidth = 2 }: IconProps) {
10
+ return (
11
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
12
+ <Path
13
+ d={SIDEBAR_PATH}
14
+ stroke={color}
15
+ strokeWidth={strokeWidth}
16
+ strokeLinecap="round"
17
+ strokeLinejoin="round"
18
+ />
19
+ </Svg>
20
+ );
21
+ }
@@ -0,0 +1,27 @@
1
+ import { SIDEBAR_PATH } from "./sidebarPath";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function SidebarIcon({ size = 20, color = colors.primary, strokeWidth = 2 }: IconProps) {
7
+ return (
8
+ <svg
9
+ width={size}
10
+ height={size}
11
+ viewBox="0 0 20 20"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ aria-hidden
15
+ >
16
+ <path
17
+ d={SIDEBAR_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ export { SIDEBAR_PATH } from "./sidebarPath";
@@ -0,0 +1 @@
1
+ export { SidebarIcon, SIDEBAR_PATH } from "./SidebarIcon";
@@ -0,0 +1,3 @@
1
+ /** Sidebar icon path — 20×20 viewBox. */
2
+ export const SIDEBAR_PATH =
3
+ "M12.5 2.5V17.5M6.5 2.5H13.5C14.9001 2.5 15.6002 2.5 16.135 2.77248C16.6054 3.01217 16.9878 3.39462 17.2275 3.86502C17.5 4.3998 17.5 5.09987 17.5 6.5V13.5C17.5 14.9001 17.5 15.6002 17.2275 16.135C16.9878 16.6054 16.6054 16.9878 16.135 17.2275C15.6002 17.5 14.9001 17.5 13.5 17.5H6.5C5.09987 17.5 4.3998 17.5 3.86502 17.2275C3.39462 16.9878 3.01217 16.6054 2.77248 16.135C2.5 15.6002 2.5 14.9001 2.5 13.5V6.5C2.5 5.09987 2.5 4.3998 2.77248 3.86502C3.01217 3.39462 3.39462 3.01217 3.86502 2.77248C4.3998 2.5 5.09987 2.5 6.5 2.5Z";
@@ -0,0 +1,14 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { USERS_ALT_PATH } from "./usersAltPath";
5
+
6
+ export { USERS_ALT_PATH } from "./usersAltPath";
7
+
8
+ export function UsersAltIcon({ size = 28, color = colors.white }: IconProps) {
9
+ return (
10
+ <Svg width={size} height={size} viewBox="0 0 28 28" fill="none">
11
+ <Path d={USERS_ALT_PATH} fill={color} />
12
+ </Svg>
13
+ );
14
+ }
@@ -0,0 +1,13 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { USERS_ALT_PATH } from "./usersAltPath";
4
+
5
+ export { USERS_ALT_PATH } from "./usersAltPath";
6
+
7
+ export function UsersAltIcon({ size = 28, color = colors.white }: IconProps) {
8
+ return (
9
+ <svg width={size} height={size} viewBox="0 0 28 28" fill="none" aria-hidden>
10
+ <path d={USERS_ALT_PATH} fill={color} />
11
+ </svg>
12
+ );
13
+ }
@@ -0,0 +1 @@
1
+ export { UsersAltIcon, USERS_ALT_PATH } from "./UsersAltIcon";
@@ -0,0 +1,2 @@
1
+ export const USERS_ALT_PATH =
2
+ "M14.35 14.2566C14.9725 13.7177 15.4718 13.0513 15.814 12.3024C16.1562 11.5536 16.3333 10.7399 16.3333 9.91659C16.3333 8.36949 15.7187 6.88576 14.6248 5.7918C13.5308 4.69783 12.0471 4.08325 10.5 4.08325C8.95289 4.08325 7.46916 4.69783 6.3752 5.7918C5.28124 6.88576 4.66666 8.36949 4.66666 9.91659C4.66665 10.7399 4.84376 11.5536 5.18597 12.3024C5.52818 13.0513 6.02748 13.7177 6.64999 14.2566C5.01682 14.9961 3.6312 16.1904 2.6588 17.6966C1.6864 19.2027 1.16837 20.9571 1.16666 22.7499C1.16666 23.0593 1.28957 23.3561 1.50837 23.5749C1.72716 23.7937 2.0239 23.9166 2.33332 23.9166C2.64274 23.9166 2.93949 23.7937 3.15828 23.5749C3.37707 23.3561 3.49999 23.0593 3.49999 22.7499C3.49999 20.8934 4.23749 19.1129 5.55024 17.8002C6.863 16.4874 8.64347 15.7499 10.5 15.7499C12.3565 15.7499 14.137 16.4874 15.4497 17.8002C16.7625 19.1129 17.5 20.8934 17.5 22.7499C17.5 23.0593 17.6229 23.3561 17.8417 23.5749C18.0605 23.7937 18.3572 23.9166 18.6667 23.9166C18.9761 23.9166 19.2728 23.7937 19.4916 23.5749C19.7104 23.3561 19.8333 23.0593 19.8333 22.7499C19.8316 20.9571 19.3136 19.2027 18.3412 17.6966C17.3688 16.1904 15.9832 14.9961 14.35 14.2566ZM10.5 13.4166C9.80775 13.4166 9.13107 13.2113 8.55549 12.8267C7.97992 12.4421 7.53132 11.8955 7.26641 11.256C7.0015 10.6164 6.93219 9.9127 7.06724 9.23377C7.20229 8.55484 7.53563 7.9312 8.02512 7.44171C8.5146 6.95223 9.13824 6.61889 9.81717 6.48384C10.4961 6.34879 11.1998 6.4181 11.8394 6.68301C12.4789 6.94791 13.0255 7.39652 13.4101 7.97209C13.7947 8.54766 14 9.22435 14 9.91659C14 10.8448 13.6312 11.7351 12.9749 12.3915C12.3185 13.0478 11.4282 13.4166 10.5 13.4166ZM21.8633 13.7899C22.61 12.9491 23.0977 11.9105 23.2678 10.799C23.4378 9.68748 23.2831 8.55051 22.822 7.52492C22.361 6.49933 21.6134 5.62885 20.6692 5.01825C19.725 4.40764 18.6244 4.08295 17.5 4.08325C17.1906 4.08325 16.8938 4.20617 16.675 4.42496C16.4562 4.64375 16.3333 4.9405 16.3333 5.24992C16.3333 5.55934 16.4562 5.85608 16.675 6.07488C16.8938 6.29367 17.1906 6.41659 17.5 6.41659C18.4282 6.41659 19.3185 6.78533 19.9749 7.44171C20.6312 8.09809 21 8.98833 21 9.91659C20.9983 10.5294 20.8358 11.131 20.5287 11.6612C20.2216 12.1915 19.7807 12.6319 19.25 12.9383C19.077 13.038 18.9325 13.1805 18.8304 13.3521C18.7283 13.5237 18.6719 13.7186 18.6667 13.9183C18.6618 14.1163 18.7074 14.3123 18.7993 14.4879C18.8911 14.6634 19.0262 14.8127 19.1917 14.9216L19.6467 15.2249L19.7983 15.3066C21.2046 15.9736 22.391 17.0286 23.2178 18.3473C24.0446 19.666 24.4773 21.1935 24.465 22.7499C24.465 23.0593 24.5879 23.3561 24.8067 23.5749C25.0255 23.7937 25.3222 23.9166 25.6317 23.9166C25.9411 23.9166 26.2378 23.7937 26.4566 23.5749C26.6754 23.3561 26.7983 23.0593 26.7983 22.7499C26.8079 20.9596 26.3594 19.1965 25.4957 17.6283C24.632 16.0601 23.3816 14.7388 21.8633 13.7899Z";