@ecohouse/ui 0.1.24 → 0.1.25
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
3
3
|
import { StyleSheet, View } from "react-native";
|
|
4
|
-
import { LayoutGridIcon } from "../../icons";
|
|
4
|
+
import { LayoutGridIcon, ListViewIcon, MapViewIcon } from "../../icons";
|
|
5
5
|
import { SegmentedToggle } from "./SegmentedToggle";
|
|
6
6
|
|
|
7
7
|
const optionsWithIcons = [
|
|
@@ -14,6 +14,21 @@ const optionsWithoutIcons = [
|
|
|
14
14
|
{ value: "second", label: "Label" },
|
|
15
15
|
] as const;
|
|
16
16
|
|
|
17
|
+
const multilingualOptions = [
|
|
18
|
+
{ value: "daily", label: "Օրավարձ" },
|
|
19
|
+
{ value: "overnight", label: "Գիշերակաց երկար տարբերակ" },
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
const multilingualIconOptions = [
|
|
23
|
+
{ value: "list", label: "Ցուցակի տարբերակ", icon: LayoutGridIcon },
|
|
24
|
+
{ value: "map", label: "Քարտեզի շատ երկար տարբերակ", icon: LayoutGridIcon },
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
const armenianViewOptions = [
|
|
28
|
+
{ value: "list", label: "Ցանկ", icon: ListViewIcon },
|
|
29
|
+
{ value: "map", label: "Քարտեզ", icon: MapViewIcon },
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
17
32
|
const meta = {
|
|
18
33
|
title: "Components/SegmentedToggle",
|
|
19
34
|
component: SegmentedToggle,
|
|
@@ -39,6 +54,38 @@ export const ContentWidth: Story = {
|
|
|
39
54
|
args: { fullWidth: false },
|
|
40
55
|
};
|
|
41
56
|
|
|
57
|
+
export const MultilingualContentWidth: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
options: multilingualOptions,
|
|
60
|
+
defaultValue: "daily",
|
|
61
|
+
fullWidth: false,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const MultilingualIconsContentWidth: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
options: multilingualIconOptions,
|
|
68
|
+
defaultValue: "list",
|
|
69
|
+
fullWidth: false,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const ArmenianViewMode: Story = {
|
|
74
|
+
args: {
|
|
75
|
+
options: armenianViewOptions,
|
|
76
|
+
defaultValue: "list",
|
|
77
|
+
fullWidth: false,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const ArmenianViewModeMapSelected: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
options: armenianViewOptions,
|
|
84
|
+
defaultValue: "map",
|
|
85
|
+
fullWidth: false,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
42
89
|
export const In400PixelContainer: Story = {
|
|
43
90
|
render: () => (
|
|
44
91
|
<View style={storyStyles.container}>
|
|
@@ -37,7 +37,9 @@ export interface SegmentedToggleProps extends Omit<ViewProps, "style" | "childre
|
|
|
37
37
|
const ICON_SIZE = 20;
|
|
38
38
|
const ANIMATION_DURATION = 200;
|
|
39
39
|
const OPTION_HORIZONTAL_PADDING = 16;
|
|
40
|
+
const OPTION_CONTENT_GAP = 12;
|
|
40
41
|
const OPTION_MIN_WIDTH = 97;
|
|
42
|
+
const OPTION_MEASUREMENT_TOLERANCE = 0.5;
|
|
41
43
|
|
|
42
44
|
export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedToggleProps>(
|
|
43
45
|
function SegmentedToggle(
|
|
@@ -70,15 +72,20 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
|
|
|
70
72
|
const [contentWidths, setContentWidths] = useState<[number, number]>([0, 0]);
|
|
71
73
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
72
74
|
const availableOptionWidth = Math.max(0, (containerWidth - 12) / 2);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const resolveContentOptionWidth = (contentWidth: number) =>
|
|
76
|
+
contentWidth > 0
|
|
77
|
+
? Math.max(
|
|
78
|
+
OPTION_MIN_WIDTH,
|
|
79
|
+
contentWidth + OPTION_HORIZONTAL_PADDING * 2 + OPTION_MEASUREMENT_TOLERANCE,
|
|
80
|
+
)
|
|
77
81
|
: 0;
|
|
78
|
-
const
|
|
82
|
+
const optionWidths: [number, number] = fullWidth
|
|
83
|
+
? [availableOptionWidth, availableOptionWidth]
|
|
84
|
+
: [resolveContentOptionWidth(contentWidths[0]), resolveContentOptionWidth(contentWidths[1])];
|
|
85
|
+
const selectedOptionWidth = optionWidths[selectedIndex];
|
|
79
86
|
const indicatorTranslateX = progress.interpolate({
|
|
80
87
|
inputRange: [0, 1],
|
|
81
|
-
outputRange: [0,
|
|
88
|
+
outputRange: [0, optionWidths[0] + 4],
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
useApplyWebClassName(containerRef, resolvedClassName);
|
|
@@ -120,12 +127,36 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
|
|
|
120
127
|
]}
|
|
121
128
|
accessibilityRole="radiogroup"
|
|
122
129
|
>
|
|
123
|
-
{
|
|
130
|
+
{!fullWidth
|
|
131
|
+
? options.map(({ value: optionValue, label, icon: Icon }, index) => (
|
|
132
|
+
<View
|
|
133
|
+
key={`${optionValue}-measurement`}
|
|
134
|
+
pointerEvents="none"
|
|
135
|
+
aria-hidden
|
|
136
|
+
accessibilityElementsHidden
|
|
137
|
+
importantForAccessibility="no-hide-descendants"
|
|
138
|
+
onLayout={(event) => {
|
|
139
|
+
const nextWidth = event.nativeEvent.layout.width;
|
|
140
|
+
setContentWidths((currentWidths) => {
|
|
141
|
+
if (currentWidths[index] === nextWidth) return currentWidths;
|
|
142
|
+
const nextWidths: [number, number] = [currentWidths[0], currentWidths[1]];
|
|
143
|
+
nextWidths[index] = nextWidth;
|
|
144
|
+
return nextWidths;
|
|
145
|
+
});
|
|
146
|
+
}}
|
|
147
|
+
style={styles.optionMeasurement}
|
|
148
|
+
>
|
|
149
|
+
{Icon ? <Icon size={ICON_SIZE} /> : null}
|
|
150
|
+
<Text style={[styles.label, styles.contentWidthLabel]}>{label}</Text>
|
|
151
|
+
</View>
|
|
152
|
+
))
|
|
153
|
+
: null}
|
|
154
|
+
{selectedOptionWidth > 0 ? (
|
|
124
155
|
<Animated.View
|
|
125
156
|
pointerEvents="none"
|
|
126
157
|
style={[
|
|
127
158
|
styles.activeIndicator,
|
|
128
|
-
{ width:
|
|
159
|
+
{ width: selectedOptionWidth, transform: [{ translateX: indicatorTranslateX }] },
|
|
129
160
|
]}
|
|
130
161
|
/>
|
|
131
162
|
) : null}
|
|
@@ -147,23 +178,22 @@ export const SegmentedToggle = forwardRef<ComponentRef<typeof View>, SegmentedTo
|
|
|
147
178
|
styles.option,
|
|
148
179
|
fullWidth
|
|
149
180
|
? styles.optionFullWidth
|
|
150
|
-
: [
|
|
181
|
+
: [
|
|
182
|
+
styles.optionContentWidth,
|
|
183
|
+
optionWidths[index] > 0 && { width: optionWidths[index] },
|
|
184
|
+
],
|
|
151
185
|
]}
|
|
152
186
|
>
|
|
153
|
-
<View
|
|
154
|
-
onLayout={(event) => {
|
|
155
|
-
const nextWidth = event.nativeEvent.layout.width;
|
|
156
|
-
setContentWidths((currentWidths) => {
|
|
157
|
-
if (currentWidths[index] === nextWidth) return currentWidths;
|
|
158
|
-
const nextWidths: [number, number] = [currentWidths[0], currentWidths[1]];
|
|
159
|
-
nextWidths[index] = nextWidth;
|
|
160
|
-
return nextWidths;
|
|
161
|
-
});
|
|
162
|
-
}}
|
|
163
|
-
style={styles.optionContent}
|
|
164
|
-
>
|
|
187
|
+
<View style={styles.optionContent}>
|
|
165
188
|
{Icon ? <Icon size={ICON_SIZE} color={iconColor} /> : null}
|
|
166
|
-
<Text
|
|
189
|
+
<Text
|
|
190
|
+
numberOfLines={fullWidth ? 1 : undefined}
|
|
191
|
+
style={[
|
|
192
|
+
styles.label,
|
|
193
|
+
!fullWidth && styles.contentWidthLabel,
|
|
194
|
+
{ color: labelColor },
|
|
195
|
+
]}
|
|
196
|
+
>
|
|
167
197
|
{label}
|
|
168
198
|
</Text>
|
|
169
199
|
</View>
|
|
@@ -207,7 +237,14 @@ const styles = StyleSheet.create({
|
|
|
207
237
|
maxWidth: "100%",
|
|
208
238
|
flexDirection: "row",
|
|
209
239
|
alignItems: "center",
|
|
210
|
-
gap:
|
|
240
|
+
gap: OPTION_CONTENT_GAP,
|
|
241
|
+
},
|
|
242
|
+
optionMeasurement: {
|
|
243
|
+
position: "absolute",
|
|
244
|
+
flexDirection: "row",
|
|
245
|
+
alignItems: "center",
|
|
246
|
+
gap: OPTION_CONTENT_GAP,
|
|
247
|
+
opacity: 0,
|
|
211
248
|
},
|
|
212
249
|
optionFullWidth: {
|
|
213
250
|
flex: 1,
|
|
@@ -228,4 +265,7 @@ const styles = StyleSheet.create({
|
|
|
228
265
|
...segmentedToggleTypography,
|
|
229
266
|
flexShrink: 1,
|
|
230
267
|
},
|
|
268
|
+
contentWidthLabel: {
|
|
269
|
+
flexShrink: 0,
|
|
270
|
+
},
|
|
231
271
|
});
|