@open-mercato/ui 0.6.1-develop.3256.1.fe3dec2464 → 0.6.1-develop.3276.1.1fcd145078
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/.turbo/turbo-build.log +1 -1
- package/dist/backend/CrudForm.js +46 -129
- package/dist/backend/CrudForm.js.map +3 -3
- package/dist/backend/DataTable.js +10 -3
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/EmptyState.js +1 -47
- package/dist/backend/EmptyState.js.map +2 -2
- package/dist/backend/FilterOverlay.js +31 -26
- package/dist/backend/FilterOverlay.js.map +2 -2
- package/dist/backend/FlashMessages.js +23 -25
- package/dist/backend/FlashMessages.js.map +2 -2
- package/dist/backend/JsonBuilder.js +4 -2
- package/dist/backend/JsonBuilder.js.map +2 -2
- package/dist/backend/LogList.js +49 -0
- package/dist/backend/LogList.js.map +7 -0
- package/dist/backend/crud/CollapsibleGroup.js +13 -3
- package/dist/backend/crud/CollapsibleGroup.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +80 -32
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +60 -6
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/DatePicker.js +6 -142
- package/dist/backend/inputs/DatePicker.js.map +2 -2
- package/dist/backend/inputs/DateTimePicker.js +6 -182
- package/dist/backend/inputs/DateTimePicker.js.map +2 -2
- package/dist/backend/inputs/TagsInput.js +16 -19
- package/dist/backend/inputs/TagsInput.js.map +2 -2
- package/dist/backend/inputs/TimeInput.js +1 -1
- package/dist/backend/inputs/TimeInput.js.map +2 -2
- package/dist/backend/inputs/TimePicker.js +83 -72
- package/dist/backend/inputs/TimePicker.js.map +2 -2
- package/dist/backend/utils/customFieldForms.js +2 -2
- package/dist/backend/utils/customFieldForms.js.map +1 -1
- package/dist/portal/components/PortalEmptyState.js +13 -9
- package/dist/portal/components/PortalEmptyState.js.map +2 -2
- package/dist/primitives/accordion.js +137 -0
- package/dist/primitives/accordion.js.map +7 -0
- package/dist/primitives/alert.js +180 -12
- package/dist/primitives/alert.js.map +2 -2
- package/dist/primitives/calendar.js +122 -32
- package/dist/primitives/calendar.js.map +2 -2
- package/dist/primitives/compact-select.js +47 -0
- package/dist/primitives/compact-select.js.map +7 -0
- package/dist/primitives/counter-input.js +174 -0
- package/dist/primitives/counter-input.js.map +7 -0
- package/dist/primitives/date-picker-helpers.js +79 -0
- package/dist/primitives/date-picker-helpers.js.map +7 -0
- package/dist/primitives/date-picker.js +216 -0
- package/dist/primitives/date-picker.js.map +7 -0
- package/dist/primitives/date-range-picker.js +239 -0
- package/dist/primitives/date-range-picker.js.map +7 -0
- package/dist/primitives/digit-input.js +157 -0
- package/dist/primitives/digit-input.js.map +7 -0
- package/dist/primitives/empty-state.js +93 -0
- package/dist/primitives/empty-state.js.map +7 -0
- package/dist/primitives/inline-input.js +29 -0
- package/dist/primitives/inline-input.js.map +7 -0
- package/dist/primitives/inline-select.js +41 -0
- package/dist/primitives/inline-select.js.map +7 -0
- package/dist/primitives/notification-stack.js +116 -0
- package/dist/primitives/notification-stack.js.map +7 -0
- package/dist/primitives/notification.js +62 -0
- package/dist/primitives/notification.js.map +7 -0
- package/dist/primitives/rich-editor.js +1230 -0
- package/dist/primitives/rich-editor.js.map +7 -0
- package/dist/primitives/select.js +1 -0
- package/dist/primitives/select.js.map +2 -2
- package/dist/primitives/skeleton.js +55 -0
- package/dist/primitives/skeleton.js.map +7 -0
- package/dist/primitives/tag-input.js +186 -0
- package/dist/primitives/tag-input.js.map +7 -0
- package/dist/primitives/tag.js +66 -13
- package/dist/primitives/tag.js.map +2 -2
- package/dist/primitives/time-picker.js +793 -0
- package/dist/primitives/time-picker.js.map +7 -0
- package/package.json +4 -3
- package/src/backend/CrudForm.tsx +61 -113
- package/src/backend/DataTable.tsx +17 -5
- package/src/backend/EmptyState.tsx +13 -63
- package/src/backend/FilterOverlay.tsx +38 -22
- package/src/backend/FlashMessages.tsx +23 -24
- package/src/backend/JsonBuilder.tsx +4 -2
- package/src/backend/LogList.tsx +109 -0
- package/src/backend/__tests__/CrudForm.datetime.test.tsx +12 -7
- package/src/backend/__tests__/FilterOverlay.dateRange.test.tsx +89 -0
- package/src/backend/__tests__/LogList.test.tsx +163 -0
- package/src/backend/crud/CollapsibleGroup.tsx +17 -3
- package/src/backend/detail/InlineEditors.tsx +78 -25
- package/src/backend/detail/__tests__/InlineEditors.test.tsx +81 -1
- package/src/backend/inputs/ComboboxInput.tsx +81 -9
- package/src/backend/inputs/DatePicker.tsx +28 -155
- package/src/backend/inputs/DateTimePicker.tsx +25 -193
- package/src/backend/inputs/TagsInput.tsx +10 -13
- package/src/backend/inputs/TimeInput.tsx +2 -1
- package/src/backend/inputs/TimePicker.tsx +99 -70
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +133 -0
- package/src/backend/inputs/__tests__/TimePicker.test.tsx +2 -2
- package/src/backend/utils/__tests__/richTextSanitizer.test.ts +5 -2
- package/src/backend/utils/customFieldForms.ts +2 -2
- package/src/portal/components/PortalEmptyState.tsx +27 -22
- package/src/primitives/__tests__/accordion.test.tsx +197 -0
- package/src/primitives/__tests__/alert.test.tsx +204 -0
- package/src/primitives/__tests__/compact-select.test.tsx +98 -0
- package/src/primitives/__tests__/counter-input.test.tsx +165 -0
- package/src/primitives/__tests__/date-picker.test.tsx +221 -0
- package/src/primitives/__tests__/date-range-picker.test.tsx +173 -0
- package/src/primitives/__tests__/digit-input.test.tsx +168 -0
- package/src/primitives/__tests__/empty-state.test.tsx +154 -0
- package/src/primitives/__tests__/inline-input.test.tsx +100 -0
- package/src/primitives/__tests__/inline-select.test.tsx +103 -0
- package/src/primitives/__tests__/notification-stack.test.tsx +174 -0
- package/src/primitives/__tests__/notification.test.tsx +119 -0
- package/src/primitives/__tests__/rich-editor.test.tsx +411 -0
- package/src/primitives/__tests__/skeleton.test.tsx +76 -0
- package/src/primitives/__tests__/tag-input.test.tsx +198 -0
- package/src/primitives/__tests__/tag.test.tsx +123 -0
- package/src/primitives/__tests__/time-picker.test.tsx +392 -0
- package/src/primitives/accordion.tsx +225 -0
- package/src/primitives/alert.tsx +234 -24
- package/src/primitives/calendar.tsx +146 -38
- package/src/primitives/compact-select.tsx +59 -0
- package/src/primitives/counter-input.tsx +211 -0
- package/src/primitives/date-picker-helpers.ts +96 -0
- package/src/primitives/date-picker.tsx +290 -0
- package/src/primitives/date-range-picker.tsx +281 -0
- package/src/primitives/digit-input.tsx +208 -0
- package/src/primitives/empty-state.tsx +140 -0
- package/src/primitives/inline-input.tsx +55 -0
- package/src/primitives/inline-select.tsx +74 -0
- package/src/primitives/notification-stack.tsx +185 -0
- package/src/primitives/notification.tsx +136 -0
- package/src/primitives/rich-editor.tsx +1578 -0
- package/src/primitives/select.tsx +1 -0
- package/src/primitives/skeleton.tsx +70 -0
- package/src/primitives/tag-input.tsx +223 -0
- package/src/primitives/tag.tsx +69 -10
- package/src/primitives/time-picker.tsx +1059 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/date-picker.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport { LinkButton } from './link-button'\nimport { TimeInput } from '../backend/inputs/TimeInput'\n\nexport type DatePickerFooter = 'apply-cancel' | 'today-clear' | 'none'\n\nexport type DatePickerProps = {\n value?: Date | null\n onChange: (value: Date | null) => void\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n /**\n * Footer mode in the popover.\n * - `'apply-cancel'` (default, Figma-aligned): selecting a day stages a draft;\n * Apply commits and closes; Cancel reverts and closes.\n * - `'today-clear'`: legacy behavior \u2014 Today commits today, Clear sets null.\n * - `'none'`: no footer; selecting a day commits immediately and closes.\n */\n footer?: DatePickerFooter\n /**\n * Whether selecting a day commits the value and closes the popover.\n * - In `footer='apply-cancel'`: ignored (commit happens via Apply).\n * - In `footer='today-clear'`: default `true` for backward compatibility.\n * - In `footer='none'`: always `true` (popover always closes after selection).\n */\n closeOnSelect?: boolean\n /**\n * Whether to show the \"Today\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showTodayButton?: boolean\n /**\n * Whether to show the \"Clear\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showClearButton?: boolean\n withTime?: boolean\n minuteStep?: number\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n locale?: Locale\n displayFormat?: string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nfunction deriveDisplayFormat(locale: Locale | undefined, withTime: boolean): string {\n const code = locale?.code?.split('-')[0]?.toLowerCase() ?? ''\n const dateFmt = code && DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy' : 'MMM d, yyyy'\n return withTime ? `${dateFmt} HH:mm` : dateFmt\n}\n\nfunction extractTime(date: Date): string {\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`\n}\n\nfunction applyTimeToDate(base: Date, time: string): Date {\n const [hourStr = '0', minuteStr = '0'] = time.split(':')\n const hour = parseInt(hourStr, 10)\n const minute = parseInt(minuteStr, 10)\n const next = new Date(base)\n next.setHours(isNaN(hour) ? 0 : hour, isNaN(minute) ? 0 : minute, 0, 0)\n return next\n}\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(0, 0, 0, 0)\n return next\n}\n\nexport function DatePicker({\n value,\n onChange,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n footer = 'apply-cancel',\n closeOnSelect,\n showTodayButton = true,\n showClearButton = true,\n withTime = false,\n minuteStep = 1,\n align = 'start',\n minDate,\n maxDate,\n locale,\n displayFormat,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DatePickerProps) {\n const resolvedCloseOnSelect = closeOnSelect ?? footer === 'today-clear'\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<Date | null>(value ?? null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = footer === 'apply-cancel'\n\n React.useEffect(() => {\n if (open) setDraft(value ?? null)\n }, [open, value])\n\n const resolvedFormat = displayFormat ?? deriveDisplayFormat(locale, withTime)\n const placeholderText = placeholder ?? (\n withTime\n ? t('ui.dateTimePicker.placeholder', 'Pick date and time')\n : t('ui.datePicker.placeholder', 'Pick a date')\n )\n\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n try {\n return format(value, resolvedFormat, locale ? { locale } : undefined)\n } catch {\n return null\n }\n }, [value, resolvedFormat, locale])\n\n const handleDaySelect = React.useCallback(\n (day: Date | undefined) => {\n if (!day || !isInteractive) return\n const reference = withTime ? (useDraft ? draft : value) : null\n const base = withTime\n ? applyTimeToDate(day, reference ? extractTime(reference) : '00:00')\n : startOfDay(day)\n if (useDraft) {\n setDraft(base)\n } else {\n onChange(base)\n if (footer === 'none' || resolvedCloseOnSelect) setOpen(false)\n }\n },\n [isInteractive, withTime, useDraft, draft, value, footer, resolvedCloseOnSelect, onChange],\n )\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n const target = useDraft ? draft : value\n if (!target) return\n const next = applyTimeToDate(target, time)\n if (useDraft) setDraft(next)\n else onChange(next)\n },\n [useDraft, draft, value, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setOpen(false)\n }, [value])\n\n const handleToday = React.useCallback(() => {\n const today = withTime ? new Date() : startOfDay(new Date())\n onChange(today)\n setOpen(false)\n }, [withTime, onChange])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n const selectedDate = useDraft ? draft : value\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent align={align} className={cn('w-auto p-0', popoverClassName)}>\n <Calendar\n mode=\"single\"\n selected={selectedDate ?? undefined}\n onSelect={handleDaySelect}\n locale={locale}\n disabled={disabledMatcher}\n initialFocus\n />\n {withTime && (\n <div className=\"flex items-center gap-2 border-t px-3 py-2\">\n <span className=\"text-sm text-muted-foreground shrink-0\">\n {t('ui.datePicker.timeLabel', 'Time')}:\n </span>\n <TimeInput\n value={selectedDate ? extractTime(selectedDate) : undefined}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n disabled={!isInteractive || !selectedDate}\n />\n </div>\n )}\n {footer === 'apply-cancel' && (\n <div className=\"flex items-center justify-end gap-2 border-t px-3 py-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancel}>\n {t('ui.datePicker.cancelButton', 'Cancel')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={handleApply}>\n {t('ui.datePicker.applyButton', 'Apply')}\n </Button>\n </div>\n )}\n {footer === 'today-clear' && (showTodayButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2 border-t px-3 py-2\">\n {showTodayButton ? (\n <LinkButton type=\"button\" variant=\"primary\" onClick={handleToday}>\n {t('ui.datePicker.todayButton', 'Today')}\n </LinkButton>\n ) : null}\n {showClearButton ? (\n <LinkButton type=\"button\" variant=\"gray\" onClick={handleClear} className=\"ml-auto\">\n {t('ui.datePicker.clearButton', 'Clear')}\n </LinkButton>\n ) : null}\n </div>\n )}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { format } from "date-fns";
|
|
5
|
+
import { CalendarIcon } from "lucide-react";
|
|
6
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
7
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
9
|
+
import { Calendar } from "./calendar.js";
|
|
10
|
+
import { Button } from "./button.js";
|
|
11
|
+
import {
|
|
12
|
+
defaultDateRangePresets
|
|
13
|
+
} from "./date-picker-helpers.js";
|
|
14
|
+
const DAY_FIRST_LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
15
|
+
"pl",
|
|
16
|
+
"de",
|
|
17
|
+
"fr",
|
|
18
|
+
"es",
|
|
19
|
+
"it",
|
|
20
|
+
"pt",
|
|
21
|
+
"nl",
|
|
22
|
+
"ru",
|
|
23
|
+
"cs",
|
|
24
|
+
"sk",
|
|
25
|
+
"hu",
|
|
26
|
+
"ro"
|
|
27
|
+
]);
|
|
28
|
+
function deriveDateFormat(locale) {
|
|
29
|
+
const code = locale?.code?.split("-")[0]?.toLowerCase() ?? "";
|
|
30
|
+
return code && DAY_FIRST_LOCALE_CODES.has(code) ? "d MMM yyyy" : "MMM d, yyyy";
|
|
31
|
+
}
|
|
32
|
+
function defaultFormatRange(range, locale) {
|
|
33
|
+
const fmt = deriveDateFormat(locale);
|
|
34
|
+
const opts = locale ? { locale } : void 0;
|
|
35
|
+
return `${format(range.start, fmt, opts)} \u2013 ${format(range.end, fmt, opts)}`;
|
|
36
|
+
}
|
|
37
|
+
function toRDPRange(range) {
|
|
38
|
+
if (!range) return void 0;
|
|
39
|
+
return { from: range.start, to: range.end };
|
|
40
|
+
}
|
|
41
|
+
function fromRDPRange(rdp) {
|
|
42
|
+
if (!rdp || !rdp.from) return null;
|
|
43
|
+
return { start: rdp.from, end: rdp.to ?? rdp.from };
|
|
44
|
+
}
|
|
45
|
+
function rangesEqual(a, b) {
|
|
46
|
+
if (!a && !b) return true;
|
|
47
|
+
if (!a || !b) return false;
|
|
48
|
+
return a.start.getTime() === b.start.getTime() && a.end.getTime() === b.end.getTime();
|
|
49
|
+
}
|
|
50
|
+
function DateRangePicker({
|
|
51
|
+
value,
|
|
52
|
+
onChange,
|
|
53
|
+
presets,
|
|
54
|
+
showPresets = true,
|
|
55
|
+
placeholder,
|
|
56
|
+
size = "default",
|
|
57
|
+
disabled = false,
|
|
58
|
+
readOnly = false,
|
|
59
|
+
withFooter = true,
|
|
60
|
+
align = "start",
|
|
61
|
+
minDate,
|
|
62
|
+
maxDate,
|
|
63
|
+
numberOfMonths = 2,
|
|
64
|
+
locale,
|
|
65
|
+
formatRange,
|
|
66
|
+
className,
|
|
67
|
+
popoverClassName,
|
|
68
|
+
id,
|
|
69
|
+
name,
|
|
70
|
+
required,
|
|
71
|
+
"aria-label": ariaLabel,
|
|
72
|
+
"aria-describedby": ariaDescribedBy
|
|
73
|
+
}) {
|
|
74
|
+
const t = useT();
|
|
75
|
+
const [open, setOpen] = React.useState(false);
|
|
76
|
+
const [draft, setDraft] = React.useState(value ?? null);
|
|
77
|
+
const [activePresetId, setActivePresetId] = React.useState(null);
|
|
78
|
+
const isInteractive = !disabled && !readOnly;
|
|
79
|
+
const useDraft = withFooter;
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (open) {
|
|
82
|
+
setDraft(value ?? null);
|
|
83
|
+
setActivePresetId(null);
|
|
84
|
+
}
|
|
85
|
+
}, [open, value]);
|
|
86
|
+
const resolvedPresets = React.useMemo(
|
|
87
|
+
() => presets ?? defaultDateRangePresets(),
|
|
88
|
+
[presets]
|
|
89
|
+
);
|
|
90
|
+
const placeholderText = placeholder ?? t("ui.dateRangePicker.placeholder", "Pick a date range");
|
|
91
|
+
const formatter = formatRange ?? defaultFormatRange;
|
|
92
|
+
const formattedValue = React.useMemo(() => {
|
|
93
|
+
if (!value) return null;
|
|
94
|
+
return formatter(value, locale);
|
|
95
|
+
}, [value, formatter, locale]);
|
|
96
|
+
const selectedDate = useDraft ? draft : value;
|
|
97
|
+
const rdpSelected = toRDPRange(selectedDate);
|
|
98
|
+
const handleRangeSelect = React.useCallback(
|
|
99
|
+
(rdpRange) => {
|
|
100
|
+
if (!isInteractive) return;
|
|
101
|
+
const next = fromRDPRange(rdpRange);
|
|
102
|
+
setActivePresetId(null);
|
|
103
|
+
if (useDraft) {
|
|
104
|
+
setDraft(next);
|
|
105
|
+
} else {
|
|
106
|
+
onChange(next);
|
|
107
|
+
if (next?.start && next?.end) setOpen(false);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
[isInteractive, useDraft, onChange]
|
|
111
|
+
);
|
|
112
|
+
const handlePresetClick = React.useCallback(
|
|
113
|
+
(preset) => {
|
|
114
|
+
if (!isInteractive) return;
|
|
115
|
+
const range = preset.range();
|
|
116
|
+
setActivePresetId(preset.id);
|
|
117
|
+
if (useDraft) {
|
|
118
|
+
setDraft(range);
|
|
119
|
+
} else {
|
|
120
|
+
onChange(range);
|
|
121
|
+
setOpen(false);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[isInteractive, useDraft, onChange]
|
|
125
|
+
);
|
|
126
|
+
const handleApply = React.useCallback(() => {
|
|
127
|
+
onChange(draft);
|
|
128
|
+
setOpen(false);
|
|
129
|
+
}, [draft, onChange]);
|
|
130
|
+
const handleCancel = React.useCallback(() => {
|
|
131
|
+
setDraft(value ?? null);
|
|
132
|
+
setActivePresetId(null);
|
|
133
|
+
setOpen(false);
|
|
134
|
+
}, [value]);
|
|
135
|
+
const disabledMatcher = React.useMemo(() => {
|
|
136
|
+
if (!minDate && !maxDate) return void 0;
|
|
137
|
+
const matchers = [];
|
|
138
|
+
if (minDate) matchers.push({ before: minDate });
|
|
139
|
+
if (maxDate) matchers.push({ after: maxDate });
|
|
140
|
+
return matchers;
|
|
141
|
+
}, [minDate, maxDate]);
|
|
142
|
+
const heightClass = size === "sm" ? "h-8 px-2.5 text-xs" : "h-9 px-3 text-sm";
|
|
143
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: isInteractive ? setOpen : void 0, children: [
|
|
144
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
145
|
+
"button",
|
|
146
|
+
{
|
|
147
|
+
type: "button",
|
|
148
|
+
id,
|
|
149
|
+
name,
|
|
150
|
+
"aria-label": ariaLabel,
|
|
151
|
+
"aria-describedby": ariaDescribedBy,
|
|
152
|
+
"aria-haspopup": "dialog",
|
|
153
|
+
"aria-required": required ? true : void 0,
|
|
154
|
+
"data-crud-focus-target": "",
|
|
155
|
+
"data-slot": "date-range-picker-trigger",
|
|
156
|
+
disabled,
|
|
157
|
+
className: cn(
|
|
158
|
+
"w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left",
|
|
159
|
+
"focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground",
|
|
160
|
+
"hover:bg-muted/40",
|
|
161
|
+
"disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed",
|
|
162
|
+
"aria-invalid:border-destructive",
|
|
163
|
+
heightClass,
|
|
164
|
+
readOnly && "cursor-default opacity-70",
|
|
165
|
+
!formattedValue && "text-muted-foreground",
|
|
166
|
+
className
|
|
167
|
+
),
|
|
168
|
+
onClick: isInteractive ? void 0 : (event) => event.preventDefault(),
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ jsx(CalendarIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
171
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: formattedValue ?? placeholderText })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
) }),
|
|
175
|
+
/* @__PURE__ */ jsxs(
|
|
176
|
+
PopoverContent,
|
|
177
|
+
{
|
|
178
|
+
align,
|
|
179
|
+
className: cn(
|
|
180
|
+
"w-auto p-0 flex flex-col overflow-hidden",
|
|
181
|
+
"max-h-[var(--radix-popover-content-available-height)]",
|
|
182
|
+
popoverClassName
|
|
183
|
+
),
|
|
184
|
+
children: [
|
|
185
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-auto", children: [
|
|
186
|
+
showPresets ? /* @__PURE__ */ jsx(
|
|
187
|
+
"div",
|
|
188
|
+
{
|
|
189
|
+
"data-slot": "date-range-presets",
|
|
190
|
+
className: "flex w-[200px] shrink-0 flex-col gap-0.5 overflow-y-auto border-r p-2",
|
|
191
|
+
children: resolvedPresets.map((preset) => {
|
|
192
|
+
const isActive = activePresetId === preset.id;
|
|
193
|
+
return /* @__PURE__ */ jsx(
|
|
194
|
+
"button",
|
|
195
|
+
{
|
|
196
|
+
type: "button",
|
|
197
|
+
onClick: () => handlePresetClick(preset),
|
|
198
|
+
className: cn(
|
|
199
|
+
"inline-flex h-8 items-center rounded-md px-3 text-left text-sm transition-colors shrink-0",
|
|
200
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
201
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
202
|
+
isActive && "bg-muted font-medium text-foreground"
|
|
203
|
+
),
|
|
204
|
+
children: t(preset.labelKey, preset.id.replace(/_/g, " "))
|
|
205
|
+
},
|
|
206
|
+
preset.id
|
|
207
|
+
);
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
) : null,
|
|
211
|
+
/* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsx(
|
|
212
|
+
Calendar,
|
|
213
|
+
{
|
|
214
|
+
mode: "range",
|
|
215
|
+
selected: rdpSelected,
|
|
216
|
+
onSelect: handleRangeSelect,
|
|
217
|
+
locale,
|
|
218
|
+
disabled: disabledMatcher,
|
|
219
|
+
numberOfMonths,
|
|
220
|
+
initialFocus: true
|
|
221
|
+
}
|
|
222
|
+
) })
|
|
223
|
+
] }),
|
|
224
|
+
withFooter ? /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-t bg-popover px-3 py-2", children: [
|
|
225
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: selectedDate ? `${t("ui.dateRangePicker.rangeLabel", "Range")}: ${formatter(selectedDate, locale)}` : t("ui.dateRangePicker.rangeEmpty", "No range selected") }),
|
|
226
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
227
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleCancel, children: t("ui.dateRangePicker.cancelButton", "Cancel") }),
|
|
228
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: handleApply, children: t("ui.dateRangePicker.applyButton", "Apply") })
|
|
229
|
+
] })
|
|
230
|
+
] }) : null
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
] });
|
|
235
|
+
}
|
|
236
|
+
export {
|
|
237
|
+
DateRangePicker
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=date-range-picker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/date-range-picker.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport type { DateRange as RDPRange } from 'react-day-picker'\nimport type { DateRange } from '../backend/date-range/dateRanges'\nimport {\n defaultDateRangePresets,\n type DateRangePresetItem,\n} from './date-picker-helpers'\n\nexport type { DateRangePresetItem } from './date-picker-helpers'\n\nexport type DateRangePickerProps = {\n value?: DateRange | null\n onChange: (value: DateRange | null) => void\n presets?: DateRangePresetItem[]\n showPresets?: boolean\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n withFooter?: boolean\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n numberOfMonths?: 1 | 2\n locale?: Locale\n formatRange?: (value: DateRange, locale?: Locale) => string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nfunction deriveDateFormat(locale?: Locale): string {\n const code = locale?.code?.split('-')[0]?.toLowerCase() ?? ''\n return code && DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy' : 'MMM d, yyyy'\n}\n\nfunction defaultFormatRange(range: DateRange, locale?: Locale): string {\n const fmt = deriveDateFormat(locale)\n const opts = locale ? { locale } : undefined\n return `${format(range.start, fmt, opts)} \u2013 ${format(range.end, fmt, opts)}`\n}\n\nfunction toRDPRange(range: DateRange | null | undefined): RDPRange | undefined {\n if (!range) return undefined\n return { from: range.start, to: range.end }\n}\n\nfunction fromRDPRange(rdp: RDPRange | undefined): DateRange | null {\n if (!rdp || !rdp.from) return null\n return { start: rdp.from, end: rdp.to ?? rdp.from }\n}\n\nfunction rangesEqual(a: DateRange | null, b: DateRange | null): boolean {\n if (!a && !b) return true\n if (!a || !b) return false\n return a.start.getTime() === b.start.getTime() && a.end.getTime() === b.end.getTime()\n}\n\nexport function DateRangePicker({\n value,\n onChange,\n presets,\n showPresets = true,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n withFooter = true,\n align = 'start',\n minDate,\n maxDate,\n numberOfMonths = 2,\n locale,\n formatRange,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DateRangePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<DateRange | null>(value ?? null)\n const [activePresetId, setActivePresetId] = React.useState<string | null>(null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = withFooter\n\n React.useEffect(() => {\n if (open) {\n setDraft(value ?? null)\n setActivePresetId(null)\n }\n }, [open, value])\n\n const resolvedPresets = React.useMemo<DateRangePresetItem[]>(\n () => presets ?? defaultDateRangePresets(),\n [presets],\n )\n\n const placeholderText = placeholder ?? t('ui.dateRangePicker.placeholder', 'Pick a date range')\n const formatter = formatRange ?? defaultFormatRange\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n return formatter(value, locale)\n }, [value, formatter, locale])\n\n const selectedDate = useDraft ? draft : value\n const rdpSelected = toRDPRange(selectedDate)\n\n const handleRangeSelect = React.useCallback(\n (rdpRange: RDPRange | undefined) => {\n if (!isInteractive) return\n const next = fromRDPRange(rdpRange)\n setActivePresetId(null)\n if (useDraft) {\n setDraft(next)\n } else {\n onChange(next)\n if (next?.start && next?.end) setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handlePresetClick = React.useCallback(\n (preset: DateRangePresetItem) => {\n if (!isInteractive) return\n const range = preset.range()\n setActivePresetId(preset.id)\n if (useDraft) {\n setDraft(range)\n } else {\n onChange(range)\n setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setActivePresetId(null)\n setOpen(false)\n }, [value])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-range-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={cn(\n 'w-auto p-0 flex flex-col overflow-hidden',\n 'max-h-[var(--radix-popover-content-available-height)]',\n popoverClassName,\n )}\n >\n <div className=\"flex min-h-0 flex-1 overflow-auto\">\n {showPresets ? (\n <div\n data-slot=\"date-range-presets\"\n className=\"flex w-[200px] shrink-0 flex-col gap-0.5 overflow-y-auto border-r p-2\"\n >\n {resolvedPresets.map((preset) => {\n const isActive = activePresetId === preset.id\n return (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => handlePresetClick(preset)}\n className={cn(\n 'inline-flex h-8 items-center rounded-md px-3 text-left text-sm transition-colors shrink-0',\n 'hover:bg-accent hover:text-accent-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n isActive && 'bg-muted font-medium text-foreground',\n )}\n >\n {t(preset.labelKey, preset.id.replace(/_/g, ' '))}\n </button>\n )\n })}\n </div>\n ) : null}\n <div className=\"p-3\">\n <Calendar\n mode=\"range\"\n selected={rdpSelected}\n onSelect={handleRangeSelect}\n locale={locale}\n disabled={disabledMatcher}\n numberOfMonths={numberOfMonths}\n initialFocus\n />\n </div>\n </div>\n {withFooter ? (\n <div className=\"flex shrink-0 items-center justify-between gap-3 border-t bg-popover px-3 py-2\">\n <span className=\"truncate text-xs text-muted-foreground\">\n {selectedDate\n ? `${t('ui.dateRangePicker.rangeLabel', 'Range')}: ${formatter(selectedDate, locale)}`\n : t('ui.dateRangePicker.rangeEmpty', 'No range selected')}\n </span>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancel}>\n {t('ui.dateRangePicker.cancelButton', 'Cancel')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={handleApply}>\n {t('ui.dateRangePicker.applyButton', 'Apply')}\n </Button>\n </div>\n </div>\n ) : null}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
5
|
+
const DigitInput = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
value: valueProp,
|
|
8
|
+
onChange,
|
|
9
|
+
length = 6,
|
|
10
|
+
disabled = false,
|
|
11
|
+
autoFocus = false,
|
|
12
|
+
inputMode = "numeric",
|
|
13
|
+
mask = false,
|
|
14
|
+
onComplete,
|
|
15
|
+
className,
|
|
16
|
+
cellClassName,
|
|
17
|
+
"aria-label": ariaLabel,
|
|
18
|
+
"aria-invalid": ariaInvalid,
|
|
19
|
+
id,
|
|
20
|
+
name
|
|
21
|
+
}, ref) => {
|
|
22
|
+
const [internalValue, setInternalValue] = React.useState("");
|
|
23
|
+
const isControlled = valueProp !== void 0;
|
|
24
|
+
const value = isControlled ? valueProp ?? "" : internalValue;
|
|
25
|
+
const cellRefs = React.useRef([]);
|
|
26
|
+
React.useImperativeHandle(
|
|
27
|
+
ref,
|
|
28
|
+
() => cellRefs.current[0] ?? null,
|
|
29
|
+
[]
|
|
30
|
+
);
|
|
31
|
+
const setCellRef = React.useCallback(
|
|
32
|
+
(index) => (el) => {
|
|
33
|
+
cellRefs.current[index] = el;
|
|
34
|
+
},
|
|
35
|
+
[]
|
|
36
|
+
);
|
|
37
|
+
const commitValue = React.useCallback(
|
|
38
|
+
(next) => {
|
|
39
|
+
const clamped = next.slice(0, length);
|
|
40
|
+
if (!isControlled) setInternalValue(clamped);
|
|
41
|
+
onChange?.(clamped);
|
|
42
|
+
if (clamped.length === length && onComplete) onComplete(clamped);
|
|
43
|
+
},
|
|
44
|
+
[isControlled, length, onChange, onComplete]
|
|
45
|
+
);
|
|
46
|
+
const filterChar = React.useCallback(
|
|
47
|
+
(char) => {
|
|
48
|
+
if (inputMode === "numeric" && !/^\d$/.test(char)) return null;
|
|
49
|
+
return char;
|
|
50
|
+
},
|
|
51
|
+
[inputMode]
|
|
52
|
+
);
|
|
53
|
+
const focusCell = React.useCallback(
|
|
54
|
+
(index) => {
|
|
55
|
+
const target = cellRefs.current[Math.max(0, Math.min(length - 1, index))];
|
|
56
|
+
if (target) target.focus();
|
|
57
|
+
},
|
|
58
|
+
[length]
|
|
59
|
+
);
|
|
60
|
+
const handleCellChange = React.useCallback(
|
|
61
|
+
(index, raw) => {
|
|
62
|
+
if (raw.length === 0) {
|
|
63
|
+
const next2 = value.slice(0, index) + value.slice(index + 1);
|
|
64
|
+
commitValue(next2);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const lastChar = raw.slice(-1);
|
|
68
|
+
const filtered = filterChar(lastChar);
|
|
69
|
+
if (filtered === null) return;
|
|
70
|
+
const next = value.slice(0, index) + filtered + value.slice(index + 1);
|
|
71
|
+
commitValue(next);
|
|
72
|
+
if (index < length - 1) focusCell(index + 1);
|
|
73
|
+
},
|
|
74
|
+
[commitValue, filterChar, focusCell, length, value]
|
|
75
|
+
);
|
|
76
|
+
const handleKeyDown = React.useCallback(
|
|
77
|
+
(index) => (event) => {
|
|
78
|
+
if (event.key === "Backspace") {
|
|
79
|
+
if (event.currentTarget.value === "" && index > 0) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
const next = value.slice(0, index - 1) + value.slice(index);
|
|
82
|
+
commitValue(next);
|
|
83
|
+
focusCell(index - 1);
|
|
84
|
+
}
|
|
85
|
+
} else if (event.key === "ArrowLeft" && index > 0) {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
focusCell(index - 1);
|
|
88
|
+
} else if (event.key === "ArrowRight" && index < length - 1) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
focusCell(index + 1);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
[commitValue, focusCell, length, value]
|
|
94
|
+
);
|
|
95
|
+
const handlePaste = React.useCallback(
|
|
96
|
+
(event) => {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
const pasted = event.clipboardData.getData("text/plain");
|
|
99
|
+
const filtered = pasted.split("").map(filterChar).filter((c) => c !== null).join("").slice(0, length);
|
|
100
|
+
if (filtered.length === 0) return;
|
|
101
|
+
commitValue(filtered);
|
|
102
|
+
const focusIndex = Math.min(filtered.length, length - 1);
|
|
103
|
+
focusCell(focusIndex);
|
|
104
|
+
},
|
|
105
|
+
[commitValue, filterChar, focusCell, length]
|
|
106
|
+
);
|
|
107
|
+
return /* @__PURE__ */ jsx(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
className: cn("flex items-center gap-2", className),
|
|
111
|
+
role: "group",
|
|
112
|
+
"aria-label": ariaLabel ?? "Verification code",
|
|
113
|
+
"aria-invalid": ariaInvalid || void 0,
|
|
114
|
+
"data-slot": "digit-input",
|
|
115
|
+
children: Array.from({ length }, (_, index) => {
|
|
116
|
+
const cellValue = value[index] ?? "";
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
"input",
|
|
119
|
+
{
|
|
120
|
+
ref: setCellRef(index),
|
|
121
|
+
id: index === 0 ? id : void 0,
|
|
122
|
+
name: index === 0 ? name : void 0,
|
|
123
|
+
type: mask ? "password" : "text",
|
|
124
|
+
inputMode: inputMode === "numeric" ? "numeric" : "text",
|
|
125
|
+
autoComplete: "one-time-code",
|
|
126
|
+
maxLength: 1,
|
|
127
|
+
autoFocus: autoFocus && index === 0,
|
|
128
|
+
value: cellValue,
|
|
129
|
+
onChange: (event) => handleCellChange(index, event.target.value),
|
|
130
|
+
onKeyDown: handleKeyDown(index),
|
|
131
|
+
onPaste: handlePaste,
|
|
132
|
+
disabled,
|
|
133
|
+
"aria-label": `${ariaLabel ?? "Verification code"} digit ${index + 1}`,
|
|
134
|
+
"aria-invalid": ariaInvalid || void 0,
|
|
135
|
+
"data-slot": "digit-input-cell",
|
|
136
|
+
"data-index": index,
|
|
137
|
+
className: cn(
|
|
138
|
+
"h-16 w-12 sm:w-14 rounded-[10px] border border-input bg-background text-center text-2xl font-medium shadow-xs transition-colors",
|
|
139
|
+
"focus:outline-none focus-visible:border-foreground focus-visible:shadow-focus",
|
|
140
|
+
"aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive",
|
|
141
|
+
"disabled:cursor-not-allowed disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none",
|
|
142
|
+
"hover:bg-muted/40",
|
|
143
|
+
cellClassName
|
|
144
|
+
)
|
|
145
|
+
},
|
|
146
|
+
index
|
|
147
|
+
);
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
DigitInput.displayName = "DigitInput";
|
|
154
|
+
export {
|
|
155
|
+
DigitInput
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=digit-input.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/digit-input.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type DigitInputProps = {\n value?: string\n onChange?: (value: string) => void\n /** Number of cells. Default `6`. */\n length?: number\n disabled?: boolean\n autoFocus?: boolean\n /** Restrict input characters. `'numeric'` filters out non-digits. */\n inputMode?: 'numeric' | 'text'\n /** Render each cell as a password input so characters appear as bullets. */\n mask?: boolean\n /** Fires when all `length` cells are filled. */\n onComplete?: (value: string) => void\n /** Override classes for the cell row wrapper. */\n className?: string\n /** Override classes applied to each cell `<input>`. */\n cellClassName?: string\n /** aria-label applied to the role=group wrapper. Defaults to `Verification code`. */\n 'aria-label'?: string\n /** Indicates that the group's current value fails validation (mirrors Input's aria-invalid styling). */\n 'aria-invalid'?: boolean\n /** Forwarded to the first cell so consumers can label the entire group with `<label htmlFor>`. */\n id?: string\n /** Forwarded to the first cell so form submissions carry the assembled value. */\n name?: string\n}\n\n/**\n * `length`-cell verification code input. Pasting a string distributes\n * characters across cells and fires `onComplete` when all cells are filled.\n * Backspace on an empty cell focuses the previous cell. ArrowLeft /\n * ArrowRight move focus without committing values.\n *\n * The `value` prop is the assembled string (e.g. `'123456'`) \u2014 cells are an\n * internal layout concern, not part of the contract. Same as `Input`,\n * `mask` is purely visual (`type='password'` swap) so consumers always\n * receive the raw characters in `onChange` / `onComplete`.\n */\nexport const DigitInput = React.forwardRef<HTMLInputElement, DigitInputProps>(\n (\n {\n value: valueProp,\n onChange,\n length = 6,\n disabled = false,\n autoFocus = false,\n inputMode = 'numeric',\n mask = false,\n onComplete,\n className,\n cellClassName,\n 'aria-label': ariaLabel,\n 'aria-invalid': ariaInvalid,\n id,\n name,\n },\n ref,\n ) => {\n const [internalValue, setInternalValue] = React.useState('')\n const isControlled = valueProp !== undefined\n const value = isControlled ? valueProp ?? '' : internalValue\n const cellRefs = React.useRef<Array<HTMLInputElement | null>>([])\n\n React.useImperativeHandle(\n ref,\n () => (cellRefs.current[0] ?? null) as HTMLInputElement,\n [],\n )\n\n const setCellRef = React.useCallback(\n (index: number) => (el: HTMLInputElement | null) => {\n cellRefs.current[index] = el\n },\n [],\n )\n\n const commitValue = React.useCallback(\n (next: string) => {\n const clamped = next.slice(0, length)\n if (!isControlled) setInternalValue(clamped)\n onChange?.(clamped)\n if (clamped.length === length && onComplete) onComplete(clamped)\n },\n [isControlled, length, onChange, onComplete],\n )\n\n const filterChar = React.useCallback(\n (char: string): string | null => {\n if (inputMode === 'numeric' && !/^\\d$/.test(char)) return null\n return char\n },\n [inputMode],\n )\n\n const focusCell = React.useCallback(\n (index: number) => {\n const target = cellRefs.current[Math.max(0, Math.min(length - 1, index))]\n if (target) target.focus()\n },\n [length],\n )\n\n const handleCellChange = React.useCallback(\n (index: number, raw: string) => {\n if (raw.length === 0) {\n const next = value.slice(0, index) + value.slice(index + 1)\n commitValue(next)\n return\n }\n const lastChar = raw.slice(-1)\n const filtered = filterChar(lastChar)\n if (filtered === null) return\n const next = value.slice(0, index) + filtered + value.slice(index + 1)\n commitValue(next)\n if (index < length - 1) focusCell(index + 1)\n },\n [commitValue, filterChar, focusCell, length, value],\n )\n\n const handleKeyDown = React.useCallback(\n (index: number) => (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Backspace') {\n if (event.currentTarget.value === '' && index > 0) {\n event.preventDefault()\n const next = value.slice(0, index - 1) + value.slice(index)\n commitValue(next)\n focusCell(index - 1)\n }\n } else if (event.key === 'ArrowLeft' && index > 0) {\n event.preventDefault()\n focusCell(index - 1)\n } else if (event.key === 'ArrowRight' && index < length - 1) {\n event.preventDefault()\n focusCell(index + 1)\n }\n },\n [commitValue, focusCell, length, value],\n )\n\n const handlePaste = React.useCallback(\n (event: React.ClipboardEvent<HTMLInputElement>) => {\n event.preventDefault()\n const pasted = event.clipboardData.getData('text/plain')\n const filtered = pasted\n .split('')\n .map(filterChar)\n .filter((c): c is string => c !== null)\n .join('')\n .slice(0, length)\n if (filtered.length === 0) return\n commitValue(filtered)\n const focusIndex = Math.min(filtered.length, length - 1)\n focusCell(focusIndex)\n },\n [commitValue, filterChar, focusCell, length],\n )\n\n return (\n <div\n className={cn('flex items-center gap-2', className)}\n role=\"group\"\n aria-label={ariaLabel ?? 'Verification code'}\n aria-invalid={ariaInvalid || undefined}\n data-slot=\"digit-input\"\n >\n {Array.from({ length }, (_, index) => {\n const cellValue = value[index] ?? ''\n return (\n <input\n key={index}\n ref={setCellRef(index)}\n id={index === 0 ? id : undefined}\n name={index === 0 ? name : undefined}\n type={mask ? 'password' : 'text'}\n inputMode={inputMode === 'numeric' ? 'numeric' : 'text'}\n autoComplete=\"one-time-code\"\n maxLength={1}\n autoFocus={autoFocus && index === 0}\n value={cellValue}\n onChange={(event) => handleCellChange(index, event.target.value)}\n onKeyDown={handleKeyDown(index)}\n onPaste={handlePaste}\n disabled={disabled}\n aria-label={`${ariaLabel ?? 'Verification code'} digit ${index + 1}`}\n aria-invalid={ariaInvalid || undefined}\n data-slot=\"digit-input-cell\"\n data-index={index}\n className={cn(\n 'h-16 w-12 sm:w-14 rounded-[10px] border border-input bg-background text-center text-2xl font-medium shadow-xs transition-colors',\n 'focus:outline-none focus-visible:border-foreground focus-visible:shadow-focus',\n 'aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive',\n 'disabled:cursor-not-allowed disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none',\n 'hover:bg-muted/40',\n cellClassName,\n )}\n />\n )\n })}\n </div>\n )\n },\n)\nDigitInput.displayName = 'DigitInput'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["next"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
6
|
+
import { Button } from "./button.js";
|
|
7
|
+
const emptyStateVariants = cva(
|
|
8
|
+
"flex flex-col items-center justify-center text-center",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
sm: "gap-2 px-4 py-6",
|
|
13
|
+
default: "gap-3 px-6 py-10",
|
|
14
|
+
lg: "gap-4 px-8 py-16"
|
|
15
|
+
},
|
|
16
|
+
variant: {
|
|
17
|
+
default: "rounded-lg border border-dashed border-muted-foreground/40 bg-muted/30",
|
|
18
|
+
subtle: "rounded-lg"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: "default",
|
|
23
|
+
variant: "default"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
const titleVariants = cva("font-medium text-foreground", {
|
|
28
|
+
variants: {
|
|
29
|
+
size: {
|
|
30
|
+
sm: "text-sm",
|
|
31
|
+
default: "text-sm",
|
|
32
|
+
lg: "text-base"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: { size: "default" }
|
|
36
|
+
});
|
|
37
|
+
function EmptyState({
|
|
38
|
+
size = "default",
|
|
39
|
+
variant = "default",
|
|
40
|
+
title,
|
|
41
|
+
description,
|
|
42
|
+
actions,
|
|
43
|
+
illustration,
|
|
44
|
+
icon,
|
|
45
|
+
className,
|
|
46
|
+
children,
|
|
47
|
+
action,
|
|
48
|
+
actionLabel,
|
|
49
|
+
onAction,
|
|
50
|
+
actionLabelClassName
|
|
51
|
+
}) {
|
|
52
|
+
const legacyAction = action ?? (actionLabel ? { label: actionLabel, onClick: onAction } : void 0);
|
|
53
|
+
const renderLegacyButton = !actions && legacyAction;
|
|
54
|
+
const iconBoxSize = size === "sm" ? "size-10" : size === "lg" ? "size-16" : "size-12";
|
|
55
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(emptyStateVariants({ size, variant }), className), "data-slot": "empty-state", children: [
|
|
56
|
+
illustration ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", "aria-hidden": "true", children: illustration }) : icon ? variant === "subtle" ? /* @__PURE__ */ jsx(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
className: cn(
|
|
60
|
+
"flex items-center justify-center rounded-full bg-muted text-muted-foreground",
|
|
61
|
+
iconBoxSize
|
|
62
|
+
),
|
|
63
|
+
"aria-hidden": "true",
|
|
64
|
+
children: icon
|
|
65
|
+
}
|
|
66
|
+
) : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", "aria-hidden": "true", children: icon }) : null,
|
|
67
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
68
|
+
/* @__PURE__ */ jsx("p", { className: cn(titleVariants({ size })), children: title }),
|
|
69
|
+
description ? /* @__PURE__ */ jsx("p", { className: "mx-auto max-w-sm text-sm text-muted-foreground", children: description }) : null
|
|
70
|
+
] }),
|
|
71
|
+
children,
|
|
72
|
+
actions ?? (renderLegacyButton ? /* @__PURE__ */ jsxs(
|
|
73
|
+
Button,
|
|
74
|
+
{
|
|
75
|
+
type: "button",
|
|
76
|
+
variant: "outline",
|
|
77
|
+
size: "sm",
|
|
78
|
+
onClick: legacyAction.onClick,
|
|
79
|
+
className: cn("inline-flex items-center gap-2 text-foreground", actionLabelClassName),
|
|
80
|
+
disabled: legacyAction.disabled,
|
|
81
|
+
children: [
|
|
82
|
+
legacyAction.icon ?? /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
83
|
+
/* @__PURE__ */ jsx("span", { children: legacyAction.label })
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
) : null)
|
|
87
|
+
] });
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
EmptyState,
|
|
91
|
+
emptyStateVariants
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=empty-state.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/empty-state.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { Plus } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from './button'\n\nconst emptyStateVariants = cva(\n 'flex flex-col items-center justify-center text-center',\n {\n variants: {\n size: {\n sm: 'gap-2 px-4 py-6',\n default: 'gap-3 px-6 py-10',\n lg: 'gap-4 px-8 py-16',\n },\n variant: {\n default: 'rounded-lg border border-dashed border-muted-foreground/40 bg-muted/30',\n subtle: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n variant: 'default',\n },\n },\n)\n\nconst titleVariants = cva('font-medium text-foreground', {\n variants: {\n size: {\n sm: 'text-sm',\n default: 'text-sm',\n lg: 'text-base',\n },\n },\n defaultVariants: { size: 'default' },\n})\n\ntype LegacyAction = {\n label: string\n onClick?: () => void\n icon?: React.ReactNode\n disabled?: boolean\n}\n\nexport type EmptyStateProps = VariantProps<typeof emptyStateVariants> & {\n title: string\n description?: string\n /** Custom action node \u2014 preferred for new code (e.g. `<Button>` or button group). */\n actions?: React.ReactNode\n /**\n * Optional Figma-style illustration (preferred for new code) \u2014 typically a\n * scaled SVG sourced from the DS Open Mercato illustrations library. When\n * provided, takes precedence over `icon`. Rendered without any icon-box\n * wrapping so the illustration's own circular background shows through.\n */\n illustration?: React.ReactNode\n /** Optional leading icon. With `variant=\"subtle\"` it is wrapped in a round muted box; with default variant it sits inline tinted as muted-foreground. Ignored when `illustration` is provided. */\n icon?: React.ReactNode\n className?: string\n children?: React.ReactNode\n /** @deprecated Use `actions` with a `<Button>` instead. Legacy object kept for backwards compat with the old backend EmptyState. */\n action?: LegacyAction\n /** @deprecated Use `actions` with a `<Button>`. */\n actionLabel?: string\n /** @deprecated Use `actions` with a `<Button>`. */\n onAction?: () => void\n /** @deprecated Kept for legacy backend EmptyState consumers. Forwarded to the auto-generated action button. */\n actionLabelClassName?: string\n}\n\nexport function EmptyState({\n size = 'default',\n variant = 'default',\n title,\n description,\n actions,\n illustration,\n icon,\n className,\n children,\n action,\n actionLabel,\n onAction,\n actionLabelClassName,\n}: EmptyStateProps) {\n const legacyAction = action ?? (actionLabel ? { label: actionLabel, onClick: onAction } : undefined)\n const renderLegacyButton = !actions && legacyAction\n const iconBoxSize = size === 'sm' ? 'size-10' : size === 'lg' ? 'size-16' : 'size-12'\n\n return (\n <div className={cn(emptyStateVariants({ size, variant }), className)} data-slot=\"empty-state\">\n {illustration ? (\n <div className=\"flex items-center justify-center\" aria-hidden=\"true\">\n {illustration}\n </div>\n ) : icon ? (\n variant === 'subtle' ? (\n <div\n className={cn(\n 'flex items-center justify-center rounded-full bg-muted text-muted-foreground',\n iconBoxSize,\n )}\n aria-hidden=\"true\"\n >\n {icon}\n </div>\n ) : (\n <div className=\"text-muted-foreground\" aria-hidden=\"true\">\n {icon}\n </div>\n )\n ) : null}\n <div className=\"space-y-1\">\n <p className={cn(titleVariants({ size }))}>{title}</p>\n {description ? (\n <p className=\"mx-auto max-w-sm text-sm text-muted-foreground\">{description}</p>\n ) : null}\n </div>\n {children}\n {actions ?? (renderLegacyButton ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={legacyAction!.onClick}\n className={cn('inline-flex items-center gap-2 text-foreground', actionLabelClassName)}\n disabled={legacyAction!.disabled}\n >\n {legacyAction!.icon ?? <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />}\n <span>{legacyAction!.label}</span>\n </Button>\n ) : null)}\n </div>\n )\n}\n\nexport { emptyStateVariants }\n"],
|
|
5
|
+
"mappings": ";AA+FQ,cAoBF,YApBE;AA5FR,SAAS,WAA8B;AACvC,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,cAAc;AAEvB,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,MACN;AAAA,MACA,SAAS;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEA,MAAM,gBAAgB,IAAI,+BAA+B;AAAA,EACvD,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,SAAS;AAAA,MACT,IAAI;AAAA,IACN;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CAAC;AAmCM,SAAS,WAAW;AAAA,EACzB,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,eAAe,WAAW,cAAc,EAAE,OAAO,aAAa,SAAS,SAAS,IAAI;AAC1F,QAAM,qBAAqB,CAAC,WAAW;AACvC,QAAM,cAAc,SAAS,OAAO,YAAY,SAAS,OAAO,YAAY;AAE5E,SACE,qBAAC,SAAI,WAAW,GAAG,mBAAmB,EAAE,MAAM,QAAQ,CAAC,GAAG,SAAS,GAAG,aAAU,eAC7E;AAAA,mBACC,oBAAC,SAAI,WAAU,oCAAmC,eAAY,QAC3D,wBACH,IACE,OACF,YAAY,WACV;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACA,eAAY;AAAA,QAEX;AAAA;AAAA,IACH,IAEA,oBAAC,SAAI,WAAU,yBAAwB,eAAY,QAChD,gBACH,IAEA;AAAA,IACJ,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,OAAE,WAAW,GAAG,cAAc,EAAE,KAAK,CAAC,CAAC,GAAI,iBAAM;AAAA,MACjD,cACC,oBAAC,OAAE,WAAU,kDAAkD,uBAAY,IACzE;AAAA,OACN;AAAA,IACC;AAAA,IACA,YAAY,qBACX;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS,aAAc;AAAA,QACvB,WAAW,GAAG,kDAAkD,oBAAoB;AAAA,QACpF,UAAU,aAAc;AAAA,QAEvB;AAAA,uBAAc,QAAQ,oBAAC,QAAK,WAAU,WAAU,eAAY,QAAO;AAAA,UACpE,oBAAC,UAAM,uBAAc,OAAM;AAAA;AAAA;AAAA,IAC7B,IACE;AAAA,KACN;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
5
|
+
import { Input } from "./input.js";
|
|
6
|
+
const InlineInput = React.forwardRef(
|
|
7
|
+
({ className, inputClassName, size = "sm", showBorderOnHover = true, ...rest }, ref) => {
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
Input,
|
|
10
|
+
{
|
|
11
|
+
ref,
|
|
12
|
+
size,
|
|
13
|
+
className: cn(
|
|
14
|
+
"border-transparent bg-transparent shadow-none",
|
|
15
|
+
showBorderOnHover ? "hover:border-input hover:bg-muted/40" : "hover:bg-transparent",
|
|
16
|
+
className
|
|
17
|
+
),
|
|
18
|
+
inputClassName,
|
|
19
|
+
"data-slot": "inline-input",
|
|
20
|
+
...rest
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
InlineInput.displayName = "InlineInput";
|
|
26
|
+
export {
|
|
27
|
+
InlineInput
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=inline-input.js.map
|