@ecohouse/ui 0.1.43 → 0.1.44

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 (38) hide show
  1. package/dist/index.cjs +182 -26
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +21 -4
  4. package/dist/index.d.ts +21 -4
  5. package/dist/index.js +179 -28
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/Button/Button.stories.tsx +5 -0
  9. package/src/components/Button/Button.tsx +34 -5
  10. package/src/components/Input/Input.tsx +6 -0
  11. package/src/components/LanguageSwitcher/LanguageSwitcher.tsx +1 -1
  12. package/src/components/SegmentedToggle/SegmentedToggle.tsx +21 -1
  13. package/src/components/Select/Select.tsx +12 -3
  14. package/src/icons/Amenity/amenityPaths.ts +3 -10
  15. package/src/icons/Dram/DramIcon.tsx +14 -0
  16. package/src/icons/Dram/DramIcon.web.tsx +20 -0
  17. package/src/icons/Dram/dramPath.ts +2 -0
  18. package/src/icons/Dram/index.ts +1 -0
  19. package/src/icons/HouseRules/houseRulesPaths.ts +3 -3
  20. package/src/icons/NoPets/NoPetsIcon.tsx +23 -0
  21. package/src/icons/NoPets/NoPetsIcon.web.tsx +29 -0
  22. package/src/icons/NoPets/index.ts +1 -0
  23. package/src/icons/NoPets/noPetsPaths.ts +8 -0
  24. package/src/icons/StarOutline/StarOutlineIcon.tsx +24 -0
  25. package/src/icons/StarOutline/StarOutlineIcon.web.tsx +30 -0
  26. package/src/icons/StarOutline/index.ts +1 -0
  27. package/src/icons/StarOutline/starOutlinePath.ts +2 -0
  28. package/src/icons/Sun/SunIcon.tsx +20 -0
  29. package/src/icons/Sun/SunIcon.web.tsx +26 -0
  30. package/src/icons/Sun/index.ts +1 -0
  31. package/src/icons/Sun/sunPath.ts +2 -0
  32. package/src/icons/Washer/WasherIcon.tsx +25 -0
  33. package/src/icons/Washer/WasherIcon.web.tsx +31 -0
  34. package/src/icons/Washer/index.ts +1 -0
  35. package/src/icons/Washer/washerPaths.ts +28 -0
  36. package/src/icons/index.ts +5 -0
  37. package/src/icons/registry.ts +15 -0
  38. package/src/index.ts +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecohouse/ui",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Cross-platform presentation-only UI components for EcoHouse (React Native + React Native Web)",
@@ -10,6 +10,7 @@ const meta = {
10
10
  title: "Button",
11
11
  onPress: fn(),
12
12
  disabled: false,
13
+ loading: false,
13
14
  variant: "primary",
14
15
  size: "lg",
15
16
  showIcon: true,
@@ -105,6 +106,10 @@ export const Disabled: Story = {
105
106
  args: { disabled: true },
106
107
  };
107
108
 
109
+ export const Loading: Story = {
110
+ args: { loading: true },
111
+ };
112
+
108
113
  const storyStyles = StyleSheet.create({
109
114
  column: {
110
115
  gap: 12,
@@ -1,5 +1,6 @@
1
1
  import { forwardRef, useMemo, useRef, type ComponentRef, type ReactNode } from "react";
2
2
  import {
3
+ ActivityIndicator,
3
4
  Platform,
4
5
  StyleSheet,
5
6
  Text,
@@ -27,6 +28,8 @@ export interface ButtonProps extends Omit<
27
28
  children?: ReactNode;
28
29
  onPress: (event: GestureResponderEvent) => void;
29
30
  disabled?: boolean;
31
+ /** Replaces the visible content with a centered spinner and prevents interaction. */
32
+ loading?: boolean;
30
33
  variant?: ButtonVariant;
31
34
  size?: ButtonSize;
32
35
  showIcon?: boolean;
@@ -123,6 +126,7 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
123
126
  children,
124
127
  onPress,
125
128
  disabled = false,
129
+ loading = false,
126
130
  variant = "primary",
127
131
  size = "lg",
128
132
  showIcon = false,
@@ -146,6 +150,7 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
146
150
  const hasCustomChildren = typeof children !== "undefined";
147
151
  const customIcon = icon != null && typeof icon !== "boolean" ? icon : undefined;
148
152
  const hasIcon = showIcon || icon === true || customIcon != null;
153
+ const interactionDisabled = disabled || loading;
149
154
  const resolvedContainerClassName = className?.trim() || undefined;
150
155
  const resolvedAccessibilityLabel =
151
156
  accessibilityLabel ?? (typeof label === "string" ? label : undefined);
@@ -173,7 +178,7 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
173
178
  gap: hasIcon ? metrics.gap : 0,
174
179
  },
175
180
  hasIcon ? styles.contentWithIcon : styles.contentWithoutIcon,
176
- disabled && styles.disabled,
181
+ disabled && !loading && styles.disabled,
177
182
  style,
178
183
  ];
179
184
 
@@ -192,16 +197,20 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
192
197
  ref={setContainerRef}
193
198
  style={containerStyle}
194
199
  onPress={onPress}
195
- disabled={disabled}
200
+ disabled={interactionDisabled}
196
201
  activeOpacity={0.7}
197
202
  accessibilityRole="button"
198
203
  accessibilityLabel={resolvedAccessibilityLabel}
199
- accessibilityState={{ disabled }}
204
+ accessibilityState={{ busy: loading, disabled: interactionDisabled }}
200
205
  >
201
206
  {hasCustomChildren ? (
202
- children
207
+ loading ? (
208
+ <View style={styles.loadingContent}>{children}</View>
209
+ ) : (
210
+ children
211
+ )
203
212
  ) : (
204
- <Text ref={textRef} style={labelStyle}>
213
+ <Text ref={textRef} style={[labelStyle, loading && styles.loadingContent]}>
205
214
  {label}
206
215
  </Text>
207
216
  )}
@@ -216,11 +225,18 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
216
225
  borderRadius: metrics.iconContainerSize / 2,
217
226
  backgroundColor: iconBadgeBackgroundColor ?? preset.iconBadgeBackgroundColor,
218
227
  },
228
+ loading && styles.loadingContent,
219
229
  ]}
220
230
  >
221
231
  {iconNode}
222
232
  </View>
223
233
  ) : null}
234
+
235
+ {loading ? (
236
+ <View pointerEvents="none" style={styles.loadingIndicator}>
237
+ <ActivityIndicator color={preset.textColor} size="small" />
238
+ </View>
239
+ ) : null}
224
240
  </TouchableOpacity>
225
241
  );
226
242
  },
@@ -232,6 +248,7 @@ const styles = StyleSheet.create({
232
248
  alignItems: "center",
233
249
  alignSelf: "flex-start",
234
250
  borderWidth: 0,
251
+ position: "relative",
235
252
  },
236
253
  disabled: {
237
254
  opacity: 0.6,
@@ -254,4 +271,16 @@ const styles = StyleSheet.create({
254
271
  justifyContent: "center",
255
272
  flexShrink: 0,
256
273
  },
274
+ loadingContent: {
275
+ opacity: 0,
276
+ },
277
+ loadingIndicator: {
278
+ alignItems: "center",
279
+ bottom: 0,
280
+ justifyContent: "center",
281
+ left: 0,
282
+ position: "absolute",
283
+ right: 0,
284
+ top: 0,
285
+ },
257
286
  });
@@ -168,6 +168,8 @@ export const Input = forwardRef<ComponentRef<typeof TextInput>, InputProps>(func
168
168
  showRightIcon = false,
169
169
  onRightIconPress,
170
170
  rightIconAccessibilityLabel,
171
+ autoComplete = "off",
172
+ importantForAutofill,
171
173
  style,
172
174
  fieldStyle,
173
175
  inputStyle,
@@ -194,6 +196,8 @@ export const Input = forwardRef<ComponentRef<typeof TextInput>, InputProps>(func
194
196
  const visualState = resolveVisualState({ disabled, error, focused, hasValue });
195
197
  const chrome = chromeFor(visualState);
196
198
  const resolvedAccessibilityLabel = accessibilityLabel ?? (showLabel ? undefined : label);
199
+ const resolvedImportantForAutofill =
200
+ importantForAutofill ?? (autoComplete === "off" ? "no" : "auto");
197
201
 
198
202
  const hasLeftIcon = showLeftIcon || leftIcon != null;
199
203
  const hasRightIcon = showRightIcon || rightIcon != null;
@@ -256,6 +260,8 @@ export const Input = forwardRef<ComponentRef<typeof TextInput>, InputProps>(func
256
260
  ref={setInputRef}
257
261
  {...textInputProps}
258
262
  nativeID={inputDomId}
263
+ autoComplete={autoComplete}
264
+ importantForAutofill={resolvedImportantForAutofill}
259
265
  value={isControlled ? value : undefined}
260
266
  defaultValue={isControlled ? undefined : defaultValue}
261
267
  editable={!disabled}
@@ -58,7 +58,7 @@ export const defaultLanguageOptions = [
58
58
  { value: "ru", label: "Рус" },
59
59
  ] as const satisfies readonly LanguageOption[];
60
60
 
61
- const CONTROL_WIDTH = 90;
61
+ const CONTROL_WIDTH = 96;
62
62
  const CONTROL_HEIGHT = 35;
63
63
  const ICON_SIZE = 16;
64
64
  const ICON_BUTTON_SIZE = 32;
@@ -90,6 +90,7 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
90
90
  const resolvedClassName = className?.trim() || undefined;
91
91
  const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
92
92
  const progress = useRef(new Animated.Value(selectedIndex)).current;
93
+ const hasMounted = useRef(false);
93
94
  const [contentWidths, setContentWidths] = useState<number[]>(() =>
94
95
  Array.from({ length: optionCount }, () => 0),
95
96
  );
@@ -128,6 +129,12 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
128
129
  }, [optionCount]);
129
130
 
130
131
  useEffect(() => {
132
+ if (!hasMounted.current) {
133
+ hasMounted.current = true;
134
+ progress.setValue(selectedIndex);
135
+ return;
136
+ }
137
+
131
138
  const animation = Animated.timing(progress, {
132
139
  toValue: selectedIndex,
133
140
  duration: ANIMATION_DURATION,
@@ -202,7 +209,7 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
202
209
  {options.map(({ value: optionValue, label, icon: Icon, showLabel = true }, index) => {
203
210
  const selected = optionValue === selectedValue;
204
211
  const iconColor = selected ? colors.primary : colors.whiteAlpha20;
205
- const labelColor = selected ? colors.whiteAlpha86 : colors.whiteAlpha40;
212
+ const labelColor = selected ? colors.white : colors.grey150;
206
213
  const optionWidth = optionWidths[index] ?? 0;
207
214
 
208
215
  return (
@@ -219,6 +226,7 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
219
226
  fullWidth
220
227
  ? styles.optionFullWidth
221
228
  : [styles.optionContentWidth, optionWidth > 0 && { width: optionWidth }],
229
+ selected && selectedOptionWidth === 0 && styles.initialSelectedOption,
222
230
  ]}
223
231
  >
224
232
  <View style={styles.optionContent}>
@@ -228,6 +236,7 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
228
236
  numberOfLines={fullWidth ? 1 : undefined}
229
237
  style={[
230
238
  styles.label,
239
+ selected ? styles.selectedLabel : styles.inactiveLabel,
231
240
  !fullWidth && styles.contentWidthLabel,
232
241
  { color: labelColor },
233
242
  ]}
@@ -292,6 +301,9 @@ const styles = StyleSheet.create({
292
301
  optionContentWidth: {
293
302
  minWidth: OPTION_MIN_WIDTH,
294
303
  },
304
+ initialSelectedOption: {
305
+ backgroundColor: colors.grey600,
306
+ },
295
307
  activeIndicator: {
296
308
  position: "absolute",
297
309
  top: TRACK_PADDING,
@@ -304,6 +316,14 @@ const styles = StyleSheet.create({
304
316
  ...segmentedToggleTypography,
305
317
  flexShrink: 1,
306
318
  },
319
+ selectedLabel: {
320
+ fontWeight: "600",
321
+ letterSpacing: 0.36,
322
+ },
323
+ inactiveLabel: {
324
+ fontWeight: "400",
325
+ letterSpacing: 0.36,
326
+ },
307
327
  contentWidthLabel: {
308
328
  flexShrink: 0,
309
329
  },
@@ -347,15 +347,24 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
347
347
  return null;
348
348
  }, [menuStyle]);
349
349
 
350
+ const resolvedOptionListMaxHeight = useMemo(() => {
351
+ const flat = StyleSheet.flatten(menuStyle);
352
+ const menuMaxHeight = flat?.maxHeight;
353
+ if (typeof menuMaxHeight !== "number") return MENU_MAX_LIST_HEIGHT;
354
+
355
+ const menuChromeHeight = 24 + (showSearch ? 48 : 0);
356
+ return Math.max(43, menuMaxHeight - menuChromeHeight);
357
+ }, [menuStyle, showSearch]);
358
+
350
359
  const estimatedMenuHeight = useMemo(() => {
351
360
  const searchBlock = showSearch ? 40 + 8 : 0;
352
361
  const optionCount = Math.max(filteredOptions.length, 1);
353
362
  const listHeight = Math.min(
354
363
  optionCount * 43 + Math.max(0, optionCount - 1) * 8,
355
- MENU_MAX_LIST_HEIGHT,
364
+ resolvedOptionListMaxHeight,
356
365
  );
357
366
  return 24 + searchBlock + listHeight;
358
- }, [filteredOptions.length, showSearch]);
367
+ }, [filteredOptions.length, resolvedOptionListMaxHeight, showSearch]);
359
368
 
360
369
  const setOpen = useCallback(
361
370
  (next: boolean) => {
@@ -675,7 +684,7 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
675
684
  ) : null}
676
685
 
677
686
  <ScrollView
678
- style={styles.optionList}
687
+ style={[styles.optionList, { maxHeight: resolvedOptionListMaxHeight }]}
679
688
  contentContainerStyle={styles.optionListContent}
680
689
  keyboardShouldPersistTaps="handled"
681
690
  showsVerticalScrollIndicator={false}
@@ -1,3 +1,5 @@
1
+ import { WASHER_PATHS } from "../Washer/washerPaths";
2
+
1
3
  export const amenityNames = [
2
4
  "kitchen",
3
5
  "wifi",
@@ -46,16 +48,7 @@ export const AMENITY_PATHS: Record<AmenityName, readonly AmenityPathDefinition[]
46
48
  strokeWidth: 2,
47
49
  },
48
50
  ],
49
- washer: [
50
- {
51
- d: "M15.833 1.66699H4.16634C3.7061 1.66699 3.33301 2.04009 3.33301 2.50033V17.5003C3.33301 17.9606 3.7061 18.3337 4.16634 18.3337H15.833C16.2932 18.3337 16.6663 17.9606 16.6663 17.5003V2.50033C16.6663 2.04009 16.2932 1.66699 15.833 1.66699ZM3.33301 5.00033C3.33301 5.46058 3.7061 5.83366 4.16634 5.83366H15.833C16.2933 5.83366 16.6663 5.46058 16.6663 5.00033V2.50033C16.6663 2.04009 16.2933 1.66699 15.833 1.66699H4.16634C3.7061 1.66699 3.33301 2.04009 3.33301 2.50033V5.00033ZM9.99967 15.0003C11.6105 15.0003 12.9163 13.6945 12.9163 12.0837C12.9163 10.4728 11.6105 9.16699 9.99967 9.16699C8.38884 9.16699 7.08301 10.4728 7.08301 12.0837C7.08301 13.6945 8.38884 15.0003 9.99967 15.0003Z",
52
- strokeWidth: 1.5,
53
- },
54
- {
55
- d: "M5.83333 4.58317C6.29357 4.58317 6.66667 4.21007 6.66667 3.74984C6.66667 3.2896 6.29357 2.9165 5.83333 2.9165C5.3731 2.9165 5 3.2896 5 3.74984C5 4.21007 5.3731 4.58317 5.83333 4.58317ZM8.33333 4.58317C8.79357 4.58317 9.16667 4.21007 9.16667 3.74984C9.16667 3.2896 8.79357 2.9165 8.33333 2.9165C7.8731 2.9165 7.5 3.2896 7.5 3.74984C7.5 4.21007 7.8731 4.58317 8.33333 4.58317Z",
56
- fill: true,
57
- },
58
- ],
51
+ washer: WASHER_PATHS,
59
52
  ac: [
60
53
  {
61
54
  d: "M17.5003 3.33301H2.50033C2.04009 3.33301 1.66699 3.7061 1.66699 4.16634V10.833C1.66699 11.2932 2.04009 11.6663 2.50033 11.6663H17.5003C17.9606 11.6663 18.3337 11.2932 18.3337 10.833V4.16634C18.3337 3.7061 17.9606 3.33301 17.5003 3.33301ZM15 8.3335H5V11.6668H15V8.3335ZM13.333 5.83301H14.9997M10 14.1665V16.6665M6.66699 15V15.8333M13.333 15V15.8333",
@@ -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 { DRAM_PATH } from "./dramPath";
5
+
6
+ export function DramIcon({ size = 12, color = colors.grey0 }: IconProps) {
7
+ return (
8
+ <Svg width={(size * 11) / 12} height={size} viewBox="0 0 11 12" fill="none">
9
+ <Path d={DRAM_PATH} fill={color} />
10
+ </Svg>
11
+ );
12
+ }
13
+
14
+ export { DRAM_PATH } from "./dramPath";
@@ -0,0 +1,20 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { DRAM_PATH } from "./dramPath";
4
+
5
+ export function DramIcon({ size = 12, color = colors.grey0 }: IconProps) {
6
+ return (
7
+ <svg
8
+ width={(size * 11) / 12}
9
+ height={size}
10
+ viewBox="0 0 11 12"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ aria-hidden
14
+ >
15
+ <path d={DRAM_PATH} fill={color} />
16
+ </svg>
17
+ );
18
+ }
19
+
20
+ export { DRAM_PATH } from "./dramPath";
@@ -0,0 +1,2 @@
1
+ export const DRAM_PATH =
2
+ "M8.72034 11.584H6.78434V4.06398C6.78434 3.23198 6.58168 2.61332 6.17634 2.20798C5.77101 1.80265 5.16834 1.59998 4.36834 1.59998C3.81368 1.59998 3.35501 1.70665 2.99234 1.91998C2.64034 2.12265 2.37901 2.44265 2.20834 2.87998C2.03768 3.31732 1.95234 3.88265 1.95234 4.57598H0.0003438C0.0003438 3.63732 0.160344 2.82665 0.480344 2.14398C0.811011 1.45065 1.30168 0.922651 1.95234 0.559984C2.61368 0.186651 3.43501 -1.62125e-05 4.41634 -1.62125e-05C5.34434 -1.62125e-05 6.12834 0.17065 6.76834 0.511984C7.41901 0.842651 7.90434 1.32798 8.22434 1.96798C8.55501 2.59732 8.72034 3.34932 8.72034 4.22398V11.584ZM4.67234 4.81598H10.8323V6.03198H4.67234V4.81598ZM4.67234 7.08798H10.8323V8.31998H4.67234V7.08798Z";
@@ -0,0 +1 @@
1
+ export { DramIcon, DRAM_PATH } from "./DramIcon";
@@ -4,10 +4,10 @@ export const QUIET_HOURS_PATH =
4
4
 
5
5
  /** No-smoking — stroke paths, 20×20 viewBox. */
6
6
  export const NO_SMOKING_PATHS = [
7
- "M6.66602 10.8334V14.1667",
8
- "M13.334 4.16663V4.58329C13.334 5.02532 13.5096 5.44924 13.8221 5.7618C14.1347 6.07436 14.5586 6.24996 15.0007 6.24996C15.4427 6.24996 15.8666 6.42555 16.1792 6.73811C16.4917 7.05068 16.6673 7.4746 16.6673 7.91663V8.33329",
7
+ "M6.66797 10.834V14.1673",
8
+ "M13.332 4.16602V4.58268C13.332 5.02471 13.5076 5.44863 13.8202 5.76119C14.1327 6.07375 14.5567 6.24935 14.9987 6.24935C15.4407 6.24935 15.8646 6.42494 16.1772 6.7375C16.4898 7.05007 16.6654 7.47399 16.6654 7.91602V8.33268",
9
9
  "M2.5 2.5L17.5 17.5",
10
- "M14.1667 10.8334H16.6667C16.8877 10.8334 17.0996 10.9212 17.2559 11.0775C17.4122 11.2337 17.5 11.4457 17.5 11.6667V13.3334C17.5 13.5667 17.4042 13.7775 17.25 13.9284M14.1667 14.1667H3.33333C3.11232 14.1667 2.90036 14.0789 2.74408 13.9226C2.5878 13.7663 2.5 13.5544 2.5 13.3334V11.6667C2.5 11.4457 2.5878 11.2337 2.74408 11.0775C2.90036 10.9212 3.11232 10.8334 3.33333 10.8334H10.8333",
10
+ "M14.1667 10.834H16.6667C16.8877 10.834 17.0996 10.9218 17.2559 11.0781C17.4122 11.2343 17.5 11.4463 17.5 11.6673V13.334C17.5 13.5673 17.4042 13.7782 17.25 13.929M14.1667 14.1673H3.33333C3.11232 14.1673 2.90036 14.0795 2.74408 13.9232C2.5878 13.767 2.5 13.555 2.5 13.334V11.6673C2.5 11.4463 2.5878 11.2343 2.74408 11.0781C2.90036 10.9218 3.11232 10.834 3.33333 10.834H10.8333",
11
11
  ] as const;
12
12
 
13
13
  /** Paw / pets — stroke paths, 20×20 viewBox. */
@@ -0,0 +1,23 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { NO_PETS_PATHS } from "./noPetsPaths";
5
+
6
+ export function NoPetsIcon({ size = 20, color = colors.white, strokeWidth = 1.5 }: IconProps) {
7
+ return (
8
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
9
+ {NO_PETS_PATHS.map((path, index) => (
10
+ <Path
11
+ key={`no-pets-${index}`}
12
+ d={path}
13
+ stroke={color}
14
+ strokeWidth={strokeWidth}
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ />
18
+ ))}
19
+ </Svg>
20
+ );
21
+ }
22
+
23
+ export { NO_PETS_PATHS } from "./noPetsPaths";
@@ -0,0 +1,29 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { NO_PETS_PATHS } from "./noPetsPaths";
4
+
5
+ export function NoPetsIcon({ size = 20, color = colors.white, strokeWidth = 1.5 }: IconProps) {
6
+ return (
7
+ <svg
8
+ width={size}
9
+ height={size}
10
+ viewBox="0 0 20 20"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ aria-hidden
14
+ >
15
+ {NO_PETS_PATHS.map((path, index) => (
16
+ <path
17
+ key={`no-pets-${index}`}
18
+ d={path}
19
+ stroke={color}
20
+ strokeWidth={strokeWidth}
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ />
24
+ ))}
25
+ </svg>
26
+ );
27
+ }
28
+
29
+ export { NO_PETS_PATHS } from "./noPetsPaths";
@@ -0,0 +1 @@
1
+ export { NoPetsIcon, NO_PETS_PATHS } from "./NoPetsIcon";
@@ -0,0 +1,8 @@
1
+ export const NO_PETS_PATHS = [
2
+ "M9.30729 9.29492C8.71562 9.55326 8.32062 10.2174 7.64062 11.4558C6.85562 12.8749 5.26896 12.9933 4.87312 14.1983C4.79229 14.4191 4.75229 14.7624 4.75396 14.9999C4.75396 15.9799 5.40979 16.6666 6.25396 16.6666C7.30312 16.6666 8.75396 15.8333 10.004 15.8333C11.254 15.8333 12.7048 16.6666 13.754 16.6666C14.5265 16.6666 15.1406 16.0924 15.2398 15.2433",
3
+ "M16.8222 6.73435C16.7151 6.6891 16.6001 6.66586 16.4838 6.66602H16.4713C15.8588 6.67602 15.1713 7.29102 14.8105 8.22102C14.378 9.33352 14.5772 10.471 15.2588 10.7643C15.3663 10.8102 15.4813 10.8327 15.5972 10.8327C16.213 10.8327 16.9097 10.2143 17.273 9.27768C17.703 8.16518 17.4997 7.02768 16.8222 6.73435Z",
4
+ "M9.16562 5.82732C9.17486 5.62532 9.16369 5.42291 9.13229 5.22315C8.96312 4.14232 8.25979 3.33398 7.52229 3.33398C7.29304 3.33531 7.07077 3.41301 6.89062 3.55482",
5
+ "M13.7119 5.61149C13.8903 4.46482 13.3994 3.44982 12.6119 3.34315C12.5672 3.33696 12.5221 3.3339 12.4769 3.33399C11.7394 3.33399 11.0369 4.14232 10.8686 5.22315C10.6903 6.36982 11.1811 7.38482 11.9686 7.49149C12.0136 7.49732 12.0586 7.50065 12.1036 7.50065C12.8411 7.50065 13.5453 6.68899 13.7119 5.61149Z",
6
+ "M4.74152 10.7643C5.42152 10.471 5.61985 9.33185 5.18819 8.22102C4.82485 7.28435 4.12902 6.66602 3.51402 6.66602C3.39735 6.66602 3.28319 6.68852 3.17485 6.73435C2.49485 7.02768 2.29652 8.16685 2.72819 9.27768C3.09152 10.2143 3.78735 10.8327 4.40235 10.8327C4.51902 10.8327 4.63319 10.8102 4.74152 10.7643Z",
7
+ "M2.5 2.5L17.5 17.5",
8
+ ] as const;
@@ -0,0 +1,24 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { STAR_OUTLINE_PATH } from "./starOutlinePath";
5
+
6
+ export function StarOutlineIcon({
7
+ size = 24,
8
+ color = colors.grey200,
9
+ strokeWidth = 1.35,
10
+ }: IconProps) {
11
+ return (
12
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
13
+ <Path
14
+ d={STAR_OUTLINE_PATH}
15
+ stroke={color}
16
+ strokeWidth={strokeWidth}
17
+ strokeLinecap="round"
18
+ strokeLinejoin="round"
19
+ />
20
+ </Svg>
21
+ );
22
+ }
23
+
24
+ export { STAR_OUTLINE_PATH } from "./starOutlinePath";
@@ -0,0 +1,30 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { STAR_OUTLINE_PATH } from "./starOutlinePath";
4
+
5
+ export function StarOutlineIcon({
6
+ size = 24,
7
+ color = colors.grey200,
8
+ strokeWidth = 1.35,
9
+ }: IconProps) {
10
+ return (
11
+ <svg
12
+ width={size}
13
+ height={size}
14
+ viewBox="0 0 24 24"
15
+ fill="none"
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ aria-hidden
18
+ >
19
+ <path
20
+ d={STAR_OUTLINE_PATH}
21
+ stroke={color}
22
+ strokeWidth={strokeWidth}
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ />
26
+ </svg>
27
+ );
28
+ }
29
+
30
+ export { STAR_OUTLINE_PATH } from "./starOutlinePath";
@@ -0,0 +1 @@
1
+ export { StarOutlineIcon, STAR_OUTLINE_PATH } from "./StarOutlineIcon";
@@ -0,0 +1,2 @@
1
+ export const STAR_OUTLINE_PATH =
2
+ "M12.0002 3.67578L14.546 8.83423L20.2387 9.66143L16.1195 13.6767L17.092 19.3463L12.0002 16.6696L6.90856 19.3463L7.88098 13.6767L3.76172 9.66143L9.45441 8.83423L12.0002 3.67578Z";
@@ -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 { SUN_PATH } from "./sunPath";
5
+
6
+ export function SunIcon({ size = 16, color = colors.primary, strokeWidth = 1.5 }: IconProps) {
7
+ return (
8
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
9
+ <Path
10
+ d={SUN_PATH}
11
+ stroke={color}
12
+ strokeWidth={strokeWidth}
13
+ strokeLinecap="round"
14
+ strokeLinejoin="round"
15
+ />
16
+ </Svg>
17
+ );
18
+ }
19
+
20
+ export { SUN_PATH } from "./sunPath";
@@ -0,0 +1,26 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { SUN_PATH } from "./sunPath";
4
+
5
+ export function SunIcon({ size = 16, color = colors.primary, strokeWidth = 1.5 }: IconProps) {
6
+ return (
7
+ <svg
8
+ width={size}
9
+ height={size}
10
+ viewBox="0 0 16 16"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ aria-hidden
14
+ >
15
+ <path
16
+ d={SUN_PATH}
17
+ stroke={color}
18
+ strokeWidth={strokeWidth}
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ />
22
+ </svg>
23
+ );
24
+ }
25
+
26
+ export { SUN_PATH } from "./sunPath";
@@ -0,0 +1 @@
1
+ export { SunIcon, SUN_PATH } from "./SunIcon";
@@ -0,0 +1,2 @@
1
+ export const SUN_PATH =
2
+ "M8 1.33333V2.66667M8 13.3333V14.6667M1.33333 8H2.66667M13.3333 8H14.6667M3.28667 3.28667L4.22667 4.22667M11.7733 11.7733L12.7133 12.7133M12.7133 3.28667L11.7733 4.22667M4.22667 11.7733L3.28667 12.7133M10.6667 8C10.6667 9.47276 9.47276 10.6667 8 10.6667C6.52724 10.6667 5.33333 9.47276 5.33333 8C5.33333 6.52724 6.52724 5.33333 8 5.33333C9.47276 5.33333 10.6667 6.52724 10.6667 8Z";
@@ -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 { WASHER_PATHS } from "./washerPaths";
5
+
6
+ export function WasherIcon({ size = 20, color = colors.white, strokeWidth = 1.5 }: IconProps) {
7
+ return (
8
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
9
+ {WASHER_PATHS.map((path, index) => {
10
+ const filled = path.fill === true;
11
+ return (
12
+ <Path
13
+ key={`washer-${index}`}
14
+ d={path.d}
15
+ fill={filled ? color : "none"}
16
+ stroke={filled ? undefined : color}
17
+ strokeWidth={filled ? undefined : strokeWidth}
18
+ />
19
+ );
20
+ })}
21
+ </Svg>
22
+ );
23
+ }
24
+
25
+ export { WASHER_PATHS } from "./washerPaths";
@@ -0,0 +1,31 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { WASHER_PATHS } from "./washerPaths";
4
+
5
+ export function WasherIcon({ size = 20, color = colors.white, strokeWidth = 1.5 }: IconProps) {
6
+ return (
7
+ <svg
8
+ width={size}
9
+ height={size}
10
+ viewBox="0 0 20 20"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ aria-hidden
14
+ >
15
+ {WASHER_PATHS.map((path, index) => {
16
+ const filled = path.fill === true;
17
+ return (
18
+ <path
19
+ key={`washer-${index}`}
20
+ d={path.d}
21
+ fill={filled ? color : "none"}
22
+ stroke={filled ? undefined : color}
23
+ strokeWidth={filled ? undefined : strokeWidth}
24
+ />
25
+ );
26
+ })}
27
+ </svg>
28
+ );
29
+ }
30
+
31
+ export { WASHER_PATHS } from "./washerPaths";
@@ -0,0 +1 @@
1
+ export { WasherIcon, WASHER_PATHS } from "./WasherIcon";
@@ -0,0 +1,28 @@
1
+ export interface WasherPathDefinition {
2
+ d: string;
3
+ fill?: boolean;
4
+ strokeWidth?: number;
5
+ }
6
+
7
+ export const WASHER_PATHS: readonly WasherPathDefinition[] = [
8
+ {
9
+ d: "M15.832 1.66602H4.16536C3.70513 1.66602 3.33203 2.03911 3.33203 2.49935V17.4993C3.33203 17.9596 3.70513 18.3327 4.16536 18.3327H15.832C16.2923 18.3327 16.6654 17.9596 16.6654 17.4993V2.49935C16.6654 2.03911 16.2923 1.66602 15.832 1.66602Z",
10
+ strokeWidth: 1.5,
11
+ },
12
+ {
13
+ d: "M3.33203 4.99935C3.33203 5.4596 3.70513 5.83268 4.16536 5.83268H15.832C16.2923 5.83268 16.6654 5.4596 16.6654 4.99935V2.49935C16.6654 2.03911 16.2923 1.66602 15.832 1.66602H4.16536C3.70513 1.66602 3.33203 2.03911 3.33203 2.49935V4.99935Z",
14
+ strokeWidth: 1.5,
15
+ },
16
+ {
17
+ d: "M5.83333 4.58268C6.29357 4.58268 6.66667 4.20959 6.66667 3.74935C6.66667 3.28911 6.29357 2.91602 5.83333 2.91602C5.3731 2.91602 5 3.28911 5 3.74935C5 4.20959 5.3731 4.58268 5.83333 4.58268Z",
18
+ fill: true,
19
+ },
20
+ {
21
+ d: "M8.33333 4.58268C8.79357 4.58268 9.16667 4.20959 9.16667 3.74935C9.16667 3.28911 8.79357 2.91602 8.33333 2.91602C7.8731 2.91602 7.5 3.28911 7.5 3.74935C7.5 4.20959 7.8731 4.58268 8.33333 4.58268Z",
22
+ fill: true,
23
+ },
24
+ {
25
+ d: "M9.9987 14.9993C11.6095 14.9993 12.9154 13.6935 12.9154 12.0827C12.9154 10.4719 11.6095 9.16602 9.9987 9.16602C8.38787 9.16602 7.08203 10.4719 7.08203 12.0827C7.08203 13.6935 8.38787 14.9993 9.9987 14.9993Z",
26
+ strokeWidth: 1.5,
27
+ },
28
+ ];