@ecohouse/ui 0.1.55 → 0.1.57

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.55",
3
+ "version": "0.1.57",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Cross-platform presentation-only UI components for EcoHouse (React Native + React Native Web)",
@@ -32,6 +32,7 @@ const meta = {
32
32
  imageUrl: "https://i.pravatar.cc/150?img=47",
33
33
  },
34
34
  onLogOut: fn(),
35
+ onLogoPress: fn(),
35
36
  logoAccessibilityLabel: "Cortel Booking",
36
37
  navigationAccessibilityLabel: "Հաշվի նավիգացիա",
37
38
  collapseAccessibilityLabel: "Թաքցնել կողագոտին",
@@ -43,6 +44,7 @@ const meta = {
43
44
  collapsed: { control: "boolean" },
44
45
  defaultCollapsed: { control: "boolean" },
45
46
  onLogOut: { action: "log out" },
47
+ onLogoPress: { action: "logo pressed" },
46
48
  onCollapsedChange: { action: "collapsed change" },
47
49
  },
48
50
  parameters: {
@@ -46,6 +46,8 @@ export interface SidebarProps extends Omit<ViewProps, "style" | "children"> {
46
46
  items: readonly SidebarItem[];
47
47
  user: SidebarUser;
48
48
  onLogOut?: () => void;
49
+ /** Makes the brand mark interactive, e.g. to navigate to the home page. */
50
+ onLogoPress?: () => void;
49
51
  /** Accessible name for the brand mark. Defaults to `"Cortel Booking"`. */
50
52
  logoAccessibilityLabel?: string;
51
53
  /** Accessible name for the nav landmark. Defaults to `"Account navigation"`. */
@@ -84,6 +86,7 @@ export const Sidebar = forwardRef<ComponentRef<typeof View>, SidebarProps>(funct
84
86
  items,
85
87
  user,
86
88
  onLogOut,
89
+ onLogoPress,
87
90
  logoAccessibilityLabel = "Cortel Booking",
88
91
  navigationAccessibilityLabel = "Account navigation",
89
92
  collapseAccessibilityLabel = "Collapse sidebar",
@@ -164,12 +167,28 @@ export const Sidebar = forwardRef<ComponentRef<typeof View>, SidebarProps>(funct
164
167
  accessibilityElementsHidden={collapsed}
165
168
  importantForAccessibility={collapsed ? "no-hide-descendants" : "auto"}
166
169
  >
167
- <BrandLogo
168
- accessibilityLabel={logoAccessibilityLabel}
169
- height={LOGO_HEIGHT}
170
- variant="brand"
171
- width={LOGO_WIDTH}
172
- />
170
+ {onLogoPress ? (
171
+ <Pressable
172
+ accessibilityRole="link"
173
+ accessibilityLabel={logoAccessibilityLabel}
174
+ hitSlop={8}
175
+ onPress={onLogoPress}
176
+ >
177
+ <BrandLogo
178
+ accessibilityLabel={logoAccessibilityLabel}
179
+ height={LOGO_HEIGHT}
180
+ variant="brand"
181
+ width={LOGO_WIDTH}
182
+ />
183
+ </Pressable>
184
+ ) : (
185
+ <BrandLogo
186
+ accessibilityLabel={logoAccessibilityLabel}
187
+ height={LOGO_HEIGHT}
188
+ variant="brand"
189
+ width={LOGO_WIDTH}
190
+ />
191
+ )}
173
192
  </Animated.View>
174
193
 
175
194
  {onClose ? (
@@ -0,0 +1,36 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { StatusIndicator } from "./StatusIndicator";
4
+
5
+ const meta = {
6
+ title: "Components/StatusIndicator",
7
+ component: StatusIndicator,
8
+ args: { label: "Ակտիվ է", status: "active" },
9
+ parameters: { backgrounds: { default: "black" } },
10
+ } satisfies Meta<typeof StatusIndicator>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Active: Story = {};
16
+
17
+ export const AllStatuses: Story = {
18
+ render: () => (
19
+ <View style={styles.column}>
20
+ <StatusIndicator label="Ակտիվ է" status="active" />
21
+ <StatusIndicator label="Սպասվող" status="pending" />
22
+ <StatusIndicator label="Ավարտված" status="completed" />
23
+ <StatusIndicator label="Չեղարկված" status="cancelled" />
24
+ <StatusIndicator label="Ապաակտիվ է" status="inactive" />
25
+ <StatusIndicator label="Հասանելի չէ" status="unavailable" />
26
+ </View>
27
+ ),
28
+ };
29
+
30
+ const styles = StyleSheet.create({
31
+ column: {
32
+ gap: 16,
33
+ padding: 24,
34
+ backgroundColor: "#090909",
35
+ },
36
+ });
@@ -0,0 +1,105 @@
1
+ import { forwardRef, useMemo, useRef, type ComponentRef } from "react";
2
+ import {
3
+ Platform,
4
+ StyleSheet,
5
+ Text,
6
+ View,
7
+ type StyleProp,
8
+ type ViewProps,
9
+ type ViewStyle,
10
+ } from "react-native";
11
+ import { colors, fonts } from "../../theme";
12
+ import { mergeRefs } from "../../utils/mergeRefs";
13
+ import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
14
+
15
+ export type StatusIndicatorStatus =
16
+ "active" | "pending" | "completed" | "cancelled" | "inactive" | "unavailable";
17
+
18
+ export interface StatusIndicatorProps extends Omit<ViewProps, "style" | "children"> {
19
+ status: StatusIndicatorStatus;
20
+ label: string;
21
+ style?: StyleProp<ViewStyle>;
22
+ className?: string;
23
+ }
24
+
25
+ const statusColors: Record<Exclude<StatusIndicatorStatus, "unavailable">, string> = {
26
+ active: colors.green,
27
+ pending: colors.primary,
28
+ completed: colors.grey0,
29
+ cancelled: colors.red,
30
+ inactive: colors.primary,
31
+ };
32
+
33
+ export const StatusIndicator = forwardRef<ComponentRef<typeof View>, StatusIndicatorProps>(
34
+ function StatusIndicator(
35
+ { status, label, style, className, accessibilityLabel, ...rest },
36
+ forwardedRef,
37
+ ) {
38
+ const containerRef = useRef<ComponentRef<typeof View>>(null);
39
+ const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
40
+ const indicatorColor = status === "unavailable" ? null : statusColors[status];
41
+
42
+ useApplyWebClassName(containerRef, className);
43
+
44
+ return (
45
+ <View
46
+ {...rest}
47
+ ref={setContainerRef}
48
+ accessibilityRole="text"
49
+ accessibilityLabel={accessibilityLabel ?? label}
50
+ style={[styles.container, style]}
51
+ >
52
+ {indicatorColor ? (
53
+ <View style={[styles.indicatorOuter, { backgroundColor: `${indicatorColor}1F` }]}>
54
+ <View style={[styles.indicatorMiddle, { backgroundColor: `${indicatorColor}52` }]}>
55
+ <View style={[styles.indicatorInner, { backgroundColor: indicatorColor }]} />
56
+ </View>
57
+ </View>
58
+ ) : null}
59
+ <Text style={[styles.label, Platform.OS === "android" ? styles.labelAndroid : null]}>
60
+ {label}
61
+ </Text>
62
+ </View>
63
+ );
64
+ },
65
+ );
66
+
67
+ const styles = StyleSheet.create({
68
+ container: {
69
+ flexDirection: "row",
70
+ alignItems: "center",
71
+ alignSelf: "flex-start",
72
+ gap: 8,
73
+ },
74
+ indicatorOuter: {
75
+ width: 14,
76
+ height: 14,
77
+ flexShrink: 0,
78
+ alignItems: "center",
79
+ justifyContent: "center",
80
+ borderRadius: 7,
81
+ },
82
+ indicatorMiddle: {
83
+ width: 10,
84
+ height: 10,
85
+ alignItems: "center",
86
+ justifyContent: "center",
87
+ borderRadius: 5,
88
+ },
89
+ indicatorInner: {
90
+ width: 6,
91
+ height: 6,
92
+ borderRadius: 3,
93
+ },
94
+ label: {
95
+ color: colors.white,
96
+ fontFamily: fonts.sans,
97
+ fontSize: 12,
98
+ fontWeight: "500",
99
+ lineHeight: 12,
100
+ letterSpacing: 0,
101
+ },
102
+ labelAndroid: {
103
+ includeFontPadding: false,
104
+ },
105
+ });
@@ -0,0 +1,2 @@
1
+ export { StatusIndicator } from "./StatusIndicator";
2
+ export type { StatusIndicatorProps, StatusIndicatorStatus } from "./StatusIndicator";
@@ -13,6 +13,9 @@ export type { BadgeProps, BadgeVariant } from "./Badge";
13
13
  export { StatusBadge } from "./StatusBadge";
14
14
  export type { StatusBadgeProps, StatusBadgeTone } from "./StatusBadge";
15
15
 
16
+ export { StatusIndicator } from "./StatusIndicator";
17
+ export type { StatusIndicatorProps, StatusIndicatorStatus } from "./StatusIndicator";
18
+
16
19
  export { PaginationDots } from "./PaginationDots";
17
20
  export type { PaginationDotsProps } from "./PaginationDots";
18
21
 
@@ -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 { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
5
+
6
+ export { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
7
+
8
+ /** Native — react-native-svg (peer dependency). */
9
+ export function ChevronRightIcon({
10
+ size = 16,
11
+ color = colors.grey100,
12
+ strokeWidth = 1.5,
13
+ }: IconProps) {
14
+ return (
15
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
16
+ <Path
17
+ d={CHEVRON_RIGHT_PATH}
18
+ stroke={color}
19
+ strokeWidth={strokeWidth}
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </Svg>
24
+ );
25
+ }
@@ -0,0 +1,31 @@
1
+ import { colors } from "../../theme";
2
+ import type { IconProps } from "../types";
3
+ import { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
4
+
5
+ /** Web / Storybook — plain SVG (no react-native-svg). */
6
+ export function ChevronRightIcon({
7
+ size = 16,
8
+ color = colors.grey100,
9
+ strokeWidth = 1.5,
10
+ }: IconProps) {
11
+ return (
12
+ <svg
13
+ width={size}
14
+ height={size}
15
+ viewBox="0 0 16 16"
16
+ fill="none"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ aria-hidden
19
+ >
20
+ <path
21
+ d={CHEVRON_RIGHT_PATH}
22
+ stroke={color}
23
+ strokeWidth={strokeWidth}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ />
27
+ </svg>
28
+ );
29
+ }
30
+
31
+ export { CHEVRON_RIGHT_PATH } from "./chevronRightPath";
@@ -0,0 +1,2 @@
1
+ /** Chevron right path — 16×16 viewBox. */
2
+ export const CHEVRON_RIGHT_PATH = "M6 12L10 8L6 4";
@@ -0,0 +1 @@
1
+ export { ChevronRightIcon, CHEVRON_RIGHT_PATH } from "./ChevronRightIcon";
@@ -27,6 +27,7 @@ export { CheckIcon } from "./Check";
27
27
  export { CheckBadgeIcon } from "./CheckBadge";
28
28
  export { ChevronDownIcon } from "./ChevronDown";
29
29
  export { ChevronLeftIcon } from "./ChevronLeft";
30
+ export { ChevronRightIcon } from "./ChevronRight";
30
31
  export { ClockIcon } from "./Clock";
31
32
  export { CloseIcon } from "./Close";
32
33
  export { CloudUploadIcon } from "./CloudUpload";
@@ -17,6 +17,7 @@ import { CheckIcon } from "./Check";
17
17
  import { CheckBadgeIcon } from "./CheckBadge";
18
18
  import { ChevronDownIcon } from "./ChevronDown";
19
19
  import { ChevronLeftIcon } from "./ChevronLeft";
20
+ import { ChevronRightIcon } from "./ChevronRight";
20
21
  import { ClockIcon } from "./Clock";
21
22
  import { CloseIcon } from "./Close";
22
23
  import { DeactivateIcon } from "./Deactivate";
@@ -127,6 +128,7 @@ export const icons = {
127
128
  checkSuccess: CheckSuccessIcon,
128
129
  chevronDown: ChevronDownIcon,
129
130
  chevronLeft: ChevronLeftIcon,
131
+ chevronRight: ChevronRightIcon,
130
132
  clock: ClockIcon,
131
133
  close: CloseIcon,
132
134
  deactivate: DeactivateIcon,
@@ -223,6 +225,7 @@ export const iconGroups = {
223
225
  "arrowRight",
224
226
  "chevronDown",
225
227
  "chevronLeft",
228
+ "chevronRight",
226
229
  "home",
227
230
  "listView",
228
231
  "locationPin",
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export {
4
4
  AccountHeader,
5
5
  Badge,
6
6
  StatusBadge,
7
+ StatusIndicator,
7
8
  PaginationDots,
8
9
  StepPager,
9
10
  InfoTile,
@@ -46,6 +47,8 @@ export type {
46
47
  BadgeVariant,
47
48
  StatusBadgeProps,
48
49
  StatusBadgeTone,
50
+ StatusIndicatorProps,
51
+ StatusIndicatorStatus,
49
52
  PaginationDotsProps,
50
53
  StepPagerProps,
51
54
  InfoTileProps,
@@ -148,6 +151,7 @@ export {
148
151
  CheckBadgeIcon,
149
152
  ChevronDownIcon,
150
153
  ChevronLeftIcon,
154
+ ChevronRightIcon,
151
155
  ClockIcon,
152
156
  CloseIcon,
153
157
  CloudUploadIcon,