@scripso-homepad/ui 0.4.2 → 0.4.4

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.
Files changed (81) hide show
  1. package/dist/{chunk-WDSMJWKC.js → chunk-66WR57JJ.js} +35 -3
  2. package/dist/chunk-66WR57JJ.js.map +1 -0
  3. package/dist/index.cjs +2258 -384
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +134 -1
  6. package/dist/index.d.ts +134 -1
  7. package/dist/index.js +1959 -97
  8. package/dist/index.js.map +1 -1
  9. package/dist/web/index.cjs +30650 -1009
  10. package/dist/web/index.cjs.map +1 -1
  11. package/dist/web/index.css +78 -0
  12. package/dist/web/index.css.map +1 -0
  13. package/dist/web/index.d.cts +267 -6
  14. package/dist/web/index.d.ts +267 -6
  15. package/dist/web/index.js +30179 -601
  16. package/dist/web/index.js.map +1 -1
  17. package/package.json +5 -4
  18. package/src/components/Calendar.stories.tsx +337 -0
  19. package/src/components/Calendar.tsx +441 -0
  20. package/src/components/DatePicker.stories.tsx +219 -0
  21. package/src/components/DatePicker.tsx +366 -0
  22. package/src/components/Select.stories.tsx +134 -0
  23. package/src/components/Select.tsx +465 -0
  24. package/src/css.d.ts +1 -0
  25. package/src/icons/CalendarIcon.tsx +28 -0
  26. package/src/icons/CalendarIcon.web.tsx +34 -0
  27. package/src/icons/ChevronDownIcon.tsx +11 -4
  28. package/src/icons/ChevronDownIcon.web.tsx +6 -4
  29. package/src/icons/ChevronNavIcons.tsx +44 -0
  30. package/src/icons/ChevronNavIcons.web.tsx +57 -0
  31. package/src/icons/calendarIconPaths.ts +2 -0
  32. package/src/icons/chevronDownPath.ts +1 -0
  33. package/src/icons/chevronNavPaths.ts +2 -0
  34. package/src/index.ts +41 -0
  35. package/src/theme/calendar.ts +357 -0
  36. package/src/theme/select.ts +247 -0
  37. package/src/utils/calendarDays.ts +273 -0
  38. package/src/utils/calendarLocaleContext.tsx +95 -0
  39. package/src/utils/calendarLocalization.ts +187 -0
  40. package/src/utils/countryDropdownScrollStyles.ts +53 -13
  41. package/src/utils/formatMultiSelectDisplay.ts +56 -0
  42. package/src/web/components/Badge.stories.tsx +43 -0
  43. package/src/web/components/Badge.tsx +35 -0
  44. package/src/web/components/Pagination.stories.tsx +78 -0
  45. package/src/web/components/Pagination.tsx +153 -0
  46. package/src/web/components/StatusBadge.tsx +20 -0
  47. package/src/web/components/Table.stories.tsx +415 -0
  48. package/src/web/components/Table.tsx +3 -0
  49. package/src/web/components/TableActionButton.tsx +67 -0
  50. package/src/web/components/TableActionsMenu.tsx +208 -0
  51. package/src/web/components/TableIconText.tsx +23 -0
  52. package/src/web/components/TableRowStatusActions.tsx +33 -0
  53. package/src/web/components/TableStatusActionsCell.tsx +29 -0
  54. package/src/web/components/pagination-utils.ts +39 -0
  55. package/src/web/components/table/DataTable.tsx +123 -0
  56. package/src/web/components/table/TablePrimitives.tsx +214 -0
  57. package/src/web/components/table/TableScrollArea.tsx +51 -0
  58. package/src/web/components/table/constants.ts +59 -0
  59. package/src/web/components/table/data-table-class-names.ts +26 -0
  60. package/src/web/components/table/index.ts +44 -0
  61. package/src/web/components/table/table-row-context.tsx +19 -0
  62. package/src/web/components/table/use-horizontal-scroll-state.ts +72 -0
  63. package/src/web/components/table-scroll.css +81 -0
  64. package/src/web/hooks/useOnClickOutside.ts +20 -5
  65. package/src/web/icons/TableMenuActivateIcon.tsx +27 -0
  66. package/src/web/icons/TableMenuCheckIcon.tsx +27 -0
  67. package/src/web/icons/TableMenuCloseIcon.tsx +27 -0
  68. package/src/web/icons/TableMenuEditIcon.tsx +27 -0
  69. package/src/web/icons/TableMenuInfoIcon.tsx +27 -0
  70. package/src/web/icons/TableMenuRefreshIcon.tsx +27 -0
  71. package/src/web/icons/TableMenuSuspendIcon.tsx +34 -0
  72. package/src/web/icons/TableMenuTrashIcon.tsx +27 -0
  73. package/src/web/icons/TableMoreIcon.tsx +41 -0
  74. package/src/web/icons/TableSortIcon.tsx +37 -0
  75. package/src/web/index.ts +74 -0
  76. package/src/web/layout/AppHeader.stories.tsx +2 -0
  77. package/src/web/layout/AppHeader.tsx +18 -14
  78. package/src/web/layout/DashboardLayout.stories.tsx +1 -0
  79. package/src/web/layout/DashboardLayout.tsx +4 -4
  80. package/src/web/layout/story-helpers.tsx +10 -2
  81. package/dist/chunk-WDSMJWKC.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scripso-homepad/ui",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "description": "Cross-platform UI components for Homepad (React Web + React Native)",
6
6
  "license": "MIT",
@@ -46,12 +46,12 @@
46
46
  "prepublishOnly": "npm run build"
47
47
  },
48
48
  "peerDependencies": {
49
+ "lucide-react": ">=0.400",
49
50
  "react": ">=18",
50
51
  "react-native": ">=0.74",
51
52
  "react-native-svg": ">=13",
52
53
  "react-native-web": ">=0.19",
53
- "react-router-dom": ">=6",
54
- "lucide-react": ">=0.400"
54
+ "react-router-dom": ">=6"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "react-native-svg": {
@@ -79,16 +79,17 @@
79
79
  "@tailwindcss/postcss": "^4.1.13",
80
80
  "@tailwindcss/vite": "^4.1.13",
81
81
  "@types/react": "^18.3.18",
82
+ "@types/react-dom": "^18.3.7",
82
83
  "eslint": "^9.17.0",
83
84
  "eslint-plugin-react-hooks": "^5.1.0",
84
85
  "globals": "^15.14.0",
85
86
  "lucide-react": "^0.563.0",
86
87
  "react": "^18.3.1",
87
88
  "react-dom": "^18.3.1",
88
- "react-router-dom": "^7.9.1",
89
89
  "react-native": "^0.76.5",
90
90
  "react-native-svg": "^15.11.2",
91
91
  "react-native-web": "^0.19.13",
92
+ "react-router-dom": "^7.9.1",
92
93
  "storybook": "^8.4.7",
93
94
  "tailwindcss": "^4.1.13",
94
95
  "tsup": "^8.3.5",
@@ -0,0 +1,337 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { StyleSheet, Text, View } from "react-native";
4
+ import { CalendarIcon } from "../icons/CalendarIcon";
5
+ import {
6
+ CALENDAR_DROPDOWN_GAP,
7
+ CALENDAR_FIELD_ICON_SIZE,
8
+ calendarFieldMetrics,
9
+ calendarFieldPlaceholderTypography,
10
+ calendarFieldTypography,
11
+ } from "../theme/calendar";
12
+ import { getSelectFieldStyles, resolveInputVisualState } from "../theme/select";
13
+ import { colors } from "../theme/tokens";
14
+ import { formatCalendarDate } from "../utils/calendarDays";
15
+ import { formatLocalizedCalendarMonthYear } from "../utils/calendarLocalization";
16
+ import {
17
+ Calendar,
18
+ type CalendarLabels,
19
+ type CalendarRangeProps,
20
+ type CalendarSingleProps,
21
+ type DateRange,
22
+ } from "./Calendar";
23
+ import { DatePicker, type DatePickerRangeProps } from "./DatePicker";
24
+
25
+ const figmaViewDate = new Date(2026, 5, 1);
26
+ const figmaSampleDate = new Date(2026, 5, 25);
27
+
28
+ function getCurrentMonthStart() {
29
+ const now = new Date();
30
+ return new Date(now.getFullYear(), now.getMonth(), 1);
31
+ }
32
+
33
+ function getSampleRange(): DateRange {
34
+ const now = new Date();
35
+ return {
36
+ start: new Date(now.getFullYear(), now.getMonth(), 20),
37
+ end: new Date(now.getFullYear(), now.getMonth(), 28),
38
+ };
39
+ }
40
+
41
+ const figmaLabels: CalendarLabels = {
42
+ weekdays: ["ԵՐ", "ԵՔ", "ՉՐ", "ՀՆ", "ՈՒՐ", "ՇԲ", "ԿՐ"],
43
+ formatMonthYear: (date) => formatLocalizedCalendarMonthYear(date, "hy-AM"),
44
+ };
45
+
46
+ const sharedCalendarArgs = {
47
+ mode: "single" as const,
48
+ locale: "hy-AM" as const,
49
+ defaultViewDate: getCurrentMonthStart(),
50
+ };
51
+
52
+ const figmaCalendarArgs = {
53
+ ...sharedCalendarArgs,
54
+ today: figmaSampleDate,
55
+ defaultViewDate: figmaViewDate,
56
+ labels: figmaLabels,
57
+ };
58
+
59
+ function FullCalendarDemo({
60
+ initialValue,
61
+ placeholder = "Ընտրել ամսաթիվ",
62
+ calendarProps = sharedCalendarArgs,
63
+ }: {
64
+ initialValue?: Date;
65
+ placeholder?: string;
66
+ calendarProps?: CalendarSingleProps;
67
+ }) {
68
+ const [value, setValue] = useState<Date | undefined>(initialValue);
69
+ const fieldStyles = getSelectFieldStyles(resolveInputVisualState({}));
70
+ const displayText = value
71
+ ? formatCalendarDate(value, calendarProps.locale ?? "hy-AM")
72
+ : placeholder;
73
+
74
+ return (
75
+ <View style={styles.full}>
76
+ <View style={fieldStyles.outline}>
77
+ <View style={[calendarFieldMetrics.container, fieldStyles.container]}>
78
+ <Text
79
+ style={[
80
+ calendarFieldMetrics.value,
81
+ value
82
+ ? { ...calendarFieldTypography, color: fieldStyles.value.color }
83
+ : calendarFieldPlaceholderTypography,
84
+ ]}
85
+ numberOfLines={1}
86
+ >
87
+ {displayText}
88
+ </Text>
89
+ <View style={styles.iconSlot}>
90
+ <CalendarIcon
91
+ size={CALENDAR_FIELD_ICON_SIZE}
92
+ color={colors.stormGray150}
93
+ />
94
+ </View>
95
+ </View>
96
+ </View>
97
+
98
+ <Calendar
99
+ {...calendarProps}
100
+ value={value}
101
+ onChange={setValue}
102
+ style={styles.calendarPanel}
103
+ />
104
+ </View>
105
+ );
106
+ }
107
+
108
+ const meta = {
109
+ title: "Components/Calendar",
110
+ component: Calendar,
111
+ parameters: {
112
+ layout: "centered",
113
+ },
114
+ args: sharedCalendarArgs,
115
+ } satisfies Meta<CalendarSingleProps>;
116
+
117
+ export default meta;
118
+ type Story = StoryObj<typeof meta>;
119
+
120
+ /** Full UI — click field to open calendar (range DatePicker). */
121
+ export const Full = {
122
+ render: function FullDatePickerStory() {
123
+ const [value, setValue] = useState<DateRange | undefined>(getSampleRange);
124
+
125
+ return (
126
+ <View style={styles.full}>
127
+ <DatePicker
128
+ mode="range"
129
+ locale={sharedCalendarArgs.locale}
130
+ defaultViewDate={sharedCalendarArgs.defaultViewDate}
131
+ placeholder="Ընտրել ժամանակահատված"
132
+ value={value}
133
+ onChange={setValue}
134
+ />
135
+ </View>
136
+ );
137
+ },
138
+ } satisfies StoryObj<DatePickerRangeProps>;
139
+
140
+ /** Static preview — field + calendar always visible (Figma layout). */
141
+ export const FullStatic: Story = {
142
+ render: () => (
143
+ <FullCalendarDemo
144
+ calendarProps={figmaCalendarArgs}
145
+ initialValue={figmaSampleDate}
146
+ />
147
+ ),
148
+ };
149
+
150
+ export const FullWithSelectedDate: Story = {
151
+ render: () => (
152
+ <FullCalendarDemo
153
+ calendarProps={figmaCalendarArgs}
154
+ initialValue={figmaSampleDate}
155
+ />
156
+ ),
157
+ };
158
+
159
+ export const WithSelectedDay: Story = {
160
+ render: function SelectedDayStory() {
161
+ const [value, setValue] = useState<Date | undefined>(new Date(2026, 5, 26));
162
+
163
+ return (
164
+ <View style={styles.panel}>
165
+ <Calendar {...figmaCalendarArgs} value={value} onChange={setValue} />
166
+ </View>
167
+ );
168
+ },
169
+ };
170
+
171
+ export const PanelOnly: Story = {
172
+ render: function PanelOnlyStory() {
173
+ const [value, setValue] = useState<Date | undefined>();
174
+
175
+ return (
176
+ <View style={styles.panel}>
177
+ <Calendar {...sharedCalendarArgs} value={value} onChange={setValue} />
178
+ </View>
179
+ );
180
+ },
181
+ };
182
+
183
+ export const Interactive: Story = {
184
+ render: function InteractiveCalendarStory() {
185
+ const [value, setValue] = useState<Date | undefined>();
186
+
187
+ return (
188
+ <View style={styles.panel}>
189
+ <Calendar {...sharedCalendarArgs} value={value} onChange={setValue} />
190
+ </View>
191
+ );
192
+ },
193
+ };
194
+
195
+ export const WithMinDate: Story = {
196
+ render: function MinDateCalendarStory() {
197
+ const minDate = new Date();
198
+ const [value, setValue] = useState<Date | undefined>(minDate);
199
+
200
+ return (
201
+ <View style={styles.panel}>
202
+ <Calendar
203
+ {...sharedCalendarArgs}
204
+ value={value}
205
+ onChange={setValue}
206
+ minDate={minDate}
207
+ />
208
+ </View>
209
+ );
210
+ },
211
+ };
212
+
213
+ export const Range = {
214
+ render: function RangeCalendarStory() {
215
+ const [value, setValue] = useState<DateRange | undefined>(getSampleRange);
216
+
217
+ return (
218
+ <View style={styles.panel}>
219
+ <Calendar
220
+ locale="en-US"
221
+ defaultViewDate={getCurrentMonthStart()}
222
+ mode="range"
223
+ value={value}
224
+ onChange={setValue}
225
+ />
226
+ </View>
227
+ );
228
+ },
229
+ } satisfies StoryObj<CalendarRangeProps>;
230
+
231
+ export const RangeInteractive = {
232
+ render: function RangeInteractiveCalendarStory() {
233
+ const [value, setValue] = useState<DateRange | undefined>(getSampleRange);
234
+
235
+ return (
236
+ <View style={styles.panel}>
237
+ <Calendar
238
+ locale="en-US"
239
+ defaultViewDate={getCurrentMonthStart()}
240
+ mode="range"
241
+ value={value}
242
+ onChange={setValue}
243
+ />
244
+ </View>
245
+ );
246
+ },
247
+ } satisfies StoryObj<CalendarRangeProps>;
248
+
249
+ /** Click the header to open month picker, then click the year for year picker. */
250
+ export const MonthYearPicker: Story = {
251
+ render: function MonthYearPickerStory() {
252
+ const [value, setValue] = useState<Date | undefined>();
253
+
254
+ return (
255
+ <View style={styles.panel}>
256
+ <Calendar {...sharedCalendarArgs} value={value} onChange={setValue} />
257
+ </View>
258
+ );
259
+ },
260
+ };
261
+
262
+ export const EnglishLocale: Story = {
263
+ render: function EnglishCalendarStory() {
264
+ const [value, setValue] = useState<Date | undefined>();
265
+
266
+ return (
267
+ <View style={styles.panel}>
268
+ <Calendar
269
+ mode="single"
270
+ locale="en-US"
271
+ defaultViewDate={getCurrentMonthStart()}
272
+ value={value}
273
+ onChange={setValue}
274
+ />
275
+ </View>
276
+ );
277
+ },
278
+ };
279
+
280
+ export const ArmenianLocale: Story = {
281
+ render: function ArmenianCalendarStory() {
282
+ const [value, setValue] = useState<Date | undefined>();
283
+
284
+ return (
285
+ <View style={styles.panel}>
286
+ <Calendar
287
+ mode="single"
288
+ locale="hy-AM"
289
+ defaultViewDate={getCurrentMonthStart()}
290
+ value={value}
291
+ onChange={setValue}
292
+ />
293
+ </View>
294
+ );
295
+ },
296
+ };
297
+
298
+ export const RussianLocale: Story = {
299
+ render: function RussianCalendarStory() {
300
+ const [value, setValue] = useState<Date | undefined>();
301
+
302
+ return (
303
+ <View style={styles.panel}>
304
+ <Calendar
305
+ mode="single"
306
+ locale="ru-RU"
307
+ defaultViewDate={getCurrentMonthStart()}
308
+ value={value}
309
+ onChange={setValue}
310
+ />
311
+ </View>
312
+ );
313
+ },
314
+ };
315
+
316
+ const styles = StyleSheet.create({
317
+ full: {
318
+ width: 296,
319
+ maxWidth: "100%",
320
+ gap: CALENDAR_DROPDOWN_GAP,
321
+ },
322
+ iconSlot: {
323
+ width: CALENDAR_FIELD_ICON_SIZE,
324
+ height: CALENDAR_FIELD_ICON_SIZE,
325
+ alignItems: "center",
326
+ justifyContent: "center",
327
+ flexShrink: 0,
328
+ },
329
+ calendarPanel: {
330
+ width: "100%",
331
+ minWidth: 0,
332
+ },
333
+ panel: {
334
+ width: 296,
335
+ maxWidth: "100%",
336
+ },
337
+ });