@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,156 +1 @@
|
|
|
1
|
-
|
|
2
|
-
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.ToggleGroup = ToggleGroup;
|
|
38
|
-
exports.ToggleGroupItem = ToggleGroupItem;
|
|
39
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
40
|
-
const React = __importStar(require("react"));
|
|
41
|
-
const ToggleGroupPrimitive = __importStar(require("@radix-ui/react-toggle-group"));
|
|
42
|
-
const class_variance_authority_1 = require("class-variance-authority");
|
|
43
|
-
const utils_1 = require("../../../lib/utils");
|
|
44
|
-
/**
|
|
45
|
-
* Item styling for the group. Kept local: sindarian-ui has no standalone
|
|
46
|
-
* `Toggle` primitive and the retirement census does not ask for one, so the
|
|
47
|
-
* variants live with their only consumer instead of leaking a new export.
|
|
48
|
-
*/
|
|
49
|
-
const toggleVariants = (0, class_variance_authority_1.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", {
|
|
50
|
-
variants: {
|
|
51
|
-
variant: {
|
|
52
|
-
default: 'bg-transparent',
|
|
53
|
-
outline: 'border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs'
|
|
54
|
-
},
|
|
55
|
-
size: {
|
|
56
|
-
default: 'h-9 min-w-9 px-2',
|
|
57
|
-
sm: 'h-8 min-w-8 px-1.5',
|
|
58
|
-
lg: 'h-10 min-w-10 px-2.5'
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
defaultVariants: {
|
|
62
|
-
variant: 'default',
|
|
63
|
-
size: 'default'
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
|
|
67
|
-
/**
|
|
68
|
-
* Is an arrow key down right now?
|
|
69
|
-
*
|
|
70
|
-
* This is how an item tells keyboard navigation apart from a pointer press or a
|
|
71
|
-
* programmatic focus, and it is the seam Radix uses for the same job in its own
|
|
72
|
-
* `RadioGroup`. It works because `RovingFocusGroup` defers the focus move to a
|
|
73
|
-
* `setTimeout`: the keydown has already bubbled to `document` and set the flag
|
|
74
|
-
* by the time the item receives `focus`.
|
|
75
|
-
*
|
|
76
|
-
* The distinction is not cosmetic. A pointer press focuses BEFORE the click
|
|
77
|
-
* lands, so selecting on every focus would turn one click into
|
|
78
|
-
* select-then-deselect and break mouse selection outright.
|
|
79
|
-
*
|
|
80
|
-
* Returns null when disabled, so `type="multiple"` carries no flag to read.
|
|
81
|
-
*/
|
|
82
|
-
function useArrowKeyPressed(enabled) {
|
|
83
|
-
const arrowKeyPressed = React.useRef(false);
|
|
84
|
-
React.useEffect(() => {
|
|
85
|
-
if (!enabled)
|
|
86
|
-
return;
|
|
87
|
-
const onKeyDown = (event) => {
|
|
88
|
-
if (ARROW_KEYS.includes(event.key))
|
|
89
|
-
arrowKeyPressed.current = true;
|
|
90
|
-
};
|
|
91
|
-
const onKeyUp = () => {
|
|
92
|
-
arrowKeyPressed.current = false;
|
|
93
|
-
};
|
|
94
|
-
document.addEventListener('keydown', onKeyDown);
|
|
95
|
-
document.addEventListener('keyup', onKeyUp);
|
|
96
|
-
window.addEventListener('blur', onKeyUp);
|
|
97
|
-
return () => {
|
|
98
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
99
|
-
document.removeEventListener('keyup', onKeyUp);
|
|
100
|
-
window.removeEventListener('blur', onKeyUp);
|
|
101
|
-
// A group unmounted mid-keystroke never sees the keyup.
|
|
102
|
-
arrowKeyPressed.current = false;
|
|
103
|
-
};
|
|
104
|
-
}, [enabled]);
|
|
105
|
-
return enabled ? arrowKeyPressed : null;
|
|
106
|
-
}
|
|
107
|
-
const ToggleGroupContext = React.createContext({
|
|
108
|
-
size: 'default',
|
|
109
|
-
variant: 'default',
|
|
110
|
-
spacing: 0,
|
|
111
|
-
arrowKeyPressed: null
|
|
112
|
-
});
|
|
113
|
-
function ToggleGroup({ className, variant, size, spacing = 0, children, style, ...props }) {
|
|
114
|
-
// Read, never destructured: `type` is the discriminant of the Radix props
|
|
115
|
-
// union, and pulling it out then spreading it back widens the union so the
|
|
116
|
-
// single/multiple props stop resolving.
|
|
117
|
-
const arrowKeyPressed = useArrowKeyPressed(props.type === 'single');
|
|
118
|
-
return ((0, jsx_runtime_1.jsx)(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing,
|
|
119
|
-
// Caller style first: `--gap` is ours and must survive, but everything
|
|
120
|
-
// else the caller passes has to reach the element.
|
|
121
|
-
style: { ...style, '--gap': spacing }, className: (0, utils_1.cn)(
|
|
122
|
-
// Legacy carried a `data-[spacing=default]:...:shadow-xs` rule here that
|
|
123
|
-
// never matched — `data-spacing` is always a number. Removed rather than
|
|
124
|
-
// activated, so the rendered pixels stay identical to sindarian-x.
|
|
125
|
-
'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: (0, jsx_runtime_1.jsx)(ToggleGroupContext.Provider, { value: { variant, size, spacing, arrowKeyPressed }, children: children }) }));
|
|
126
|
-
}
|
|
127
|
-
function ToggleGroupItem({ className, children, variant, size, onFocus, ...props }) {
|
|
128
|
-
const context = React.useContext(ToggleGroupContext);
|
|
129
|
-
/**
|
|
130
|
-
* Selection follows focus, which is what `role="radio"` promises.
|
|
131
|
-
*
|
|
132
|
-
* With `type="single"` Radix puts the group on the ARIA radio pattern: the
|
|
133
|
-
* root is a `radiogroup`, each item a `radio` with `aria-checked`. Under that
|
|
134
|
-
* pattern an arrow key both moves focus and checks the item it lands on.
|
|
135
|
-
* Radix only moves focus, so a screen-reader operator used to arrow across
|
|
136
|
-
* "radio, not checked, 2 of 3" with nothing ever selected.
|
|
137
|
-
*
|
|
138
|
-
* `type="multiple"` is a `toolbar` of `aria-pressed` buttons and keeps its
|
|
139
|
-
* current behavior, where moving focus without pressing is correct: the flag
|
|
140
|
-
* is null there.
|
|
141
|
-
*
|
|
142
|
-
* Clicking the element rather than writing the value keeps a single
|
|
143
|
-
* activation path, so Radix's own toggle and any caller `onClick` both fire
|
|
144
|
-
* exactly as they do for a real click.
|
|
145
|
-
*/
|
|
146
|
-
const handleFocus = (event) => {
|
|
147
|
-
onFocus?.(event);
|
|
148
|
-
if (context.arrowKeyPressed?.current) {
|
|
149
|
-
event.currentTarget.click();
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
return ((0, jsx_runtime_1.jsx)(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: (0, utils_1.cn)(toggleVariants({
|
|
153
|
-
variant: context.variant || variant,
|
|
154
|
-
size: context.size || size
|
|
155
|
-
}), '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 }));
|
|
156
|
-
}
|
|
1
|
+
"use client";"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(a,e,r,t){if(t===void 0)t=r;var n=Object.getOwnPropertyDescriptor(e,r);if(!n||("get"in n?!e.__esModule:n.writable||n.configurable)){n={enumerable:true,get:function(){return e[r]}}}Object.defineProperty(a,t,n)}):(function(a,e,r,t){if(t===void 0)t=r;a[t]=e[r]}));var __setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(a,e){Object.defineProperty(a,"default",{enumerable:true,value:e})}):function(a,e){a["default"]=e});var __importStar=this&&this.__importStar||(function(){var a=function(e){a=Object.getOwnPropertyNames||function(r){var t=[];for(var n in r)if(Object.prototype.hasOwnProperty.call(r,n))t[t.length]=n;return t};return a(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null){for(var t=a(e),n=0;n<t.length;n++)if(t[n]!=="default")__createBinding(r,e,t[n])}__setModuleDefault(r,e);return r}})();Object.defineProperty(exports,"__esModule",{value:true});exports.ToggleGroup=ToggleGroup;exports.ToggleGroupItem=ToggleGroupItem;const jsx_runtime_1=require("react/jsx-runtime");const React=__importStar(require("react"));const ToggleGroupPrimitive=__importStar(require("@radix-ui/react-toggle-group"));const class_variance_authority_1=require("class-variance-authority");const utils_1=require("../../../lib/utils");const toggleVariants=(0,class_variance_authority_1.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",{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 ARROW_KEYS=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"];function useArrowKeyPressed(a){const e=React.useRef(false);React.useEffect(()=>{if(!a)return;const r=n=>{if(ARROW_KEYS.includes(n.key))e.current=true};const t=()=>{e.current=false};document.addEventListener("keydown",r);document.addEventListener("keyup",t);window.addEventListener("blur",t);return()=>{document.removeEventListener("keydown",r);document.removeEventListener("keyup",t);window.removeEventListener("blur",t);e.current=false}},[a]);return a?e:null}const ToggleGroupContext=React.createContext({size:"default",variant:"default",spacing:0,arrowKeyPressed:null});function ToggleGroup({className:a,variant:e,size:r,spacing:t=0,children:n,style:o,...i}){const s=useArrowKeyPressed(i.type==="single");return(0,jsx_runtime_1.jsx)(ToggleGroupPrimitive.Root,{"data-slot":"toggle-group","data-variant":e,"data-size":r,"data-spacing":t,style:{...o,"--gap":t},className:(0,utils_1.cn)("group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md",a),...i,children:(0,jsx_runtime_1.jsx)(ToggleGroupContext.Provider,{value:{variant:e,size:r,spacing:t,arrowKeyPressed:s},children:n})})}function ToggleGroupItem({className:a,children:e,variant:r,size:t,onFocus:n,...o}){const i=React.useContext(ToggleGroupContext);const s=u=>{n?.(u);if(i.arrowKeyPressed?.current){u.currentTarget.click()}};return(0,jsx_runtime_1.jsx)(ToggleGroupPrimitive.Item,{"data-slot":"toggle-group-item","data-variant":i.variant||r,"data-size":i.size||t,"data-spacing":i.spacing,className:(0,utils_1.cn)(toggleVariants({variant:i.variant||r,size:i.size||t}),"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),...o,onFocus:s,children:e})}
|
|
@@ -1,81 +1 @@
|
|
|
1
|
-
|
|
2
|
-
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.Tooltip = Tooltip;
|
|
38
|
-
exports.TooltipTrigger = TooltipTrigger;
|
|
39
|
-
exports.TooltipContent = TooltipContent;
|
|
40
|
-
exports.TooltipProvider = TooltipProvider;
|
|
41
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
42
|
-
const React = __importStar(require("react"));
|
|
43
|
-
const TooltipPrimitive = __importStar(require("@radix-ui/react-tooltip"));
|
|
44
|
-
const utils_1 = require("../../../lib/utils");
|
|
45
|
-
function TooltipProvider({ delayDuration = 0, ...props }) {
|
|
46
|
-
return ((0, jsx_runtime_1.jsx)(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration: delayDuration, ...props }));
|
|
47
|
-
}
|
|
48
|
-
function Tooltip({ open: _open, onOpenChange, disabled, ...props }) {
|
|
49
|
-
const [open, setOpen] = React.useState(_open ?? false);
|
|
50
|
-
const handleOpenChange = (open) => {
|
|
51
|
-
if (disabled) {
|
|
52
|
-
onOpenChange?.(false);
|
|
53
|
-
setOpen(false);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
setOpen(open);
|
|
57
|
-
onOpenChange?.(open);
|
|
58
|
-
};
|
|
59
|
-
return ((0, jsx_runtime_1.jsx)(TooltipProvider, { children: (0, jsx_runtime_1.jsx)(TooltipPrimitive.Root, { "data-slot": "tooltip", open: open, onOpenChange: handleOpenChange, ...props }) }));
|
|
60
|
-
}
|
|
61
|
-
function TooltipTrigger({ ...props }) {
|
|
62
|
-
return (0, jsx_runtime_1.jsx)(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* The fill is a RAW palette step, not a theme token: `--color-shadcn-600` is a
|
|
66
|
-
* flat hex, so an inverted tooltip stays #27272A under both themes. The ink has
|
|
67
|
-
* to be theme-fixed too, which rules out `--primary-foreground` and
|
|
68
|
-
* `--background`: both invert, and both land on 1.00:1 against this fill in
|
|
69
|
-
* dark.
|
|
70
|
-
*
|
|
71
|
-
* base/400 was not unreadable here (5.81:1 on this fill; the 2.56:1 figure that
|
|
72
|
-
* class earns elsewhere is against a LIGHT ground, which this is not). It moved
|
|
73
|
-
* one step to base/300 because base/400 is banned kit-wide in
|
|
74
|
-
* `__tests__/ink-classes.test.ts`: its only safe ground is a fixed-dark fill,
|
|
75
|
-
* and that narrow exception had already leaked into eight slots that were not
|
|
76
|
-
* one. base/300 reads 10.08:1 here, and keeps this line quieter than the
|
|
77
|
-
* `text-white` a consumer puts beside it (`page-header/index.tsx:226`).
|
|
78
|
-
*/
|
|
79
|
-
function TooltipContent({ className, sideOffset = 0, children, ...props }) {
|
|
80
|
-
return ((0, jsx_runtime_1.jsx)(TooltipPrimitive.Portal, { children: (0, jsx_runtime_1.jsxs)(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: (0, utils_1.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, (0, jsx_runtime_1.jsx)(TooltipPrimitive.Arrow, { className: "bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })] }) }));
|
|
81
|
-
}
|
|
1
|
+
"use client";"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(e,t,o,r){if(r===void 0)r=o;var i=Object.getOwnPropertyDescriptor(t,o);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[o]}}}Object.defineProperty(e,r,i)}):(function(e,t,o,r){if(r===void 0)r=o;e[r]=t[o]}));var __setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}):function(e,t){e["default"]=t});var __importStar=this&&this.__importStar||(function(){var e=function(t){e=Object.getOwnPropertyNames||function(o){var r=[];for(var i in o)if(Object.prototype.hasOwnProperty.call(o,i))r[r.length]=i;return r};return e(t)};return function(t){if(t&&t.__esModule)return t;var o={};if(t!=null){for(var r=e(t),i=0;i<r.length;i++)if(r[i]!=="default")__createBinding(o,t,r[i])}__setModuleDefault(o,t);return o}})();Object.defineProperty(exports,"__esModule",{value:true});exports.Tooltip=Tooltip;exports.TooltipTrigger=TooltipTrigger;exports.TooltipContent=TooltipContent;exports.TooltipProvider=TooltipProvider;const jsx_runtime_1=require("react/jsx-runtime");const React=__importStar(require("react"));const TooltipPrimitive=__importStar(require("@radix-ui/react-tooltip"));const utils_1=require("../../../lib/utils");function TooltipProvider({delayDuration:e=0,...t}){return(0,jsx_runtime_1.jsx)(TooltipPrimitive.Provider,{"data-slot":"tooltip-provider",delayDuration:e,...t})}function Tooltip({open:e,onOpenChange:t,disabled:o,...r}){const[i,n]=React.useState(e??false);const s=a=>{if(o){t?.(false);n(false);return}n(a);t?.(a)};return(0,jsx_runtime_1.jsx)(TooltipProvider,{children:(0,jsx_runtime_1.jsx)(TooltipPrimitive.Root,{"data-slot":"tooltip",open:i,onOpenChange:s,...r})})}function TooltipTrigger({...e}){return(0,jsx_runtime_1.jsx)(TooltipPrimitive.Trigger,{"data-slot":"tooltip-trigger",...e})}function TooltipContent({className:e,sideOffset:t=0,children:o,...r}){return(0,jsx_runtime_1.jsx)(TooltipPrimitive.Portal,{children:(0,jsx_runtime_1.jsxs)(TooltipPrimitive.Content,{"data-slot":"tooltip-content",sideOffset:t,className:(0,utils_1.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",e),...r,children:[o,(0,jsx_runtime_1.jsx)(TooltipPrimitive.Arrow,{className:"bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Blotter = Blotter;
|
|
4
|
-
exports.BlotterRow = BlotterRow;
|
|
5
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
-
const skeleton_1 = require("../../components/ui/skeleton");
|
|
7
|
-
const typography_1 = require("../../lib/typography");
|
|
8
|
-
const utils_1 = require("../../lib/utils");
|
|
9
|
-
function Blotter({ children, className }) {
|
|
10
|
-
return (0, jsx_runtime_1.jsx)("dl", { className: (0, utils_1.cn)('divide-border divide-y', className), children: children });
|
|
11
|
-
}
|
|
12
|
-
function BlotterRow({ label, value, valueClassName, className, stacked = false, loading = false }) {
|
|
13
|
-
// The skeleton stands in for the value slot's own shape: a full-width prose
|
|
14
|
-
// line when stacked, the mono figure's width when inline.
|
|
15
|
-
const shownValue = loading ? ((0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: (0, utils_1.cn)('h-4', stacked ? 'w-full' : 'w-24') })) : (value);
|
|
16
|
-
if (stacked) {
|
|
17
|
-
return ((0, jsx_runtime_1.jsxs)("div", { "aria-busy": loading || undefined, className: (0, utils_1.cn)('hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out', className), children: [(0, jsx_runtime_1.jsx)("dt", { className: typography_1.LABEL_VOICE_CLASS, children: label }), (0, jsx_runtime_1.jsx)("dd", { className: (0, utils_1.cn)('text-foreground text-sm break-words whitespace-pre-wrap', valueClassName), children: shownValue })] }));
|
|
18
|
-
}
|
|
19
|
-
return ((0, jsx_runtime_1.jsxs)("div", { "aria-busy": loading || undefined, className: (0, utils_1.cn)('hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out', className), children: [(0, jsx_runtime_1.jsx)("dt", { className: typography_1.LABEL_VOICE_CLASS, children: label }), (0, jsx_runtime_1.jsx)("dd", { className: (0, utils_1.cn)('font-mono text-sm font-medium tabular-nums', valueClassName), children: shownValue })] }));
|
|
20
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.Blotter=Blotter;exports.BlotterRow=BlotterRow;const jsx_runtime_1=require("react/jsx-runtime");const skeleton_1=require("../../components/ui/skeleton");const typography_1=require("../../lib/typography");const utils_1=require("../../lib/utils");function Blotter({children:e,className:s}){return(0,jsx_runtime_1.jsx)("dl",{className:(0,utils_1.cn)("divide-border divide-y",s),children:e})}function BlotterRow({label:e,value:s,valueClassName:r,className:n,stacked:o=false,loading:t=false}){const i=t?(0,jsx_runtime_1.jsx)(skeleton_1.Skeleton,{className:(0,utils_1.cn)("h-4",o?"w-full":"w-24")}):s;if(o){return(0,jsx_runtime_1.jsxs)("div",{"aria-busy":t||void 0,className:(0,utils_1.cn)("hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out",n),children:[(0,jsx_runtime_1.jsx)("dt",{className:typography_1.LABEL_VOICE_CLASS,children:e}),(0,jsx_runtime_1.jsx)("dd",{className:(0,utils_1.cn)("text-foreground text-sm break-words whitespace-pre-wrap",r),children:i})]})}return(0,jsx_runtime_1.jsxs)("div",{"aria-busy":t||void 0,className:(0,utils_1.cn)("hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out",n),children:[(0,jsx_runtime_1.jsx)("dt",{className:typography_1.LABEL_VOICE_CLASS,children:e}),(0,jsx_runtime_1.jsx)("dd",{className:(0,utils_1.cn)("font-mono text-sm font-medium tabular-nums",r),children:i})]})}
|
|
@@ -1,120 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bucketBand = bucketBand;
|
|
4
|
-
exports.sumBucketTotals = sumBucketTotals;
|
|
5
|
-
exports.AgingBuckets = AgingBuckets;
|
|
6
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
-
/**
|
|
8
|
-
* AgingBuckets — an ordered, exhaustive bucketed distribution (the receivables
|
|
9
|
-
* aging schedule: current / 1–30 / 31–60 / 61–90 / 90+), each bucket carrying a
|
|
10
|
-
* count and a money total.
|
|
11
|
-
*
|
|
12
|
-
* INTERNAL to `DelinquencyAging`: not re-exported from `src/domain/index.ts`,
|
|
13
|
-
* because the port census only lists the composite. It lives here as the
|
|
14
|
-
* distribution half of that composite.
|
|
15
|
-
*
|
|
16
|
-
* INVARIANT: the buckets are ordered current → oldest and are exhaustive. The
|
|
17
|
-
* ORDER carries the information — older money is worse money — so the ordinal
|
|
18
|
-
* position and its label are the PRIMARY cue; the band glyph + sr-only word are
|
|
19
|
-
* the accessible reinforcement (WCAG 1.4.1 — the escalation survives grayscale
|
|
20
|
-
* and color-vision deficiency), and the color tint is reinforcement only, never
|
|
21
|
-
* the sole carrier of severity. The caller supplies the order; the component
|
|
22
|
-
* never reorders, because a sorted-by-amount view would destroy the one fact the
|
|
23
|
-
* widget exists to show.
|
|
24
|
-
*
|
|
25
|
-
* THIRD RAIL (money): a zero bucket renders an explicit `0` — a zero in the 90+
|
|
26
|
-
* bucket is information ("nothing slipped that far"), never an omission. When a
|
|
27
|
-
* grand total is shown it is summed through `money-math` integer minor units
|
|
28
|
-
* (BigInt, at the currency's CLDR scale) — never IEEE-754 — and rendered via
|
|
29
|
-
* `MoneyText`. A single unparseable bucket total poisons the sum to NO_VALUE
|
|
30
|
-
* rather than silently displaying a wrong number. The SAME currency scale that
|
|
31
|
-
* drives the sum is also the print scale handed to `MoneyText`, so the digits on
|
|
32
|
-
* screen are the digits that were summed — the printed total can never be a
|
|
33
|
-
* re-rounding of the computed one.
|
|
34
|
-
*
|
|
35
|
-
* Pure display: no directive, no interactivity, server-safe for RSC.
|
|
36
|
-
*/
|
|
37
|
-
const lucide_react_1 = require("lucide-react");
|
|
38
|
-
const typography_1 = require("../../lib/typography");
|
|
39
|
-
const utils_1 = require("../../lib/utils");
|
|
40
|
-
const figure_1 = require("../figure");
|
|
41
|
-
const format_1 = require("../format");
|
|
42
|
-
const money_math_1 = require("../money-math");
|
|
43
|
-
const money_text_1 = require("../money-text");
|
|
44
|
-
/** Band → glyph + tint + accessible word. The glyph (and the sr-only word) is
|
|
45
|
-
* the load-bearing severity cue; the tint only reinforces it, so the
|
|
46
|
-
* escalation survives grayscale and color-vision deficiency.
|
|
47
|
-
*
|
|
48
|
-
* The tint is an INK token (`-h1a`). A load-bearing glyph answers to the 3:1
|
|
49
|
-
* floor of SC 1.4.11, and the bare fill token `--system-alert` reads 1.78:1 on
|
|
50
|
-
* `--card` and 1.62:1 on `--body-surface` in light — the cue was invisible on
|
|
51
|
-
* the row it escalates. */
|
|
52
|
-
const BAND = {
|
|
53
|
-
current: {
|
|
54
|
-
Icon: lucide_react_1.CircleDot,
|
|
55
|
-
tint: 'text-system-success-h1a',
|
|
56
|
-
word: 'Em dia'
|
|
57
|
-
},
|
|
58
|
-
recent: {
|
|
59
|
-
Icon: lucide_react_1.Clock,
|
|
60
|
-
tint: 'text-muted-foreground',
|
|
61
|
-
word: 'Vencido recentemente'
|
|
62
|
-
},
|
|
63
|
-
aging: {
|
|
64
|
-
Icon: lucide_react_1.AlertTriangle,
|
|
65
|
-
tint: 'text-system-alert-h1a',
|
|
66
|
-
word: 'Em atraso'
|
|
67
|
-
},
|
|
68
|
-
overdue: { Icon: lucide_react_1.AlertOctagon, tint: 'text-credit', word: 'Vencido' }
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Resolve a bucket's severity band from its ORDINAL POSITION, not its amount —
|
|
72
|
-
* the order is the information (older = worse). The first bucket is `current`;
|
|
73
|
-
* the LAST bucket (the oldest, the 90+ tail) is always `overdue`; the middle
|
|
74
|
-
* escalates `recent` → `aging` toward the tail. A single-bucket list reads as
|
|
75
|
-
* `current` (there is no escalation to show). Pure: no React, unit-tested.
|
|
76
|
-
*/
|
|
77
|
-
function bucketBand(index, count) {
|
|
78
|
-
if (count <= 1)
|
|
79
|
-
return 'current';
|
|
80
|
-
if (index === 0)
|
|
81
|
-
return 'current';
|
|
82
|
-
if (index === count - 1)
|
|
83
|
-
return 'overdue';
|
|
84
|
-
// Middle buckets: the second-oldest reads `aging`, the rest `recent`.
|
|
85
|
-
return index === count - 2 ? 'aging' : 'recent';
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Sum the bucket totals through integer minor units (BigInt) at `scale`, then
|
|
89
|
-
* render losslessly to a decimal string. Returns null if ANY bucket total is
|
|
90
|
-
* unparseable (indeterminate) — the caller renders NO_VALUE rather than a wrong
|
|
91
|
-
* grand total. Pure: the money-math third rail, unit-tested.
|
|
92
|
-
*/
|
|
93
|
-
function sumBucketTotals(buckets, scale) {
|
|
94
|
-
const total = (0, money_math_1.sumMinor)(buckets.map((b) => b.total), scale);
|
|
95
|
-
return total === null ? null : (0, money_math_1.minorToDecimal)(total, scale);
|
|
96
|
-
}
|
|
97
|
-
function AgingBuckets({ buckets, currency, locale, showTotal = false, labels, healthyTone = 'success', className }) {
|
|
98
|
-
const countLabel = labels?.count ?? 'Itens';
|
|
99
|
-
const totalLabel = labels?.total ?? 'Total';
|
|
100
|
-
const grandTotalLabel = labels?.grandTotal ?? 'Total geral';
|
|
101
|
-
// THIRD RAIL: one scale for BOTH the arithmetic and the print. `MoneyText`
|
|
102
|
-
// defaults to 2 fraction digits, so leaving it unset printed JPY (scale 0)
|
|
103
|
-
// with two decimals the currency does not have, and re-rounded the exact BHD
|
|
104
|
-
// (scale 3) grand total `1.005` to `1.01` — a printed total that disagreed
|
|
105
|
-
// with the total that was actually summed.
|
|
106
|
-
const scale = currency ? (0, money_math_1.minorDigitsOf)(currency) : 2;
|
|
107
|
-
const grandTotal = showTotal ? sumBucketTotals(buckets, scale) : null;
|
|
108
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('flex flex-col', className), children: [(0, jsx_runtime_1.jsx)("ul", { className: "flex flex-col", children: buckets.map((bucket, i) => {
|
|
109
|
-
const band = bucketBand(i, buckets.length);
|
|
110
|
-
const { Icon, word } = BAND[band];
|
|
111
|
-
// `ink` retones ONLY the healthy band — the escalated bands keep their
|
|
112
|
-
// alert and credit-red tints, which are the whole point of the scale.
|
|
113
|
-
const tint = healthyTone === 'ink' && band === 'current'
|
|
114
|
-
? 'text-foreground'
|
|
115
|
-
: BAND[band].tint;
|
|
116
|
-
// Shallow merge: an unlisted band keeps its pt-BR default.
|
|
117
|
-
const bandWord = labels?.bands?.[band] ?? word;
|
|
118
|
-
return ((0, jsx_runtime_1.jsxs)("li", { className: "border-border flex items-center gap-3 border-b py-2.5 last:border-b-0", children: [(0, jsx_runtime_1.jsx)(Icon, { "aria-hidden": true, className: (0, utils_1.cn)('size-4 shrink-0', tint) }), (0, jsx_runtime_1.jsxs)("span", { className: "sr-only", children: [bandWord, ": "] }), (0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: bucket.label }), (0, jsx_runtime_1.jsxs)("span", { className: "flex shrink-0 flex-col items-end leading-none", children: [(0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: "text-foreground", children: (0, format_1.formatCount)(bucket.count, locale) }), (0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: countLabel })] }), (0, jsx_runtime_1.jsxs)("span", { className: "flex w-32 shrink-0 flex-col items-end leading-none sm:w-40", children: [(0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: (0, utils_1.cn)(band === 'overdue' && 'text-credit'), children: (0, jsx_runtime_1.jsx)(money_text_1.MoneyText, { amount: bucket.total, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) }), (0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: totalLabel })] })] }, `${bucket.label}-${i}`));
|
|
119
|
-
}) }), showTotal ? ((0, jsx_runtime_1.jsxs)("div", { className: "border-border flex items-center gap-3 border-t-2 pt-2.5", children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": true, className: "size-4 shrink-0" }), (0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: grandTotalLabel }), (0, jsx_runtime_1.jsx)("span", { className: "w-32 shrink-0 text-right sm:w-40", children: grandTotal === null ? ((0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: "text-muted-foreground", children: format_1.NO_VALUE })) : ((0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: "text-foreground font-semibold", children: (0, jsx_runtime_1.jsx)(money_text_1.MoneyText, { amount: grandTotal, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) })) })] })) : null] }));
|
|
120
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.bucketBand=bucketBand;exports.sumBucketTotals=sumBucketTotals;exports.AgingBuckets=AgingBuckets;const jsx_runtime_1=require("react/jsx-runtime");const lucide_react_1=require("lucide-react");const typography_1=require("../../lib/typography");const utils_1=require("../../lib/utils");const figure_1=require("../figure");const format_1=require("../format");const money_math_1=require("../money-math");const money_text_1=require("../money-text");const BAND={current:{Icon:lucide_react_1.CircleDot,tint:"text-system-success-h1a",word:"Em dia"},recent:{Icon:lucide_react_1.Clock,tint:"text-muted-foreground",word:"Vencido recentemente"},aging:{Icon:lucide_react_1.AlertTriangle,tint:"text-system-alert-h1a",word:"Em atraso"},overdue:{Icon:lucide_react_1.AlertOctagon,tint:"text-credit",word:"Vencido"}};function bucketBand(t,e){if(e<=1)return"current";if(t===0)return"current";if(t===e-1)return"overdue";return t===e-2?"aging":"recent"}function sumBucketTotals(t,e){const n=(0,money_math_1.sumMinor)(t.map(s=>s.total),e);return n===null?null:(0,money_math_1.minorToDecimal)(n,e)}function AgingBuckets({buckets:t,currency:e,locale:n,showTotal:s=false,labels:o,healthyTone:d="success",className:u}){const m=o?.count??"Itens";const x=o?.total??"Total";const g=o?.grandTotal??"Total geral";const i=e?(0,money_math_1.minorDigitsOf)(e):2;const c=s?sumBucketTotals(t,i):null;return(0,jsx_runtime_1.jsxs)("div",{className:(0,utils_1.cn)("flex flex-col",u),children:[(0,jsx_runtime_1.jsx)("ul",{className:"flex flex-col",children:t.map((a,l)=>{const r=bucketBand(l,t.length);const{Icon:f,word:h}=BAND[r];const p=d==="ink"&&r==="current"?"text-foreground":BAND[r].tint;const j=o?.bands?.[r]??h;return(0,jsx_runtime_1.jsxs)("li",{className:"border-border flex items-center gap-3 border-b py-2.5 last:border-b-0",children:[(0,jsx_runtime_1.jsx)(f,{"aria-hidden":true,className:(0,utils_1.cn)("size-4 shrink-0",p)}),(0,jsx_runtime_1.jsxs)("span",{className:"sr-only",children:[j,": "]}),(0,jsx_runtime_1.jsx)("span",{className:(0,utils_1.cn)(typography_1.LABEL_VOICE_CLASS,"text-foreground min-w-0 flex-1"),children:a.label}),(0,jsx_runtime_1.jsxs)("span",{className:"flex shrink-0 flex-col items-end leading-none",children:[(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:"text-foreground",children:(0,format_1.formatCount)(a.count,n)}),(0,jsx_runtime_1.jsx)("span",{className:(0,utils_1.cn)(typography_1.LABEL_VOICE_CLASS,"mt-1 text-xs"),children:m})]}),(0,jsx_runtime_1.jsxs)("span",{className:"flex w-32 shrink-0 flex-col items-end leading-none sm:w-40",children:[(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:(0,utils_1.cn)(r==="overdue"&&"text-credit"),children:(0,jsx_runtime_1.jsx)(money_text_1.MoneyText,{amount:a.total,currency:e,fractionDigits:i,locale:n,signColor:false})}),(0,jsx_runtime_1.jsx)("span",{className:(0,utils_1.cn)(typography_1.LABEL_VOICE_CLASS,"mt-1 text-xs"),children:x})]})]},`${a.label}-${l}`)})}),s?(0,jsx_runtime_1.jsxs)("div",{className:"border-border flex items-center gap-3 border-t-2 pt-2.5",children:[(0,jsx_runtime_1.jsx)("span",{"aria-hidden":true,className:"size-4 shrink-0"}),(0,jsx_runtime_1.jsx)("span",{className:(0,utils_1.cn)(typography_1.LABEL_VOICE_CLASS,"text-foreground min-w-0 flex-1"),children:g}),(0,jsx_runtime_1.jsx)("span",{className:"w-32 shrink-0 text-right sm:w-40",children:c===null?(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:"text-muted-foreground",children:format_1.NO_VALUE}):(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:"text-foreground font-semibold",children:(0,jsx_runtime_1.jsx)(money_text_1.MoneyText,{amount:c,currency:e,fractionDigits:i,locale:n,signColor:false})})})]}):null]})}
|
|
@@ -1,222 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.delinquencyRate = delinquencyRate;
|
|
4
|
-
exports.DelinquencyAging = DelinquencyAging;
|
|
5
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
-
/**
|
|
7
|
-
* DelinquencyAging — a loan/receivable delinquency surface: the ordered overdue
|
|
8
|
-
* aging distribution PLUS the headline delinquency rate (the share of portfolio
|
|
9
|
-
* value that is past due).
|
|
10
|
-
*
|
|
11
|
-
* <DelinquencyAging
|
|
12
|
-
* currency="BRL"
|
|
13
|
-
* locale="pt-BR"
|
|
14
|
-
* warn={0.05}
|
|
15
|
-
* breach={0.1}
|
|
16
|
-
* buckets={[
|
|
17
|
-
* { label: 'A vencer', count: 312, total: '1840500.00', overdue: false },
|
|
18
|
-
* { label: '1–30', count: 84, total: '402300.00', overdue: true },
|
|
19
|
-
* { label: '31–60', count: 21, total: '118900.00', overdue: true },
|
|
20
|
-
* { label: '61–90', count: 9, total: '54200.00', overdue: true },
|
|
21
|
-
* { label: '90+', count: 4, total: '38100.00', overdue: true }
|
|
22
|
-
* ]}
|
|
23
|
-
* />
|
|
24
|
-
*
|
|
25
|
-
* The distribution is delegated whole to `AgingBuckets`: it owns the ordered
|
|
26
|
-
* current → 90+ schedule, the per-bucket count/total, the ordinal band cue, and
|
|
27
|
-
* the money-math-exact grand total. This composite adds only the one fact aging
|
|
28
|
-
* alone does not state — the delinquency RATE.
|
|
29
|
-
*
|
|
30
|
-
* THIRD RAIL (money): the rate is overdueTotal / portfolioTotal, and BOTH totals
|
|
31
|
-
* are summed in integer minor units (BigInt, at the currency's CLDR scale) via
|
|
32
|
-
* `money-math` — never an IEEE-754 sum of decimals (0.1 + 0.2 ≠ 0.3 in float, a
|
|
33
|
-
* silent basis-point error on a portfolio). The single floating-point operation
|
|
34
|
-
* is the FINAL division of two already-exact integers, which is the correct,
|
|
35
|
-
* lossless-enough place for it (a ratio is not money). A zero portfolio yields a
|
|
36
|
-
* null rate — an explicit empty readout, never a NaN. A single unparseable
|
|
37
|
-
* bucket total poisons the rate to indeterminate rather than reporting a wrong
|
|
38
|
-
* share.
|
|
39
|
-
*
|
|
40
|
-
* NON-COLOR CUE (WCAG 1.4.1): the rate band (healthy / elevated / distressed) is
|
|
41
|
-
* resolved through the gauge's strict-edge `gaugeBand` (higher-is-worse: more
|
|
42
|
-
* overdue is worse) and named with a glyph + an sr-only word + a tint — the tint
|
|
43
|
-
* is reinforcement only, so the band survives grayscale and color-vision
|
|
44
|
-
* deficiency. A distressed rate reads loud (credit-red, role='alert').
|
|
45
|
-
*
|
|
46
|
-
* Pure display: no directive, no interactivity, server-safe for RSC.
|
|
47
|
-
*/
|
|
48
|
-
const lucide_react_1 = require("lucide-react");
|
|
49
|
-
const typography_1 = require("../../lib/typography");
|
|
50
|
-
const utils_1 = require("../../lib/utils");
|
|
51
|
-
const figure_1 = require("../figure");
|
|
52
|
-
const format_1 = require("../format");
|
|
53
|
-
const money_math_1 = require("../money-math");
|
|
54
|
-
const section_label_1 = require("../section-label");
|
|
55
|
-
const aging_buckets_1 = require("./aging-buckets");
|
|
56
|
-
/** Fixed-point scale for the rate division: 1e12 keeps twelve decimal places of
|
|
57
|
-
* a 0..1 ratio, and both it and any in-range scaled quotient stay well inside
|
|
58
|
-
* the exact-integer range of a double (2^53), so the conversion is lossless. */
|
|
59
|
-
const RATE_PRECISION = 1000000000000n;
|
|
60
|
-
/**
|
|
61
|
-
* THIRD RAIL: the delinquency rate = overdue total / portfolio total, with both
|
|
62
|
-
* totals summed through integer minor units (BigInt) at `scale`. The only float
|
|
63
|
-
* operation is the final division of two EXACT integers — never a float sum of
|
|
64
|
-
* decimals. A zero portfolio gives a null rate (explicit empty, no NaN); an
|
|
65
|
-
* unparseable total poisons everything to indeterminate. Pure: unit-tested.
|
|
66
|
-
*
|
|
67
|
-
* Internal: exported for its unit tests, never from `src/domain/index.ts`.
|
|
68
|
-
*/
|
|
69
|
-
function delinquencyRate(buckets, scale) {
|
|
70
|
-
// Parse each bucket exactly once: the sums and the per-bucket SIGN check both
|
|
71
|
-
// come off the same pass, so the two can never disagree about the same input.
|
|
72
|
-
const parsed = buckets.map((b) => (0, money_math_1.toMinor)(b.total, scale));
|
|
73
|
-
if (parsed.some((m) => m === null)) {
|
|
74
|
-
return {
|
|
75
|
-
rate: null,
|
|
76
|
-
overdueMinor: null,
|
|
77
|
-
totalMinor: null,
|
|
78
|
-
indeterminate: true
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
let totalMinor = 0n;
|
|
82
|
-
let overdueMinor = 0n;
|
|
83
|
-
let hasNegativeBucket = false;
|
|
84
|
-
parsed.forEach((m, i) => {
|
|
85
|
-
const minor = m;
|
|
86
|
-
if (minor < 0n)
|
|
87
|
-
hasNegativeBucket = true;
|
|
88
|
-
totalMinor += minor;
|
|
89
|
-
if (buckets[i].overdue)
|
|
90
|
-
overdueMinor += minor;
|
|
91
|
-
});
|
|
92
|
-
// A NEGATIVE bucket total is parseable but not interpretable as a share of a
|
|
93
|
-
// portfolio: overdue/total stops being bounded by 0..1 the moment a component
|
|
94
|
-
// is negative (a -50 current band against a 100 overdue band yields "200%
|
|
95
|
-
// delinquent"), and it can also cancel the denominator toward zero. A credit
|
|
96
|
-
// adjustment is real data, but it is not aging data, so the honest answer is
|
|
97
|
-
// that the RATE cannot be determined — the sums stay, since they are still
|
|
98
|
-
// correct, and the readout takes the no-value path. Legacy divided anyway and
|
|
99
|
-
// printed the out-of-range percentage.
|
|
100
|
-
if (hasNegativeBucket) {
|
|
101
|
-
return { rate: null, overdueMinor, totalMinor, indeterminate: true };
|
|
102
|
-
}
|
|
103
|
-
// A zero portfolio has no rate to report — null, never overdueMinor/0 (NaN).
|
|
104
|
-
if (totalMinor === 0n) {
|
|
105
|
-
return { rate: null, overdueMinor, totalMinor, indeterminate: false };
|
|
106
|
-
}
|
|
107
|
-
// The division is done as a BOUNDED BigInt ratio first, then converted once.
|
|
108
|
-
// Converting the two totals to Number separately overflowed on a large
|
|
109
|
-
// portfolio: past ~1.8e308 each side becomes Infinity and Infinity/Infinity is
|
|
110
|
-
// NaN — a portfolio big enough to matter reported no rate at all. Scaling the
|
|
111
|
-
// numerator by RATE_PRECISION keeps the quotient the size of a ratio (not the
|
|
112
|
-
// size of the money), so the single Number() sees a value it can hold exactly.
|
|
113
|
-
const scaled = (overdueMinor * RATE_PRECISION) / totalMinor;
|
|
114
|
-
const rate = Number(scaled) / Number(RATE_PRECISION);
|
|
115
|
-
// A ratio that still cannot be represented is no rate at all — the readout
|
|
116
|
-
// shows the no-value placeholder rather than a NaN/Infinity artifact.
|
|
117
|
-
if (!Number.isFinite(rate)) {
|
|
118
|
-
return { rate: null, overdueMinor, totalMinor, indeterminate: false };
|
|
119
|
-
}
|
|
120
|
-
return { rate, overdueMinor, totalMinor, indeterminate: false };
|
|
121
|
-
}
|
|
122
|
-
/** Fixed-point scale for comparing the ratio against a float threshold as exact
|
|
123
|
-
* integers. 1e18 is well past the ~1e-16 relative precision a double carries,
|
|
124
|
-
* so the comparison is exact to the limit of the threshold value itself. */
|
|
125
|
-
const THRESHOLD_SCALE = 10n ** 18n;
|
|
126
|
-
/**
|
|
127
|
-
* THIRD RAIL: `overdue / total > threshold`, decided on EXACT integers.
|
|
128
|
-
*
|
|
129
|
-
* The float `rate` is a TRUNCATED view of the ratio — RATE_PRECISION drops every
|
|
130
|
-
* digit past 1e-12 — so a rate a hair ABOVE a threshold rounds down onto it, and
|
|
131
|
-
* the strict-edge rule then reads it as the calmer band. 500000000001 /
|
|
132
|
-
* 10000000000000 is 0.0500000000001, genuinely past a 0.05 warn edge, and
|
|
133
|
-
* truncates to exactly 0.05, which reported "Saudável" on a portfolio that had
|
|
134
|
-
* crossed into "Elevada". Cross-multiplying keeps the comparison in integers,
|
|
135
|
-
* where no digit is dropped.
|
|
136
|
-
*/
|
|
137
|
-
function ratioExceeds(overdue, total, threshold) {
|
|
138
|
-
// A non-finite threshold is no threshold: nothing can be past it.
|
|
139
|
-
if (!Number.isFinite(threshold))
|
|
140
|
-
return false;
|
|
141
|
-
// Scaling overflows to ±Infinity for a threshold that is finite but huge —
|
|
142
|
-
// 1e300 is enough, MAX_VALUE certainly — and `BigInt(Infinity)` throws a
|
|
143
|
-
// RangeError, mid-render, taking the surface down. Both overflow directions
|
|
144
|
-
// have an EXACT answer, so this degrades to the right result rather than a
|
|
145
|
-
// merely safe one: an overflowed positive threshold is one no 0..1 ratio could
|
|
146
|
-
// ever reach (not exceeded), an overflowed negative one sits below every ratio
|
|
147
|
-
// (always exceeded).
|
|
148
|
-
const scaled = Math.round(threshold * Number(THRESHOLD_SCALE));
|
|
149
|
-
if (!Number.isFinite(scaled))
|
|
150
|
-
return scaled < 0;
|
|
151
|
-
let scaledInt = BigInt(scaled);
|
|
152
|
-
// A threshold FINER than one unit of the scale rounds to zero, and a zero
|
|
153
|
-
// threshold means "anything above zero is past it" — the exact opposite of what
|
|
154
|
-
// a tiny positive threshold asks for. 4e-19 scaled to 0, so a ratio of 1e-19
|
|
155
|
-
// was reported as exceeding it. Clamp to one unit instead, keeping the sign, so
|
|
156
|
-
// a sub-scale threshold behaves as the finest one this comparison supports.
|
|
157
|
-
//
|
|
158
|
-
// ponytail: the floor is 1e-18 (THRESHOLD_SCALE), so a threshold between 0 and
|
|
159
|
-
// 1e-18 is treated as 1e-18 and a ratio in that sliver reads calmer than the
|
|
160
|
-
// literal number asks. Immaterial for a delinquency ratio — a portfolio would
|
|
161
|
-
// need ~1e18 currency units for one minor unit to land there — and the honest
|
|
162
|
-
// alternative is exact rational threshold conversion, which buys nothing real.
|
|
163
|
-
// An EXACT zero is left alone: every positive ratio does exceed it.
|
|
164
|
-
if (scaledInt === 0n && threshold !== 0) {
|
|
165
|
-
scaledInt = threshold < 0 ? -1n : 1n;
|
|
166
|
-
}
|
|
167
|
-
return overdue * THRESHOLD_SCALE > scaledInt * total;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* The rate band, resolved from the exact minor-unit ratio. Mirrors `gaugeBand`'s
|
|
171
|
-
* higher-is-worse STRICT-edge canon (a ratio exactly ON a threshold stays in the
|
|
172
|
-
* calmer band) rather than calling it: `gaugeBand` takes a float, and handing it
|
|
173
|
-
* the truncated rate is precisely the defect above.
|
|
174
|
-
*/
|
|
175
|
-
function exactBand(overdue, total, warn, breach) {
|
|
176
|
-
if (ratioExceeds(overdue, total, breach))
|
|
177
|
-
return 'breach';
|
|
178
|
-
if (ratioExceeds(overdue, total, warn))
|
|
179
|
-
return 'warn';
|
|
180
|
-
return 'low';
|
|
181
|
-
}
|
|
182
|
-
/** Band → glyph + tint + accessible word. The glyph and sr-only word are the
|
|
183
|
-
* load-bearing cue; the tint only reinforces, so the band survives grayscale
|
|
184
|
-
* and color-vision deficiency. Mirrors the gauge's three-band canon, renamed
|
|
185
|
-
* to the delinquency vocabulary.
|
|
186
|
-
*
|
|
187
|
-
* The tint is an INK token (`-h1a`), because it is painted on the `Figure`
|
|
188
|
-
* below as well as on the glyph. The bare fill tokens read 1.78:1
|
|
189
|
-
* (`--system-alert`) and 3.35:1 (`--system-success`) on every light surface in
|
|
190
|
-
* the kit — the alert one under even the 3:1 floor a glyph answers to. */
|
|
191
|
-
const RATE_BAND = {
|
|
192
|
-
low: { Icon: lucide_react_1.ShieldCheck, tint: 'text-system-success-h1a', word: 'Saudável' },
|
|
193
|
-
warn: { Icon: lucide_react_1.TriangleAlert, tint: 'text-system-alert-h1a', word: 'Elevada' },
|
|
194
|
-
breach: { Icon: lucide_react_1.AlertOctagon, tint: 'text-credit', word: 'Em estresse' }
|
|
195
|
-
};
|
|
196
|
-
function DelinquencyAging({ buckets, currency, locale, rateLabel = 'Inadimplência', rateBandLabels, emptyLabel = 'sem carteira', indeterminateLabel = 'indeterminada', bucketLabels, warn = 0.05, breach = 0.1, showTotal = false, healthyTone = 'success', className }) {
|
|
197
|
-
// THIRD RAIL: the rate scale and the two totals come from money-math, never
|
|
198
|
-
// float. The division below is of two exact BigInt-derived integers.
|
|
199
|
-
const scale = currency ? (0, money_math_1.minorDigitsOf)(currency) : 2;
|
|
200
|
-
const { rate, overdueMinor, totalMinor, indeterminate } = delinquencyRate(buckets, scale);
|
|
201
|
-
// The band is only meaningful when there is a rate; an empty/indeterminate
|
|
202
|
-
// portfolio has no band to name (rendered as the explicit no-value readout).
|
|
203
|
-
const hasRate = rate !== null;
|
|
204
|
-
// higher-is-worse: more value past due is worse. The band comes off the EXACT
|
|
205
|
-
// minor-unit ratio, never `rate` — that float is truncated at 1e-12 and is for
|
|
206
|
-
// DISPLAY only (one decimal place, where the truncation cannot show).
|
|
207
|
-
const band = hasRate && overdueMinor !== null && totalMinor !== null
|
|
208
|
-
? exactBand(overdueMinor, totalMinor, warn, breach)
|
|
209
|
-
: 'low';
|
|
210
|
-
const { Icon, word } = RATE_BAND[band];
|
|
211
|
-
// `ink` retones ONLY the healthy band; `warn`/`breach` keep their alarm tints.
|
|
212
|
-
const tint = healthyTone === 'ink' && band === 'low'
|
|
213
|
-
? 'text-foreground'
|
|
214
|
-
: RATE_BAND[band].tint;
|
|
215
|
-
// Shallow merge: an unlisted band keeps its pt-BR default.
|
|
216
|
-
const bandWord = rateBandLabels?.[band] ?? word;
|
|
217
|
-
const distressed = band === 'breach';
|
|
218
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('flex flex-col gap-4', className), children: [(0, jsx_runtime_1.jsxs)("header", { className: "flex items-baseline justify-between gap-3", children: [(0, jsx_runtime_1.jsx)(section_label_1.SectionLabel, { as: "span", children: rateLabel }), hasRate ? ((0, jsx_runtime_1.jsxs)("span", {
|
|
219
|
-
// A distressed rate is a credit alarm: announced the moment it
|
|
220
|
-
// crosses the band, so assistive tech does not lean on the tint.
|
|
221
|
-
role: distressed ? 'alert' : undefined, className: (0, utils_1.cn)('inline-flex items-center gap-1.5', tint), children: [(0, jsx_runtime_1.jsx)(Icon, { "aria-hidden": true, className: "size-4 shrink-0" }), (0, jsx_runtime_1.jsxs)("span", { className: "sr-only", children: [bandWord, ": "] }), (0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: tint, children: (0, format_1.formatPercent)(rate, { unit: 'ratio', locale }) })] })) : ((0, jsx_runtime_1.jsxs)("span", { className: "text-muted-foreground inline-flex items-center gap-1.5", children: [(0, jsx_runtime_1.jsx)("span", { className: typography_1.LABEL_VOICE_CLASS, children: indeterminate ? indeterminateLabel : emptyLabel }), (0, jsx_runtime_1.jsx)(figure_1.Figure, { size: "row", className: "text-muted-foreground", children: format_1.NO_VALUE })] }))] }), (0, jsx_runtime_1.jsx)(aging_buckets_1.AgingBuckets, { buckets: buckets, currency: currency, locale: locale, showTotal: showTotal, labels: bucketLabels, healthyTone: healthyTone })] }));
|
|
222
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.delinquencyRate=delinquencyRate;exports.DelinquencyAging=DelinquencyAging;const jsx_runtime_1=require("react/jsx-runtime");const lucide_react_1=require("lucide-react");const typography_1=require("../../lib/typography");const utils_1=require("../../lib/utils");const figure_1=require("../figure");const format_1=require("../format");const money_math_1=require("../money-math");const section_label_1=require("../section-label");const aging_buckets_1=require("./aging-buckets");const RATE_PRECISION=1000000000000n;function delinquencyRate(r,i){const n=r.map(s=>(0,money_math_1.toMinor)(s.total,i));if(n.some(s=>s===null)){return{rate:null,overdueMinor:null,totalMinor:null,indeterminate:true}}let e=0n;let t=0n;let c=false;n.forEach((s,d)=>{const a=s;if(a<0n)c=true;e+=a;if(r[d].overdue)t+=a});if(c){return{rate:null,overdueMinor:t,totalMinor:e,indeterminate:true}}if(e===0n){return{rate:null,overdueMinor:t,totalMinor:e,indeterminate:false}}const u=t*RATE_PRECISION/e;const o=Number(u)/Number(RATE_PRECISION);if(!Number.isFinite(o)){return{rate:null,overdueMinor:t,totalMinor:e,indeterminate:false}}return{rate:o,overdueMinor:t,totalMinor:e,indeterminate:false}}const THRESHOLD_SCALE=10n**18n;function ratioExceeds(r,i,n){if(!Number.isFinite(n))return false;const e=Math.round(n*Number(THRESHOLD_SCALE));if(!Number.isFinite(e))return e<0;let t=BigInt(e);if(t===0n&&n!==0){t=n<0?-1n:1n}return r*THRESHOLD_SCALE>t*i}function exactBand(r,i,n,e){if(ratioExceeds(r,i,e))return"breach";if(ratioExceeds(r,i,n))return"warn";return"low"}const RATE_BAND={low:{Icon:lucide_react_1.ShieldCheck,tint:"text-system-success-h1a",word:"Saud\xE1vel"},warn:{Icon:lucide_react_1.TriangleAlert,tint:"text-system-alert-h1a",word:"Elevada"},breach:{Icon:lucide_react_1.AlertOctagon,tint:"text-credit",word:"Em estresse"}};function DelinquencyAging({buckets:r,currency:i,locale:n,rateLabel:e="Inadimpl\xEAncia",rateBandLabels:t,emptyLabel:c="sem carteira",indeterminateLabel:u="indeterminada",bucketLabels:o,warn:s=.05,breach:d=.1,showTotal:a=false,healthyTone:m="success",className:b}){const p=i?(0,money_math_1.minorDigitsOf)(i):2;const{rate:f,overdueMinor:x,totalMinor:g,indeterminate:N}=delinquencyRate(r,p);const h=f!==null;const l=h&&x!==null&&g!==null?exactBand(x,g,s,d):"low";const{Icon:j,word:w}=RATE_BAND[l];const _=m==="ink"&&l==="low"?"text-foreground":RATE_BAND[l].tint;const y=t?.[l]??w;const q=l==="breach";return(0,jsx_runtime_1.jsxs)("div",{className:(0,utils_1.cn)("flex flex-col gap-4",b),children:[(0,jsx_runtime_1.jsxs)("header",{className:"flex items-baseline justify-between gap-3",children:[(0,jsx_runtime_1.jsx)(section_label_1.SectionLabel,{as:"span",children:e}),h?(0,jsx_runtime_1.jsxs)("span",{role:q?"alert":void 0,className:(0,utils_1.cn)("inline-flex items-center gap-1.5",_),children:[(0,jsx_runtime_1.jsx)(j,{"aria-hidden":true,className:"size-4 shrink-0"}),(0,jsx_runtime_1.jsxs)("span",{className:"sr-only",children:[y,": "]}),(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:_,children:(0,format_1.formatPercent)(f,{unit:"ratio",locale:n})})]}):(0,jsx_runtime_1.jsxs)("span",{className:"text-muted-foreground inline-flex items-center gap-1.5",children:[(0,jsx_runtime_1.jsx)("span",{className:typography_1.LABEL_VOICE_CLASS,children:N?u:c}),(0,jsx_runtime_1.jsx)(figure_1.Figure,{size:"row",className:"text-muted-foreground",children:format_1.NO_VALUE})]})]}),(0,jsx_runtime_1.jsx)(aging_buckets_1.AgingBuckets,{buckets:r,currency:i,locale:n,showTotal:a,labels:o,healthyTone:m})]})}
|