@ecohouse/ui 0.1.42 → 0.1.43
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 +130 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +129 -12
- package/dist/index.js.map +1 -1
- package/package.json +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/index.ts +3 -0
- 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/index.ts +1 -0
- package/src/icons/registry.ts +3 -0
- package/src/index.ts +4 -0
package/package.json
CHANGED
|
@@ -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
|
+
});
|
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";
|
|
@@ -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;
|
package/src/icons/index.ts
CHANGED
|
@@ -80,6 +80,7 @@ export { MapCollapseIcon, MapExpandIcon } from "./MapControls";
|
|
|
80
80
|
export { MenuIcon } from "./Menu";
|
|
81
81
|
export type { MenuIconProps } from "./Menu";
|
|
82
82
|
export { MenuLinesIcon } from "./MenuLines";
|
|
83
|
+
export { MoonStarsIcon } from "./MoonStars";
|
|
83
84
|
export { MoreHorizontalIcon } from "./MoreHorizontal";
|
|
84
85
|
export { NavigateIcon } from "./Navigate";
|
|
85
86
|
export { PhoneIcon } from "./Phone";
|
package/src/icons/registry.ts
CHANGED
|
@@ -68,6 +68,7 @@ import { MapCollapseIcon, MapExpandIcon } from "./MapControls";
|
|
|
68
68
|
import { MenuIcon } from "./Menu";
|
|
69
69
|
import { MenuLinesIcon } from "./MenuLines";
|
|
70
70
|
import { MinusIcon } from "./Minus";
|
|
71
|
+
import { MoonStarsIcon } from "./MoonStars";
|
|
71
72
|
import { MoreHorizontalIcon } from "./MoreHorizontal";
|
|
72
73
|
import { NavigateIcon } from "./Navigate";
|
|
73
74
|
import { PhoneIcon } from "./Phone";
|
|
@@ -145,6 +146,7 @@ export const icons = {
|
|
|
145
146
|
menu: MenuIcon,
|
|
146
147
|
menuLines: MenuLinesIcon,
|
|
147
148
|
minus: MinusIcon,
|
|
149
|
+
moonStars: MoonStarsIcon,
|
|
148
150
|
moreHorizontal: MoreHorizontalIcon,
|
|
149
151
|
navigate: NavigateIcon,
|
|
150
152
|
noSmoking: NoSmokingIcon,
|
|
@@ -235,6 +237,7 @@ export const iconGroups = {
|
|
|
235
237
|
"star",
|
|
236
238
|
"starFilled",
|
|
237
239
|
"minus",
|
|
240
|
+
"moonStars",
|
|
238
241
|
"moreHorizontal",
|
|
239
242
|
"plus",
|
|
240
243
|
"refresh",
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export {
|
|
|
34
34
|
RatingInput,
|
|
35
35
|
CommentCard,
|
|
36
36
|
Range,
|
|
37
|
+
RadioButton,
|
|
37
38
|
} from "./components";
|
|
38
39
|
export type {
|
|
39
40
|
AvatarProps,
|
|
@@ -94,6 +95,8 @@ export type {
|
|
|
94
95
|
CommentCardProps,
|
|
95
96
|
RangeProps,
|
|
96
97
|
RangeValue,
|
|
98
|
+
RadioButtonProps,
|
|
99
|
+
RadioButtonVariant,
|
|
97
100
|
} from "./components";
|
|
98
101
|
|
|
99
102
|
export {
|
|
@@ -194,6 +197,7 @@ export {
|
|
|
194
197
|
MenuIcon,
|
|
195
198
|
MenuLinesIcon,
|
|
196
199
|
MinusIcon,
|
|
200
|
+
MoonStarsIcon,
|
|
197
201
|
NavigateIcon,
|
|
198
202
|
PhoneIcon,
|
|
199
203
|
PlusIcon,
|