@ecohouse/ui 0.1.42 → 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.
- package/dist/index.cjs +312 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -4
- package/dist/index.d.ts +40 -4
- package/dist/index.js +307 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Button/Button.stories.tsx +5 -0
- package/src/components/Button/Button.tsx +34 -5
- package/src/components/Input/Input.tsx +6 -0
- package/src/components/LanguageSwitcher/LanguageSwitcher.tsx +1 -1
- package/src/components/RadioButton/RadioButton.stories.tsx +64 -0
- package/src/components/RadioButton/RadioButton.tsx +135 -0
- package/src/components/RadioButton/index.ts +2 -0
- package/src/components/SegmentedToggle/SegmentedToggle.tsx +21 -1
- package/src/components/Select/Select.tsx +12 -3
- package/src/components/index.ts +3 -0
- package/src/icons/Amenity/amenityPaths.ts +3 -10
- package/src/icons/Dram/DramIcon.tsx +14 -0
- package/src/icons/Dram/DramIcon.web.tsx +20 -0
- package/src/icons/Dram/dramPath.ts +2 -0
- package/src/icons/Dram/index.ts +1 -0
- package/src/icons/HouseRules/houseRulesPaths.ts +3 -3
- package/src/icons/MoonStars/MoonStarsIcon.tsx +27 -0
- package/src/icons/MoonStars/MoonStarsIcon.web.tsx +33 -0
- package/src/icons/MoonStars/index.ts +6 -0
- package/src/icons/MoonStars/moonStarsPaths.ts +7 -0
- package/src/icons/NoPets/NoPetsIcon.tsx +23 -0
- package/src/icons/NoPets/NoPetsIcon.web.tsx +29 -0
- package/src/icons/NoPets/index.ts +1 -0
- package/src/icons/NoPets/noPetsPaths.ts +8 -0
- package/src/icons/StarOutline/StarOutlineIcon.tsx +24 -0
- package/src/icons/StarOutline/StarOutlineIcon.web.tsx +30 -0
- package/src/icons/StarOutline/index.ts +1 -0
- package/src/icons/StarOutline/starOutlinePath.ts +2 -0
- package/src/icons/Sun/SunIcon.tsx +20 -0
- package/src/icons/Sun/SunIcon.web.tsx +26 -0
- package/src/icons/Sun/index.ts +1 -0
- package/src/icons/Sun/sunPath.ts +2 -0
- package/src/icons/Washer/WasherIcon.tsx +25 -0
- package/src/icons/Washer/WasherIcon.web.tsx +31 -0
- package/src/icons/Washer/index.ts +1 -0
- package/src/icons/Washer/washerPaths.ts +28 -0
- package/src/icons/index.ts +6 -0
- package/src/icons/registry.ts +18 -0
- package/src/index.ts +9 -0
package/package.json
CHANGED
|
@@ -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={
|
|
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
|
-
|
|
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 =
|
|
61
|
+
const CONTROL_WIDTH = 96;
|
|
62
62
|
const CONTROL_HEIGHT = 35;
|
|
63
63
|
const ICON_SIZE = 16;
|
|
64
64
|
const ICON_BUTTON_SIZE = 32;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
3
|
+
import { fn } from "storybook/test";
|
|
4
|
+
import { StyleSheet, Text, View } from "react-native";
|
|
5
|
+
import { colors } from "../../theme";
|
|
6
|
+
import { RadioButton } from "./RadioButton";
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Components/RadioButton",
|
|
10
|
+
component: RadioButton,
|
|
11
|
+
args: {
|
|
12
|
+
defaultValue: false,
|
|
13
|
+
disabled: false,
|
|
14
|
+
variant: "default",
|
|
15
|
+
onValueChange: fn(),
|
|
16
|
+
},
|
|
17
|
+
parameters: {
|
|
18
|
+
backgrounds: { default: "black" },
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof RadioButton>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {};
|
|
26
|
+
|
|
27
|
+
export const Active: Story = {
|
|
28
|
+
args: { defaultValue: true },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Disabled: Story = {
|
|
32
|
+
args: { disabled: true },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithLabel: Story = {
|
|
36
|
+
args: { defaultValue: true, label: "Option" },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function ControlledGroup() {
|
|
40
|
+
const [value, setValue] = useState("first");
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View style={styles.group} accessibilityRole="radiogroup">
|
|
44
|
+
{["first", "second", "third"].map((option) => (
|
|
45
|
+
<RadioButton key={option} value={value === option} onValueChange={() => setValue(option)}>
|
|
46
|
+
<Text style={styles.label}>{option}</Text>
|
|
47
|
+
</RadioButton>
|
|
48
|
+
))}
|
|
49
|
+
</View>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const Group: Story = {
|
|
54
|
+
render: () => <ControlledGroup />,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const styles = StyleSheet.create({
|
|
58
|
+
group: {
|
|
59
|
+
gap: 12,
|
|
60
|
+
},
|
|
61
|
+
label: {
|
|
62
|
+
color: colors.white,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { forwardRef, useMemo, useRef, useState, type ComponentRef, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
Text,
|
|
6
|
+
View,
|
|
7
|
+
type PressableProps,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from "react-native";
|
|
12
|
+
import { colors, fonts } from "../../theme";
|
|
13
|
+
import { mergeRefs } from "../../utils/mergeRefs";
|
|
14
|
+
import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
|
|
15
|
+
|
|
16
|
+
export type RadioButtonVariant = "default";
|
|
17
|
+
|
|
18
|
+
export interface RadioButtonProps extends Omit<
|
|
19
|
+
PressableProps,
|
|
20
|
+
"onPress" | "disabled" | "style" | "children" | "hitSlop"
|
|
21
|
+
> {
|
|
22
|
+
value?: boolean;
|
|
23
|
+
defaultValue?: boolean;
|
|
24
|
+
onValueChange?: (value: true) => void;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
variant?: RadioButtonVariant;
|
|
27
|
+
label?: string;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
30
|
+
labelStyle?: StyleProp<TextStyle>;
|
|
31
|
+
className?: string;
|
|
32
|
+
hitSlop?: PressableProps["hitSlop"];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const CONTROL_SIZE = 16;
|
|
36
|
+
const DOT_SIZE = 8;
|
|
37
|
+
|
|
38
|
+
export const RadioButton = forwardRef<ComponentRef<typeof Pressable>, RadioButtonProps>(
|
|
39
|
+
function RadioButton(
|
|
40
|
+
{
|
|
41
|
+
value,
|
|
42
|
+
defaultValue = false,
|
|
43
|
+
onValueChange,
|
|
44
|
+
disabled = false,
|
|
45
|
+
variant: _variant = "default",
|
|
46
|
+
label,
|
|
47
|
+
children,
|
|
48
|
+
style,
|
|
49
|
+
labelStyle,
|
|
50
|
+
className,
|
|
51
|
+
hitSlop = 8,
|
|
52
|
+
accessibilityLabel,
|
|
53
|
+
...rest
|
|
54
|
+
},
|
|
55
|
+
forwardedRef,
|
|
56
|
+
) {
|
|
57
|
+
const containerRef = useRef<ComponentRef<typeof Pressable>>(null);
|
|
58
|
+
const isControlled = typeof value === "boolean";
|
|
59
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
60
|
+
const isActive = isControlled ? value : uncontrolledValue;
|
|
61
|
+
const labelContent = children ?? label;
|
|
62
|
+
const resolvedAccessibilityLabel =
|
|
63
|
+
accessibilityLabel ?? (typeof labelContent === "string" ? labelContent : undefined);
|
|
64
|
+
const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
|
|
65
|
+
|
|
66
|
+
useApplyWebClassName(containerRef, className);
|
|
67
|
+
|
|
68
|
+
const handlePress = () => {
|
|
69
|
+
if (disabled || isActive) return;
|
|
70
|
+
if (!isControlled) setUncontrolledValue(true);
|
|
71
|
+
onValueChange?.(true);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Pressable
|
|
76
|
+
{...rest}
|
|
77
|
+
ref={setContainerRef}
|
|
78
|
+
onPress={handlePress}
|
|
79
|
+
disabled={disabled}
|
|
80
|
+
hitSlop={hitSlop}
|
|
81
|
+
accessibilityRole="radio"
|
|
82
|
+
accessibilityLabel={resolvedAccessibilityLabel}
|
|
83
|
+
accessibilityState={{ selected: isActive, disabled }}
|
|
84
|
+
style={[styles.root, disabled && styles.disabled, style]}
|
|
85
|
+
>
|
|
86
|
+
<View style={styles.control}>{isActive ? <View style={styles.dot} /> : null}</View>
|
|
87
|
+
|
|
88
|
+
{labelContent != null && labelContent !== false ? (
|
|
89
|
+
typeof labelContent === "string" || typeof labelContent === "number" ? (
|
|
90
|
+
<Text style={[styles.label, labelStyle]}>{labelContent}</Text>
|
|
91
|
+
) : (
|
|
92
|
+
labelContent
|
|
93
|
+
)
|
|
94
|
+
) : null}
|
|
95
|
+
</Pressable>
|
|
96
|
+
);
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const styles = StyleSheet.create({
|
|
101
|
+
root: {
|
|
102
|
+
flexDirection: "row",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
alignSelf: "flex-start",
|
|
105
|
+
gap: 6,
|
|
106
|
+
},
|
|
107
|
+
disabled: {
|
|
108
|
+
opacity: 0.5,
|
|
109
|
+
},
|
|
110
|
+
control: {
|
|
111
|
+
width: CONTROL_SIZE,
|
|
112
|
+
height: CONTROL_SIZE,
|
|
113
|
+
borderRadius: 20,
|
|
114
|
+
borderWidth: 1,
|
|
115
|
+
borderColor: colors.grey700,
|
|
116
|
+
backgroundColor: colors.dark,
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
justifyContent: "center",
|
|
119
|
+
flexShrink: 0,
|
|
120
|
+
},
|
|
121
|
+
dot: {
|
|
122
|
+
width: DOT_SIZE,
|
|
123
|
+
height: DOT_SIZE,
|
|
124
|
+
borderRadius: 43,
|
|
125
|
+
backgroundColor: colors.primary,
|
|
126
|
+
},
|
|
127
|
+
label: {
|
|
128
|
+
flexShrink: 1,
|
|
129
|
+
textAlign: "center",
|
|
130
|
+
fontFamily: fonts.sans,
|
|
131
|
+
fontSize: 12,
|
|
132
|
+
fontWeight: "400",
|
|
133
|
+
color: colors.white,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
@@ -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.
|
|
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
|
-
|
|
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}
|
package/src/components/index.ts
CHANGED
|
@@ -118,3 +118,6 @@ export type { CommentCardProps } from "./CommentCard";
|
|
|
118
118
|
|
|
119
119
|
export { Range } from "./Range";
|
|
120
120
|
export type { RangeProps, RangeValue } from "./Range";
|
|
121
|
+
|
|
122
|
+
export { RadioButton } from "./RadioButton";
|
|
123
|
+
export type { RadioButtonProps, RadioButtonVariant } from "./RadioButton";
|
|
@@ -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.
|
|
8
|
-
"M13.
|
|
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.
|
|
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,27 @@
|
|
|
1
|
+
import Svg, { Path } from "react-native-svg";
|
|
2
|
+
import { colors } from "../../theme";
|
|
3
|
+
import type { IconProps } from "../types";
|
|
4
|
+
import { MOON_STARS_MOON_PATH, MOON_STARS_SPARKLE_PATH } from "./moonStarsPaths";
|
|
5
|
+
|
|
6
|
+
export function MoonStarsIcon({ size = 15, color = colors.primary, strokeWidth = 1.5 }: IconProps) {
|
|
7
|
+
return (
|
|
8
|
+
<Svg width={size} height={size} viewBox="0 0 15 15" fill="none">
|
|
9
|
+
<Path
|
|
10
|
+
d={MOON_STARS_SPARKLE_PATH}
|
|
11
|
+
stroke={color}
|
|
12
|
+
strokeWidth={strokeWidth}
|
|
13
|
+
strokeLinecap="round"
|
|
14
|
+
strokeLinejoin="round"
|
|
15
|
+
/>
|
|
16
|
+
<Path
|
|
17
|
+
d={MOON_STARS_MOON_PATH}
|
|
18
|
+
stroke={color}
|
|
19
|
+
strokeWidth={strokeWidth}
|
|
20
|
+
strokeLinecap="round"
|
|
21
|
+
strokeLinejoin="round"
|
|
22
|
+
/>
|
|
23
|
+
</Svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { MOON_STARS_MOON_PATH, MOON_STARS_PATHS, MOON_STARS_SPARKLE_PATH } from "./moonStarsPaths";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { colors } from "../../theme";
|
|
2
|
+
import type { IconProps } from "../types";
|
|
3
|
+
import { MOON_STARS_MOON_PATH, MOON_STARS_SPARKLE_PATH } from "./moonStarsPaths";
|
|
4
|
+
|
|
5
|
+
export function MoonStarsIcon({ size = 15, color = colors.primary, strokeWidth = 1.5 }: IconProps) {
|
|
6
|
+
return (
|
|
7
|
+
<svg
|
|
8
|
+
width={size}
|
|
9
|
+
height={size}
|
|
10
|
+
viewBox="0 0 15 15"
|
|
11
|
+
fill="none"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
aria-hidden
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d={MOON_STARS_SPARKLE_PATH}
|
|
17
|
+
stroke={color}
|
|
18
|
+
strokeWidth={strokeWidth}
|
|
19
|
+
strokeLinecap="round"
|
|
20
|
+
strokeLinejoin="round"
|
|
21
|
+
/>
|
|
22
|
+
<path
|
|
23
|
+
d={MOON_STARS_MOON_PATH}
|
|
24
|
+
stroke={color}
|
|
25
|
+
strokeWidth={strokeWidth}
|
|
26
|
+
strokeLinecap="round"
|
|
27
|
+
strokeLinejoin="round"
|
|
28
|
+
/>
|
|
29
|
+
</svg>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { MOON_STARS_MOON_PATH, MOON_STARS_PATHS, MOON_STARS_SPARKLE_PATH } from "./moonStarsPaths";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const MOON_STARS_SPARKLE_PATH =
|
|
2
|
+
"M11.4167 0.75L11.8285 1.57372C12.0055 1.92771 12.094 2.1047 12.2122 2.25808C12.3171 2.39417 12.4392 2.51618 12.5753 2.62109C12.7286 2.73932 12.9056 2.82781 13.2596 3.00481L14.0833 3.41667L13.2596 3.82853C12.9056 4.00552 12.7286 4.09402 12.5753 4.21224C12.4392 4.31715 12.3171 4.43916 12.2122 4.57526C12.094 4.72863 12.0055 4.90563 11.8285 5.25961L11.4167 6.08333L11.0048 5.25961C10.8278 4.90563 10.7393 4.72863 10.6211 4.57526C10.5162 4.43916 10.3942 4.31715 10.2581 4.21224C10.1047 4.09402 9.92771 4.00552 9.57372 3.82853L8.75 3.41667L9.57372 3.00481C9.92771 2.82781 10.1047 2.73932 10.2581 2.62109C10.3942 2.51618 10.5162 2.39417 10.6211 2.25808C10.7393 2.1047 10.8278 1.92771 11.0048 1.57372L11.4167 0.75Z";
|
|
3
|
+
|
|
4
|
+
export const MOON_STARS_MOON_PATH =
|
|
5
|
+
"M13.4167 8.34286C12.5427 9.876 10.893 10.9097 9.00196 10.9097C6.19739 10.9097 3.92383 8.63612 3.92383 5.83155C3.92383 3.94036 4.95766 2.29063 6.491 1.41667C3.26986 1.72208 0.75 4.43461 0.75 7.73569C0.75 11.2414 3.59195 14.0833 7.09766 14.0833C10.3986 14.0833 13.111 11.5637 13.4167 8.34286Z";
|
|
6
|
+
|
|
7
|
+
export const MOON_STARS_PATHS = [MOON_STARS_SPARKLE_PATH, MOON_STARS_MOON_PATH] as const;
|