@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,6 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { cn } from '../../../lib/utils.js';
|
|
3
|
-
function Textarea({ className, ...props }) {
|
|
4
|
-
return (_jsx("textarea", { className: cn('border-input-border aria-invalid:border-destructive bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden', 'read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
|
|
5
|
-
}
|
|
6
|
-
export { Textarea };
|
|
1
|
+
import{jsx as o}from"react/jsx-runtime";import{cn as d}from"../../../lib/utils.js";function n({className:e,...r}){return o("textarea",{className:d("border-input-border aria-invalid:border-destructive bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden","read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden","disabled:cursor-not-allowed disabled:opacity-50",e),...r})}export{n as Textarea};
|
|
@@ -1,49 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import { cn } from '../../../lib/utils.js';
|
|
5
|
-
export const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
|
|
6
|
-
export const MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0'));
|
|
7
|
-
function TimeScrollColumn({ items, selected, onSelect, label, disabled }) {
|
|
8
|
-
const listRef = React.useRef(null);
|
|
9
|
-
const hasScrolled = React.useRef(false);
|
|
10
|
-
const prevSelected = React.useRef(selected);
|
|
11
|
-
React.useEffect(() => {
|
|
12
|
-
if (prevSelected.current !== selected) {
|
|
13
|
-
hasScrolled.current = false;
|
|
14
|
-
prevSelected.current = selected;
|
|
15
|
-
}
|
|
16
|
-
}, [selected]);
|
|
17
|
-
React.useEffect(() => {
|
|
18
|
-
if (hasScrolled.current)
|
|
19
|
-
return;
|
|
20
|
-
if (!listRef.current || !selected)
|
|
21
|
-
return;
|
|
22
|
-
const selectedIndex = items.indexOf(selected);
|
|
23
|
-
if (selectedIndex < 0)
|
|
24
|
-
return;
|
|
25
|
-
const selectedElement = listRef.current.children[0]?.children[selectedIndex];
|
|
26
|
-
if (selectedElement) {
|
|
27
|
-
selectedElement.scrollIntoView({ block: 'nearest' });
|
|
28
|
-
hasScrolled.current = true;
|
|
29
|
-
}
|
|
30
|
-
}, [selected, items]);
|
|
31
|
-
return (_jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col items-center", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium select-none", children: label }), _jsx("div", { className: "h-1.5" }), _jsx("div", { ref: listRef, className: "max-h-60 overflow-y-auto", role: "group", "aria-label": label, children: _jsx("div", { className: "flex flex-col gap-1 p-1", children: items.map((item) => {
|
|
32
|
-
const isSelected = item === selected;
|
|
33
|
-
return (_jsx("button", { type: "button", "aria-pressed": isSelected, disabled: disabled, className: cn('flex h-8 w-11 items-center justify-center rounded-md text-sm tabular-nums transition-colors', isSelected
|
|
34
|
-
? 'bg-accent text-accent-foreground font-bold'
|
|
35
|
-
: 'text-muted-foreground hover:bg-muted font-normal', disabled && 'pointer-events-none opacity-50'), onClick: () => onSelect(item), children: item }, item));
|
|
36
|
-
}) }) })] }));
|
|
37
|
-
}
|
|
38
|
-
function TimePicker({ hour, minute, onChange, disabled, className, hourLabel = 'Hour', minuteLabel = 'Minute' }) {
|
|
39
|
-
const selectedHour = String(hour ?? 0).padStart(2, '0');
|
|
40
|
-
const selectedMinute = String(minute ?? 0).padStart(2, '0');
|
|
41
|
-
const handleHourSelect = React.useCallback((value) => {
|
|
42
|
-
onChange?.({ hour: parseInt(value, 10), minute: minute ?? 0 });
|
|
43
|
-
}, [onChange, minute]);
|
|
44
|
-
const handleMinuteSelect = React.useCallback((value) => {
|
|
45
|
-
onChange?.({ hour: hour ?? 0, minute: parseInt(value, 10) });
|
|
46
|
-
}, [onChange, hour]);
|
|
47
|
-
return (_jsxs("div", { "data-slot": "time-picker", className: cn('flex gap-2 p-3', className), children: [_jsx(TimeScrollColumn, { items: HOURS, selected: selectedHour, onSelect: handleHourSelect, label: hourLabel, disabled: disabled }), _jsx(TimeScrollColumn, { items: MINUTES, selected: selectedMinute, onSelect: handleMinuteSelect, label: minuteLabel, disabled: disabled })] }));
|
|
48
|
-
}
|
|
49
|
-
export { TimePicker };
|
|
1
|
+
"use client";import{jsx as c,jsxs as m}from"react/jsx-runtime";import*as l from"react";import{cn as p}from"../../../lib/utils.js";export const HOURS=Array.from({length:24},(t,e)=>String(e).padStart(2,"0"));export const MINUTES=Array.from({length:60},(t,e)=>String(e).padStart(2,"0"));function x({items:t,selected:e,onSelect:s,label:o,disabled:u}){const a=l.useRef(null);const i=l.useRef(false);const f=l.useRef(e);l.useEffect(()=>{if(f.current!==e){i.current=false;f.current=e}},[e]);l.useEffect(()=>{if(i.current)return;if(!a.current||!e)return;const r=t.indexOf(e);if(r<0)return;const n=a.current.children[0]?.children[r];if(n){n.scrollIntoView({block:"nearest"});i.current=true}},[e,t]);return m("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col items-center",children:[c("span",{className:"text-muted-foreground text-xs font-medium select-none",children:o}),c("div",{className:"h-1.5"}),c("div",{ref:a,className:"max-h-60 overflow-y-auto",role:"group","aria-label":o,children:c("div",{className:"flex flex-col gap-1 p-1",children:t.map(r=>{const n=r===e;return c("button",{type:"button","aria-pressed":n,disabled:u,className:p("flex h-8 w-11 items-center justify-center rounded-md text-sm tabular-nums transition-colors",n?"bg-accent text-accent-foreground font-bold":"text-muted-foreground hover:bg-muted font-normal",u&&"pointer-events-none opacity-50"),onClick:()=>s(r),children:r},r)})})})]})}function h({hour:t,minute:e,onChange:s,disabled:o,className:u,hourLabel:a="Hour",minuteLabel:i="Minute"}){const f=String(t??0).padStart(2,"0");const r=String(e??0).padStart(2,"0");const n=l.useCallback(d=>{s?.({hour:parseInt(d,10),minute:e??0})},[s,e]);const S=l.useCallback(d=>{s?.({hour:t??0,minute:parseInt(d,10)})},[s,t]);return m("div",{"data-slot":"time-picker",className:p("flex gap-2 p-3",u),children:[c(x,{items:HOURS,selected:f,onSelect:n,label:a,disabled:o}),c(x,{items:MINUTES,selected:r,onSelect:S,label:i,disabled:o})]})}export{h as TimePicker};
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export {};
|
|
1
|
+
"use client";export{};
|
|
@@ -1,13 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Toaster as SonnerToaster } from 'sonner';
|
|
4
|
-
import { DEFAULT_DURATION } from '../../../hooks/use-toast.js';
|
|
5
|
-
import { useOptionalTheme } from '../../../theme/theme-provider.js';
|
|
6
|
-
export function Toaster({ theme, position = 'bottom-right' }) {
|
|
7
|
-
// Optional read: a Toaster mounted outside any ThemeProvider must still
|
|
8
|
-
// render, so this cannot go through `useTheme`, which throws.
|
|
9
|
-
const themeContext = useOptionalTheme();
|
|
10
|
-
return (_jsx(SonnerToaster, { theme: theme ?? themeContext?.resolvedTheme ?? 'system', position: position, visibleToasts: 3, duration: DEFAULT_DURATION, expand: false, closeButton: true, richColors: false, offset: 16, gap: 8, toastOptions: {
|
|
11
|
-
className: 'font-sans !text-sm !font-medium'
|
|
12
|
-
} }));
|
|
13
|
-
}
|
|
1
|
+
"use client";import{jsx as s}from"react/jsx-runtime";import{Toaster as r}from"sonner";import{DEFAULT_DURATION as m}from"../../../hooks/use-toast.js";import{useOptionalTheme as n}from"../../../theme/theme-provider.js";export function Toaster({theme:t,position:o="bottom-right"}){const e=n();return s(r,{theme:t??e?.resolvedTheme??"system",position:o,visibleToasts:3,duration:m,expand:false,closeButton:true,richColors:false,offset:16,gap:8,toastOptions:{className:"font-sans !text-sm !font-medium"}})}
|
|
@@ -1,120 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
|
|
5
|
-
import { cva } from 'class-variance-authority';
|
|
6
|
-
import { cn } from '../../../lib/utils.js';
|
|
7
|
-
/**
|
|
8
|
-
* Item styling for the group. Kept local: sindarian-ui has no standalone
|
|
9
|
-
* `Toggle` primitive and the retirement census does not ask for one, so the
|
|
10
|
-
* variants live with their only consumer instead of leaking a new export.
|
|
11
|
-
*/
|
|
12
|
-
const toggleVariants = cva("hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
13
|
-
variants: {
|
|
14
|
-
variant: {
|
|
15
|
-
default: 'bg-transparent',
|
|
16
|
-
outline: 'border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs'
|
|
17
|
-
},
|
|
18
|
-
size: {
|
|
19
|
-
default: 'h-9 min-w-9 px-2',
|
|
20
|
-
sm: 'h-8 min-w-8 px-1.5',
|
|
21
|
-
lg: 'h-10 min-w-10 px-2.5'
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
defaultVariants: {
|
|
25
|
-
variant: 'default',
|
|
26
|
-
size: 'default'
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
|
|
30
|
-
/**
|
|
31
|
-
* Is an arrow key down right now?
|
|
32
|
-
*
|
|
33
|
-
* This is how an item tells keyboard navigation apart from a pointer press or a
|
|
34
|
-
* programmatic focus, and it is the seam Radix uses for the same job in its own
|
|
35
|
-
* `RadioGroup`. It works because `RovingFocusGroup` defers the focus move to a
|
|
36
|
-
* `setTimeout`: the keydown has already bubbled to `document` and set the flag
|
|
37
|
-
* by the time the item receives `focus`.
|
|
38
|
-
*
|
|
39
|
-
* The distinction is not cosmetic. A pointer press focuses BEFORE the click
|
|
40
|
-
* lands, so selecting on every focus would turn one click into
|
|
41
|
-
* select-then-deselect and break mouse selection outright.
|
|
42
|
-
*
|
|
43
|
-
* Returns null when disabled, so `type="multiple"` carries no flag to read.
|
|
44
|
-
*/
|
|
45
|
-
function useArrowKeyPressed(enabled) {
|
|
46
|
-
const arrowKeyPressed = React.useRef(false);
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
if (!enabled)
|
|
49
|
-
return;
|
|
50
|
-
const onKeyDown = (event) => {
|
|
51
|
-
if (ARROW_KEYS.includes(event.key))
|
|
52
|
-
arrowKeyPressed.current = true;
|
|
53
|
-
};
|
|
54
|
-
const onKeyUp = () => {
|
|
55
|
-
arrowKeyPressed.current = false;
|
|
56
|
-
};
|
|
57
|
-
document.addEventListener('keydown', onKeyDown);
|
|
58
|
-
document.addEventListener('keyup', onKeyUp);
|
|
59
|
-
window.addEventListener('blur', onKeyUp);
|
|
60
|
-
return () => {
|
|
61
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
62
|
-
document.removeEventListener('keyup', onKeyUp);
|
|
63
|
-
window.removeEventListener('blur', onKeyUp);
|
|
64
|
-
// A group unmounted mid-keystroke never sees the keyup.
|
|
65
|
-
arrowKeyPressed.current = false;
|
|
66
|
-
};
|
|
67
|
-
}, [enabled]);
|
|
68
|
-
return enabled ? arrowKeyPressed : null;
|
|
69
|
-
}
|
|
70
|
-
const ToggleGroupContext = React.createContext({
|
|
71
|
-
size: 'default',
|
|
72
|
-
variant: 'default',
|
|
73
|
-
spacing: 0,
|
|
74
|
-
arrowKeyPressed: null
|
|
75
|
-
});
|
|
76
|
-
function ToggleGroup({ className, variant, size, spacing = 0, children, style, ...props }) {
|
|
77
|
-
// Read, never destructured: `type` is the discriminant of the Radix props
|
|
78
|
-
// union, and pulling it out then spreading it back widens the union so the
|
|
79
|
-
// single/multiple props stop resolving.
|
|
80
|
-
const arrowKeyPressed = useArrowKeyPressed(props.type === 'single');
|
|
81
|
-
return (_jsx(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing,
|
|
82
|
-
// Caller style first: `--gap` is ours and must survive, but everything
|
|
83
|
-
// else the caller passes has to reach the element.
|
|
84
|
-
style: { ...style, '--gap': spacing }, className: cn(
|
|
85
|
-
// Legacy carried a `data-[spacing=default]:...:shadow-xs` rule here that
|
|
86
|
-
// never matched — `data-spacing` is always a number. Removed rather than
|
|
87
|
-
// activated, so the rendered pixels stay identical to sindarian-x.
|
|
88
|
-
'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: { variant, size, spacing, arrowKeyPressed }, children: children }) }));
|
|
89
|
-
}
|
|
90
|
-
function ToggleGroupItem({ className, children, variant, size, onFocus, ...props }) {
|
|
91
|
-
const context = React.useContext(ToggleGroupContext);
|
|
92
|
-
/**
|
|
93
|
-
* Selection follows focus, which is what `role="radio"` promises.
|
|
94
|
-
*
|
|
95
|
-
* With `type="single"` Radix puts the group on the ARIA radio pattern: the
|
|
96
|
-
* root is a `radiogroup`, each item a `radio` with `aria-checked`. Under that
|
|
97
|
-
* pattern an arrow key both moves focus and checks the item it lands on.
|
|
98
|
-
* Radix only moves focus, so a screen-reader operator used to arrow across
|
|
99
|
-
* "radio, not checked, 2 of 3" with nothing ever selected.
|
|
100
|
-
*
|
|
101
|
-
* `type="multiple"` is a `toolbar` of `aria-pressed` buttons and keeps its
|
|
102
|
-
* current behavior, where moving focus without pressing is correct: the flag
|
|
103
|
-
* is null there.
|
|
104
|
-
*
|
|
105
|
-
* Clicking the element rather than writing the value keeps a single
|
|
106
|
-
* activation path, so Radix's own toggle and any caller `onClick` both fire
|
|
107
|
-
* exactly as they do for a real click.
|
|
108
|
-
*/
|
|
109
|
-
const handleFocus = (event) => {
|
|
110
|
-
onFocus?.(event);
|
|
111
|
-
if (context.arrowKeyPressed?.current) {
|
|
112
|
-
event.currentTarget.click();
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
|
|
116
|
-
variant: context.variant || variant,
|
|
117
|
-
size: context.size || size
|
|
118
|
-
}), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, onFocus: handleFocus, children: children }));
|
|
119
|
-
}
|
|
120
|
-
export { ToggleGroup, ToggleGroupItem };
|
|
1
|
+
"use client";import{jsx as u}from"react/jsx-runtime";import*as i from"react";import*as l from"@radix-ui/react-toggle-group";import{cva as v}from"class-variance-authority";import{cn as g}from"../../../lib/utils.js";const f=v("hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",{variants:{variant:{default:"bg-transparent",outline:"border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs"},size:{default:"h-9 min-w-9 px-2",sm:"h-8 min-w-8 px-1.5",lg:"h-10 min-w-10 px-2.5"}},defaultVariants:{variant:"default",size:"default"}});const m=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"];function w(a){const t=i.useRef(false);i.useEffect(()=>{if(!a)return;const n=o=>{if(m.includes(o.key))t.current=true};const e=()=>{t.current=false};document.addEventListener("keydown",n);document.addEventListener("keyup",e);window.addEventListener("blur",e);return()=>{document.removeEventListener("keydown",n);document.removeEventListener("keyup",e);window.removeEventListener("blur",e);t.current=false}},[a]);return a?t:null}const p=i.createContext({size:"default",variant:"default",spacing:0,arrowKeyPressed:null});function x({className:a,variant:t,size:n,spacing:e=0,children:o,style:s,...r}){const d=w(r.type==="single");return u(l.Root,{"data-slot":"toggle-group","data-variant":t,"data-size":n,"data-spacing":e,style:{...s,"--gap":e},className:g("group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md",a),...r,children:u(p.Provider,{value:{variant:t,size:n,spacing:e,arrowKeyPressed:d},children:o})})}function b({className:a,children:t,variant:n,size:e,onFocus:o,...s}){const r=i.useContext(p);const d=c=>{o?.(c);if(r.arrowKeyPressed?.current){c.currentTarget.click()}};return u(l.Item,{"data-slot":"toggle-group-item","data-variant":r.variant||n,"data-size":r.size||e,"data-spacing":r.spacing,className:g(f({variant:r.variant||n,size:r.size||e}),"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10","data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",a),...s,onFocus:d,children:t})}export{x as ToggleGroup,b as ToggleGroupItem};
|
|
@@ -1,43 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
5
|
-
import { cn } from '../../../lib/utils.js';
|
|
6
|
-
function TooltipProvider({ delayDuration = 0, ...props }) {
|
|
7
|
-
return (_jsx(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration: delayDuration, ...props }));
|
|
8
|
-
}
|
|
9
|
-
function Tooltip({ open: _open, onOpenChange, disabled, ...props }) {
|
|
10
|
-
const [open, setOpen] = React.useState(_open ?? false);
|
|
11
|
-
const handleOpenChange = (open) => {
|
|
12
|
-
if (disabled) {
|
|
13
|
-
onOpenChange?.(false);
|
|
14
|
-
setOpen(false);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
setOpen(open);
|
|
18
|
-
onOpenChange?.(open);
|
|
19
|
-
};
|
|
20
|
-
return (_jsx(TooltipProvider, { children: _jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", open: open, onOpenChange: handleOpenChange, ...props }) }));
|
|
21
|
-
}
|
|
22
|
-
function TooltipTrigger({ ...props }) {
|
|
23
|
-
return _jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* The fill is a RAW palette step, not a theme token: `--color-shadcn-600` is a
|
|
27
|
-
* flat hex, so an inverted tooltip stays #27272A under both themes. The ink has
|
|
28
|
-
* to be theme-fixed too, which rules out `--primary-foreground` and
|
|
29
|
-
* `--background`: both invert, and both land on 1.00:1 against this fill in
|
|
30
|
-
* dark.
|
|
31
|
-
*
|
|
32
|
-
* base/400 was not unreadable here (5.81:1 on this fill; the 2.56:1 figure that
|
|
33
|
-
* class earns elsewhere is against a LIGHT ground, which this is not). It moved
|
|
34
|
-
* one step to base/300 because base/400 is banned kit-wide in
|
|
35
|
-
* `__tests__/ink-classes.test.ts`: its only safe ground is a fixed-dark fill,
|
|
36
|
-
* and that narrow exception had already leaked into eight slots that were not
|
|
37
|
-
* one. base/300 reads 10.08:1 here, and keeps this line quieter than the
|
|
38
|
-
* `text-white` a consumer puts beside it (`page-header/index.tsx:226`).
|
|
39
|
-
*/
|
|
40
|
-
function TooltipContent({ className, sideOffset = 0, children, ...props }) {
|
|
41
|
-
return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn('bg-shadcn-600 text-shadcn-300 motion-safe:animate-in fade-in-0 zoom-in-95 motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 rounded-md px-3 py-1.5 text-sm shadow-md', className), ...props, children: [children, _jsx(TooltipPrimitive.Arrow, { className: "bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })] }) }));
|
|
42
|
-
}
|
|
43
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
1
|
+
"use client";import{jsx as e,jsxs as f}from"react/jsx-runtime";import*as p from"react";import*as a from"@radix-ui/react-tooltip";import{cn as c}from"../../../lib/utils.js";function d({delayDuration:t=0,...o}){return e(a.Provider,{"data-slot":"tooltip-provider",delayDuration:t,...o})}function u({open:t,onOpenChange:o,disabled:i,...s}){const[l,n]=p.useState(t??false);const m=r=>{if(i){o?.(false);n(false);return}n(r);o?.(r)};return e(d,{children:e(a.Root,{"data-slot":"tooltip",open:l,onOpenChange:m,...s})})}function h({...t}){return e(a.Trigger,{"data-slot":"tooltip-trigger",...t})}function g({className:t,sideOffset:o=0,children:i,...s}){return e(a.Portal,{children:f(a.Content,{"data-slot":"tooltip-content",sideOffset:o,className:c("bg-shadcn-600 text-shadcn-300 motion-safe:animate-in fade-in-0 zoom-in-95 motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 rounded-md px-3 py-1.5 text-sm shadow-md",t),...s,children:[i,e(a.Arrow,{className:"bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}export{u as Tooltip,h as TooltipTrigger,g as TooltipContent,d as TooltipProvider};
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Skeleton } from '../../components/ui/skeleton/index.js';
|
|
3
|
-
import { LABEL_VOICE_CLASS } from '../../lib/typography.js';
|
|
4
|
-
import { cn } from '../../lib/utils.js';
|
|
5
|
-
export function Blotter({ children, className }) {
|
|
6
|
-
return _jsx("dl", { className: cn('divide-border divide-y', className), children: children });
|
|
7
|
-
}
|
|
8
|
-
export function BlotterRow({ label, value, valueClassName, className, stacked = false, loading = false }) {
|
|
9
|
-
// The skeleton stands in for the value slot's own shape: a full-width prose
|
|
10
|
-
// line when stacked, the mono figure's width when inline.
|
|
11
|
-
const shownValue = loading ? (_jsx(Skeleton, { className: cn('h-4', stacked ? 'w-full' : 'w-24') })) : (value);
|
|
12
|
-
if (stacked) {
|
|
13
|
-
return (_jsxs("div", { "aria-busy": loading || undefined, className: cn('hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out', className), children: [_jsx("dt", { className: LABEL_VOICE_CLASS, children: label }), _jsx("dd", { className: cn('text-foreground text-sm break-words whitespace-pre-wrap', valueClassName), children: shownValue })] }));
|
|
14
|
-
}
|
|
15
|
-
return (_jsxs("div", { "aria-busy": loading || undefined, className: cn('hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out', className), children: [_jsx("dt", { className: LABEL_VOICE_CLASS, children: label }), _jsx("dd", { className: cn('font-mono text-sm font-medium tabular-nums', valueClassName), children: shownValue })] }));
|
|
16
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as c}from"react/jsx-runtime";import{Skeleton as m}from"../../components/ui/skeleton/index.js";import{LABEL_VOICE_CLASS as l}from"../../lib/typography.js";import{cn as r}from"../../lib/utils.js";export function Blotter({children:s,className:t}){return e("dl",{className:r("divide-border divide-y",t),children:s})}export function BlotterRow({label:s,value:t,valueClassName:n,className:i,stacked:a=false,loading:o=false}){const d=o?e(m,{className:r("h-4",a?"w-full":"w-24")}):t;if(a){return c("div",{"aria-busy":o||void 0,className:r("hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out",i),children:[e("dt",{className:l,children:s}),e("dd",{className:r("text-foreground text-sm break-words whitespace-pre-wrap",n),children:d})]})}return c("div",{"aria-busy":o||void 0,className:r("hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out",i),children:[e("dt",{className:l,children:s}),e("dd",{className:r("font-mono text-sm font-medium tabular-nums",n),children:d})]})}
|
|
@@ -1,115 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* AgingBuckets — an ordered, exhaustive bucketed distribution (the receivables
|
|
4
|
-
* aging schedule: current / 1–30 / 31–60 / 61–90 / 90+), each bucket carrying a
|
|
5
|
-
* count and a money total.
|
|
6
|
-
*
|
|
7
|
-
* INTERNAL to `DelinquencyAging`: not re-exported from `src/domain/index.ts`,
|
|
8
|
-
* because the port census only lists the composite. It lives here as the
|
|
9
|
-
* distribution half of that composite.
|
|
10
|
-
*
|
|
11
|
-
* INVARIANT: the buckets are ordered current → oldest and are exhaustive. The
|
|
12
|
-
* ORDER carries the information — older money is worse money — so the ordinal
|
|
13
|
-
* position and its label are the PRIMARY cue; the band glyph + sr-only word are
|
|
14
|
-
* the accessible reinforcement (WCAG 1.4.1 — the escalation survives grayscale
|
|
15
|
-
* and color-vision deficiency), and the color tint is reinforcement only, never
|
|
16
|
-
* the sole carrier of severity. The caller supplies the order; the component
|
|
17
|
-
* never reorders, because a sorted-by-amount view would destroy the one fact the
|
|
18
|
-
* widget exists to show.
|
|
19
|
-
*
|
|
20
|
-
* THIRD RAIL (money): a zero bucket renders an explicit `0` — a zero in the 90+
|
|
21
|
-
* bucket is information ("nothing slipped that far"), never an omission. When a
|
|
22
|
-
* grand total is shown it is summed through `money-math` integer minor units
|
|
23
|
-
* (BigInt, at the currency's CLDR scale) — never IEEE-754 — and rendered via
|
|
24
|
-
* `MoneyText`. A single unparseable bucket total poisons the sum to NO_VALUE
|
|
25
|
-
* rather than silently displaying a wrong number. The SAME currency scale that
|
|
26
|
-
* drives the sum is also the print scale handed to `MoneyText`, so the digits on
|
|
27
|
-
* screen are the digits that were summed — the printed total can never be a
|
|
28
|
-
* re-rounding of the computed one.
|
|
29
|
-
*
|
|
30
|
-
* Pure display: no directive, no interactivity, server-safe for RSC.
|
|
31
|
-
*/
|
|
32
|
-
import { AlertOctagon, AlertTriangle, CircleDot, Clock } from 'lucide-react';
|
|
33
|
-
import { LABEL_VOICE_CLASS } from '../../lib/typography.js';
|
|
34
|
-
import { cn } from '../../lib/utils.js';
|
|
35
|
-
import { Figure } from '../figure/index.js';
|
|
36
|
-
import { formatCount, NO_VALUE } from '../format.js';
|
|
37
|
-
import { minorDigitsOf, minorToDecimal, sumMinor } from '../money-math.js';
|
|
38
|
-
import { MoneyText } from '../money-text/index.js';
|
|
39
|
-
/** Band → glyph + tint + accessible word. The glyph (and the sr-only word) is
|
|
40
|
-
* the load-bearing severity cue; the tint only reinforces it, so the
|
|
41
|
-
* escalation survives grayscale and color-vision deficiency.
|
|
42
|
-
*
|
|
43
|
-
* The tint is an INK token (`-h1a`). A load-bearing glyph answers to the 3:1
|
|
44
|
-
* floor of SC 1.4.11, and the bare fill token `--system-alert` reads 1.78:1 on
|
|
45
|
-
* `--card` and 1.62:1 on `--body-surface` in light — the cue was invisible on
|
|
46
|
-
* the row it escalates. */
|
|
47
|
-
const BAND = {
|
|
48
|
-
current: {
|
|
49
|
-
Icon: CircleDot,
|
|
50
|
-
tint: 'text-system-success-h1a',
|
|
51
|
-
word: 'Em dia'
|
|
52
|
-
},
|
|
53
|
-
recent: {
|
|
54
|
-
Icon: Clock,
|
|
55
|
-
tint: 'text-muted-foreground',
|
|
56
|
-
word: 'Vencido recentemente'
|
|
57
|
-
},
|
|
58
|
-
aging: {
|
|
59
|
-
Icon: AlertTriangle,
|
|
60
|
-
tint: 'text-system-alert-h1a',
|
|
61
|
-
word: 'Em atraso'
|
|
62
|
-
},
|
|
63
|
-
overdue: { Icon: AlertOctagon, tint: 'text-credit', word: 'Vencido' }
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* Resolve a bucket's severity band from its ORDINAL POSITION, not its amount —
|
|
67
|
-
* the order is the information (older = worse). The first bucket is `current`;
|
|
68
|
-
* the LAST bucket (the oldest, the 90+ tail) is always `overdue`; the middle
|
|
69
|
-
* escalates `recent` → `aging` toward the tail. A single-bucket list reads as
|
|
70
|
-
* `current` (there is no escalation to show). Pure: no React, unit-tested.
|
|
71
|
-
*/
|
|
72
|
-
export function bucketBand(index, count) {
|
|
73
|
-
if (count <= 1)
|
|
74
|
-
return 'current';
|
|
75
|
-
if (index === 0)
|
|
76
|
-
return 'current';
|
|
77
|
-
if (index === count - 1)
|
|
78
|
-
return 'overdue';
|
|
79
|
-
// Middle buckets: the second-oldest reads `aging`, the rest `recent`.
|
|
80
|
-
return index === count - 2 ? 'aging' : 'recent';
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Sum the bucket totals through integer minor units (BigInt) at `scale`, then
|
|
84
|
-
* render losslessly to a decimal string. Returns null if ANY bucket total is
|
|
85
|
-
* unparseable (indeterminate) — the caller renders NO_VALUE rather than a wrong
|
|
86
|
-
* grand total. Pure: the money-math third rail, unit-tested.
|
|
87
|
-
*/
|
|
88
|
-
export function sumBucketTotals(buckets, scale) {
|
|
89
|
-
const total = sumMinor(buckets.map((b) => b.total), scale);
|
|
90
|
-
return total === null ? null : minorToDecimal(total, scale);
|
|
91
|
-
}
|
|
92
|
-
export function AgingBuckets({ buckets, currency, locale, showTotal = false, labels, healthyTone = 'success', className }) {
|
|
93
|
-
const countLabel = labels?.count ?? 'Itens';
|
|
94
|
-
const totalLabel = labels?.total ?? 'Total';
|
|
95
|
-
const grandTotalLabel = labels?.grandTotal ?? 'Total geral';
|
|
96
|
-
// THIRD RAIL: one scale for BOTH the arithmetic and the print. `MoneyText`
|
|
97
|
-
// defaults to 2 fraction digits, so leaving it unset printed JPY (scale 0)
|
|
98
|
-
// with two decimals the currency does not have, and re-rounded the exact BHD
|
|
99
|
-
// (scale 3) grand total `1.005` to `1.01` — a printed total that disagreed
|
|
100
|
-
// with the total that was actually summed.
|
|
101
|
-
const scale = currency ? minorDigitsOf(currency) : 2;
|
|
102
|
-
const grandTotal = showTotal ? sumBucketTotals(buckets, scale) : null;
|
|
103
|
-
return (_jsxs("div", { className: cn('flex flex-col', className), children: [_jsx("ul", { className: "flex flex-col", children: buckets.map((bucket, i) => {
|
|
104
|
-
const band = bucketBand(i, buckets.length);
|
|
105
|
-
const { Icon, word } = BAND[band];
|
|
106
|
-
// `ink` retones ONLY the healthy band — the escalated bands keep their
|
|
107
|
-
// alert and credit-red tints, which are the whole point of the scale.
|
|
108
|
-
const tint = healthyTone === 'ink' && band === 'current'
|
|
109
|
-
? 'text-foreground'
|
|
110
|
-
: BAND[band].tint;
|
|
111
|
-
// Shallow merge: an unlisted band keeps its pt-BR default.
|
|
112
|
-
const bandWord = labels?.bands?.[band] ?? word;
|
|
113
|
-
return (_jsxs("li", { className: "border-border flex items-center gap-3 border-b py-2.5 last:border-b-0", children: [_jsx(Icon, { "aria-hidden": true, className: cn('size-4 shrink-0', tint) }), _jsxs("span", { className: "sr-only", children: [bandWord, ": "] }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: bucket.label }), _jsxs("span", { className: "flex shrink-0 flex-col items-end leading-none", children: [_jsx(Figure, { size: "row", className: "text-foreground", children: formatCount(bucket.count, locale) }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: countLabel })] }), _jsxs("span", { className: "flex w-32 shrink-0 flex-col items-end leading-none sm:w-40", children: [_jsx(Figure, { size: "row", className: cn(band === 'overdue' && 'text-credit'), children: _jsx(MoneyText, { amount: bucket.total, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: totalLabel })] })] }, `${bucket.label}-${i}`));
|
|
114
|
-
}) }), showTotal ? (_jsxs("div", { className: "border-border flex items-center gap-3 border-t-2 pt-2.5", children: [_jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: grandTotalLabel }), _jsx("span", { className: "w-32 shrink-0 text-right sm:w-40", children: grandTotal === null ? (_jsx(Figure, { size: "row", className: "text-muted-foreground", children: NO_VALUE })) : (_jsx(Figure, { size: "row", className: "text-foreground font-semibold", children: _jsx(MoneyText, { amount: grandTotal, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) })) })] })) : null] }));
|
|
115
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as s}from"react/jsx-runtime";import{AlertOctagon as z,AlertTriangle as D,CircleDot as k,Clock as v}from"lucide-react";import{LABEL_VOICE_CLASS as d}from"../../lib/typography.js";import{cn as n}from"../../lib/utils.js";import{Figure as m}from"../figure/index.js";import{formatCount as B,NO_VALUE as E}from"../format.js";import{minorDigitsOf as _,minorToDecimal as j,sumMinor as y}from"../money-math.js";import{MoneyText as g}from"../money-text/index.js";const h={current:{Icon:k,tint:"text-system-success-h1a",word:"Em dia"},recent:{Icon:v,tint:"text-muted-foreground",word:"Vencido recentemente"},aging:{Icon:D,tint:"text-system-alert-h1a",word:"Em atraso"},overdue:{Icon:z,tint:"text-credit",word:"Vencido"}};export function bucketBand(t,r){if(r<=1)return"current";if(t===0)return"current";if(t===r-1)return"overdue";return t===r-2?"aging":"recent"}export function sumBucketTotals(t,r){const o=y(t.map(l=>l.total),r);return o===null?null:j(o,r)}export function AgingBuckets({buckets:t,currency:r,locale:o,showTotal:l=false,labels:i,healthyTone:p="success",className:N}){const w=i?.count??"Itens";const b=i?.total??"Total";const T=i?.grandTotal??"Total geral";const f=r?_(r):2;const u=l?sumBucketTotals(t,f):null;return s("div",{className:n("flex flex-col",N),children:[e("ul",{className:"flex flex-col",children:t.map((c,x)=>{const a=bucketBand(x,t.length);const{Icon:A,word:C}=h[a];const I=p==="ink"&&a==="current"?"text-foreground":h[a].tint;const L=i?.bands?.[a]??C;return s("li",{className:"border-border flex items-center gap-3 border-b py-2.5 last:border-b-0",children:[e(A,{"aria-hidden":true,className:n("size-4 shrink-0",I)}),s("span",{className:"sr-only",children:[L,": "]}),e("span",{className:n(d,"text-foreground min-w-0 flex-1"),children:c.label}),s("span",{className:"flex shrink-0 flex-col items-end leading-none",children:[e(m,{size:"row",className:"text-foreground",children:B(c.count,o)}),e("span",{className:n(d,"mt-1 text-xs"),children:w})]}),s("span",{className:"flex w-32 shrink-0 flex-col items-end leading-none sm:w-40",children:[e(m,{size:"row",className:n(a==="overdue"&&"text-credit"),children:e(g,{amount:c.total,currency:r,fractionDigits:f,locale:o,signColor:false})}),e("span",{className:n(d,"mt-1 text-xs"),children:b})]})]},`${c.label}-${x}`)})}),l?s("div",{className:"border-border flex items-center gap-3 border-t-2 pt-2.5",children:[e("span",{"aria-hidden":true,className:"size-4 shrink-0"}),e("span",{className:n(d,"text-foreground min-w-0 flex-1"),children:T}),e("span",{className:"w-32 shrink-0 text-right sm:w-40",children:u===null?e(m,{size:"row",className:"text-muted-foreground",children:E}):e(m,{size:"row",className:"text-foreground font-semibold",children:e(g,{amount:u,currency:r,fractionDigits:f,locale:o,signColor:false})})})]}):null]})}
|