@lerianstudio/sindarian-ui 2.0.0-beta.25 → 2.0.0-beta.26
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/charts/chart.js +4 -231
- package/dist/charts/index.js +1 -11
- package/dist/components/breadcrumb/get-breadcrumb-paths.js +1 -18
- package/dist/components/breadcrumb/index.js +1 -37
- package/dist/components/card/account-card/index.js +1 -69
- package/dist/components/card/basic-card/index.js +1 -20
- package/dist/components/card/dashboard-card/index.js +1 -20
- package/dist/components/card/entity-card/index.js +1 -68
- package/dist/components/card/index.js +1 -20
- package/dist/components/dialog/confirmation-dialog/index.js +1 -96
- package/dist/components/dialog/confirmation-dialog/use-confirm-dialog.js +1 -55
- package/dist/components/dialog/index.js +1 -19
- package/dist/components/dialog/large-dialog/index.js +1 -21
- package/dist/components/entity-box/index.js +1 -67
- package/dist/components/entity-data-table/index.js +1 -20
- package/dist/components/form/combo-box-field/index.js +1 -35
- package/dist/components/form/country-field/index.js +1 -11
- package/dist/components/form/date-picker-field/index.js +1 -83
- package/dist/components/form/date-range-field/index.js +1 -58
- package/dist/components/form/date-time-picker-field/index.js +1 -114
- package/dist/components/form/file-upload-field/index.js +1 -69
- package/dist/components/form/index.js +1 -31
- package/dist/components/form/input-field/index.js +1 -96
- package/dist/components/form/otp-field/index.js +1 -19
- package/dist/components/form/pagination-limit-field/index.js +1 -10
- package/dist/components/form/password-field/index.js +1 -16
- package/dist/components/form/radio-group-field/index.js +1 -36
- package/dist/components/form/renderable-label.js +1 -57
- package/dist/components/form/select-field/index.js +1 -68
- package/dist/components/form/state-field/index.js +1 -42
- package/dist/components/form/switch-field/index.js +1 -16
- package/dist/components/form/textarea-field/index.js +1 -23
- package/dist/components/page/index.js +1 -20
- package/dist/components/page-header/index.js +1 -80
- package/dist/components/pagination/index.js +1 -10
- package/dist/components/table/id-table-cell.js +1 -109
- package/dist/components/table/index.js +1 -19
- package/dist/components/table/locked-table-actions.js +1 -41
- package/dist/components/table/name-table-cell.js +1 -58
- package/dist/components/ui/accordion/index.js +1 -62
- package/dist/components/ui/alert/index.js +1 -45
- package/dist/components/ui/alert-dialog/index.js +1 -84
- package/dist/components/ui/autocomplete/index.js +1 -440
- package/dist/components/ui/autosize-textarea/index.js +1 -89
- package/dist/components/ui/avatar/index.js +1 -51
- package/dist/components/ui/badge/index.js +1 -83
- package/dist/components/ui/breadcrumb/index.js +1 -35
- package/dist/components/ui/button/index.js +1 -65
- package/dist/components/ui/calendar/index.js +1 -118
- package/dist/components/ui/card/index.js +1 -32
- package/dist/components/ui/checkbox/index.js +1 -44
- package/dist/components/ui/collapsible/index.js +1 -50
- package/dist/components/ui/command/index.js +1 -95
- package/dist/components/ui/copy-field/index.js +1 -163
- package/dist/components/ui/dialog/index.js +1 -80
- package/dist/components/ui/dropdown-menu/index.js +1 -110
- package/dist/components/ui/field/index.js +1 -82
- package/dist/components/ui/file-upload/index.js +1 -574
- package/dist/components/ui/form.js +1 -162
- package/dist/components/ui/hover-card/index.js +1 -51
- package/dist/components/ui/icon-button/index.js +1 -35
- package/dist/components/ui/input/adornment.js +1 -21
- package/dist/components/ui/input/index.js +1 -99
- package/dist/components/ui/label/index.js +1 -45
- package/dist/components/ui/lerian-wordmark/index.js +1 -7
- package/dist/components/ui/loading-button/index.js +1 -9
- package/dist/components/ui/multiple-select/index.js +1 -235
- package/dist/components/ui/otp-input/index.js +1 -62
- package/dist/components/ui/paper/index.js +1 -8
- package/dist/components/ui/paper-collapsible/index.js +1 -24
- package/dist/components/ui/popover/index.js +1 -55
- package/dist/components/ui/progress/index.js +1 -66
- package/dist/components/ui/qr-code/index.js +1 -27
- package/dist/components/ui/radio-group/index.js +1 -48
- package/dist/components/ui/scroll-area/index.js +1 -49
- package/dist/components/ui/select/index.js +1 -119
- package/dist/components/ui/separator/index.js +1 -43
- package/dist/components/ui/sheet/index.js +1 -136
- package/dist/components/ui/sidebar/index.js +1 -27
- package/dist/components/ui/sidebar/next-router.cjs +1 -119
- package/dist/components/ui/sidebar/sidebar-back-button.js +1 -8
- package/dist/components/ui/sidebar/sidebar-components.js +1 -23
- package/dist/components/ui/sidebar/sidebar-expand-button.js +1 -49
- package/dist/components/ui/sidebar/sidebar-item-button.js +1 -21
- package/dist/components/ui/sidebar/sidebar-item-collapsible.js +1 -52
- package/dist/components/ui/sidebar/sidebar-item-icon-button.js +1 -18
- package/dist/components/ui/sidebar/sidebar-item.js +1 -44
- package/dist/components/ui/sidebar/sidebar-provider.js +1 -201
- package/dist/components/ui/sidebar/sidebar-root.js +1 -130
- package/dist/components/ui/sidebar/sidebar-router.js +1 -126
- package/dist/components/ui/sidebar/sidebar-trigger.js +1 -57
- package/dist/components/ui/skeleton/index.js +1 -8
- package/dist/components/ui/stepper/index.js +1 -29
- package/dist/components/ui/switch/index.js +1 -43
- package/dist/components/ui/table/index.js +1 -56
- package/dist/components/ui/tabs/index.js +1 -55
- package/dist/components/ui/textarea/index.js +1 -8
- package/dist/components/ui/time-picker/index.js +1 -85
- package/dist/components/ui/toast/index.js +1 -3
- package/dist/components/ui/toast/toaster.js +1 -16
- package/dist/components/ui/toggle-group/index.js +1 -156
- package/dist/components/ui/tooltip/index.js +1 -81
- package/dist/domain/blotter/index.js +1 -20
- package/dist/domain/delinquency-aging/aging-buckets.js +1 -120
- package/dist/domain/delinquency-aging/index.js +1 -222
- package/dist/domain/figure/index.js +1 -19
- package/dist/domain/format.js +1 -137
- package/dist/domain/index.js +1 -43
- package/dist/domain/key-id/index.js +1 -174
- package/dist/domain/ledger-sheet/index.js +1 -28
- package/dist/domain/money-diff.js +1 -48
- package/dist/domain/money-math.js +1 -104
- package/dist/domain/money-text/index.js +1 -123
- package/dist/domain/section-label/index.js +1 -17
- package/dist/domain/status-rail/index.js +1 -76
- package/dist/domain/threshold-gauge/index.js +1 -161
- package/dist/enterprise/alert-banner/index.js +1 -24
- package/dist/enterprise/app-shell/index.js +1 -87
- package/dist/enterprise/cursor-pager/index.js +1 -26
- package/dist/enterprise/data-table/column-size.js +1 -103
- package/dist/enterprise/data-table/index.js +1 -345
- package/dist/enterprise/date-range-picker/index.js +1 -115
- package/dist/enterprise/detail-panel/index.js +1 -20
- package/dist/enterprise/empty-state/index.js +1 -14
- package/dist/enterprise/index.js +1 -39
- package/dist/enterprise/number-input/index.js +1 -252
- package/dist/enterprise/search-input/index.js +1 -121
- package/dist/enterprise/stat-card/index.js +1 -75
- package/dist/enterprise/status-badge/index.js +1 -120
- package/dist/enterprise/use-is-mobile.js +1 -68
- package/dist/enterprise/virtualized-table/index.js +1 -141
- package/dist/esm/charts/chart.js +4 -190
- package/dist/esm/charts/index.js +1 -3
- package/dist/esm/components/breadcrumb/get-breadcrumb-paths.js +1 -12
- package/dist/esm/components/breadcrumb/index.js +1 -30
- package/dist/esm/components/card/account-card/index.js +1 -53
- package/dist/esm/components/card/basic-card/index.js +1 -14
- package/dist/esm/components/card/dashboard-card/index.js +1 -14
- package/dist/esm/components/card/entity-card/index.js +1 -52
- package/dist/esm/components/card/index.js +1 -4
- package/dist/esm/components/dialog/confirmation-dialog/index.js +1 -60
- package/dist/esm/components/dialog/confirmation-dialog/use-confirm-dialog.js +1 -49
- package/dist/esm/components/dialog/index.js +1 -3
- package/dist/esm/components/dialog/large-dialog/index.js +1 -16
- package/dist/esm/components/entity-box/index.js +1 -56
- package/dist/esm/components/entity-data-table/index.js +1 -17
- package/dist/esm/components/form/combo-box-field/index.js +1 -28
- package/dist/esm/components/form/country-field/index.js +1 -7
- package/dist/esm/components/form/date-picker-field/index.js +1 -43
- package/dist/esm/components/form/date-range-field/index.js +1 -18
- package/dist/esm/components/form/date-time-picker-field/index.js +1 -74
- package/dist/esm/components/form/file-upload-field/index.js +1 -65
- package/dist/esm/components/form/index.js +1 -15
- package/dist/esm/components/form/input-field/index.js +1 -92
- package/dist/esm/components/form/otp-field/index.js +1 -15
- package/dist/esm/components/form/pagination-limit-field/index.js +1 -6
- package/dist/esm/components/form/password-field/index.js +1 -10
- package/dist/esm/components/form/radio-group-field/index.js +1 -32
- package/dist/esm/components/form/renderable-label.js +1 -54
- package/dist/esm/components/form/select-field/index.js +1 -64
- package/dist/esm/components/form/state-field/index.js +1 -35
- package/dist/esm/components/form/switch-field/index.js +1 -12
- package/dist/esm/components/form/textarea-field/index.js +1 -19
- package/dist/esm/components/page/index.js +1 -15
- package/dist/esm/components/page-header/index.js +1 -71
- package/dist/esm/components/pagination/index.js +1 -6
- package/dist/esm/components/table/id-table-cell.js +1 -105
- package/dist/esm/components/table/index.js +1 -3
- package/dist/esm/components/table/locked-table-actions.js +1 -37
- package/dist/esm/components/table/name-table-cell.js +1 -54
- package/dist/esm/components/ui/accordion/index.js +1 -24
- package/dist/esm/components/ui/alert/index.js +1 -38
- package/dist/esm/components/ui/alert-dialog/index.js +1 -39
- package/dist/esm/components/ui/autocomplete/index.js +1 -392
- package/dist/esm/components/ui/autosize-textarea/index.js +1 -51
- package/dist/esm/components/ui/avatar/index.js +1 -14
- package/dist/esm/components/ui/badge/index.js +1 -79
- package/dist/esm/components/ui/breadcrumb/index.js +1 -27
- package/dist/esm/components/ui/button/index.js +1 -60
- package/dist/esm/components/ui/calendar/index.js +1 -82
- package/dist/esm/components/ui/card/index.js +1 -24
- package/dist/esm/components/ui/checkbox/index.js +1 -9
- package/dist/esm/components/ui/collapsible/index.js +1 -13
- package/dist/esm/components/ui/command/index.js +1 -51
- package/dist/esm/components/ui/copy-field/index.js +1 -127
- package/dist/esm/components/ui/dialog/index.js +1 -36
- package/dist/esm/components/ui/dropdown-menu/index.js +1 -60
- package/dist/esm/components/ui/field/index.js +1 -71
- package/dist/esm/components/ui/file-upload/index.js +1 -536
- package/dist/esm/components/ui/form.js +1 -118
- package/dist/esm/components/ui/hover-card/index.js +1 -14
- package/dist/esm/components/ui/icon-button/index.js +1 -31
- package/dist/esm/components/ui/input/adornment.js +1 -17
- package/dist/esm/components/ui/input/index.js +1 -60
- package/dist/esm/components/ui/label/index.js +1 -10
- package/dist/esm/components/ui/lerian-wordmark/index.js +1 -4
- package/dist/esm/components/ui/loading-button/index.js +1 -7
- package/dist/esm/components/ui/multiple-select/index.js +1 -196
- package/dist/esm/components/ui/otp-input/index.js +1 -21
- package/dist/esm/components/ui/paper/index.js +1 -5
- package/dist/esm/components/ui/paper-collapsible/index.js +1 -19
- package/dist/esm/components/ui/popover/index.js +1 -17
- package/dist/esm/components/ui/progress/index.js +1 -31
- package/dist/esm/components/ui/qr-code/index.js +1 -24
- package/dist/esm/components/ui/radio-group/index.js +1 -12
- package/dist/esm/components/ui/scroll-area/index.js +1 -13
- package/dist/esm/components/ui/select/index.js +1 -74
- package/dist/esm/components/ui/separator/index.js +1 -8
- package/dist/esm/components/ui/sheet/index.js +1 -92
- package/dist/esm/components/ui/sidebar/index.js +1 -11
- package/dist/esm/components/ui/sidebar/next-router.cjs +1 -119
- package/dist/esm/components/ui/sidebar/sidebar-back-button.js +1 -5
- package/dist/esm/components/ui/sidebar/sidebar-components.js +1 -15
- package/dist/esm/components/ui/sidebar/sidebar-expand-button.js +1 -42
- package/dist/esm/components/ui/sidebar/sidebar-item-button.js +1 -18
- package/dist/esm/components/ui/sidebar/sidebar-item-collapsible.js +1 -44
- package/dist/esm/components/ui/sidebar/sidebar-item-icon-button.js +1 -14
- package/dist/esm/components/ui/sidebar/sidebar-item.js +1 -40
- package/dist/esm/components/ui/sidebar/sidebar-provider.js +1 -193
- package/dist/esm/components/ui/sidebar/sidebar-root.js +1 -126
- package/dist/esm/components/ui/sidebar/sidebar-router.js +1 -89
- package/dist/esm/components/ui/sidebar/sidebar-trigger.js +1 -53
- package/dist/esm/components/ui/skeleton/index.js +1 -6
- package/dist/esm/components/ui/stepper/index.js +1 -20
- package/dist/esm/components/ui/switch/index.js +1 -8
- package/dist/esm/components/ui/table/index.js +1 -44
- package/dist/esm/components/ui/tabs/index.js +1 -17
- package/dist/esm/components/ui/textarea/index.js +1 -6
- package/dist/esm/components/ui/time-picker/index.js +1 -49
- package/dist/esm/components/ui/toast/index.js +1 -2
- package/dist/esm/components/ui/toast/toaster.js +1 -13
- package/dist/esm/components/ui/toggle-group/index.js +1 -120
- package/dist/esm/components/ui/tooltip/index.js +1 -43
- package/dist/esm/domain/blotter/index.js +1 -16
- package/dist/esm/domain/delinquency-aging/aging-buckets.js +1 -115
- package/dist/esm/domain/delinquency-aging/index.js +1 -218
- package/dist/esm/domain/figure/index.js +1 -15
- package/dist/esm/domain/format.js +1 -128
- package/dist/esm/domain/index.js +1 -19
- package/dist/esm/domain/key-id/index.js +1 -169
- package/dist/esm/domain/ledger-sheet/index.js +1 -24
- package/dist/esm/domain/money-diff.js +1 -45
- package/dist/esm/domain/money-math.js +1 -98
- package/dist/esm/domain/money-text/index.js +1 -118
- package/dist/esm/domain/section-label/index.js +1 -14
- package/dist/esm/domain/status-rail/index.js +1 -71
- package/dist/esm/domain/threshold-gauge/index.js +1 -157
- package/dist/esm/enterprise/alert-banner/index.js +1 -21
- package/dist/esm/enterprise/app-shell/index.js +1 -51
- package/dist/esm/enterprise/cursor-pager/index.js +1 -23
- package/dist/esm/enterprise/data-table/column-size.js +1 -97
- package/dist/esm/enterprise/data-table/index.js +1 -342
- package/dist/esm/enterprise/date-range-picker/index.js +1 -112
- package/dist/esm/enterprise/detail-panel/index.js +1 -17
- package/dist/esm/enterprise/empty-state/index.js +1 -11
- package/dist/esm/enterprise/index.js +1 -22
- package/dist/esm/enterprise/number-input/index.js +1 -214
- package/dist/esm/enterprise/search-input/index.js +1 -85
- package/dist/esm/enterprise/stat-card/index.js +1 -72
- package/dist/esm/enterprise/status-badge/index.js +1 -116
- package/dist/esm/enterprise/use-is-mobile.js +1 -31
- package/dist/esm/enterprise/virtualized-table/index.js +1 -138
- package/dist/esm/hooks/use-click-away.js +1 -22
- package/dist/esm/hooks/use-pagination.js +1 -38
- package/dist/esm/hooks/use-stepper.js +1 -22
- package/dist/esm/hooks/use-time.js +1 -13
- package/dist/esm/hooks/use-toast.js +1 -107
- package/dist/esm/index.js +1 -77
- package/dist/esm/lib/storage/get-storage-object.js +1 -17
- package/dist/esm/lib/storage/get-storage.js +1 -14
- package/dist/esm/lib/storage/index.js +1 -2
- package/dist/esm/lib/typography.js +1 -31
- package/dist/esm/lib/utils.js +1 -41
- package/dist/esm/theme/get-theme-script.js +1 -41
- package/dist/esm/theme/index.js +1 -4
- package/dist/esm/theme/mode-toggle.js +1 -45
- package/dist/esm/theme/theme-provider.js +1 -122
- package/dist/esm/toast/helpers.js +1 -36
- package/dist/esm/toast/index.js +1 -4
- package/dist/esm/types/metadata-type.js +1 -1
- package/dist/esm/utils/countries.cjs +1 -17
- package/dist/esm/utils/country-utils.js +1 -18
- package/dist/esm/utils/currency-codes.js +1 -181
- package/dist/hooks/use-click-away.js +1 -29
- package/dist/hooks/use-pagination.js +1 -41
- package/dist/hooks/use-stepper.js +1 -26
- package/dist/hooks/use-time.js +1 -20
- package/dist/hooks/use-toast.js +1 -111
- package/dist/index.js +1 -96
- package/dist/lib/storage/get-storage-object.js +1 -23
- package/dist/lib/storage/get-storage.js +1 -17
- package/dist/lib/storage/index.js +1 -18
- package/dist/lib/typography.js +1 -34
- package/dist/lib/utils.js +1 -45
- package/dist/theme/get-theme-script.js +1 -44
- package/dist/theme/index.js +1 -11
- package/dist/theme/mode-toggle.js +1 -48
- package/dist/theme/theme-provider.js +1 -160
- package/dist/toast/helpers.js +1 -41
- package/dist/toast/index.js +1 -10
- package/dist/types/metadata-type.js +1 -2
- package/dist/utils/countries.cjs +1 -17
- package/dist/utils/country-utils.js +1 -25
- package/dist/utils/currency-codes.js +1 -184
- package/package.json +4 -3
|
@@ -1,74 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Button } from '../../../components/ui/button/index.js';
|
|
4
|
-
import { Calendar } from '../../../components/ui/calendar/index.js';
|
|
5
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
6
|
-
import { Popover, PopoverContent, PopoverTrigger } from '../../../components/ui/popover/index.js';
|
|
7
|
-
import { TimePicker } from '../../../components/ui/time-picker/index.js';
|
|
8
|
-
import { cn } from '../../../lib/utils.js';
|
|
9
|
-
import dayjs from 'dayjs';
|
|
10
|
-
import { CalendarIcon, XIcon } from 'lucide-react';
|
|
11
|
-
import * as React from 'react';
|
|
12
|
-
import { enUS } from 'react-day-picker/locale';
|
|
13
|
-
export const DateTimePickerField = ({ name, label, tooltip, labelExtra, description, placeholder = 'Select date and time...', disabled, readOnly, control, required, dateFormat = 'MMM DD, YYYY HH:mm', align = 'start', locale = enUS, minDate, maxDate, defaultTime = '00:00', ...others }) => {
|
|
14
|
-
const [open, setOpen] = React.useState(false);
|
|
15
|
-
const parseDefaultTime = (time) => {
|
|
16
|
-
const [h, m] = time.split(':').map(Number);
|
|
17
|
-
return {
|
|
18
|
-
hour: Number.isFinite(h) && h >= 0 && h <= 23 ? h : 0,
|
|
19
|
-
minute: Number.isFinite(m) && m >= 0 && m <= 59 ? m : 0
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
return (_jsx(FormField, { name: name, control: control, render: ({ field }) => {
|
|
23
|
-
const current = field.value ? dayjs(field.value) : null;
|
|
24
|
-
const isValid = current?.isValid() ?? false;
|
|
25
|
-
const validCurrent = isValid ? current : null;
|
|
26
|
-
const handleDateSelect = (date) => {
|
|
27
|
-
if (!date)
|
|
28
|
-
return;
|
|
29
|
-
const time = validCurrent
|
|
30
|
-
? { hour: validCurrent.hour(), minute: validCurrent.minute() }
|
|
31
|
-
: parseDefaultTime(defaultTime);
|
|
32
|
-
const combined = dayjs(date)
|
|
33
|
-
.hour(time.hour)
|
|
34
|
-
.minute(time.minute)
|
|
35
|
-
.second(0)
|
|
36
|
-
.millisecond(0);
|
|
37
|
-
field.onChange(combined.toISOString());
|
|
38
|
-
};
|
|
39
|
-
const handleTimeChange = (time) => {
|
|
40
|
-
if (!validCurrent)
|
|
41
|
-
return;
|
|
42
|
-
const updated = validCurrent
|
|
43
|
-
.hour(time.hour)
|
|
44
|
-
.minute(time.minute)
|
|
45
|
-
.second(0)
|
|
46
|
-
.millisecond(0);
|
|
47
|
-
field.onChange(updated.toISOString());
|
|
48
|
-
};
|
|
49
|
-
const handleClear = (e) => {
|
|
50
|
-
e.stopPropagation();
|
|
51
|
-
field.onChange('');
|
|
52
|
-
setOpen(false);
|
|
53
|
-
};
|
|
54
|
-
const showClear = !!field.value && !readOnly && !disabled;
|
|
55
|
-
return (_jsxs(FormItem, { required: required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs("div", { className: "relative w-full", children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(FormControl, { children: _jsx(Button, { variant: "outline", disabled: disabled, className: cn('bg-input hover:bg-input border-input-border text-foreground h-10 w-full justify-start rounded-md px-2.5 font-normal', readOnly && 'pointer-events-none', showClear && 'pr-8', open && 'ring-ring ring-2 ring-offset-0'), "data-testid": others['data-testid'], icon: _jsx(CalendarIcon, { className: "size-4" }), children: _jsx("span", { className: cn('text-foreground flex-1 text-left', validCurrent && 'font-bold'), children: validCurrent
|
|
56
|
-
? validCurrent.format(dateFormat)
|
|
57
|
-
: placeholder }) }) }) }), showClear && (_jsx("span", { role: "button", tabIndex: 0, "aria-label": "Clear date and time", className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring absolute top-1/2 right-2 z-10 inline-flex shrink-0 -translate-y-1/2 rounded-sm outline-none focus-visible:ring-2", onClick: handleClear, onKeyDown: (e) => {
|
|
58
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
handleClear(e);
|
|
61
|
-
}
|
|
62
|
-
}, children: _jsx(XIcon, { className: "size-4" }) }))] }), _jsx(PopoverContent, { className: "w-auto p-0", align: align, children: _jsxs("div", { className: "flex flex-col sm:flex-row sm:items-stretch", children: [_jsx(Calendar, { mode: "single", defaultMonth: validCurrent ? validCurrent.toDate() : undefined, selected: validCurrent ? validCurrent.toDate() : undefined, onSelect: handleDateSelect, locale: locale, disabled: (date) => {
|
|
63
|
-
if (disabled || readOnly)
|
|
64
|
-
return true;
|
|
65
|
-
if (minDate &&
|
|
66
|
-
date < dayjs(minDate).startOf('day').toDate())
|
|
67
|
-
return true;
|
|
68
|
-
if (maxDate &&
|
|
69
|
-
date > dayjs(maxDate).endOf('day').toDate())
|
|
70
|
-
return true;
|
|
71
|
-
return false;
|
|
72
|
-
} }), _jsx("div", { className: "bg-border hidden w-px self-stretch sm:block" }), _jsx("div", { className: "border-border border-t sm:hidden" }), _jsx(TimePicker, { hour: validCurrent ? validCurrent.hour() : undefined, minute: validCurrent ? validCurrent.minute() : undefined, onChange: handleTimeChange, disabled: disabled || readOnly || !validCurrent })] }) })] }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
|
|
73
|
-
} }));
|
|
74
|
-
};
|
|
1
|
+
"use client";import{jsx as r,jsxs as m}from"react/jsx-runtime";import{Button as O}from"../../../components/ui/button/index.js";import{Calendar as H}from"../../../components/ui/calendar/index.js";import{FormControl as V,FormDescription as _,FormField as q,FormItem as B,FormLabel as E,FormMessage as K,FormTooltip as L}from"../../../components/ui/form.js";import{Popover as R,PopoverContent as U,PopoverTrigger as X}from"../../../components/ui/popover/index.js";import{TimePicker as A}from"../../../components/ui/time-picker/index.js";import{cn as C}from"../../../lib/utils.js";import u from"dayjs";import{CalendarIcon as G,XIcon as J}from"lucide-react";import*as Q from"react";import{enUS as W}from"react-day-picker/locale";export const DateTimePickerField=({name:N,label:d,tooltip:c,labelExtra:F,description:f,placeholder:D="Select date and time...",disabled:s,readOnly:a,control:S,required:k,dateFormat:w="MMM DD, YYYY HH:mm",align:j="start",locale:I=W,minDate:h,maxDate:p,defaultTime:P="00:00",...T})=>{const[g,b]=Q.useState(false);const y=t=>{const[o,i]=t.split(":").map(Number);return{hour:Number.isFinite(o)&&o>=0&&o<=23?o:0,minute:Number.isFinite(i)&&i>=0&&i<=59?i:0}};return r(q,{name:N,control:S,render:({field:t})=>{const o=t.value?u(t.value):null;const i=o?.isValid()??false;const e=i?o:null;const M=n=>{if(!n)return;const l=e?{hour:e.hour(),minute:e.minute()}:y(P);const z=u(n).hour(l.hour).minute(l.minute).second(0).millisecond(0);t.onChange(z.toISOString())};const Y=n=>{if(!e)return;const l=e.hour(n.hour).minute(n.minute).second(0).millisecond(0);t.onChange(l.toISOString())};const v=n=>{n.stopPropagation();t.onChange("");b(false)};const x=!!t.value&&!a&&!s;return m(B,{required:k,children:[d&&r(E,{extra:c?r(L,{children:c}):F,children:d}),m(R,{open:g,onOpenChange:b,children:[m("div",{className:"relative w-full",children:[r(X,{asChild:true,children:r(V,{children:r(O,{variant:"outline",disabled:s,className:C("bg-input hover:bg-input border-input-border text-foreground h-10 w-full justify-start rounded-md px-2.5 font-normal",a&&"pointer-events-none",x&&"pr-8",g&&"ring-ring ring-2 ring-offset-0"),"data-testid":T["data-testid"],icon:r(G,{className:"size-4"}),children:r("span",{className:C("text-foreground flex-1 text-left",e&&"font-bold"),children:e?e.format(w):D})})})}),x&&r("span",{role:"button",tabIndex:0,"aria-label":"Clear date and time",className:"text-muted-foreground hover:text-foreground focus-visible:ring-ring absolute top-1/2 right-2 z-10 inline-flex shrink-0 -translate-y-1/2 rounded-sm outline-none focus-visible:ring-2",onClick:v,onKeyDown:n=>{if(n.key==="Enter"||n.key===" "){n.preventDefault();v(n)}},children:r(J,{className:"size-4"})})]}),r(U,{className:"w-auto p-0",align:j,children:m("div",{className:"flex flex-col sm:flex-row sm:items-stretch",children:[r(H,{mode:"single",defaultMonth:e?e.toDate():void 0,selected:e?e.toDate():void 0,onSelect:M,locale:I,disabled:n=>{if(s||a)return true;if(h&&n<u(h).startOf("day").toDate())return true;if(p&&n>u(p).endOf("day").toDate())return true;return false}}),r("div",{className:"bg-border hidden w-px self-stretch sm:block"}),r("div",{className:"border-border border-t sm:hidden"}),r(A,{hour:e?e.hour():void 0,minute:e?e.minute():void 0,onChange:Y,disabled:s||a||!e})]})})]}),r(K,{}),f&&r(_,{children:f})]})}})};
|
|
@@ -1,65 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
5
|
-
import { FileUpload } from '../../../components/ui/file-upload/index.js';
|
|
6
|
-
import { hasRenderableLabel } from '../../../components/form/renderable-label.js';
|
|
7
|
-
/**
|
|
8
|
-
* FileUploadField — the react-hook-form field for FileUpload.
|
|
9
|
-
*
|
|
10
|
-
* The field VALUE is the file's UTF-8 TEXT string (so a Zod `z.string()` PEM
|
|
11
|
-
* schema validates directly), `''` when cleared — never the File, never null —
|
|
12
|
-
* so `z.string().min(1)` fires the required error with zero adapter code. The
|
|
13
|
-
* File metadata for the visible chip lives in field-LOCAL state, since the form
|
|
14
|
-
* only holds the text. The shown chip is DERIVED from the form field value, so
|
|
15
|
-
* an external `form.reset()` / `setValue(name, '')` that clears the text also
|
|
16
|
-
* clears the chip — local state can never linger out of sync with the form. A
|
|
17
|
-
* rejected pick (size/type/read) clears the form value so it can't pass
|
|
18
|
-
* validation, while the primitive's own inline `role="alert"` announces the
|
|
19
|
-
* rejection; on resolver failure the FormMessage announces. Both surfaces are
|
|
20
|
-
* wired.
|
|
21
|
-
*/
|
|
22
|
-
export const FileUploadField = ({ control, name, label, description, tooltip, required, accept, maxSizeBytes, disabled, className, labels, onError, onSelect, 'aria-label': ariaLabelProp }) => {
|
|
23
|
-
const [localValue, setLocalValue] = useState(null);
|
|
24
|
-
const showLabel = hasRenderableLabel(label);
|
|
25
|
-
// An empty or whitespace-only aria-label is worse than none: it names the
|
|
26
|
-
// control "". Drop it so the attribute never reaches the DOM.
|
|
27
|
-
const ariaLabel = ariaLabelProp?.trim() ? ariaLabelProp : undefined;
|
|
28
|
-
// The type cannot rule out `label=""`, so this is the only signal a developer
|
|
29
|
-
// gets that the control shipped nameless.
|
|
30
|
-
if (process.env.NODE_ENV !== 'production' && !showLabel && !ariaLabel) {
|
|
31
|
-
console.error(`FileUploadField "${name}" has no accessible name: pass a non-empty label or aria-label.`);
|
|
32
|
-
}
|
|
33
|
-
return (_jsx(FormField, { control: control, name: name, render: ({ field }) => {
|
|
34
|
-
// The chip follows the LOCAL selection, reconciled against the form
|
|
35
|
-
// value — not the form value's truthiness.
|
|
36
|
-
//
|
|
37
|
-
// Truthiness cannot tell "no file" from "an accepted EMPTY file": both
|
|
38
|
-
// store ''. So picking a legitimately empty file hid the chip, the zone
|
|
39
|
-
// went back to "Choose a file or drag and drop" even though the pick was
|
|
40
|
-
// accepted, and a `z.string().min(1)` rule then failed the field with
|
|
41
|
-
// nothing on screen to explain why.
|
|
42
|
-
//
|
|
43
|
-
// Comparing against the text we last wrote keeps the external-reset
|
|
44
|
-
// behaviour that truthiness was there for: after form.reset() or
|
|
45
|
-
// setValue(name, ''), the stored text no longer matches the selection, so
|
|
46
|
-
// the stale File metadata still cannot linger.
|
|
47
|
-
//
|
|
48
|
-
// ponytail: one residual case is irreducible from the form value alone —
|
|
49
|
-
// resetting AFTER picking an empty file leaves the chip up, because ''
|
|
50
|
-
// (reset) and '' (that file's text) are the same string and the form
|
|
51
|
-
// value carries nothing else to tell them apart. Fix needs a reset epoch
|
|
52
|
-
// from react-hook-form, or a form value richer than the file text.
|
|
53
|
-
const shown = localValue && field.value === localValue.text ? localValue : null;
|
|
54
|
-
return (_jsxs(FormItem, { required: required, children: [showLabel && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label })), _jsx(FormControl, { children: _jsx(FileUpload, { ref: field.ref, name: field.name, onBlur: field.onBlur, "aria-label": ariaLabel, labels: labels, accept: accept, maxSizeBytes: maxSizeBytes, disabled: disabled, className: className, value: shown, onSelect: (result) => {
|
|
55
|
-
setLocalValue(result);
|
|
56
|
-
field.onChange(result?.text ?? '');
|
|
57
|
-
onSelect?.(result);
|
|
58
|
-
}, onError: (error) => {
|
|
59
|
-
setLocalValue(null);
|
|
60
|
-
field.onChange('');
|
|
61
|
-
onSelect?.(null);
|
|
62
|
-
onError?.(error);
|
|
63
|
-
} }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
|
|
64
|
-
} }));
|
|
65
|
-
};
|
|
1
|
+
"use client";import{jsx as e,jsxs as v}from"react/jsx-runtime";import{useState as B}from"react";import{FormControl as C,FormDescription as E,FormField as N,FormItem as U,FormLabel as V,FormMessage as _,FormTooltip as w}from"../../../components/ui/form.js";import{FileUpload as y}from"../../../components/ui/file-upload/index.js";import{hasRenderableLabel as D}from"../../../components/form/renderable-label.js";export const FileUploadField=({control:p,name:l,label:n,description:s,tooltip:t,required:F,accept:h,maxSizeBytes:b,disabled:x,className:f,labels:j,onError:L,onSelect:m,"aria-label":i})=>{const[a,c]=B(null);const u=D(n);const d=i?.trim()?i:void 0;if(process.env.NODE_ENV!=="production"&&!u&&!d){console.error(`FileUploadField "${l}" has no accessible name: pass a non-empty label or aria-label.`)}return e(N,{control:p,name:l,render:({field:r})=>{const g=a&&r.value===a.text?a:null;return v(U,{required:F,children:[u&&e(V,{extra:t?e(w,{children:t}):void 0,children:n}),e(C,{children:e(y,{ref:r.ref,name:r.name,onBlur:r.onBlur,"aria-label":d,labels:j,accept:h,maxSizeBytes:b,disabled:x,className:f,value:g,onSelect:o=>{c(o);r.onChange(o?.text??"");m?.(o)},onError:o=>{c(null);r.onChange("");m?.(null);L?.(o)}})}),e(_,{}),s&&e(E,{children:s})]})}})};
|
|
@@ -1,15 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from './select-field/index.js';
|
|
3
|
-
export * from './switch-field/index.js';
|
|
4
|
-
export * from './pagination-limit-field/index.js';
|
|
5
|
-
export * from './combo-box-field/index.js';
|
|
6
|
-
export * from './country-field/index.js';
|
|
7
|
-
export * from './state-field/index.js';
|
|
8
|
-
export * from './date-range-field/index.js';
|
|
9
|
-
export * from './date-picker-field/index.js';
|
|
10
|
-
export * from './date-time-picker-field/index.js';
|
|
11
|
-
export * from './otp-field/index.js';
|
|
12
|
-
export * from './password-field/index.js';
|
|
13
|
-
export * from './file-upload-field/index.js';
|
|
14
|
-
export * from './radio-group-field/index.js';
|
|
15
|
-
export * from './textarea-field/index.js';
|
|
1
|
+
export*from"./input-field/index.js";export*from"./select-field/index.js";export*from"./switch-field/index.js";export*from"./pagination-limit-field/index.js";export*from"./combo-box-field/index.js";export*from"./country-field/index.js";export*from"./state-field/index.js";export*from"./date-range-field/index.js";export*from"./date-picker-field/index.js";export*from"./date-time-picker-field/index.js";export*from"./otp-field/index.js";export*from"./password-field/index.js";export*from"./file-upload-field/index.js";export*from"./radio-group-field/index.js";export*from"./textarea-field/index.js";
|
|
@@ -1,92 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { AutosizeTextarea } from '../../../components/ui/autosize-textarea/index.js';
|
|
3
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
4
|
-
import { Input } from '../../../components/ui/input/index.js';
|
|
5
|
-
import { useMemo, useRef } from 'react';
|
|
6
|
-
/**
|
|
7
|
-
* Fan one node out to several refs. React hands a node to ONE ref, so a
|
|
8
|
-
* consumer ref and react-hook-form's `field.ref` cannot both be passed
|
|
9
|
-
* directly — one silently wins. Only used when the consumer actually supplied
|
|
10
|
-
* a ref, so a field without one keeps `field.ref`'s own identity and React
|
|
11
|
-
* never re-attaches it.
|
|
12
|
-
*/
|
|
13
|
-
function fanOutRef(...refs) {
|
|
14
|
-
return (node) => {
|
|
15
|
-
const cleanups = [];
|
|
16
|
-
for (const ref of refs) {
|
|
17
|
-
if (typeof ref === 'function') {
|
|
18
|
-
const cleanup = ref(node);
|
|
19
|
-
cleanups.push(typeof cleanup === 'function' ? cleanup : () => ref(null));
|
|
20
|
-
}
|
|
21
|
-
else if (ref) {
|
|
22
|
-
;
|
|
23
|
-
ref.current = node;
|
|
24
|
-
cleanups.push(() => {
|
|
25
|
-
;
|
|
26
|
-
ref.current = null;
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return () => cleanups.forEach((cleanup) => cleanup());
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export const InputField = ({ className, type, label, tooltip, labelExtra, placeholder, description, startAdornment, endAdornment, required, readOnly, minHeight, maxHeight, textArea, defaultValue, value, onChange, min, max, step, maxLength, autoComplete, inputMode, pattern, autoFocus, ref, 'aria-invalid': ariaInvalid, ...others }) => {
|
|
34
|
-
// OR with the form's error, expressed through the Slot merge rather than by
|
|
35
|
-
// reading the form: FormControl already injects `aria-invalid={!!error}`, and
|
|
36
|
-
// a child prop only wins that merge when the KEY is present. So emit the key
|
|
37
|
-
// only for an explicit `true`, and let FormControl answer for every other
|
|
38
|
-
// case. `true || error` is true; `false || error` is whatever the form says,
|
|
39
|
-
// which is the whole point — an explicit `false` cannot mute a live error.
|
|
40
|
-
// With no `control` there is no error to OR against, so FormControl injects
|
|
41
|
-
// `false` and today's behaviour is unchanged.
|
|
42
|
-
const ariaInvalidProp = ariaInvalid ? { 'aria-invalid': true } : undefined;
|
|
43
|
-
const formRef = useRef(undefined);
|
|
44
|
-
const composedRef = useMemo(() => ref ? fanOutRef((node) => formRef.current?.(node), ref) : ref,
|
|
45
|
-
// `others.name` belongs here, and leaving it out is not a lint nit.
|
|
46
|
-
// react-hook-form hands a fresh `field.ref` for every name, and that ref
|
|
47
|
-
// only receives the element when React reattaches, which it does only when
|
|
48
|
-
// this callback's identity changes. Memoised on `ref` alone, one identity
|
|
49
|
-
// survived a name change, the new field never got its element, and
|
|
50
|
-
// `form.setFocus(newName)` then found nothing to focus and said nothing.
|
|
51
|
-
[ref, others.name]);
|
|
52
|
-
const renderItem = (binding) => (_jsxs(FormItem, { required: required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsx(FormControl, { children: textArea ? (_jsx(AutosizeTextarea, { className: className, placeholder: placeholder, readOnly: readOnly, minHeight: minHeight, maxHeight: maxHeight, maxLength: maxLength, autoComplete: autoComplete, inputMode: inputMode, autoFocus: autoFocus, "data-testid": others['data-testid'], ...ariaInvalidProp, ...binding })) : (_jsx(Input, { className: className, type: type, placeholder: placeholder, readOnly: readOnly, startAdornment: startAdornment, endAdornment: endAdornment,
|
|
53
|
-
// Bounds belong to the single-line control only: `min`/`max`/`step`
|
|
54
|
-
// mean nothing on a textarea and would render as invalid markup.
|
|
55
|
-
min: min, max: max, step: step, maxLength: maxLength, autoComplete: autoComplete, inputMode: inputMode,
|
|
56
|
-
// Single-line only: `<textarea pattern>` is not a thing.
|
|
57
|
-
pattern: pattern, autoFocus: autoFocus, "data-testid": others['data-testid'], ...ariaInvalidProp, ...binding })) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
|
|
58
|
-
if (!others.control) {
|
|
59
|
-
return renderItem({
|
|
60
|
-
name: others.name,
|
|
61
|
-
disabled: others.disabled,
|
|
62
|
-
// Controlled when the caller holds the value, uncontrolled when it only
|
|
63
|
-
// seeds one — supplying both is what React warns about.
|
|
64
|
-
...(value !== undefined ? { value } : { defaultValue }),
|
|
65
|
-
onChange: (e) => onChange?.(e),
|
|
66
|
-
// Nothing else claims the node on this branch, so the consumer ref goes
|
|
67
|
-
// straight through. Without it there was no node to focus at all.
|
|
68
|
-
ref: ref
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return (_jsx(FormField, { ...others, name: others.name,
|
|
72
|
-
// The seed goes through react-hook-form, never straight to the DOM: a
|
|
73
|
-
// value painted on the box only makes the field LOOK filled while the
|
|
74
|
-
// form still submits nothing for that name. Controller seeds its own
|
|
75
|
-
// state from this, so `field.value` carries the seed on both branches.
|
|
76
|
-
defaultValue: defaultValue, render: ({ field }) => {
|
|
77
|
-
formRef.current = field.ref;
|
|
78
|
-
return renderItem({
|
|
79
|
-
...field,
|
|
80
|
-
// Controlled from mount. react-hook-form hands `undefined` for any
|
|
81
|
-
// name the form declares no default for; React then mounts the box
|
|
82
|
-
// uncontrolled and flips it on the first keystroke, losing that
|
|
83
|
-
// character.
|
|
84
|
-
value: field.value ?? '',
|
|
85
|
-
onChange: (e) => {
|
|
86
|
-
field.onChange(e);
|
|
87
|
-
onChange?.(e);
|
|
88
|
-
},
|
|
89
|
-
ref: (ref ? composedRef : field.ref)
|
|
90
|
-
});
|
|
91
|
-
} }));
|
|
92
|
-
};
|
|
1
|
+
import{jsx as t,jsxs as D}from"react/jsx-runtime";import{AutosizeTextarea as E}from"../../../components/ui/autosize-textarea/index.js";import{FormControl as N,FormDescription as P,FormField as k,FormItem as w,FormLabel as B,FormMessage as G,FormTooltip as J}from"../../../components/ui/form.js";import{Input as K}from"../../../components/ui/input/index.js";import{useMemo as Q,useRef as S}from"react";function U(...i){return m=>{const a=[];for(const n of i){if(typeof n==="function"){const u=n(m);a.push(typeof u==="function"?u:()=>n(null))}else if(n){;n.current=m;a.push(()=>{;n.current=null})}}return()=>a.forEach(n=>n())}}export const InputField=({className:i,type:m,label:a,tooltip:n,labelExtra:u,placeholder:d,description:s,startAdornment:C,endAdornment:R,required:A,readOnly:c,minHeight:M,maxHeight:H,textArea:L,defaultValue:l,value:p,onChange:f,min:O,max:T,step:_,maxLength:x,autoComplete:F,inputMode:h,pattern:b,autoFocus:g,ref:o,"aria-invalid":q,...r})=>{const I=q?{"aria-invalid":true}:void 0;const j=S(void 0);const z=Q(()=>o?U(e=>j.current?.(e),o):o,[o,r.name]);const v=e=>D(w,{required:A,children:[a&&t(B,{extra:n?t(J,{children:n}):u,children:a}),t(N,{children:L?t(E,{className:i,placeholder:d,readOnly:c,minHeight:M,maxHeight:H,maxLength:x,autoComplete:F,inputMode:h,autoFocus:g,"data-testid":r["data-testid"],...I,...e}):t(K,{className:i,type:m,placeholder:d,readOnly:c,startAdornment:C,endAdornment:R,min:O,max:T,step:_,maxLength:x,autoComplete:F,inputMode:h,pattern:b,autoFocus:g,"data-testid":r["data-testid"],...I,...e})}),t(G,{}),s&&t(P,{children:s})]});if(!r.control){return v({name:r.name,disabled:r.disabled,...p!==void 0?{value:p}:{defaultValue:l},onChange:e=>f?.(e),ref:o})}return t(k,{...r,name:r.name,defaultValue:l,render:({field:e})=>{j.current=e.ref;return v({...e,value:e.value??"",onChange:y=>{e.onChange(y);f?.(y)},ref:o?z:e.ref})}})};
|
|
@@ -1,15 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
3
|
-
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '../../../components/ui/otp-input/index.js';
|
|
4
|
-
export const OtpField = ({ name, label, tooltip, labelExtra, description, maxLength = 6, pattern, separator = false, ...others }) => {
|
|
5
|
-
const midpoint = Math.floor(maxLength / 2);
|
|
6
|
-
const renderSlots = () => {
|
|
7
|
-
if (separator && maxLength > 1) {
|
|
8
|
-
const firstGroup = Array.from({ length: midpoint }, (_, i) => (_jsx(InputOTPSlot, { index: i }, i)));
|
|
9
|
-
const secondGroup = Array.from({ length: maxLength - midpoint }, (_, i) => _jsx(InputOTPSlot, { index: midpoint + i }, midpoint + i));
|
|
10
|
-
return (_jsxs(_Fragment, { children: [_jsx(InputOTPGroup, { children: firstGroup }), _jsx(InputOTPSeparator, {}), _jsx(InputOTPGroup, { children: secondGroup })] }));
|
|
11
|
-
}
|
|
12
|
-
return (_jsx(InputOTPGroup, { children: Array.from({ length: maxLength }, (_, i) => (_jsx(InputOTPSlot, { index: i }, i))) }));
|
|
13
|
-
};
|
|
14
|
-
return (_jsx(FormField, { ...others, name: name, render: ({ field }) => (_jsxs(FormItem, { required: others.required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsx(FormControl, { children: _jsx(InputOTP, { maxLength: maxLength, pattern: pattern, value: field.value ?? '', onChange: field.onChange, onBlur: field.onBlur, ref: field.ref, name: field.name, disabled: others.disabled, "data-testid": others['data-testid'], children: renderSlots() }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] })) }));
|
|
15
|
-
};
|
|
1
|
+
import{jsx as r,Fragment as g,jsxs as u}from"react/jsx-runtime";import{FormControl as I,FormDescription as O,FormField as T,FormItem as j,FormLabel as P,FormMessage as y,FormTooltip as A}from"../../../components/ui/form.js";import{InputOTP as C,InputOTPGroup as i,InputOTPSlot as l,InputOTPSeparator as G}from"../../../components/ui/otp-input/index.js";export const OtpField=({name:p,label:s,tooltip:m,labelExtra:F,description:c,maxLength:n=6,pattern:h,separator:f=false,...t})=>{const a=Math.floor(n/2);const x=()=>{if(f&&n>1){const e=Array.from({length:a},(_,o)=>r(l,{index:o},o));const d=Array.from({length:n-a},(_,o)=>r(l,{index:a+o},a+o));return u(g,{children:[r(i,{children:e}),r(G,{}),r(i,{children:d})]})}return r(i,{children:Array.from({length:n},(e,d)=>r(l,{index:d},d))})};return r(T,{...t,name:p,render:({field:e})=>u(j,{required:t.required,children:[s&&r(P,{extra:m?r(A,{children:m}):F,children:s}),r(I,{children:r(C,{maxLength:n,pattern:h,value:e.value??"",onChange:e.onChange,onBlur:e.onBlur,ref:e.ref,name:e.name,disabled:t.disabled,"data-testid":t["data-testid"],children:x()})}),r(y,{}),c&&r(O,{children:c})]})})};
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { SelectField } from '../select-field/index.js';
|
|
3
|
-
import { SelectItem } from '../../../components/ui/select/index.js';
|
|
4
|
-
export const PaginationLimitField = ({ options = [10, 50, 100], label = 'Items per page', control, ['data-testid']: dataTestId }) => {
|
|
5
|
-
return (_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium whitespace-nowrap", children: label }), _jsx(SelectField, { name: "limit", control: control, "data-testid": dataTestId, children: options.map((pageSize) => (_jsx(SelectItem, { value: String(pageSize), children: pageSize }, pageSize))) })] }));
|
|
6
|
-
};
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{SelectField as n}from"../select-field/index.js";import{SelectItem as o}from"../../../components/ui/select/index.js";export const PaginationLimitField=({options:i=[10,50,100],label:r="Items per page",control:m,["data-testid"]:s})=>{return a("div",{className:"flex items-center gap-4",children:[e("p",{className:"text-muted-foreground text-sm font-medium whitespace-nowrap",children:r}),e(n,{name:"limit",control:m,"data-testid":s,children:i.map(t=>e(o,{value:String(t),children:t},t))})]})};
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { FormControl, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
4
|
-
import { Input, InputAdornment } from '../../../components/ui/input/index.js';
|
|
5
|
-
import { Eye, EyeOff } from 'lucide-react';
|
|
6
|
-
import { IconButton } from '../../../components/ui/icon-button/index.js';
|
|
7
|
-
export function PasswordField({ name, label, tooltip, placeholder, control, required = false, disabled = false, 'data-testid': dataTestId, showPasswordLabel = 'Show password', hidePasswordLabel = 'Hide password' }) {
|
|
8
|
-
const [show, setShow] = React.useState(false);
|
|
9
|
-
return (_jsx(FormField, { control: control, name: name, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: show ? 'text' : 'password', placeholder: placeholder, endAdornment: _jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { type: "button", variant: "outline", rounded: true, "aria-label": show ? hidePasswordLabel : showPasswordLabel, onClick: () => setShow(!show), children: show ? _jsx(EyeOff, { "aria-hidden": true }) : _jsx(Eye, { "aria-hidden": true }) }) }), disabled: disabled, "data-testid": dataTestId }) }), _jsx(FormMessage, {})] })) }));
|
|
10
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as f}from"react/jsx-runtime";import h from"react";import{FormControl as F,FormField as w,FormItem as x,FormLabel as b,FormMessage as j,FormTooltip as y}from"../../../components/ui/form.js";import{Input as I,InputAdornment as S}from"../../../components/ui/input/index.js";import{Eye as A,EyeOff as C}from"lucide-react";import{IconButton as E}from"../../../components/ui/icon-button/index.js";export function PasswordField({name:t,label:d,tooltip:o,placeholder:n,control:i,required:a=false,disabled:s=false,"data-testid":m,showPasswordLabel:l="Show password",hidePasswordLabel:p="Hide password"}){const[r,u]=h.useState(false);return e(w,{control:i,name:t,render:({field:c})=>f(x,{required:a,children:[e(b,{extra:o?e(y,{children:o}):void 0,children:d}),e(F,{children:e(I,{...c,type:r?"text":"password",placeholder:n,endAdornment:e(S,{position:"end",children:e(E,{type:"button",variant:"outline",rounded:true,"aria-label":r?p:l,onClick:()=>u(!r),children:r?e(C,{"aria-hidden":true}):e(A,{"aria-hidden":true})})}),disabled:s,"data-testid":m})}),e(j,{})]})})}
|
|
@@ -1,32 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useId } from 'react';
|
|
4
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
5
|
-
import { Label } from '../../../components/ui/label/index.js';
|
|
6
|
-
import { RadioGroup, RadioGroupItem } from '../../../components/ui/radio-group/index.js';
|
|
7
|
-
import { hasRenderableLabel } from '../../../components/form/renderable-label.js';
|
|
8
|
-
export const RadioGroupField = ({ control, name, options, label, description, tooltip, required, disabled, 'aria-label': ariaLabelProp }) => {
|
|
9
|
-
const showLabel = hasRenderableLabel(label);
|
|
10
|
-
// An empty or whitespace-only aria-label is worse than none: it names the
|
|
11
|
-
// control "". Drop it so the attribute never reaches the DOM.
|
|
12
|
-
const ariaLabel = ariaLabelProp?.trim() ? ariaLabelProp : undefined;
|
|
13
|
-
// `FormLabel` names its control through `htmlFor`, which only works on a
|
|
14
|
-
// labelable element — and the Radix radiogroup root is a plain div. So the
|
|
15
|
-
// visible label reached nobody: a screen-reader user landing on the group
|
|
16
|
-
// heard the options and no field name. The group points BACK at the label
|
|
17
|
-
// instead, via aria-labelledby on a stable generated id.
|
|
18
|
-
const labelId = useId();
|
|
19
|
-
// The type cannot rule out `label=""`, so this is the only signal a developer
|
|
20
|
-
// gets that the control shipped nameless.
|
|
21
|
-
if (process.env.NODE_ENV !== 'production' && !showLabel && !ariaLabel) {
|
|
22
|
-
console.error(`RadioGroupField "${name}" has no accessible name: pass a non-empty label or aria-label.`);
|
|
23
|
-
}
|
|
24
|
-
// `setFocus(name)` / focus-on-error needs a ref to something focusable. The
|
|
25
|
-
// RadioGroup root is a plain div, so the ref goes on the first option a user
|
|
26
|
-
// could actually reach — a disabled radio is not focusable.
|
|
27
|
-
const focusTargetIndex = options.findIndex((option) => !option.disabled);
|
|
28
|
-
return (_jsx(FormField, { control: control, name: name, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [showLabel && (_jsx(FormLabel, { id: labelId, extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label })), _jsx(FormControl, { children: _jsx(RadioGroup, { name: field.name, value: field.value ?? '', onValueChange: field.onChange, onBlur: field.onBlur, disabled: disabled, "aria-labelledby": showLabel ? labelId : undefined, "aria-label": showLabel ? undefined : ariaLabel, className: "gap-2", children: options.map((option, index) => {
|
|
29
|
-
const id = `${name}-${option.value}`;
|
|
30
|
-
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(RadioGroupItem, { ref: index === focusTargetIndex ? field.ref : undefined, value: option.value, id: id, disabled: option.disabled }), _jsx(Label, { htmlFor: id, className: "font-normal", children: option.label })] }, option.value));
|
|
31
|
-
}) }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] })) }));
|
|
32
|
-
};
|
|
1
|
+
"use client";import{jsx as e,jsxs as b}from"react/jsx-runtime";import{useId as v}from"react";import{FormControl as g,FormDescription as I,FormField as N,FormItem as R,FormLabel as j,FormMessage as G,FormTooltip as L}from"../../../components/ui/form.js";import{Label as C}from"../../../components/ui/label/index.js";import{RadioGroup as _,RadioGroupItem as $}from"../../../components/ui/radio-group/index.js";import{hasRenderableLabel as y}from"../../../components/form/renderable-label.js";export const RadioGroupField=({control:f,name:n,options:o,label:d,description:s,tooltip:i,required:h,disabled:p,"aria-label":m})=>{const l=y(d);const c=m?.trim()?m:void 0;const u=v();if(process.env.NODE_ENV!=="production"&&!l&&!c){console.error(`RadioGroupField "${n}" has no accessible name: pass a non-empty label or aria-label.`)}const F=o.findIndex(r=>!r.disabled);return e(N,{control:f,name:n,render:({field:r})=>b(R,{required:h,children:[l&&e(j,{id:u,extra:i?e(L,{children:i}):void 0,children:d}),e(g,{children:e(_,{name:r.name,value:r.value??"",onValueChange:r.onChange,onBlur:r.onBlur,disabled:p,"aria-labelledby":l?u:void 0,"aria-label":l?void 0:c,className:"gap-2",children:o.map((a,x)=>{const t=`${n}-${a.value}`;return b("div",{className:"flex items-center gap-2",children:[e($,{ref:x===F?r.ref:void 0,value:a.value,id:t,disabled:a.disabled}),e(C,{htmlFor:t,className:"font-normal",children:a.label})]},a.value)})})}),e(G,{}),s&&e(I,{children:s})]})})};
|
|
@@ -1,54 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* Does this label put readable content INSIDE the label element — i.e. does it
|
|
4
|
-
* give the control an accessible name?
|
|
5
|
-
*
|
|
6
|
-
* Ceiling, on purpose: a non-fragment element always counts, even an empty one.
|
|
7
|
-
* We cannot render a component to find out whether it emits text, so
|
|
8
|
-
* `label={<EmptyThing />}` is accepted. Fragments ARE unwrapped, since they add
|
|
9
|
-
* no markup of their own and `<></>` is a plausible way to say "no label".
|
|
10
|
-
*
|
|
11
|
-
* A PORTAL DOES NOT COUNT. It is the one node that renders its content
|
|
12
|
-
* somewhere else in the DOM — by definition outside the label subtree — so the
|
|
13
|
-
* label element itself stays empty and names nothing. The content is visible on
|
|
14
|
-
* screen, which is exactly what makes this worth guarding: it looks labelled
|
|
15
|
-
* and is silent to a screen reader. Pass `aria-label` when the visible text
|
|
16
|
-
* lives in a portal.
|
|
17
|
-
*
|
|
18
|
-
* BRANCH ORDER IS LOAD-BEARING — element before portal before collection:
|
|
19
|
-
* `Children.toArray` hands a portal straight back unchanged, so reaching the
|
|
20
|
-
* collection branch with one would recurse forever. The portal branch is the
|
|
21
|
-
* crash guard as much as the verdict. Every field in this directory shares this
|
|
22
|
-
* one copy precisely so that ordering cannot drift.
|
|
23
|
-
*/
|
|
24
|
-
export function hasRenderableLabel(label) {
|
|
25
|
-
if (label === null || label === undefined || typeof label === 'boolean') {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
// Empty AND whitespace-only strings render a label box with nothing to
|
|
29
|
-
// announce, so both count as absent. A number (`0`) is a real label.
|
|
30
|
-
if (typeof label === 'string')
|
|
31
|
-
return label.trim() !== '';
|
|
32
|
-
if (typeof label === 'number' || typeof label === 'bigint')
|
|
33
|
-
return true;
|
|
34
|
-
if (isValidElement(label)) {
|
|
35
|
-
return label.type === Fragment
|
|
36
|
-
? hasRenderableLabel(label.props?.children ?? null)
|
|
37
|
-
: true;
|
|
38
|
-
}
|
|
39
|
-
// Portals and any other React-internal node that is not an element: their
|
|
40
|
-
// content lands outside this subtree (or is unknowable), so it cannot name
|
|
41
|
-
// the control. Must stay ABOVE the collection branch: isValidElement is false
|
|
42
|
-
// for a portal and Children.toArray hands the identical node straight back,
|
|
43
|
-
// so recursing on it would never terminate.
|
|
44
|
-
if (typeof label === 'object' && '$$typeof' in label)
|
|
45
|
-
return false;
|
|
46
|
-
const children = Children.toArray(label);
|
|
47
|
-
// Belt for anything else toArray cannot break down: no progress left to make,
|
|
48
|
-
// and nothing we can confirm renders in place, so treat it as absent rather
|
|
49
|
-
// than recurse forever.
|
|
50
|
-
if (children.length === 1 && children[0] === label)
|
|
51
|
-
return false;
|
|
52
|
-
// Arrays and other iterables: renderable only if some child is.
|
|
53
|
-
return children.some((child) => hasRenderableLabel(child));
|
|
54
|
-
}
|
|
1
|
+
import{Children as e,Fragment as i,isValidElement as f}from"react";export function hasRenderableLabel(r){if(r===null||r===void 0||typeof r==="boolean"){return false}if(typeof r==="string")return r.trim()!=="";if(typeof r==="number"||typeof r==="bigint")return true;if(f(r)){return r.type===i?hasRenderableLabel(r.props?.children??null):true}if(typeof r==="object"&&"$$typeof"in r)return false;const t=e.toArray(r);if(t.length===1&&t[0]===r)return false;return t.some(n=>hasRenderableLabel(n))}
|
|
@@ -1,64 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
3
|
-
import { MultipleSelect, MultipleSelectContent, MultipleSelectTrigger, MultipleSelectValue } from '../../../components/ui/multiple-select/index.js';
|
|
4
|
-
import { Select, SelectContent, SelectEmpty, SelectTrigger, SelectValue } from '../../../components/ui/select/index.js';
|
|
5
|
-
import { cn } from '../../../lib/utils.js';
|
|
6
|
-
export const SelectField = ({ name, label, tooltip, labelExtra, required, placeholder, description, disabled, readOnly, multi, control, value, children, emptyMessage = 'No options found.', leadingIcon, className, onChange, ...others }) => {
|
|
7
|
-
// The props union already pairs `multi` with the callback shape that branch
|
|
8
|
-
// emits, but destructuring an intersection loses that correlation — TS sees
|
|
9
|
-
// two unrelated callbacks. Both branches report through this one seam, each
|
|
10
|
-
// with the payload its own union member declared.
|
|
11
|
-
const emitChange = onChange;
|
|
12
|
-
/**
|
|
13
|
-
* ⛔ THE GLYPH AND THE VALUE ARE ONE FLEX CHILD, NEVER TWO.
|
|
14
|
-
*
|
|
15
|
-
* `.select-trigger` is `flex w-full items-center justify-between`, so a
|
|
16
|
-
* third child makes it split the free space into TWO gaps: on any trigger
|
|
17
|
-
* wider than its content — which is every filter bar — the selected value
|
|
18
|
-
* drifted to the middle of the box instead of sitting beside the glyph.
|
|
19
|
-
* Wrapped, the trigger is back to two children, content and chevron,
|
|
20
|
-
* whatever the caller passes.
|
|
21
|
-
*
|
|
22
|
-
* `min-w-0` keeps the value truncating: the wrapper is the flex item now,
|
|
23
|
-
* and a flex item never shrinks below its own min-content width. The
|
|
24
|
-
* trigger's `VALUE_OVERFLOW_CLASS` reaches through it by descendant
|
|
25
|
-
* selector.
|
|
26
|
-
*
|
|
27
|
-
* No `gap`: the prop's contract is that spacing is the caller's (the Tracer
|
|
28
|
-
* filter bars carry their own `mr-2`), and a kit gap would double with it.
|
|
29
|
-
*
|
|
30
|
-
* The MULTI trigger needs none of this — it already renders its children
|
|
31
|
-
* inside one `flex grow flex-wrap` box, so the icon already shares a parent
|
|
32
|
-
* with the value and the trigger root already has exactly two children.
|
|
33
|
-
* Wrapping there would put the selected-value badges in a non-wrapping row
|
|
34
|
-
* and stop the trigger growing to a second line.
|
|
35
|
-
*/
|
|
36
|
-
const withLeadingIcon = (value) => leadingIcon ? (_jsxs("span", { className: "flex min-w-0 items-center", children: [leadingIcon, value] })) : (value);
|
|
37
|
-
const renderItem = (field) => {
|
|
38
|
-
return (_jsxs(FormItem, { required: required, className: className, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), multi ? (_jsxs(MultipleSelect, { onValueChange: (value) => {
|
|
39
|
-
field.onChange(value);
|
|
40
|
-
emitChange?.(value);
|
|
41
|
-
}, disabled: disabled, ...field, children: [_jsxs(MultipleSelectTrigger, { readOnly: readOnly, children: [leadingIcon, _jsx(MultipleSelectValue, { placeholder: placeholder })] }), _jsx(MultipleSelectContent, { children: children })] })) : (_jsxs(Select, { onValueChange: (value) => {
|
|
42
|
-
field.onChange(value);
|
|
43
|
-
emitChange?.(value);
|
|
44
|
-
}, value: field.value, disabled: disabled, open: readOnly ? false : undefined, onOpenChange: readOnly ? () => { } : undefined, children: [_jsx(FormControl, { children: _jsx(SelectTrigger, { className: cn(disabled && 'bg-shadcn-100'), readOnly: readOnly, "data-testid": others['data-testid'], children: withLeadingIcon(_jsx(SelectValue, { placeholder: placeholder })) }) }), _jsxs(SelectContent, { children: [_jsx(SelectEmpty, { children: emptyMessage }), children] })] })), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
|
|
45
|
-
};
|
|
46
|
-
if (!control) {
|
|
47
|
-
// `value` undefined leaves Radix to keep its own state, so an uncontrolled
|
|
48
|
-
// standalone select still opens and picks; `onChange` reports either way.
|
|
49
|
-
// The caller's own `onChange` is invoked by the body, so this is a no-op.
|
|
50
|
-
return renderItem({
|
|
51
|
-
name,
|
|
52
|
-
value,
|
|
53
|
-
onChange: () => { },
|
|
54
|
-
onBlur: () => { },
|
|
55
|
-
ref: () => { },
|
|
56
|
-
disabled
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return (_jsx(FormField, { name: name, control: control, ...others,
|
|
60
|
-
// An unset field falls back to the EMPTY value of its own shape: `''`
|
|
61
|
-
// for the single select, `[]` for the multi one. Falling back to `''`
|
|
62
|
-
// for both handed MultipleSelect a string it silently discarded.
|
|
63
|
-
render: ({ field }) => renderItem({ ...field, value: field.value ?? (multi ? [] : '') }) }));
|
|
64
|
-
};
|
|
1
|
+
import{jsxs as r,jsx as e}from"react/jsx-runtime";import{FormControl as T,FormDescription as w,FormField as L,FormItem as _,FormLabel as q,FormMessage as v,FormTooltip as B}from"../../../components/ui/form.js";import{MultipleSelect as D,MultipleSelectContent as E,MultipleSelectTrigger as I,MultipleSelectValue as b}from"../../../components/ui/multiple-select/index.js";import{Select as k,SelectContent as y,SelectEmpty as z,SelectTrigger as A,SelectValue as G}from"../../../components/ui/select/index.js";import{cn as H}from"../../../lib/utils.js";export const SelectField=({name:i,label:a,tooltip:m,labelExtra:F,required:f,placeholder:s,description:h,disabled:o,readOnly:l,multi:d,control:p,value:x,children:u,emptyMessage:M="No options found.",leadingIcon:c,className:j,onChange:N,...g})=>{const C=N;const V=n=>c?r("span",{className:"flex min-w-0 items-center",children:[c,n]}):n;const S=n=>{return r(_,{required:f,className:j,children:[a&&e(q,{extra:m?e(B,{children:m}):F,children:a}),d?r(D,{onValueChange:t=>{n.onChange(t);C?.(t)},disabled:o,...n,children:[r(I,{readOnly:l,children:[c,e(b,{placeholder:s})]}),e(E,{children:u})]}):r(k,{onValueChange:t=>{n.onChange(t);C?.(t)},value:n.value,disabled:o,open:l?false:void 0,onOpenChange:l?()=>{}:void 0,children:[e(T,{children:e(A,{className:H(o&&"bg-shadcn-100"),readOnly:l,"data-testid":g["data-testid"],children:V(e(G,{placeholder:s}))})}),r(y,{children:[e(z,{children:M}),u]})]}),e(v,{}),h&&e(w,{children:h})]})};if(!p){return S({name:i,value:x,onChange:()=>{},onBlur:()=>{},ref:()=>{},disabled:o})}return e(L,{name:i,control:p,...g,render:({field:n})=>S({...n,value:n.value??(d?[]:"")})})};
|
|
@@ -1,35 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { getStateCountry } from '../../../utils/country-utils.js';
|
|
4
|
-
import { useFormContext } from 'react-hook-form';
|
|
5
|
-
import { FormField, FormItem, FormLabel, FormMessage } from '../../../components/ui/form.js';
|
|
6
|
-
import { Button } from '../../../components/ui/button/index.js';
|
|
7
|
-
import { cn } from '../../../lib/utils.js';
|
|
8
|
-
import { Popover, PopoverTrigger, PopoverContent } from '../../../components/ui/popover/index.js';
|
|
9
|
-
import { ChevronsUpDown } from 'lucide-react';
|
|
10
|
-
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../../../components/ui/command/index.js';
|
|
11
|
-
function StateComboBox({ value, placeholder, onChange, countryName, emptyMessage, readOnly }) {
|
|
12
|
-
const [open, setOpen] = React.useState(false);
|
|
13
|
-
const form = useFormContext();
|
|
14
|
-
const country = form.watch(countryName);
|
|
15
|
-
const options = React.useMemo(() => {
|
|
16
|
-
const states = getStateCountry(country)?.map((state) => ({
|
|
17
|
-
value: state.code,
|
|
18
|
-
label: state.name
|
|
19
|
-
}));
|
|
20
|
-
if (!states) {
|
|
21
|
-
onChange?.('');
|
|
22
|
-
}
|
|
23
|
-
return states ?? [];
|
|
24
|
-
}, [country, onChange]);
|
|
25
|
-
const getDisplayValue = React.useCallback((value) => {
|
|
26
|
-
return options.find((option) => option.value === value)?.label ?? null;
|
|
27
|
-
}, [options]);
|
|
28
|
-
return (_jsxs(Popover, { open: readOnly ? false : open, onOpenChange: readOnly ? () => { } : setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": readOnly ? false : open, readOnly: readOnly, tabIndex: 0, className: cn('border-input-border bg-input hover:bg-input text-foreground h-10 w-full justify-between rounded-md border px-4 py-2 pl-6 text-left text-base font-normal focus:ring-2 focus:ring-offset-0 focus:outline-none', !value && 'text-muted-foreground'), children: [getDisplayValue(value) ?? placeholder, !readOnly && (_jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" }))] }) }), _jsx(PopoverContent, { className: "w-(--radix-popover-trigger-width) p-0", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: placeholder }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: emptyMessage }), _jsx(CommandGroup, { children: options.map((option) => (_jsx(CommandItem, { value: option.value, keywords: [option.label], onSelect: (currentValue) => {
|
|
29
|
-
onChange?.(value !== currentValue ? currentValue : '');
|
|
30
|
-
setOpen(false);
|
|
31
|
-
}, children: option.label }, option.value))) })] })] }) })] }));
|
|
32
|
-
}
|
|
33
|
-
export const StateField = ({ countryName = 'address.country', label, placeholder, emptyMessage, required, readOnly, ...others }) => {
|
|
34
|
-
return (_jsx(FormField, { ...others, name: others.name, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [label && _jsx(FormLabel, { children: label }), _jsx(StateComboBox, { countryName: countryName, placeholder: placeholder, emptyMessage: emptyMessage, readOnly: readOnly, ...field }), _jsx(FormMessage, {})] })) }));
|
|
35
|
-
};
|
|
1
|
+
import{jsx as e,jsxs as l}from"react/jsx-runtime";import u from"react";import{getStateCountry as x}from"../../../utils/country-utils.js";import{useFormContext as g}from"react-hook-form";import{FormField as C,FormItem as v,FormLabel as y,FormMessage as w}from"../../../components/ui/form.js";import{Button as F}from"../../../components/ui/button/index.js";import{cn as j}from"../../../lib/utils.js";import{Popover as S,PopoverTrigger as I,PopoverContent as N}from"../../../components/ui/popover/index.js";import{ChevronsUpDown as k}from"lucide-react";import{Command as M,CommandEmpty as P,CommandGroup as B,CommandInput as D,CommandItem as L,CommandList as _}from"../../../components/ui/command/index.js";function q({value:n,placeholder:m,onChange:a,countryName:d,emptyMessage:c,readOnly:r}){const[s,i]=u.useState(false);const h=g();const f=h.watch(d);const p=u.useMemo(()=>{const o=x(f)?.map(t=>({value:t.code,label:t.name}));if(!o){a?.("")}return o??[]},[f,a]);const b=u.useCallback(o=>{return p.find(t=>t.value===o)?.label??null},[p]);return l(S,{open:r?false:s,onOpenChange:r?()=>{}:i,children:[e(I,{asChild:true,children:l(F,{variant:"outline",role:"combobox","aria-expanded":r?false:s,readOnly:r,tabIndex:0,className:j("border-input-border bg-input hover:bg-input text-foreground h-10 w-full justify-between rounded-md border px-4 py-2 pl-6 text-left text-base font-normal focus:ring-2 focus:ring-offset-0 focus:outline-none",!n&&"text-muted-foreground"),children:[b(n)??m,!r&&e(k,{className:"ml-2 h-4 w-4 shrink-0 opacity-50"})]})}),e(N,{className:"w-(--radix-popover-trigger-width) p-0",children:l(M,{children:[e(D,{placeholder:m}),l(_,{children:[e(P,{children:c}),e(B,{children:p.map(o=>e(L,{value:o.value,keywords:[o.label],onSelect:t=>{a?.(n!==t?t:"");i(false)},children:o.label},o.value))})]})]})})]})}export const StateField=({countryName:n="address.country",label:m,placeholder:a,emptyMessage:d,required:c,readOnly:r,...s})=>{return e(C,{...s,name:s.name,render:({field:i})=>l(v,{required:c,children:[m&&e(y,{children:m}),e(q,{countryName:n,placeholder:a,emptyMessage:d,readOnly:r,...i}),e(w,{})]})})};
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FormControl, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
3
|
-
import { Switch } from '../../../components/ui/switch/index.js';
|
|
4
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '../../../components/ui/tooltip/index.js';
|
|
5
|
-
export const SwitchField = ({ label, name, control, labelExtra, tooltip, required, disabled, disabledTooltip, 'data-testid': dataTestId }) => {
|
|
6
|
-
return (_jsx(FormField, { name: name, control: control, render: ({ field }) => {
|
|
7
|
-
// FormControl stamps the id the label points at on its direct child,
|
|
8
|
-
// so it must wrap the Switch itself, never the tooltip around it.
|
|
9
|
-
const control = (_jsx(FormControl, { children: _jsx(Switch, { checked: field.value, onCheckedChange: field.onChange, disabled: disabled, "data-testid": dataTestId }) }));
|
|
10
|
-
return (_jsxs(FormItem, { required: required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsx("div", { className: "relative", children: disabled && disabledTooltip ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: "inline-flex w-auto", children: control }) }), _jsx(TooltipContent, { children: disabledTooltip })] })) : (control) }), _jsx(FormMessage, {})] }));
|
|
11
|
-
} }));
|
|
12
|
-
};
|
|
1
|
+
import{jsx as r,jsxs as l}from"react/jsx-runtime";import{FormControl as F,FormField as p,FormItem as x,FormLabel as u,FormMessage as C,FormTooltip as g}from"../../../components/ui/form.js";import{Switch as f}from"../../../components/ui/switch/index.js";import{Tooltip as j,TooltipContent as v,TooltipTrigger as T}from"../../../components/ui/tooltip/index.js";export const SwitchField=({label:e,name:d,control:a,labelExtra:m,tooltip:o,required:s,disabled:t,disabledTooltip:i,"data-testid":h})=>{return r(p,{name:d,control:a,render:({field:n})=>{const c=r(F,{children:r(f,{checked:n.value,onCheckedChange:n.onChange,disabled:t,"data-testid":h})});return l(x,{required:s,children:[e&&r(u,{extra:o?r(g,{children:o}):m,children:e}),r("div",{className:"relative",children:t&&i?l(j,{children:[r(T,{asChild:true,children:r("div",{className:"inline-flex w-auto",children:c})}),r(v,{children:i})]}):c}),r(C,{})]})}})};
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
|
|
3
|
-
import { Textarea } from '../../../components/ui/textarea/index.js';
|
|
4
|
-
import { hasRenderableLabel } from '../../../components/form/renderable-label.js';
|
|
5
|
-
export const TextareaField = ({ control, name, label, description, placeholder, tooltip, required, disabled, readOnly, rows, className, 'data-testid': dataTestId, 'aria-label': ariaLabelProp }) => {
|
|
6
|
-
const showLabel = hasRenderableLabel(label);
|
|
7
|
-
// An empty or whitespace-only aria-label is worse than none: it names the
|
|
8
|
-
// control "". Drop it so the attribute never reaches the DOM.
|
|
9
|
-
const ariaLabel = ariaLabelProp?.trim() ? ariaLabelProp : undefined;
|
|
10
|
-
// The type cannot rule out `label=""`, so this is the only signal a developer
|
|
11
|
-
// gets that the control shipped nameless.
|
|
12
|
-
if (process.env.NODE_ENV !== 'production' && !showLabel && !ariaLabel) {
|
|
13
|
-
console.error(`TextareaField "${name}" has no accessible name: pass a non-empty label or aria-label.`);
|
|
14
|
-
}
|
|
15
|
-
return (_jsx(FormField, { control: control, name: name,
|
|
16
|
-
// Controller-level, not just on the element: this is what keeps a
|
|
17
|
-
// disabled field out of the submitted values.
|
|
18
|
-
disabled: disabled, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [showLabel && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label })), _jsx(FormControl, { children: _jsx(Textarea, { placeholder: placeholder, readOnly: readOnly, rows: rows, className: className, "data-testid": dataTestId, "aria-label": ariaLabel, ...field }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] })) }));
|
|
19
|
-
};
|
|
1
|
+
import{jsx as e,jsxs as f}from"react/jsx-runtime";import{FormControl as u,FormDescription as j,FormField as T,FormItem as L,FormLabel as N,FormMessage as _,FormTooltip as w}from"../../../components/ui/form.js";import{Textarea as y}from"../../../components/ui/textarea/index.js";import{hasRenderableLabel as D}from"../../../components/form/renderable-label.js";export const TextareaField=({control:t,name:r,label:a,description:o,placeholder:d,tooltip:s,required:m,disabled:c,readOnly:F,rows:b,className:h,"data-testid":p,"aria-label":l})=>{const i=D(a);const n=l?.trim()?l:void 0;if(process.env.NODE_ENV!=="production"&&!i&&!n){console.error(`TextareaField "${r}" has no accessible name: pass a non-empty label or aria-label.`)}return e(T,{control:t,name:r,disabled:c,render:({field:x})=>f(L,{required:m,children:[i&&e(N,{extra:s?e(w,{children:s}):void 0,children:a}),e(u,{children:e(y,{placeholder:d,readOnly:F,rows:b,className:h,"data-testid":p,"aria-label":n,...x})}),e(_,{}),o&&e(j,{children:o})]})})};
|
|
@@ -1,15 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { cn } from '../../lib/utils.js';
|
|
3
|
-
export function PageRoot({ className, ...props }) {
|
|
4
|
-
return (_jsx("div", { "data-slot": "page-root", className: cn('text-foreground bg-body-surface flex h-screen min-h-screen w-full flex-col overflow-y-auto', className), ...props }));
|
|
5
|
-
}
|
|
6
|
-
export function PageView({ className, ...props }) {
|
|
7
|
-
return (_jsx("div", { "data-slot": "page-view", className: cn('flex min-h-0 flex-1', className), ...props }));
|
|
8
|
-
}
|
|
9
|
-
export function PageContent({ className, padding = 'default', ...props }) {
|
|
10
|
-
return (_jsx("div", { "data-slot": "page-content",
|
|
11
|
-
// Order is the contract, not a style: `className` last is what lets a
|
|
12
|
-
// caller's `p-0` beat `p-16` and a caller's `max-sm:p-0` replace the
|
|
13
|
-
// compact rule instead of stacking with it.
|
|
14
|
-
className: cn('flex grow flex-col overflow-y-auto p-16', padding === 'compact' && 'max-sm:p-4', className), ...props }));
|
|
15
|
-
}
|
|
1
|
+
import{jsx as t}from"react/jsx-runtime";import{cn as a}from"../../lib/utils.js";export function PageRoot({className:e,...o}){return t("div",{"data-slot":"page-root",className:a("text-foreground bg-body-surface flex h-screen min-h-screen w-full flex-col overflow-y-auto",e),...o})}export function PageView({className:e,...o}){return t("div",{"data-slot":"page-view",className:a("flex min-h-0 flex-1",e),...o})}export function PageContent({className:e,padding:o="default",...r}){return t("div",{"data-slot":"page-content",className:a("flex grow flex-col overflow-y-auto p-16",o==="compact"&&"max-sm:p-4",e),...r})}
|