@scripso-homepad/ui 0.4.2 → 0.4.3
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/{chunk-WDSMJWKC.js → chunk-66WR57JJ.js} +35 -3
- package/dist/chunk-66WR57JJ.js.map +1 -0
- package/dist/index.cjs +2260 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -1
- package/dist/index.d.ts +133 -1
- package/dist/index.js +1960 -97
- package/dist/index.js.map +1 -1
- package/dist/web/index.cjs +30650 -1009
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.css +78 -0
- package/dist/web/index.css.map +1 -0
- package/dist/web/index.d.cts +267 -6
- package/dist/web/index.d.ts +267 -6
- package/dist/web/index.js +30179 -601
- package/dist/web/index.js.map +1 -1
- package/package.json +5 -4
- package/src/components/Calendar.stories.tsx +337 -0
- package/src/components/Calendar.tsx +441 -0
- package/src/components/DatePicker.stories.tsx +219 -0
- package/src/components/DatePicker.tsx +366 -0
- package/src/components/Select.stories.tsx +134 -0
- package/src/components/Select.tsx +462 -0
- package/src/css.d.ts +1 -0
- package/src/icons/CalendarIcon.tsx +28 -0
- package/src/icons/CalendarIcon.web.tsx +34 -0
- package/src/icons/ChevronDownIcon.tsx +11 -4
- package/src/icons/ChevronDownIcon.web.tsx +6 -4
- package/src/icons/ChevronNavIcons.tsx +44 -0
- package/src/icons/ChevronNavIcons.web.tsx +57 -0
- package/src/icons/calendarIconPaths.ts +2 -0
- package/src/icons/chevronDownPath.ts +1 -0
- package/src/icons/chevronNavPaths.ts +2 -0
- package/src/index.ts +41 -0
- package/src/theme/calendar.ts +357 -0
- package/src/theme/select.ts +249 -0
- package/src/utils/calendarDays.ts +273 -0
- package/src/utils/calendarLocaleContext.tsx +95 -0
- package/src/utils/calendarLocalization.ts +187 -0
- package/src/utils/countryDropdownScrollStyles.ts +53 -13
- package/src/utils/formatMultiSelectDisplay.ts +56 -0
- package/src/web/components/Badge.stories.tsx +43 -0
- package/src/web/components/Badge.tsx +35 -0
- package/src/web/components/Pagination.stories.tsx +78 -0
- package/src/web/components/Pagination.tsx +153 -0
- package/src/web/components/StatusBadge.tsx +20 -0
- package/src/web/components/Table.stories.tsx +415 -0
- package/src/web/components/Table.tsx +3 -0
- package/src/web/components/TableActionButton.tsx +67 -0
- package/src/web/components/TableActionsMenu.tsx +208 -0
- package/src/web/components/TableIconText.tsx +23 -0
- package/src/web/components/TableRowStatusActions.tsx +33 -0
- package/src/web/components/TableStatusActionsCell.tsx +29 -0
- package/src/web/components/pagination-utils.ts +39 -0
- package/src/web/components/table/DataTable.tsx +123 -0
- package/src/web/components/table/TablePrimitives.tsx +214 -0
- package/src/web/components/table/TableScrollArea.tsx +51 -0
- package/src/web/components/table/constants.ts +59 -0
- package/src/web/components/table/data-table-class-names.ts +26 -0
- package/src/web/components/table/index.ts +44 -0
- package/src/web/components/table/table-row-context.tsx +19 -0
- package/src/web/components/table/use-horizontal-scroll-state.ts +72 -0
- package/src/web/components/table-scroll.css +81 -0
- package/src/web/hooks/useOnClickOutside.ts +20 -5
- package/src/web/icons/TableMenuActivateIcon.tsx +27 -0
- package/src/web/icons/TableMenuCheckIcon.tsx +27 -0
- package/src/web/icons/TableMenuCloseIcon.tsx +27 -0
- package/src/web/icons/TableMenuEditIcon.tsx +27 -0
- package/src/web/icons/TableMenuInfoIcon.tsx +27 -0
- package/src/web/icons/TableMenuRefreshIcon.tsx +27 -0
- package/src/web/icons/TableMenuSuspendIcon.tsx +34 -0
- package/src/web/icons/TableMenuTrashIcon.tsx +27 -0
- package/src/web/icons/TableMoreIcon.tsx +41 -0
- package/src/web/icons/TableSortIcon.tsx +37 -0
- package/src/web/index.ts +74 -0
- package/src/web/layout/AppHeader.stories.tsx +2 -0
- package/src/web/layout/AppHeader.tsx +18 -14
- package/src/web/layout/DashboardLayout.stories.tsx +1 -0
- package/src/web/layout/DashboardLayout.tsx +4 -4
- package/src/web/layout/story-helpers.tsx +10 -2
- package/dist/chunk-WDSMJWKC.js.map +0 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
export function stripTime(date: Date): Date {
|
|
2
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export type DateRange = {
|
|
6
|
+
start: Date;
|
|
7
|
+
end?: Date;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function normalizeDateRange(range?: DateRange): { start?: Date; end?: Date } {
|
|
11
|
+
if (!range?.start) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const start = stripTime(range.start);
|
|
16
|
+
|
|
17
|
+
if (!range.end) {
|
|
18
|
+
return { start };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const end = stripTime(range.end);
|
|
22
|
+
|
|
23
|
+
if (isBeforeDay(end, start)) {
|
|
24
|
+
return { start: end, end: start };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { start, end };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isInDateRange(date: Date, start?: Date, end?: Date): boolean {
|
|
31
|
+
if (!start) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const day = stripTime(date);
|
|
36
|
+
|
|
37
|
+
if (!end) {
|
|
38
|
+
return isSameDay(day, start);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return !isBeforeDay(day, start) && !isAfterDay(day, end);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function selectRangeDate(clicked: Date, current?: DateRange): DateRange {
|
|
45
|
+
const clickedDate = stripTime(clicked);
|
|
46
|
+
|
|
47
|
+
if (!current?.start || current.end) {
|
|
48
|
+
return { start: clickedDate };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const start = stripTime(current.start);
|
|
52
|
+
|
|
53
|
+
if (isSameDay(clickedDate, start)) {
|
|
54
|
+
return { start, end: clickedDate };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (isBeforeDay(clickedDate, start)) {
|
|
58
|
+
return { start: clickedDate };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { start, end: clickedDate };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isSameDay(left: Date, right: Date): boolean {
|
|
65
|
+
return (
|
|
66
|
+
left.getFullYear() === right.getFullYear() &&
|
|
67
|
+
left.getMonth() === right.getMonth() &&
|
|
68
|
+
left.getDate() === right.getDate()
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isBeforeDay(left: Date, right: Date): boolean {
|
|
73
|
+
return stripTime(left).getTime() < stripTime(right).getTime();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isAfterDay(left: Date, right: Date): boolean {
|
|
77
|
+
return stripTime(left).getTime() > stripTime(right).getTime();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function addMonths(date: Date, amount: number): Date {
|
|
81
|
+
return new Date(date.getFullYear(), date.getMonth() + amount, 1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isDateDisabled(
|
|
85
|
+
date: Date,
|
|
86
|
+
minDate?: Date,
|
|
87
|
+
maxDate?: Date,
|
|
88
|
+
): boolean {
|
|
89
|
+
if (minDate && isBeforeDay(date, minDate)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (maxDate && isAfterDay(date, maxDate)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type CalendarDayCell = {
|
|
101
|
+
date: Date;
|
|
102
|
+
inCurrentMonth: boolean;
|
|
103
|
+
isToday: boolean;
|
|
104
|
+
isSelected: boolean;
|
|
105
|
+
isRangeStart: boolean;
|
|
106
|
+
isRangeEnd: boolean;
|
|
107
|
+
isInRange: boolean;
|
|
108
|
+
isPast: boolean;
|
|
109
|
+
isDisabled: boolean;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const CALENDAR_VISIBLE_DAY_COUNT = 42;
|
|
113
|
+
const MONDAY_WEEK_START = 1;
|
|
114
|
+
|
|
115
|
+
export function buildCalendarDays({
|
|
116
|
+
viewDate,
|
|
117
|
+
selectedDate,
|
|
118
|
+
selectedRange,
|
|
119
|
+
today,
|
|
120
|
+
minDate,
|
|
121
|
+
maxDate,
|
|
122
|
+
}: {
|
|
123
|
+
viewDate: Date;
|
|
124
|
+
selectedDate?: Date;
|
|
125
|
+
selectedRange?: DateRange;
|
|
126
|
+
today: Date;
|
|
127
|
+
minDate?: Date;
|
|
128
|
+
maxDate?: Date;
|
|
129
|
+
}): CalendarDayCell[] {
|
|
130
|
+
const { start: rangeStart, end: rangeEnd } = normalizeDateRange(selectedRange);
|
|
131
|
+
const year = viewDate.getFullYear();
|
|
132
|
+
const month = viewDate.getMonth();
|
|
133
|
+
const firstOfMonth = new Date(year, month, 1);
|
|
134
|
+
const startOffset = (firstOfMonth.getDay() - MONDAY_WEEK_START + 7) % 7;
|
|
135
|
+
const gridStart = new Date(year, month, 1 - startOffset);
|
|
136
|
+
const days: CalendarDayCell[] = [];
|
|
137
|
+
|
|
138
|
+
for (let index = 0; index < CALENDAR_VISIBLE_DAY_COUNT; index += 1) {
|
|
139
|
+
const date = new Date(
|
|
140
|
+
gridStart.getFullYear(),
|
|
141
|
+
gridStart.getMonth(),
|
|
142
|
+
gridStart.getDate() + index,
|
|
143
|
+
);
|
|
144
|
+
const inCurrentMonth = date.getMonth() === month;
|
|
145
|
+
const isPast = inCurrentMonth && isBeforeDay(date, today);
|
|
146
|
+
const isDisabled = isDateDisabled(date, minDate, maxDate);
|
|
147
|
+
const isRangeStart = rangeStart ? isSameDay(date, rangeStart) : false;
|
|
148
|
+
const isRangeEnd = rangeEnd ? isSameDay(date, rangeEnd) : false;
|
|
149
|
+
const inSelectedRange =
|
|
150
|
+
rangeStart && rangeEnd ? isInDateRange(date, rangeStart, rangeEnd) : false;
|
|
151
|
+
const isInRange = inSelectedRange && !isRangeStart && !isRangeEnd;
|
|
152
|
+
const isSelected = selectedDate
|
|
153
|
+
? isSameDay(date, selectedDate)
|
|
154
|
+
: inSelectedRange || Boolean(rangeStart && !rangeEnd && isRangeStart);
|
|
155
|
+
|
|
156
|
+
days.push({
|
|
157
|
+
date,
|
|
158
|
+
inCurrentMonth,
|
|
159
|
+
isToday: isSameDay(date, today),
|
|
160
|
+
isSelected,
|
|
161
|
+
isRangeStart,
|
|
162
|
+
isRangeEnd,
|
|
163
|
+
isInRange,
|
|
164
|
+
isPast,
|
|
165
|
+
isDisabled,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return days;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function formatCalendarMonthYear(date: Date, locale: string): string {
|
|
173
|
+
const formatted = new Intl.DateTimeFormat(locale, {
|
|
174
|
+
month: "short",
|
|
175
|
+
year: "numeric",
|
|
176
|
+
}).format(date);
|
|
177
|
+
|
|
178
|
+
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function formatCalendarYear(year: number, locale: string): string {
|
|
182
|
+
return new Intl.DateTimeFormat(locale, { year: "numeric" }).format(new Date(year, 0, 1));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function getMonthLabels(locale: string): string[] {
|
|
186
|
+
return Array.from({ length: 12 }, (_, month) => {
|
|
187
|
+
const label = new Intl.DateTimeFormat(locale, { month: "short" }).format(
|
|
188
|
+
new Date(2024, month, 1),
|
|
189
|
+
);
|
|
190
|
+
const normalized = label.replace(/\./g, "");
|
|
191
|
+
|
|
192
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function getYearPageStart(year: number, yearsPerPage = 12): number {
|
|
197
|
+
return Math.floor(year / yearsPerPage) * yearsPerPage;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function isMonthDisabled(
|
|
201
|
+
year: number,
|
|
202
|
+
month: number,
|
|
203
|
+
minDate?: Date,
|
|
204
|
+
maxDate?: Date,
|
|
205
|
+
): boolean {
|
|
206
|
+
const firstDay = new Date(year, month, 1);
|
|
207
|
+
const lastDay = new Date(year, month + 1, 0);
|
|
208
|
+
|
|
209
|
+
if (minDate && isBeforeDay(lastDay, minDate)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (maxDate && isAfterDay(firstDay, maxDate)) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function isYearDisabled(year: number, minDate?: Date, maxDate?: Date): boolean {
|
|
221
|
+
const firstDay = new Date(year, 0, 1);
|
|
222
|
+
const lastDay = new Date(year, 11, 31);
|
|
223
|
+
|
|
224
|
+
if (minDate && isBeforeDay(lastDay, minDate)) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (maxDate && isAfterDay(firstDay, maxDate)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function formatCalendarDate(date: Date, locale: string): string {
|
|
236
|
+
return new Intl.DateTimeFormat(locale, {
|
|
237
|
+
day: "numeric",
|
|
238
|
+
month: "long",
|
|
239
|
+
year: "numeric",
|
|
240
|
+
}).format(date);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function formatCalendarDateRange(
|
|
244
|
+
range: DateRange,
|
|
245
|
+
locale: string,
|
|
246
|
+
separator = " – ",
|
|
247
|
+
): string {
|
|
248
|
+
const { start, end } = normalizeDateRange(range);
|
|
249
|
+
|
|
250
|
+
if (!start) {
|
|
251
|
+
return "";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!end) {
|
|
255
|
+
return formatCalendarDate(start, locale);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return `${formatCalendarDate(start, locale)}${separator}${formatCalendarDate(end, locale)}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @deprecated Use `resolveWeekdayLabels` from `calendarLocalization`; calendars are Monday-first. */
|
|
262
|
+
export function getDefaultWeekdayLabels(locale: string): string[] {
|
|
263
|
+
const labels: string[] = [];
|
|
264
|
+
const formatter = new Intl.DateTimeFormat(locale, { weekday: "short" });
|
|
265
|
+
|
|
266
|
+
for (let index = 0; index < 7; index += 1) {
|
|
267
|
+
const day = new Date(2024, 0, index + 1);
|
|
268
|
+
const label = formatter.format(day).replace(/\./g, "").toUpperCase();
|
|
269
|
+
labels.push(label.slice(0, 2));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return labels;
|
|
273
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
|
|
2
|
+
import { resolveCalendarLocale, type CalendarLocale } from "./calendarLocalization";
|
|
3
|
+
|
|
4
|
+
const LOCALE_STORAGE_KEY = "homepad.locale";
|
|
5
|
+
const LOCALE_CHANGE_EVENT = "homepad:locale-change";
|
|
6
|
+
const DEFAULT_CALENDAR_LOCALE: CalendarLocale = "hy-AM";
|
|
7
|
+
|
|
8
|
+
const CalendarLocaleContext = createContext<string | undefined>(undefined);
|
|
9
|
+
|
|
10
|
+
export type CalendarLocaleProviderProps = {
|
|
11
|
+
locale: string;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function CalendarLocaleProvider({ locale, children }: CalendarLocaleProviderProps) {
|
|
16
|
+
return (
|
|
17
|
+
<CalendarLocaleContext.Provider value={locale}>{children}</CalendarLocaleContext.Provider>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeProjectLocale(locale: string | null | undefined): CalendarLocale | null {
|
|
22
|
+
if (!locale) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return resolveCalendarLocale(locale);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readBrowserLocale(): CalendarLocale | null {
|
|
30
|
+
if (typeof window === "undefined") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let storedLocale: CalendarLocale | null = null;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
storedLocale = normalizeProjectLocale(window.localStorage.getItem(LOCALE_STORAGE_KEY));
|
|
38
|
+
} catch {
|
|
39
|
+
storedLocale = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (storedLocale) {
|
|
43
|
+
return storedLocale;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const documentLocale = normalizeProjectLocale(window.document?.documentElement.lang);
|
|
47
|
+
if (documentLocale) {
|
|
48
|
+
return documentLocale;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return normalizeProjectLocale(window.navigator.language);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function useCalendarLocale(locale?: string): CalendarLocale {
|
|
55
|
+
const contextLocale = useContext(CalendarLocaleContext);
|
|
56
|
+
const controlledLocale = locale ?? contextLocale;
|
|
57
|
+
const [browserLocale, setBrowserLocale] = useState<CalendarLocale>(() =>
|
|
58
|
+
readBrowserLocale() ?? DEFAULT_CALENDAR_LOCALE,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (controlledLocale !== undefined || typeof window === "undefined") {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const syncLocale = () => {
|
|
67
|
+
setBrowserLocale(readBrowserLocale() ?? DEFAULT_CALENDAR_LOCALE);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleLocaleChange = (event: Event) => {
|
|
71
|
+
const customEvent = event as CustomEvent<string>;
|
|
72
|
+
const nextLocale = normalizeProjectLocale(customEvent.detail);
|
|
73
|
+
|
|
74
|
+
if (nextLocale) {
|
|
75
|
+
setBrowserLocale(nextLocale);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
syncLocale();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
window.addEventListener(LOCALE_CHANGE_EVENT, handleLocaleChange);
|
|
83
|
+
window.addEventListener("storage", syncLocale);
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
window.removeEventListener(LOCALE_CHANGE_EVENT, handleLocaleChange);
|
|
87
|
+
window.removeEventListener("storage", syncLocale);
|
|
88
|
+
};
|
|
89
|
+
}, [controlledLocale]);
|
|
90
|
+
|
|
91
|
+
return useMemo(
|
|
92
|
+
() => normalizeProjectLocale(controlledLocale) ?? browserLocale,
|
|
93
|
+
[browserLocale, controlledLocale],
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
export type CalendarLocale = "hy-AM" | "en-US" | "ru-RU";
|
|
2
|
+
|
|
3
|
+
export type CalendarTranslationKey =
|
|
4
|
+
| "datePlaceholder"
|
|
5
|
+
| "rangePlaceholder"
|
|
6
|
+
| "previousMonth"
|
|
7
|
+
| "nextMonth"
|
|
8
|
+
| "previousYear"
|
|
9
|
+
| "nextYear"
|
|
10
|
+
| "previousYears"
|
|
11
|
+
| "nextYears"
|
|
12
|
+
| "selectMonth"
|
|
13
|
+
| "selectYear"
|
|
14
|
+
| "closeCalendar";
|
|
15
|
+
|
|
16
|
+
export type CalendarTranslations = Record<CalendarTranslationKey, string> & {
|
|
17
|
+
/** Weekday labels in Monday-first order. */
|
|
18
|
+
weekdays: string[];
|
|
19
|
+
/** Short month labels used in compact calendar headers and month picker cells. */
|
|
20
|
+
monthsShort: string[];
|
|
21
|
+
/** Full month labels available for consumers that need long-form display. */
|
|
22
|
+
monthsLong: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const calendarTranslations: Record<CalendarLocale, CalendarTranslations> = {
|
|
26
|
+
"hy-AM": {
|
|
27
|
+
weekdays: ["ԵՐ", "ԵՔ", "ՉՐ", "ՀՆ", "ՈՒՐ", "ՇԲ", "ԿՐ"],
|
|
28
|
+
monthsShort: [
|
|
29
|
+
"Հնվ",
|
|
30
|
+
"Փտվ",
|
|
31
|
+
"Մրտ",
|
|
32
|
+
"Ապր",
|
|
33
|
+
"Մյս",
|
|
34
|
+
"Հուն",
|
|
35
|
+
"Հուլ",
|
|
36
|
+
"Օգս",
|
|
37
|
+
"Սեպ",
|
|
38
|
+
"Հոկ",
|
|
39
|
+
"Նոյ",
|
|
40
|
+
"Դեկ",
|
|
41
|
+
],
|
|
42
|
+
monthsLong: [
|
|
43
|
+
"Հունվար",
|
|
44
|
+
"Փետրվար",
|
|
45
|
+
"Մարտ",
|
|
46
|
+
"Ապրիլ",
|
|
47
|
+
"Մայիս",
|
|
48
|
+
"Հունիս",
|
|
49
|
+
"Հուլիս",
|
|
50
|
+
"Օգոստոս",
|
|
51
|
+
"Սեպտեմբեր",
|
|
52
|
+
"Հոկտեմբեր",
|
|
53
|
+
"Նոյեմբեր",
|
|
54
|
+
"Դեկտեմբեր",
|
|
55
|
+
],
|
|
56
|
+
datePlaceholder: "Ընտրել ամսաթիվ",
|
|
57
|
+
rangePlaceholder: "Ընտրել ժամանակահատված",
|
|
58
|
+
previousMonth: "Նախորդ ամիս",
|
|
59
|
+
nextMonth: "Հաջորդ ամիս",
|
|
60
|
+
previousYear: "Նախորդ տարի",
|
|
61
|
+
nextYear: "Հաջորդ տարի",
|
|
62
|
+
previousYears: "Նախորդ տարիներ",
|
|
63
|
+
nextYears: "Հաջորդ տարիներ",
|
|
64
|
+
selectMonth: "Ընտրել ամիս",
|
|
65
|
+
selectYear: "Ընտրել տարի",
|
|
66
|
+
closeCalendar: "Փակել օրացույցը",
|
|
67
|
+
},
|
|
68
|
+
"en-US": {
|
|
69
|
+
weekdays: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
70
|
+
monthsShort: [
|
|
71
|
+
"Jan",
|
|
72
|
+
"Feb",
|
|
73
|
+
"Mar",
|
|
74
|
+
"Apr",
|
|
75
|
+
"May",
|
|
76
|
+
"Jun",
|
|
77
|
+
"Jul",
|
|
78
|
+
"Aug",
|
|
79
|
+
"Sep",
|
|
80
|
+
"Oct",
|
|
81
|
+
"Nov",
|
|
82
|
+
"Dec",
|
|
83
|
+
],
|
|
84
|
+
monthsLong: [
|
|
85
|
+
"January",
|
|
86
|
+
"February",
|
|
87
|
+
"March",
|
|
88
|
+
"April",
|
|
89
|
+
"May",
|
|
90
|
+
"June",
|
|
91
|
+
"July",
|
|
92
|
+
"August",
|
|
93
|
+
"September",
|
|
94
|
+
"October",
|
|
95
|
+
"November",
|
|
96
|
+
"December",
|
|
97
|
+
],
|
|
98
|
+
datePlaceholder: "Select date",
|
|
99
|
+
rangePlaceholder: "Select date range",
|
|
100
|
+
previousMonth: "Previous month",
|
|
101
|
+
nextMonth: "Next month",
|
|
102
|
+
previousYear: "Previous year",
|
|
103
|
+
nextYear: "Next year",
|
|
104
|
+
previousYears: "Previous years",
|
|
105
|
+
nextYears: "Next years",
|
|
106
|
+
selectMonth: "Select month",
|
|
107
|
+
selectYear: "Select year",
|
|
108
|
+
closeCalendar: "Close calendar",
|
|
109
|
+
},
|
|
110
|
+
"ru-RU": {
|
|
111
|
+
weekdays: ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"],
|
|
112
|
+
monthsShort: [
|
|
113
|
+
"Янв",
|
|
114
|
+
"Фев",
|
|
115
|
+
"Мар",
|
|
116
|
+
"Апр",
|
|
117
|
+
"Май",
|
|
118
|
+
"Июн",
|
|
119
|
+
"Июл",
|
|
120
|
+
"Авг",
|
|
121
|
+
"Сен",
|
|
122
|
+
"Окт",
|
|
123
|
+
"Ноя",
|
|
124
|
+
"Дек",
|
|
125
|
+
],
|
|
126
|
+
monthsLong: [
|
|
127
|
+
"Январь",
|
|
128
|
+
"Февраль",
|
|
129
|
+
"Март",
|
|
130
|
+
"Апрель",
|
|
131
|
+
"Май",
|
|
132
|
+
"Июнь",
|
|
133
|
+
"Июль",
|
|
134
|
+
"Август",
|
|
135
|
+
"Сентябрь",
|
|
136
|
+
"Октябрь",
|
|
137
|
+
"Ноябрь",
|
|
138
|
+
"Декабрь",
|
|
139
|
+
],
|
|
140
|
+
datePlaceholder: "Выбрать дату",
|
|
141
|
+
rangePlaceholder: "Выбрать период",
|
|
142
|
+
previousMonth: "Предыдущий месяц",
|
|
143
|
+
nextMonth: "Следующий месяц",
|
|
144
|
+
previousYear: "Предыдущий год",
|
|
145
|
+
nextYear: "Следующий год",
|
|
146
|
+
previousYears: "Предыдущие годы",
|
|
147
|
+
nextYears: "Следующие годы",
|
|
148
|
+
selectMonth: "Выбрать месяц",
|
|
149
|
+
selectYear: "Выбрать год",
|
|
150
|
+
closeCalendar: "Закрыть календарь",
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export function resolveCalendarLocale(locale: string): CalendarLocale {
|
|
155
|
+
const normalizedLocale = locale.toLowerCase();
|
|
156
|
+
|
|
157
|
+
if (normalizedLocale.startsWith("hy")) {
|
|
158
|
+
return "hy-AM";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (normalizedLocale.startsWith("ru")) {
|
|
162
|
+
return "ru-RU";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return "en-US";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function getCalendarTranslations(locale: string): CalendarTranslations {
|
|
169
|
+
return calendarTranslations[resolveCalendarLocale(locale)];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function resolveWeekdayLabels(locale: string): string[] {
|
|
173
|
+
return getCalendarTranslations(locale).weekdays;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getCalendarMonthLabels(
|
|
177
|
+
locale: string,
|
|
178
|
+
width: "short" | "long" = "short",
|
|
179
|
+
): string[] {
|
|
180
|
+
const translations = getCalendarTranslations(locale);
|
|
181
|
+
|
|
182
|
+
return width === "short" ? translations.monthsShort : translations.monthsLong;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function formatLocalizedCalendarMonthYear(date: Date, locale: string): string {
|
|
186
|
+
return `${getCalendarMonthLabels(locale)[date.getMonth()]} ${date.getFullYear()}`;
|
|
187
|
+
}
|
|
@@ -2,51 +2,91 @@ import { Platform } from "react-native";
|
|
|
2
2
|
import { colors } from "../theme/tokens";
|
|
3
3
|
|
|
4
4
|
export const COUNTRY_DROPDOWN_SCROLL_CLASS = "hp-country-dropdown-scroll";
|
|
5
|
+
export const SELECT_DROPDOWN_SCROLL_CLASS = "hp-select-dropdown-scroll";
|
|
5
6
|
|
|
6
|
-
const STYLE_ID = "hp-
|
|
7
|
+
const STYLE_ID = "hp-dropdown-scroll-styles";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
function createDropdownScrollCss(className: string): string {
|
|
10
|
+
return `
|
|
11
|
+
.${className} {
|
|
10
12
|
scrollbar-width: thin;
|
|
11
13
|
scrollbar-color: ${colors.stormGray300} transparent;
|
|
12
14
|
overflow-y: auto;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
.${
|
|
17
|
+
.${className}::-webkit-scrollbar {
|
|
16
18
|
width: 4px;
|
|
19
|
+
-webkit-appearance: none;
|
|
20
|
+
appearance: none;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
.${
|
|
23
|
+
.${className}::-webkit-scrollbar-corner {
|
|
20
24
|
background: transparent;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
.${
|
|
24
|
-
.${
|
|
25
|
-
.${
|
|
26
|
-
.${
|
|
27
|
+
.${className}::-webkit-scrollbar-button,
|
|
28
|
+
.${className}::-webkit-scrollbar-button:single-button,
|
|
29
|
+
.${className}::-webkit-scrollbar-button:double-button,
|
|
30
|
+
.${className}::-webkit-scrollbar-button:horizontal:decrement,
|
|
31
|
+
.${className}::-webkit-scrollbar-button:horizontal:increment,
|
|
32
|
+
.${className}::-webkit-scrollbar-button:horizontal:start:decrement,
|
|
33
|
+
.${className}::-webkit-scrollbar-button:horizontal:end:increment,
|
|
34
|
+
.${className}::-webkit-scrollbar-button:vertical:decrement,
|
|
35
|
+
.${className}::-webkit-scrollbar-button:vertical:increment,
|
|
36
|
+
.${className}::-webkit-scrollbar-button:vertical:start:decrement,
|
|
37
|
+
.${className}::-webkit-scrollbar-button:vertical:end:increment,
|
|
38
|
+
.${className}::-webkit-scrollbar-button:start:decrement,
|
|
39
|
+
.${className}::-webkit-scrollbar-button:end:increment,
|
|
40
|
+
.${className}::-webkit-scrollbar-button:decrement,
|
|
41
|
+
.${className}::-webkit-scrollbar-button:increment {
|
|
27
42
|
display: none;
|
|
28
43
|
width: 0;
|
|
29
44
|
height: 0;
|
|
45
|
+
max-width: 0;
|
|
46
|
+
max-height: 0;
|
|
47
|
+
min-width: 0;
|
|
48
|
+
min-height: 0;
|
|
49
|
+
padding: 0;
|
|
50
|
+
margin: 0;
|
|
51
|
+
border: 0;
|
|
52
|
+
background: transparent;
|
|
30
53
|
-webkit-appearance: none;
|
|
31
54
|
appearance: none;
|
|
32
55
|
}
|
|
33
56
|
|
|
34
|
-
.${
|
|
57
|
+
.${className}::-webkit-scrollbar-track {
|
|
35
58
|
background: transparent;
|
|
36
59
|
}
|
|
37
60
|
|
|
38
|
-
.${
|
|
61
|
+
.${className}::-webkit-scrollbar-thumb {
|
|
39
62
|
background-color: ${colors.stormGray300};
|
|
40
63
|
border-radius: 9999px;
|
|
41
64
|
}
|
|
42
65
|
`;
|
|
66
|
+
}
|
|
43
67
|
|
|
44
|
-
export
|
|
68
|
+
export const dropdownScrollCss = [
|
|
69
|
+
createDropdownScrollCss(COUNTRY_DROPDOWN_SCROLL_CLASS),
|
|
70
|
+
createDropdownScrollCss(SELECT_DROPDOWN_SCROLL_CLASS),
|
|
71
|
+
].join("\n");
|
|
72
|
+
|
|
73
|
+
/** @deprecated Use `dropdownScrollCss` */
|
|
74
|
+
export const countryDropdownScrollCss = dropdownScrollCss;
|
|
75
|
+
|
|
76
|
+
export function ensureDropdownScrollStyles(): void {
|
|
45
77
|
if (Platform.OS !== "web" || typeof document === "undefined") return;
|
|
46
78
|
if (document.getElementById(STYLE_ID)) return;
|
|
47
79
|
|
|
48
80
|
const style = document.createElement("style");
|
|
49
81
|
style.id = STYLE_ID;
|
|
50
|
-
style.textContent =
|
|
82
|
+
style.textContent = dropdownScrollCss;
|
|
51
83
|
document.head.appendChild(style);
|
|
52
84
|
}
|
|
85
|
+
|
|
86
|
+
export function ensureCountryDropdownScrollStyles(): void {
|
|
87
|
+
ensureDropdownScrollStyles();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function ensureSelectDropdownScrollStyles(): void {
|
|
91
|
+
ensureDropdownScrollStyles();
|
|
92
|
+
}
|