@ecohouse/ui 0.1.11 → 0.1.12

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecohouse/ui",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Cross-platform presentation-only UI components for EcoHouse (React Native + React Native Web)",
@@ -52,6 +52,17 @@ export const WithCustomIcon: Story = {
52
52
  },
53
53
  };
54
54
 
55
+ export const WithCustomWidth: Story = {
56
+ args: {
57
+ title: "Button",
58
+ showIcon: true,
59
+ style: { width: 400 },
60
+ },
61
+ parameters: {
62
+ backgrounds: { default: "black" },
63
+ },
64
+ };
65
+
55
66
  export const AllVariants: Story = {
56
67
  render: () => (
57
68
  <View style={storyStyles.grid}>
@@ -140,6 +140,7 @@ export const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonPr
140
140
  paddingRight: hasIcon ? metrics.paddingRightWithIcon : metrics.paddingRight,
141
141
  gap: hasIcon ? metrics.gap : 0,
142
142
  },
143
+ hasIcon ? styles.contentWithIcon : styles.contentWithoutIcon,
143
144
  disabled && styles.disabled,
144
145
  style,
145
146
  ];
@@ -203,6 +204,12 @@ const styles = StyleSheet.create({
203
204
  disabled: {
204
205
  opacity: 0.6,
205
206
  },
207
+ contentWithIcon: {
208
+ justifyContent: "space-between",
209
+ },
210
+ contentWithoutIcon: {
211
+ justifyContent: "center",
212
+ },
206
213
  label: {},
207
214
  labelAndroid: {
208
215
  includeFontPadding: false,
@@ -35,6 +35,14 @@ export const Open: Story = {
35
35
  args: { defaultOpen: true },
36
36
  };
37
37
 
38
+ export const IndependentWidths: Story = {
39
+ args: {
40
+ defaultOpen: true,
41
+ style: { width: 240 },
42
+ calendarStyle: { width: 420 },
43
+ },
44
+ };
45
+
38
46
  export const WithValue: Story = {
39
47
  args: {
40
48
  defaultValue: new Date(2026, 8, 15),
@@ -78,6 +78,8 @@ type DatePickerBaseProps = Omit<ViewProps, "style" | "children"> & {
78
78
  formatValue?: (date: Date) => string;
79
79
  /** First day of the week column (0 = Sunday, 1 = Monday). Defaults to Monday. */
80
80
  weekStartsOn?: 0 | 1;
81
+ /** Styles the calendar popup independently from the trigger width. */
82
+ calendarStyle?: StyleProp<ViewStyle>;
81
83
  style?: StyleProp<ViewStyle>;
82
84
  className?: string;
83
85
  };
@@ -123,6 +125,7 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
123
125
  weekdayLabels = DEFAULT_WEEKDAY_LABELS,
124
126
  formatValue = formatDate,
125
127
  weekStartsOn = 1,
128
+ calendarStyle,
126
129
  style,
127
130
  className,
128
131
  accessibilityLabel,
@@ -329,7 +332,7 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
329
332
  </Pressable>
330
333
 
331
334
  {isOpen ? (
332
- <View style={styles.menu}>
335
+ <View style={[styles.menu, calendarStyle]}>
333
336
  <View style={styles.calendarHeader}>
334
337
  <Pressable
335
338
  onPress={handlePrevMonth}
@@ -536,7 +539,7 @@ const styles = StyleSheet.create({
536
539
  position: "absolute",
537
540
  top: "100%",
538
541
  left: 0,
539
- right: 0,
542
+ width: DEFAULT_WIDTH,
540
543
  marginTop: 8,
541
544
  zIndex: 10,
542
545
  borderRadius: MENU_RADIUS,
@@ -1,7 +1,9 @@
1
+ import { useState } from "react";
1
2
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { View, Text, StyleSheet } from "react-native";
3
+ import { Pressable, View, Text, StyleSheet } from "react-native";
3
4
  import { colors } from "../theme";
4
5
  import { Icon } from "./Icon";
6
+ import { MenuIcon } from "./Menu";
5
7
  import {
6
8
  getIconsByGroup,
7
9
  iconGroupNames,
@@ -140,6 +142,30 @@ export const Social: Story = {
140
142
  ),
141
143
  };
142
144
 
145
+ function AnimatedMenuPreview() {
146
+ const [open, setOpen] = useState(false);
147
+
148
+ return (
149
+ <View style={styles.menuPreview}>
150
+ <Pressable
151
+ accessibilityLabel={open ? "Close menu" : "Open menu"}
152
+ accessibilityRole="button"
153
+ accessibilityState={{ expanded: open }}
154
+ onPress={() => setOpen((current) => !current)}
155
+ style={styles.menuButton}
156
+ >
157
+ <MenuIcon open={open} size={24} />
158
+ </Pressable>
159
+ <Text style={styles.label}>Click to {open ? "close" : "open"}</Text>
160
+ </View>
161
+ );
162
+ }
163
+
164
+ export const AnimatedMenu: Story = {
165
+ name: "Animated / Menu",
166
+ render: () => <AnimatedMenuPreview />,
167
+ };
168
+
143
169
  const styles = StyleSheet.create({
144
170
  sections: {
145
171
  gap: 32,
@@ -171,4 +197,16 @@ const styles = StyleSheet.create({
171
197
  fontSize: 12,
172
198
  fontFamily: "Noto Sans Armenian",
173
199
  },
200
+ menuPreview: {
201
+ alignItems: "center",
202
+ gap: 12,
203
+ },
204
+ menuButton: {
205
+ width: 48,
206
+ height: 48,
207
+ alignItems: "center",
208
+ justifyContent: "center",
209
+ borderRadius: 24,
210
+ backgroundColor: colors.primary,
211
+ },
174
212
  });
@@ -0,0 +1,30 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { colors } from "../../theme";
3
+ import type { IconProps } from "../types";
4
+ import { MENU_CLOSE_PATH, MENU_PATH } from "./menuPath";
5
+
6
+ export { MENU_CLOSE_PATH, MENU_PATH } from "./menuPath";
7
+
8
+ export interface MenuIconProps extends IconProps {
9
+ open?: boolean;
10
+ }
11
+
12
+ export function MenuIcon({
13
+ size = 19,
14
+ color = colors.white,
15
+ strokeWidth = 2,
16
+ open = false,
17
+ }: MenuIconProps) {
18
+ return (
19
+ <Svg width={size} height={size} viewBox="0 0 19 19" fill="none">
20
+ <Path
21
+ d={open ? MENU_CLOSE_PATH : MENU_PATH}
22
+ stroke={color}
23
+ strokeWidth={strokeWidth}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ transform={open ? "translate(1.5 1.5)" : "translate(0 2.5)"}
27
+ />
28
+ </Svg>
29
+ );
30
+ }
@@ -0,0 +1,58 @@
1
+ import { colors } from "../../theme";
2
+ import type { MenuIconProps } from "./MenuIcon";
3
+ import { MENU_CLOSE_PATH, MENU_PATH } from "./menuPath";
4
+
5
+ export function MenuIcon({
6
+ size = 19,
7
+ color = colors.white,
8
+ strokeWidth = 2,
9
+ open = false,
10
+ }: MenuIconProps) {
11
+ const transition = "opacity 180ms ease, transform 180ms ease";
12
+
13
+ return (
14
+ <svg
15
+ width={size}
16
+ height={size}
17
+ viewBox="0 0 19 19"
18
+ fill="none"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ aria-hidden
21
+ >
22
+ <g transform="translate(0 2.5)">
23
+ <path
24
+ d={MENU_PATH}
25
+ stroke={color}
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ style={{
30
+ opacity: open ? 0 : 1,
31
+ transform: open ? "rotate(45deg) scale(0.75)" : "rotate(0deg) scale(1)",
32
+ transformBox: "fill-box",
33
+ transformOrigin: "center",
34
+ transition,
35
+ }}
36
+ />
37
+ </g>
38
+ <g transform="translate(1.5 1.5)">
39
+ <path
40
+ d={MENU_CLOSE_PATH}
41
+ stroke={color}
42
+ strokeWidth={strokeWidth}
43
+ strokeLinecap="round"
44
+ strokeLinejoin="round"
45
+ style={{
46
+ opacity: open ? 1 : 0,
47
+ transform: open ? "rotate(0deg) scale(1)" : "rotate(-45deg) scale(0.75)",
48
+ transformBox: "fill-box",
49
+ transformOrigin: "center",
50
+ transition,
51
+ }}
52
+ />
53
+ </g>
54
+ </svg>
55
+ );
56
+ }
57
+
58
+ export { MENU_CLOSE_PATH, MENU_PATH } from "./menuPath";
@@ -0,0 +1,2 @@
1
+ export { MenuIcon, MENU_CLOSE_PATH, MENU_PATH } from "./MenuIcon";
2
+ export type { MenuIconProps } from "./MenuIcon";
@@ -0,0 +1,3 @@
1
+ export const MENU_PATH = "M8 12.6667H17.3333M1 6.83332H17.3333M8 0.999985H17.3333";
2
+
3
+ export const MENU_CLOSE_PATH = "M15 1L1 15M1 1L15 15";
@@ -28,6 +28,8 @@ export { LayoutGridIcon } from "./LayoutGrid";
28
28
  export { LinkedInIcon } from "./LinkedIn";
29
29
  export { LogOutIcon } from "./LogOut";
30
30
  export { MailIcon } from "./Mail";
31
+ export { MenuIcon } from "./Menu";
32
+ export type { MenuIconProps } from "./Menu";
31
33
  export { NavigateIcon } from "./Navigate";
32
34
  export { PhoneIcon } from "./Phone";
33
35
  export { PlusIcon } from "./Plus";
@@ -21,6 +21,7 @@ import { LayoutGridIcon } from "./LayoutGrid";
21
21
  import { LinkedInIcon } from "./LinkedIn";
22
22
  import { LogOutIcon } from "./LogOut";
23
23
  import { MailIcon } from "./Mail";
24
+ import { MenuIcon } from "./Menu";
24
25
  import { MinusIcon } from "./Minus";
25
26
  import { NavigateIcon } from "./Navigate";
26
27
  import { PhoneIcon } from "./Phone";
@@ -60,6 +61,7 @@ export const icons = {
60
61
  linkedin: LinkedInIcon,
61
62
  logOut: LogOutIcon,
62
63
  mail: MailIcon,
64
+ menu: MenuIcon,
63
65
  minus: MinusIcon,
64
66
  navigate: NavigateIcon,
65
67
  phone: PhoneIcon,
@@ -80,7 +82,7 @@ export type IconName = keyof typeof icons;
80
82
  export const iconNames = Object.keys(icons) as IconName[];
81
83
 
82
84
  export const iconGroups = {
83
- navigation: ["arrowRight", "chevronDown", "chevronLeft", "home", "navigate"],
85
+ navigation: ["arrowRight", "chevronDown", "chevronLeft", "home", "menu", "navigate"],
84
86
  actions: [
85
87
  "show",
86
88
  "bell",
package/src/index.ts CHANGED
@@ -87,6 +87,7 @@ export {
87
87
  LinkedInIcon,
88
88
  LogOutIcon,
89
89
  MailIcon,
90
+ MenuIcon,
90
91
  MinusIcon,
91
92
  NavigateIcon,
92
93
  PhoneIcon,
@@ -107,6 +108,7 @@ export type {
107
108
  IconGroup,
108
109
  IconComponent,
109
110
  IconByNameProps,
111
+ MenuIconProps,
110
112
  StarIconProps,
111
113
  } from "./icons";
112
114