@eifi1/ui-kit 0.52.0 → 0.54.0
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/README.md +52 -59
- package/dist/account/delete-account-setting.d.ts +3 -2
- package/dist/account/delete-account-setting.js.map +1 -1
- package/dist/auth/auth-errors.d.ts +36 -0
- package/dist/auth/auth-errors.js +20 -0
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/billing/cancel-subscription.d.ts +5 -14
- package/dist/billing/cancel-subscription.js +1 -3
- package/dist/billing/cancel-subscription.js.map +1 -1
- package/dist/billing/plan-card.d.ts +5 -13
- package/dist/billing/plan-card.js +3 -11
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/billing/plan-switch.d.ts +5 -14
- package/dist/billing/plan-switch.js +1 -3
- package/dist/billing/plan-switch.js.map +1 -1
- package/dist/chart.d.ts +3 -14
- package/dist/chart.js +64 -11
- package/dist/chart.js.map +1 -1
- package/dist/components/amount-input.d.ts +2 -1
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/calendar-heatmap.js +4 -2
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/currency-option-list.d.ts +2 -1
- package/dist/components/currency-option-list.js +10 -3
- package/dist/components/currency-option-list.js.map +1 -1
- package/dist/components/currency-select.d.ts +3 -1
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +22 -1
- package/dist/components/danger-confirm.js +50 -28
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-filter-popover.d.ts +2 -1
- package/dist/components/data-table-filter-popover.js +3 -2
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +33 -3
- package/dist/components/data-table-filters.js +6 -4
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/date-field.d.ts +1 -1
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.d.ts +1 -1
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.d.ts +4 -17
- package/dist/components/date-range-picker.js +2 -2
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +3 -1
- package/dist/components/dialog-frame.js +6 -2
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/mini-calendar.js +3 -2
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +27 -0
- package/dist/components/modal.js +5 -3
- package/dist/components/modal.js.map +1 -1
- package/dist/components/nav-pills.d.ts +1 -1
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/series-chart-axes.d.ts +1 -1
- package/dist/components/series-chart-axes.js.map +1 -1
- package/dist/components/series-chart.d.ts +64 -19
- package/dist/components/series-chart.js +2 -1
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/stat-tile.d.ts +21 -2
- package/dist/components/stat-tile.js +4 -2
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/treemap-labels.d.ts +6 -1
- package/dist/components/treemap-labels.js +2 -2
- package/dist/components/treemap-labels.js.map +1 -1
- package/dist/components/treemap.d.ts +29 -4
- package/dist/components/treemap.js +22 -9
- package/dist/components/treemap.js.map +1 -1
- package/dist/currencies.d.ts +16 -2
- package/dist/currencies.js +2 -1
- package/dist/currencies.js.map +1 -1
- package/dist/data-table.d.ts +1 -6
- package/dist/data-table.js +1 -9
- package/dist/data-table.js.map +1 -1
- package/dist/demo/demo-countdown.d.ts +0 -5
- package/dist/demo/demo-countdown.js +0 -4
- package/dist/demo/demo-countdown.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-context-labels.js +2 -1
- package/dist/feedback/feedback-context-labels.js.map +1 -1
- package/dist/feedback/feedback-context.d.ts +19 -3
- package/dist/feedback/feedback-context.js +12 -1
- package/dist/feedback/feedback-context.js.map +1 -1
- package/dist/feedback/feedback-diagnostics.d.ts +17 -0
- package/dist/feedback/feedback-diagnostics.js +27 -0
- package/dist/feedback/feedback-diagnostics.js.map +1 -0
- package/dist/feedback/feedback-dialog.d.ts +10 -0
- package/dist/feedback/feedback-dialog.js +6 -4
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-mutation.d.ts +11 -23
- package/dist/feedback/feedback-mutation.js +1 -9
- package/dist/feedback/feedback-mutation.js.map +1 -1
- package/dist/feedback/feedback-public-list.d.ts +1 -6
- package/dist/feedback/feedback-public-list.js +1 -2
- package/dist/feedback/feedback-public-list.js.map +1 -1
- package/dist/feedback/feedback-record.d.ts +5 -0
- package/dist/feedback/feedback-record.js.map +1 -1
- package/dist/feedback/feedback-row-detail-labels.js +1 -8
- package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +6 -50
- package/dist/feedback/feedback-row-detail.js +3 -8
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +3 -19
- package/dist/feedback/feedback-status-undo.js.map +1 -1
- package/dist/feedback/feedback-submit-dialog.d.ts +5 -1
- package/dist/feedback/feedback-submit-dialog.js +15 -3
- package/dist/feedback/feedback-submit-dialog.js.map +1 -1
- package/dist/feedback/feedback-submit.d.ts +30 -2
- package/dist/feedback/feedback-submit.js +5 -2
- package/dist/feedback/feedback-submit.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +0 -9
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +6 -40
- package/dist/feedback.js +88 -46
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-hotkey.js +11 -1
- package/dist/hooks/use-hotkey.js.map +1 -1
- package/dist/i18n/defaults.js +0 -4
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +5 -20
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +5 -14
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +5 -31
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +5 -20
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +5 -20
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +5 -20
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +5 -21
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +5 -20
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +9 -4
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +0 -1
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +18 -67
- package/dist/index.js +28 -103
- package/dist/index.js.map +1 -1
- package/dist/lib/dates.d.ts +7 -1
- package/dist/lib/dates.js +8 -1
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/format.d.ts +37 -2
- package/dist/lib/format.js +25 -1
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/intl-cache.d.ts +4 -2
- package/dist/lib/intl-cache.js.map +1 -1
- package/dist/lib/translation-review.d.ts +26 -0
- package/dist/lib/translation-review.js +14 -0
- package/dist/lib/translation-review.js.map +1 -1
- package/dist/rhf/fields.d.ts +2 -2
- package/dist/rhf/fields.js.map +1 -1
- package/dist/search/search-index.d.ts +0 -6
- package/dist/search/search-index.js +0 -4
- package/dist/search/search-index.js.map +1 -1
- package/dist/search.d.ts +1 -8
- package/dist/search.js +2 -7
- package/dist/search.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +7 -21
- package/dist/shell/account-menu-language.js +1 -3
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/account-menu-text-size.d.ts +9 -23
- package/dist/shell/account-menu-text-size.js +2 -3
- package/dist/shell/account-menu-text-size.js.map +1 -1
- package/dist/shell/app-shell.d.ts +0 -10
- package/dist/shell/app-shell.js +1 -7
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/company-switcher.d.ts +1 -1
- package/dist/shell/company-switcher.js.map +1 -1
- package/dist/shell/dev-menu.d.ts +1 -1
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +1 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell.d.ts +7 -29
- package/dist/shell.js +12 -42
- package/dist/shell.js.map +1 -1
- package/dist/sync.d.ts +2 -6
- package/dist/sync.js +2 -15
- package/dist/sync.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +0 -5
- package/dist/theme/chart-palette.js +0 -2
- package/dist/theme/chart-palette.js.map +1 -1
- package/package.json +1 -1
- package/src/account/delete-account-setting.tsx +3 -2
- package/src/auth/auth-errors.ts +69 -0
- package/src/billing/cancel-subscription.tsx +5 -16
- package/src/billing/plan-card.tsx +7 -24
- package/src/billing/plan-switch.tsx +5 -16
- package/src/chart.ts +62 -13
- package/src/components/amount-input.tsx +2 -1
- package/src/components/calendar-heatmap.tsx +8 -2
- package/src/components/currency-option-list.tsx +12 -4
- package/src/components/currency-select.tsx +3 -1
- package/src/components/danger-confirm.tsx +80 -27
- package/src/components/data-table-filter-popover.tsx +19 -11
- package/src/components/data-table-filters.ts +36 -8
- package/src/components/date-field.tsx +1 -1
- package/src/components/date-picker.tsx +0 -1
- package/src/components/date-range-picker.tsx +6 -22
- package/src/components/dialog-frame.tsx +12 -4
- package/src/components/mini-calendar.tsx +5 -2
- package/src/components/modal.tsx +36 -7
- package/src/components/nav-pills.tsx +1 -1
- package/src/components/number-input.tsx +4 -3
- package/src/components/series-chart-axes.ts +6 -1
- package/src/components/series-chart.tsx +78 -21
- package/src/components/stat-tile.tsx +32 -3
- package/src/components/treemap-labels.ts +11 -3
- package/src/components/treemap.tsx +65 -12
- package/src/currencies.ts +23 -3
- package/src/data-table.ts +1 -11
- package/src/demo/demo-countdown.ts +3 -8
- package/src/feedback/feedback-context-labels.ts +1 -0
- package/src/feedback/feedback-context.tsx +34 -2
- package/src/feedback/feedback-diagnostics.tsx +41 -0
- package/src/feedback/feedback-dialog.tsx +16 -4
- package/src/feedback/feedback-mutation.ts +12 -33
- package/src/feedback/feedback-public-list.tsx +1 -7
- package/src/feedback/feedback-record.ts +5 -0
- package/src/feedback/feedback-row-detail-labels.ts +1 -8
- package/src/feedback/feedback-row-detail.tsx +21 -58
- package/src/feedback/feedback-status-undo.ts +4 -18
- package/src/feedback/feedback-submit-dialog.tsx +15 -2
- package/src/feedback/feedback-submit.tsx +36 -3
- package/src/feedback/feedback-thread.tsx +0 -10
- package/src/feedback.ts +89 -48
- package/src/hooks/use-hotkey.ts +24 -1
- package/src/i18n/defaults.ts +0 -4
- package/src/i18n/german.ts +4 -19
- package/src/i18n/kit-labels.tsx +5 -14
- package/src/i18n/locales/es.ts +4 -19
- package/src/i18n/locales/fr.ts +4 -19
- package/src/i18n/locales/hu.ts +4 -19
- package/src/i18n/locales/it.ts +4 -20
- package/src/i18n/locales/zh.ts +4 -19
- package/src/i18n/review.ts +9 -4
- package/src/i18n/shared-labels.ts +0 -1
- package/src/index.ts +44 -114
- package/src/lib/dates.ts +24 -2
- package/src/lib/format.ts +66 -3
- package/src/lib/intl-cache.ts +4 -2
- package/src/lib/translation-review.ts +52 -0
- package/src/rhf/fields.tsx +2 -2
- package/src/search/search-index.ts +0 -15
- package/src/search.ts +8 -8
- package/src/shell/account-menu-language.tsx +7 -27
- package/src/shell/account-menu-text-size.tsx +13 -32
- package/src/shell/app-shell.tsx +0 -13
- package/src/shell/company-switcher.tsx +1 -1
- package/src/shell/dev-menu.tsx +1 -1
- package/src/shell/top-bar-parts-labels.ts +1 -1
- package/src/shell.ts +17 -44
- package/src/sync.ts +2 -15
- package/src/theme/chart-palette.ts +0 -2
- package/tokens.css +4 -4
- package/dist/billing/dialog-close.d.ts +0 -29
- package/dist/billing/dialog-close.js +0 -8
- package/dist/billing/dialog-close.js.map +0 -1
- package/dist/components/field-sync-labels.d.ts +0 -3
- package/dist/components/field-sync-labels.js +0 -12
- package/dist/components/field-sync-labels.js.map +0 -1
- package/dist/components/field-sync.d.ts +0 -186
- package/dist/components/field-sync.js +0 -269
- package/dist/components/field-sync.js.map +0 -1
- package/dist/components/section-nav.d.ts +0 -44
- package/dist/components/section-nav.js +0 -33
- package/dist/components/section-nav.js.map +0 -1
- package/dist/components/sign-chip-labels.d.ts +0 -3
- package/dist/components/sign-chip-labels.js +0 -11
- package/dist/components/sign-chip-labels.js.map +0 -1
- package/dist/components/sign-chip.d.ts +0 -77
- package/dist/components/sign-chip.js +0 -47
- package/dist/components/sign-chip.js.map +0 -1
- package/dist/lib/isin.d.ts +0 -38
- package/dist/lib/isin.js +0 -36
- package/dist/lib/isin.js.map +0 -1
- package/dist/shell/role-switcher.d.ts +0 -28
- package/dist/shell/role-switcher.js +0 -31
- package/dist/shell/role-switcher.js.map +0 -1
- package/src/billing/dialog-close.ts +0 -34
- package/src/components/field-sync-labels.ts +0 -14
- package/src/components/field-sync.tsx +0 -504
- package/src/components/section-nav.tsx +0 -72
- package/src/components/sign-chip-labels.ts +0 -13
- package/src/components/sign-chip.tsx +0 -133
- package/src/lib/isin.ts +0 -72
- package/src/shell/role-switcher.tsx +0 -53
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
-
import { AlertCircle, Check, Loader2, Pencil, PencilLine } from "lucide-react";
|
|
5
|
-
import { cn } from "../lib/cn.js";
|
|
6
|
-
import { Tooltip } from "./tooltip.js";
|
|
7
|
-
import { FOCUS_RING } from "./focus-ring.js";
|
|
8
|
-
import { useKitLabels } from "../i18n/kit-labels.js";
|
|
9
|
-
import { useLargeText } from "../hooks/use-large-text.js";
|
|
10
|
-
import { DEFAULT_FIELD_SYNC_LABELS } from "./field-sync-labels.js";
|
|
11
|
-
function toError(cause) {
|
|
12
|
-
if (cause instanceof Error) return cause;
|
|
13
|
-
return new Error(typeof cause === "string" ? cause : "", { cause });
|
|
14
|
-
}
|
|
15
|
-
function useFieldSync({
|
|
16
|
-
value,
|
|
17
|
-
onSave,
|
|
18
|
-
debounceMs = 0,
|
|
19
|
-
equals = Object.is,
|
|
20
|
-
onError
|
|
21
|
-
}) {
|
|
22
|
-
const [draft, setDraft] = useState(value);
|
|
23
|
-
const [state, setState] = useState("synced");
|
|
24
|
-
const [error, setError] = useState(null);
|
|
25
|
-
const savedRef = useRef(value);
|
|
26
|
-
const draftRef = useRef(value);
|
|
27
|
-
const inFlightRef = useRef(false);
|
|
28
|
-
const timerRef = useRef(null);
|
|
29
|
-
const mountedRef = useRef(true);
|
|
30
|
-
const onSaveRef = useRef(onSave);
|
|
31
|
-
const onErrorRef = useRef(onError);
|
|
32
|
-
const equalsRef = useRef(equals);
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
onSaveRef.current = onSave;
|
|
35
|
-
onErrorRef.current = onError;
|
|
36
|
-
equalsRef.current = equals;
|
|
37
|
-
});
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
mountedRef.current = true;
|
|
40
|
-
return () => {
|
|
41
|
-
mountedRef.current = false;
|
|
42
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
43
|
-
};
|
|
44
|
-
}, []);
|
|
45
|
-
const flush = useCallback(() => {
|
|
46
|
-
const run = async () => {
|
|
47
|
-
if (inFlightRef.current) return;
|
|
48
|
-
const next = draftRef.current;
|
|
49
|
-
if (equalsRef.current(next, savedRef.current)) return;
|
|
50
|
-
inFlightRef.current = true;
|
|
51
|
-
setState("pending");
|
|
52
|
-
setError(null);
|
|
53
|
-
try {
|
|
54
|
-
await onSaveRef.current(next);
|
|
55
|
-
savedRef.current = next;
|
|
56
|
-
inFlightRef.current = false;
|
|
57
|
-
if (!mountedRef.current) return;
|
|
58
|
-
if (equalsRef.current(draftRef.current, next)) {
|
|
59
|
-
setState("synced");
|
|
60
|
-
} else {
|
|
61
|
-
setState("edited");
|
|
62
|
-
void run();
|
|
63
|
-
}
|
|
64
|
-
} catch (cause) {
|
|
65
|
-
inFlightRef.current = false;
|
|
66
|
-
const err = toError(cause);
|
|
67
|
-
onErrorRef.current?.(err);
|
|
68
|
-
if (!mountedRef.current) return;
|
|
69
|
-
setState("error");
|
|
70
|
-
setError(err);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
return run();
|
|
74
|
-
}, []);
|
|
75
|
-
const setValue = useCallback(
|
|
76
|
-
(next) => {
|
|
77
|
-
draftRef.current = next;
|
|
78
|
-
setDraft(next);
|
|
79
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
80
|
-
if (equalsRef.current(next, savedRef.current)) {
|
|
81
|
-
setState(inFlightRef.current ? "pending" : "synced");
|
|
82
|
-
setError(null);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
setState("edited");
|
|
86
|
-
setError(null);
|
|
87
|
-
if (debounceMs > 0) timerRef.current = setTimeout(() => void flush(), debounceMs);
|
|
88
|
-
},
|
|
89
|
-
[debounceMs, flush]
|
|
90
|
-
);
|
|
91
|
-
const save = useCallback(() => {
|
|
92
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
93
|
-
void flush();
|
|
94
|
-
}, [flush]);
|
|
95
|
-
const retry = save;
|
|
96
|
-
const reset = useCallback(() => {
|
|
97
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
98
|
-
draftRef.current = savedRef.current;
|
|
99
|
-
setDraft(savedRef.current);
|
|
100
|
-
setState(inFlightRef.current ? "pending" : "synced");
|
|
101
|
-
setError(null);
|
|
102
|
-
}, []);
|
|
103
|
-
const dirty = state !== "synced";
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
if (inFlightRef.current) return;
|
|
106
|
-
if (!equalsRef.current(draftRef.current, savedRef.current)) return;
|
|
107
|
-
if (equalsRef.current(value, savedRef.current)) return;
|
|
108
|
-
savedRef.current = value;
|
|
109
|
-
draftRef.current = value;
|
|
110
|
-
setDraft(value);
|
|
111
|
-
setState("synced");
|
|
112
|
-
setError(null);
|
|
113
|
-
}, [value]);
|
|
114
|
-
return { value: draft, setValue, state, error, dirty, save, retry, reset };
|
|
115
|
-
}
|
|
116
|
-
const STATE_STYLE = {
|
|
117
|
-
synced: { color: "var(--status-synced)", Icon: Check },
|
|
118
|
-
edited: { color: "var(--status-edited)", Icon: Pencil },
|
|
119
|
-
pending: { color: "var(--status-pending)", Icon: Loader2 },
|
|
120
|
-
error: { color: "var(--status-error)", Icon: AlertCircle }
|
|
121
|
-
};
|
|
122
|
-
function FieldSyncIndicator({
|
|
123
|
-
state,
|
|
124
|
-
error,
|
|
125
|
-
labels,
|
|
126
|
-
showLabel = false,
|
|
127
|
-
onRetry,
|
|
128
|
-
className
|
|
129
|
-
}) {
|
|
130
|
-
const l = useKitLabels("fieldSync", DEFAULT_FIELD_SYNC_LABELS, labels);
|
|
131
|
-
const large = useLargeText();
|
|
132
|
-
showLabel = showLabel || large;
|
|
133
|
-
const { color, Icon } = STATE_STYLE[state];
|
|
134
|
-
const text = state === "error" ? error?.message?.trim() ? error.message : l.error : l[state];
|
|
135
|
-
return /* @__PURE__ */ jsxs(
|
|
136
|
-
"span",
|
|
137
|
-
{
|
|
138
|
-
className: cn("inline-flex items-center gap-1.5 text-xs text-secondary", className),
|
|
139
|
-
role: state === "error" ? "alert" : "status",
|
|
140
|
-
"aria-live": state === "error" ? "assertive" : "polite",
|
|
141
|
-
children: [
|
|
142
|
-
/* @__PURE__ */ jsx(Tooltip, { label: showLabel ? void 0 : text, redact: state === "error", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
143
|
-
/* @__PURE__ */ jsx(
|
|
144
|
-
Icon,
|
|
145
|
-
{
|
|
146
|
-
"aria-hidden": true,
|
|
147
|
-
style: { color },
|
|
148
|
-
className: cn("size-3.5 shrink-0", state === "pending" && "animate-spin")
|
|
149
|
-
}
|
|
150
|
-
),
|
|
151
|
-
/* @__PURE__ */ jsx("span", { className: showLabel ? void 0 : "sr-only", children: text })
|
|
152
|
-
] }) }),
|
|
153
|
-
state === "error" && onRetry && /* @__PURE__ */ jsx(
|
|
154
|
-
"button",
|
|
155
|
-
{
|
|
156
|
-
type: "button",
|
|
157
|
-
onClick: onRetry,
|
|
158
|
-
className: cn(
|
|
159
|
-
"rounded underline underline-offset-2 hover:no-underline focus-visible:ring-offset-2 focus-visible:ring-offset-surface",
|
|
160
|
-
FOCUS_RING
|
|
161
|
-
),
|
|
162
|
-
style: { color },
|
|
163
|
-
children: l.retry
|
|
164
|
-
}
|
|
165
|
-
)
|
|
166
|
-
]
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
const FIELD_SYNC_FRAME = {
|
|
171
|
-
synced: "[&_:is(input,select,textarea)]:border-[var(--status-synced)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-synced)] [&_:is(input,select,textarea):focus]:border-[var(--status-synced)] [&_:is(input,select,textarea):focus]:ring-[var(--status-synced)]",
|
|
172
|
-
edited: "[&_:is(input,select,textarea)]:border-[var(--status-edited)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-edited)] [&_:is(input,select,textarea):focus]:border-[var(--status-edited)] [&_:is(input,select,textarea):focus]:ring-[var(--status-edited)]",
|
|
173
|
-
pending: "[&_:is(input,select,textarea)]:border-[var(--status-pending)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-pending)] [&_:is(input,select,textarea):focus]:border-[var(--status-pending)] [&_:is(input,select,textarea):focus]:ring-[var(--status-pending)]",
|
|
174
|
-
error: "[&_:is(input,select,textarea)]:border-[var(--status-error)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-error)] [&_:is(input,select,textarea):focus]:border-[var(--status-error)] [&_:is(input,select,textarea):focus]:ring-[var(--status-error)]"
|
|
175
|
-
};
|
|
176
|
-
const FIELD_SYNC_SAVED_MS = 1500;
|
|
177
|
-
function FieldSyncRow({
|
|
178
|
-
sync,
|
|
179
|
-
children,
|
|
180
|
-
className,
|
|
181
|
-
labels,
|
|
182
|
-
onRetry,
|
|
183
|
-
saveOnBlur = true,
|
|
184
|
-
savedMs = FIELD_SYNC_SAVED_MS
|
|
185
|
-
}) {
|
|
186
|
-
const l = useKitLabels("fieldSync", DEFAULT_FIELD_SYNC_LABELS, labels);
|
|
187
|
-
const { state, error } = sync;
|
|
188
|
-
const isError = state === "error";
|
|
189
|
-
const text = isError ? error?.message?.trim() ? error.message : l.error : l[state];
|
|
190
|
-
const retry = onRetry ?? sync.retry;
|
|
191
|
-
const [previous, setPrevious] = useState(state);
|
|
192
|
-
const [justSaved, setJustSaved] = useState(false);
|
|
193
|
-
if (state !== previous) {
|
|
194
|
-
setPrevious(state);
|
|
195
|
-
setJustSaved(state === "synced" && previous === "pending");
|
|
196
|
-
}
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
if (!justSaved) return;
|
|
199
|
-
const t = setTimeout(() => setJustSaved(false), savedMs);
|
|
200
|
-
return () => clearTimeout(t);
|
|
201
|
-
}, [justSaved, savedMs]);
|
|
202
|
-
const shown = state === "synced" ? justSaved ? "synced" : null : state;
|
|
203
|
-
return (
|
|
204
|
-
/* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- not an interaction:
|
|
205
|
-
`onBlur` listens for focus leaving the row's own controls (which are the
|
|
206
|
-
interactive elements) to save; the wrapper itself is never focused. */
|
|
207
|
-
/* @__PURE__ */ jsxs(
|
|
208
|
-
"div",
|
|
209
|
-
{
|
|
210
|
-
"data-field-sync": state,
|
|
211
|
-
className: cn(
|
|
212
|
-
// `relative` for the icon and for the sr-only live region's containing block.
|
|
213
|
-
"relative [&_:is(input,select,textarea)]:pe-9",
|
|
214
|
-
shown && FIELD_SYNC_FRAME[shown],
|
|
215
|
-
className
|
|
216
|
-
),
|
|
217
|
-
onBlur: saveOnBlur ? (e) => {
|
|
218
|
-
if (!e.currentTarget.contains(e.relatedTarget)) sync.save();
|
|
219
|
-
} : void 0,
|
|
220
|
-
children: [
|
|
221
|
-
children,
|
|
222
|
-
/* @__PURE__ */ jsxs("span", { className: "pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3", children: [
|
|
223
|
-
state === "edited" && /* @__PURE__ */ jsx(PencilLine, { "aria-hidden": true, className: "size-4 text-[var(--status-edited)]" }),
|
|
224
|
-
state === "pending" && /* @__PURE__ */ jsx(
|
|
225
|
-
Loader2,
|
|
226
|
-
{
|
|
227
|
-
"aria-hidden": true,
|
|
228
|
-
className: "size-4 animate-spin text-[var(--status-pending)] motion-reduce:animate-none"
|
|
229
|
-
}
|
|
230
|
-
),
|
|
231
|
-
state === "synced" && /* @__PURE__ */ jsx(
|
|
232
|
-
Check,
|
|
233
|
-
{
|
|
234
|
-
"aria-hidden": true,
|
|
235
|
-
className: cn(
|
|
236
|
-
"size-4 text-[var(--status-synced)] transition-opacity duration-500 motion-reduce:transition-none",
|
|
237
|
-
justSaved ? "opacity-100" : "opacity-0"
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
),
|
|
241
|
-
isError && /* @__PURE__ */ jsx(Tooltip, { label: text, portal: true, redact: true, children: /* @__PURE__ */ jsx(
|
|
242
|
-
"button",
|
|
243
|
-
{
|
|
244
|
-
type: "button",
|
|
245
|
-
onClick: retry,
|
|
246
|
-
"aria-label": l.retry,
|
|
247
|
-
className: cn(
|
|
248
|
-
"pointer-events-auto flex rounded-full text-[var(--status-error)] focus-visible:ring-offset-2 focus-visible:ring-offset-surface",
|
|
249
|
-
FOCUS_RING
|
|
250
|
-
),
|
|
251
|
-
children: /* @__PURE__ */ jsx(AlertCircle, { "aria-hidden": true, className: "size-4" })
|
|
252
|
-
}
|
|
253
|
-
) })
|
|
254
|
-
] }),
|
|
255
|
-
/* @__PURE__ */ jsx("span", { role: isError ? "alert" : "status", "aria-live": isError ? "assertive" : "polite", className: "sr-only", children: shown === null ? "" : text })
|
|
256
|
-
]
|
|
257
|
-
}
|
|
258
|
-
)
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
export {
|
|
262
|
-
DEFAULT_FIELD_SYNC_LABELS,
|
|
263
|
-
FIELD_SYNC_FRAME,
|
|
264
|
-
FIELD_SYNC_SAVED_MS,
|
|
265
|
-
FieldSyncIndicator,
|
|
266
|
-
FieldSyncRow,
|
|
267
|
-
useFieldSync
|
|
268
|
-
};
|
|
269
|
-
//# sourceMappingURL=field-sync.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/field-sync.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { AlertCircle, Check, Loader2, Pencil, PencilLine } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { Tooltip } from \"./tooltip\";\nimport { FOCUS_RING } from \"./focus-ring\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { useLargeText } from \"../hooks/use-large-text\";\nimport { DEFAULT_FIELD_SYNC_LABELS } from \"./field-sync-labels\";\n\n/**\n * Sync state for a field backed by a database row.\n *\n * The four states a value can be in between the keyboard and the server:\n *\n * synced the draft equals what was last persisted\n * edited the draft differs and nothing has been sent yet\n * pending a save is in flight\n * error the last save was rejected; `error` carries why\n *\n * `useFieldSync` is the engine and holds no opinion about markup;\n * `FieldSyncIndicator` is the affordance and holds no state. They are separate\n * because the state is useful without the dot — a form can disable its submit\n * button while any field is `pending`, and a router can block navigation while\n * any field is `edited`.\n *\n * Deprecated in 0.52 as a whole, removed in 0.53 (decision 4 of the refactor audit\n * 2026-10-30, MOD-5): no app renders it or calls the hook — 483 lines and a namespace in\n * seven catalogues that only this package's tests and showcase read. An app that wants\n * it keeps a copy of this file.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53.\n */\nexport type FieldSyncState = \"synced\" | \"edited\" | \"pending\" | \"error\";\n\n/** @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport interface FieldSyncLabels {\n synced: string;\n edited: string;\n pending: string;\n /** Shown when the failure carries no message of its own. */\n error: string;\n retry: string;\n}\n\n// Defined in ./field-sync-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_FIELD_SYNC_LABELS };\n\n/** @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport interface UseFieldSyncOptions<T> {\n /** What the database currently holds. Changing it re-seeds a CLEAN field. */\n value: T;\n /** Persist `next`. Reject — or throw — to put the field in `error`. */\n onSave: (next: T) => Promise<void> | void;\n /**\n * Quiet period before an edit saves itself, in ms. Default `0`: no auto-save at\n * all, so `save()` is the only way to persist — called on blur by `FieldSyncRow`,\n * and on Enter by the caller. Saving after a pause in typing wrote half-finished\n * values to the database whenever somebody stopped to think; pass a positive\n * number only for a field where that is genuinely wanted.\n */\n debounceMs?: number;\n /** How to tell two values apart. Defaults to `Object.is`. */\n equals?: (a: T, b: T) => boolean;\n /** Called once per rejected save, with whatever `onSave` threw. */\n onError?: (error: Error) => void;\n}\n\n/** @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport interface UseFieldSyncReturn<T> {\n /** The draft — bind this to the input, not the `value` option. */\n value: T;\n setValue: (next: T) => void;\n state: FieldSyncState;\n /** Non-null exactly when `state === \"error\"`. */\n error: Error | null;\n /** True whenever the draft differs from the last persisted value. */\n dirty: boolean;\n /** Persist now, cancelling any pending debounce. A no-op on a clean field. */\n save: () => void;\n /** Send the current draft again after a failure. */\n retry: () => void;\n /** Throw the draft away and go back to the last persisted value. */\n reset: () => void;\n}\n\nfunction toError(cause: unknown): Error {\n if (cause instanceof Error) return cause;\n // An EMPTY message for a rejection that carried none, not an English one: the\n // indicator shows `error.message` when there is one and the translated\n // `fieldSync.error` otherwise, so a fallback message here was an English sentence\n // that outranked the app's own translation. The original value stays on `cause`\n // for whoever logs it from `onError`.\n return new Error(typeof cause === \"string\" ? cause : \"\", { cause });\n}\n\n/**\n * Track one database-backed field: a local draft, a debounced save, and the state\n * of the round trip.\n *\n * Four things this handles that a `useState` + `useEffect` in a page does not:\n *\n * 1. **An edit during a save is never lost.** A save in flight is not cancelled\n * and not raced — when it settles, the draft is compared again and sent once\n * more if it moved. Cancelling instead would drop a keystroke that arrived\n * mid-flight; firing immediately would let two writes land out of order.\n * 2. **The server value is only adopted when the field is clean.** An external\n * refresh that overwrote what somebody was typing would be a data-loss bug, so\n * a dirty draft wins and keeps winning until it is saved or reset.\n * 3. **Nothing is set after unmount.** The save resolves into a component that\n * may be gone — a row scrolled out of a virtualised table, a closed dialog.\n * 4. **`onSave` is read through a ref**, so an inline arrow — which every call\n * site will pass — does not restart the debounce on every render.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53.\n */\nexport function useFieldSync<T>({\n value,\n onSave,\n debounceMs = 0,\n equals = Object.is,\n onError,\n}: UseFieldSyncOptions<T>): UseFieldSyncReturn<T> {\n const [draft, setDraft] = useState<T>(value);\n const [state, setState] = useState<FieldSyncState>(\"synced\");\n const [error, setError] = useState<Error | null>(null);\n\n // The last value known to be on the server. Not state: it changes together with\n // `state` inside the save, and a second render for it would be a wasted one.\n const savedRef = useRef<T>(value);\n const draftRef = useRef<T>(value);\n const inFlightRef = useRef(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const mountedRef = useRef(true);\n\n // Latest-ref for the callbacks and comparators, so a caller passing inline\n // functions (all of them) does not invalidate the timer on every render.\n const onSaveRef = useRef(onSave);\n const onErrorRef = useRef(onError);\n const equalsRef = useRef(equals);\n useEffect(() => {\n onSaveRef.current = onSave;\n onErrorRef.current = onError;\n equalsRef.current = equals;\n });\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, []);\n\n // A named inner function so the settle path can start the next round by calling it\n // again; a `useCallback` cannot refer to its own binding while it is being built.\n const flush = useCallback(() => {\n const run = async (): Promise<void> => {\n if (inFlightRef.current) return; // the settle path below picks the new draft up\n const next = draftRef.current;\n if (equalsRef.current(next, savedRef.current)) return;\n\n inFlightRef.current = true;\n setState(\"pending\");\n setError(null);\n try {\n await onSaveRef.current(next);\n savedRef.current = next;\n inFlightRef.current = false;\n if (!mountedRef.current) return;\n // The draft may have moved while this was in the air. Compare against what is\n // on screen NOW, not against what was sent.\n if (equalsRef.current(draftRef.current, next)) {\n setState(\"synced\");\n } else {\n setState(\"edited\");\n void run();\n }\n } catch (cause) {\n inFlightRef.current = false;\n const err = toError(cause);\n onErrorRef.current?.(err);\n if (!mountedRef.current) return;\n setState(\"error\");\n setError(err);\n }\n };\n return run();\n }, []);\n\n const setValue = useCallback(\n (next: T) => {\n draftRef.current = next;\n setDraft(next);\n if (timerRef.current) clearTimeout(timerRef.current);\n\n if (equalsRef.current(next, savedRef.current)) {\n // Typed back to what the server already has. Nothing to send, and an error\n // from the previous attempt no longer describes anything.\n setState(inFlightRef.current ? \"pending\" : \"synced\");\n setError(null);\n return;\n }\n\n setState(\"edited\");\n setError(null);\n if (debounceMs > 0) timerRef.current = setTimeout(() => void flush(), debounceMs);\n },\n [debounceMs, flush],\n );\n\n const save = useCallback(() => {\n if (timerRef.current) clearTimeout(timerRef.current);\n void flush();\n }, [flush]);\n\n const retry = save;\n\n const reset = useCallback(() => {\n if (timerRef.current) clearTimeout(timerRef.current);\n draftRef.current = savedRef.current;\n setDraft(savedRef.current);\n setState(inFlightRef.current ? \"pending\" : \"synced\");\n setError(null);\n }, []);\n\n // Anything that is not `synced` has something not yet on the server: `edited` and\n // `error` obviously, and `pending` too — the write is in the air, not landed.\n // Derived from state rather than by comparing against `savedRef` during render,\n // because a ref read here would not re-render when the comparison's answer changes.\n const dirty = state !== \"synced\";\n\n // Adopt a new server value ONLY when there is nothing local to lose.\n useEffect(() => {\n if (inFlightRef.current) return;\n if (!equalsRef.current(draftRef.current, savedRef.current)) return; // dirty: keep it\n if (equalsRef.current(value, savedRef.current)) return; // unchanged\n savedRef.current = value;\n draftRef.current = value;\n setDraft(value);\n setState(\"synced\");\n setError(null);\n }, [value]);\n\n return { value: draft, setValue, state, error, dirty, save, retry, reset };\n}\n\nconst STATE_STYLE: Record<FieldSyncState, { color: string; Icon: typeof Check }> = {\n synced: { color: \"var(--status-synced)\", Icon: Check },\n edited: { color: \"var(--status-edited)\", Icon: Pencil },\n pending: { color: \"var(--status-pending)\", Icon: Loader2 },\n error: { color: \"var(--status-error)\", Icon: AlertCircle },\n};\n\n/** @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport interface FieldSyncIndicatorProps {\n state: FieldSyncState;\n /** The failure. Its message becomes the tooltip when `state === \"error\"`. */\n error?: Error | null;\n /** Override any of the five English defaults. */\n labels?: LabelOverride<FieldSyncLabels>;\n /** Render the label beside the icon instead of only in the tooltip. Always on at\n * Large and Extra large (0.32, docs/text-size-harmonization.md §4 \"No fact only in a\n * tooltip\"): the reader who asked for bigger type gets the words, not a 14 px glyph\n * and a bubble. */\n showLabel?: boolean;\n /** When given, the error state renders a retry button after the icon. */\n onRetry?: () => void;\n className?: string;\n}\n\n/**\n * The affordance for {@link useFieldSync}: a coloured icon, and the reason on hover.\n *\n * **Every state has its own icon, and that is not decoration.** The four colours\n * include the green/red pair, which is the one combination this package's palette\n * otherwise refuses (see the note in `tokens.css`) because red-green colour blindness\n * affects roughly one man in twelve. The icon is what carries the meaning for them,\n * and what carries it into a greyscale print or a screenshot. WCAG 1.4.1 is the same\n * requirement stated formally.\n *\n * The state is also announced: the wrapper is a polite live region, so a screen\n * reader hears \"Saving…\" then \"Saved\" without the user going looking. `error` is\n * assertive instead — a failed write is worth interrupting for.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53.\n */\nexport function FieldSyncIndicator({\n state,\n error,\n labels,\n showLabel = false,\n onRetry,\n className,\n}: FieldSyncIndicatorProps) {\n const l = useKitLabels(\"fieldSync\", DEFAULT_FIELD_SYNC_LABELS, labels);\n const large = useLargeText();\n showLabel = showLabel || large;\n const { color, Icon } = STATE_STYLE[state];\n const text = state === \"error\" ? (error?.message?.trim() ? error.message : l.error) : l[state];\n\n return (\n <span\n // The ICON carries the status colour; the words are `--text-secondary`, because\n // the amber of `edited` is a graphic colour (3:1), not a text colour (4.5:1).\n className={cn(\"inline-flex items-center gap-1.5 text-xs text-secondary\", className)}\n // Polite for the three ordinary states so save chatter does not interrupt\n // typing; assertive for a failure, which the user has to know about now.\n role={state === \"error\" ? \"alert\" : \"status\"}\n aria-live={state === \"error\" ? \"assertive\" : \"polite\"}\n >\n <Tooltip label={showLabel ? undefined : text} redact={state === \"error\"}>\n <span className=\"inline-flex items-center gap-1.5\">\n <Icon\n aria-hidden\n style={{ color }}\n className={cn(\"size-3.5 shrink-0\", state === \"pending\" && \"animate-spin\")}\n />\n {/* Always in the DOM, so the live region has something to announce even\n when the label is visually a tooltip. */}\n <span className={showLabel ? undefined : \"sr-only\"}>{text}</span>\n </span>\n </Tooltip>\n {state === \"error\" && onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n // The kit's focus frame (0.32, §5), stood off the word.\n className={cn(\n \"rounded underline underline-offset-2 hover:no-underline focus-visible:ring-offset-2 focus-visible:ring-offset-surface\",\n FOCUS_RING,\n )}\n style={{ color }}\n >\n {l.retry}\n </button>\n )}\n </span>\n );\n}\n\n/**\n * The field's own frame, coloured by state — applied from the WRAPPER to whatever\n * native control sits inside it, so {@link FieldSyncRow} works with any of the kit's\n * inputs (and a caller's own) without each one growing a `syncState` prop.\n *\n * Border AND a 1px ring, for the reason {@link FIELD_INVALID} gives: a lone 1px\n * border loses a vertical edge to sub-pixel spread at 125% display scaling. The\n * `:focus` twins are what keep the state visible while the reader is typing —\n * the base field class's `focus:` brand colour is `(0,2,0)` and would otherwise win\n * over a descendant selector exactly when the field is in use.\n *\n * `synced` is green, but {@link FieldSyncRow} paints it only for a moment after a\n * save lands — a form of eight permanently green boxes says nothing the absence of\n * colour does not.\n *\n * Every class is a literal so Tailwind's scanner can see it.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53.\n */\nexport const FIELD_SYNC_FRAME: Record<FieldSyncState, string> = {\n synced:\n \"[&_:is(input,select,textarea)]:border-[var(--status-synced)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-synced)] [&_:is(input,select,textarea):focus]:border-[var(--status-synced)] [&_:is(input,select,textarea):focus]:ring-[var(--status-synced)]\",\n edited:\n \"[&_:is(input,select,textarea)]:border-[var(--status-edited)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-edited)] [&_:is(input,select,textarea):focus]:border-[var(--status-edited)] [&_:is(input,select,textarea):focus]:ring-[var(--status-edited)]\",\n pending:\n \"[&_:is(input,select,textarea)]:border-[var(--status-pending)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-pending)] [&_:is(input,select,textarea):focus]:border-[var(--status-pending)] [&_:is(input,select,textarea):focus]:ring-[var(--status-pending)]\",\n error:\n \"[&_:is(input,select,textarea)]:border-[var(--status-error)] [&_:is(input,select,textarea)]:ring-1 [&_:is(input,select,textarea)]:ring-[var(--status-error)] [&_:is(input,select,textarea):focus]:border-[var(--status-error)] [&_:is(input,select,textarea):focus]:ring-[var(--status-error)]\",\n};\n\n/** How long the green \"saved\" frame and check stay after a save lands, in ms.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport const FIELD_SYNC_SAVED_MS = 1500;\n\n/** @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53. */\nexport interface FieldSyncRowProps<T> extends Omit<FieldSyncIndicatorProps, \"state\" | \"error\" | \"showLabel\"> {\n sync: UseFieldSyncReturn<T>;\n children: ReactNode;\n /** Call `sync.save()` when focus leaves the field. Default true — together with the\n * hook's default `debounceMs: 0` this is \"save on blur\": nothing is written while\n * the reader is still typing, however long they pause. */\n saveOnBlur?: boolean;\n /** How long the saved confirmation shows. Default {@link FIELD_SYNC_SAVED_MS}. */\n savedMs?: number;\n}\n\n/**\n * A field wearing its sync state — as its FRAME COLOUR and an ICON AT THE END OF THE\n * FIELD, so the field never changes size (a helper line appearing under it moved\n * everything below it on every save):\n *\n * edited amber frame, pencil — changed, not yet saved\n * pending blue frame, spinning circle — for as long as the save is in flight\n * synced green frame, check — for {@link FIELD_SYNC_SAVED_MS} after a\n * save lands, then an ordinary field again\n * error red frame, alert mark — hover or focus the mark for the reason;\n * click it to retry\n *\n * It saves when focus LEAVES the field (`saveOnBlur`), not after a pause in typing: a\n * database write in the middle of a word is a write nobody asked for. Enter in a\n * single-line field is the caller's to wire to `sync.save`.\n *\n * Colour is never the only signal: every state has its own icon, and the state text is\n * always in the DOM as a live region — polite for the ordinary states, assertive for\n * a failure. The input gets end padding for the icon whether or not one is showing,\n * so the text never reflows when the state changes.\n *\n * @deprecated 0.52 — none: no app uses the FieldSync family (MOD-5); removed in 0.53.\n */\nexport function FieldSyncRow<T>({\n sync,\n children,\n className,\n labels,\n onRetry,\n saveOnBlur = true,\n savedMs = FIELD_SYNC_SAVED_MS,\n}: FieldSyncRowProps<T>) {\n const l = useKitLabels(\"fieldSync\", DEFAULT_FIELD_SYNC_LABELS, labels);\n const { state, error } = sync;\n const isError = state === \"error\";\n const text = isError ? (error?.message?.trim() ? error.message : l.error) : l[state];\n const retry = onRetry ?? sync.retry;\n\n // The saved confirmation: on only for the moment after a save lands. Keyed on the\n // TRANSITION pending → synced, so a field that starts synced shows nothing. The\n // transition is seen while rendering, against the previous state; the effect only\n // owns the timer that turns it off again.\n const [previous, setPrevious] = useState(state);\n const [justSaved, setJustSaved] = useState(false);\n if (state !== previous) {\n setPrevious(state);\n setJustSaved(state === \"synced\" && previous === \"pending\");\n }\n useEffect(() => {\n if (!justSaved) return;\n const t = setTimeout(() => setJustSaved(false), savedMs);\n return () => clearTimeout(t);\n }, [justSaved, savedMs]);\n const shown: FieldSyncState | null = state === \"synced\" ? (justSaved ? \"synced\" : null) : state;\n\n return (\n /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- not an interaction:\n `onBlur` listens for focus leaving the row's own controls (which are the\n interactive elements) to save; the wrapper itself is never focused. */\n <div\n data-field-sync={state}\n className={cn(\n // `relative` for the icon and for the sr-only live region's containing block.\n \"relative [&_:is(input,select,textarea)]:pe-9\",\n shown && FIELD_SYNC_FRAME[shown],\n className,\n )}\n onBlur={\n saveOnBlur\n ? (e) => {\n // Focus moving between two controls INSIDE the row is not leaving it.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) sync.save();\n }\n : undefined\n }\n >\n {children}\n <span className=\"pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3\">\n {state === \"edited\" && <PencilLine aria-hidden className=\"size-4 text-[var(--status-edited)]\" />}\n {state === \"pending\" && (\n <Loader2\n aria-hidden\n className=\"size-4 animate-spin text-[var(--status-pending)] motion-reduce:animate-none\"\n />\n )}\n {state === \"synced\" && (\n <Check\n aria-hidden\n className={cn(\n \"size-4 text-[var(--status-synced)] transition-opacity duration-500 motion-reduce:transition-none\",\n justSaved ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n )}\n {isError && (\n <Tooltip label={text} portal redact>\n <button\n type=\"button\"\n onClick={retry}\n aria-label={l.retry}\n className={cn(\n \"pointer-events-auto flex rounded-full text-[var(--status-error)] focus-visible:ring-offset-2 focus-visible:ring-offset-surface\",\n FOCUS_RING,\n )}\n >\n <AlertCircle aria-hidden className=\"size-4\" />\n </button>\n </Tooltip>\n )}\n </span>\n <span role={isError ? \"alert\" : \"status\"} aria-live={isError ? \"assertive\" : \"polite\"} className=\"sr-only\">\n {shown === null ? \"\" : text}\n </span>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { NavPillItem, NavPillLinkProps } from "./nav-pills.js";
|
|
2
|
-
import type { ReactElement, Ref } from "react";
|
|
3
|
-
/** @deprecated 0.52 — `NavPillsProps` with `layout="scroll"`, what SectionNav wraps; removed in
|
|
4
|
-
* 0.53 (no app renders SectionNav: refactor audit 2026-10-30, MOD-5). */
|
|
5
|
-
export interface SectionNavProps<T extends string> {
|
|
6
|
-
/** The section's pages, each with its `href` (a pill without one is a button that
|
|
7
|
-
* calls `onSelect`). Labels short: the row is one line. */
|
|
8
|
-
items: NavPillItem<T>[];
|
|
9
|
-
/** The page that is open. `null`: none of them. */
|
|
10
|
-
current: T | null;
|
|
11
|
-
/** The section's name — the landmark's accessible name ("Accounting"). */
|
|
12
|
-
label: string;
|
|
13
|
-
/** Called with a pill's value on a click, before the router follows its link. */
|
|
14
|
-
onSelect?: (value: T) => void;
|
|
15
|
-
/** Your router's link. Default: the `<UiKitProvider linkComponent>`, then `<a>`. */
|
|
16
|
-
renderLink?: (props: NavPillLinkProps) => ReactElement;
|
|
17
|
-
/** Default `true`: only below `md`, where the sidebar that lists the same pages is
|
|
18
|
-
* gone. `false` shows it at every width. */
|
|
19
|
-
phoneOnly?: boolean;
|
|
20
|
-
className?: string;
|
|
21
|
-
ref?: Ref<HTMLElement>;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* The pages of a section on a phone, as one row of pills under the page title (0.49
|
|
25
|
-
* KIT-6) — kastlan's section pills, which each section page drew by hand.
|
|
26
|
-
*
|
|
27
|
-
* On a desktop the sidebar lists a group's pages; on a phone the AppShell's bar holds
|
|
28
|
-
* the groups, and a group's pages are a sheet away. This row puts them on the page
|
|
29
|
-
* itself: {@link NavPills} with `layout="scroll"` — one line that scrolls sideways, faded
|
|
30
|
-
* where it is cut, the open page scrolled into view (the row only, never the page). At
|
|
31
|
-
* most 48 px high: 44 px pills, the touch target a finger needs, and 2 px above and
|
|
32
|
-
* below. Each pill is a link (`aria-current="page"` on the open one) and its own tab
|
|
33
|
-
* stop; the row is a `<nav>` named by `label`.
|
|
34
|
-
*
|
|
35
|
-
* Place it right under the page's title (`PageHeader`); the AppShell's own row of a
|
|
36
|
-
* group's pages above the bar (`mobileSubNav`) is the shell-wide alternative — use one,
|
|
37
|
-
* not both, for the same pages.
|
|
38
|
-
*
|
|
39
|
-
* @deprecated 0.52 — `<NavPills layout="scroll" itemClassName="min-h-11 focus-visible:ring-inset"
|
|
40
|
-
* className="md:hidden">`, the same row; removed in 0.53. kastlan, whose section pills it
|
|
41
|
-
* was made for (0.49 KIT-6), never adopted it: no app renders it (refactor audit
|
|
42
|
-
* 2026-10-30, MOD-5).
|
|
43
|
-
*/
|
|
44
|
-
export declare function SectionNav<T extends string>({ items, current, label, onSelect, renderLink, phoneOnly, className, ref, }: SectionNavProps<T>): import("react").JSX.Element;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { cn } from "../lib/cn.js";
|
|
4
|
-
import { NavPills } from "./nav-pills.js";
|
|
5
|
-
function SectionNav({
|
|
6
|
-
items,
|
|
7
|
-
current,
|
|
8
|
-
label,
|
|
9
|
-
onSelect,
|
|
10
|
-
renderLink,
|
|
11
|
-
phoneOnly = true,
|
|
12
|
-
className,
|
|
13
|
-
ref
|
|
14
|
-
}) {
|
|
15
|
-
return /* @__PURE__ */ jsx(
|
|
16
|
-
NavPills,
|
|
17
|
-
{
|
|
18
|
-
ref,
|
|
19
|
-
"aria-label": label,
|
|
20
|
-
items,
|
|
21
|
-
current,
|
|
22
|
-
onSelect,
|
|
23
|
-
renderLink,
|
|
24
|
-
layout: "scroll",
|
|
25
|
-
itemClassName: "min-h-11 focus-visible:ring-inset",
|
|
26
|
-
className: cn("py-0.5 large:py-0", phoneOnly && "md:hidden", className)
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
export {
|
|
31
|
-
SectionNav
|
|
32
|
-
};
|
|
33
|
-
//# sourceMappingURL=section-nav.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/section-nav.tsx"],"sourcesContent":["import { cn } from \"../lib/cn\";\nimport { NavPills } from \"./nav-pills\";\nimport type { NavPillItem, NavPillLinkProps } from \"./nav-pills\";\nimport type { ReactElement, Ref } from \"react\";\n\n/** @deprecated 0.52 — `NavPillsProps` with `layout=\"scroll\"`, what SectionNav wraps; removed in\n * 0.53 (no app renders SectionNav: refactor audit 2026-10-30, MOD-5). */\nexport interface SectionNavProps<T extends string> {\n /** The section's pages, each with its `href` (a pill without one is a button that\n * calls `onSelect`). Labels short: the row is one line. */\n items: NavPillItem<T>[];\n /** The page that is open. `null`: none of them. */\n current: T | null;\n /** The section's name — the landmark's accessible name (\"Accounting\"). */\n label: string;\n /** Called with a pill's value on a click, before the router follows its link. */\n onSelect?: (value: T) => void;\n /** Your router's link. Default: the `<UiKitProvider linkComponent>`, then `<a>`. */\n renderLink?: (props: NavPillLinkProps) => ReactElement;\n /** Default `true`: only below `md`, where the sidebar that lists the same pages is\n * gone. `false` shows it at every width. */\n phoneOnly?: boolean;\n className?: string;\n ref?: Ref<HTMLElement>;\n}\n\n/**\n * The pages of a section on a phone, as one row of pills under the page title (0.49\n * KIT-6) — kastlan's section pills, which each section page drew by hand.\n *\n * On a desktop the sidebar lists a group's pages; on a phone the AppShell's bar holds\n * the groups, and a group's pages are a sheet away. This row puts them on the page\n * itself: {@link NavPills} with `layout=\"scroll\"` — one line that scrolls sideways, faded\n * where it is cut, the open page scrolled into view (the row only, never the page). At\n * most 48 px high: 44 px pills, the touch target a finger needs, and 2 px above and\n * below. Each pill is a link (`aria-current=\"page\"` on the open one) and its own tab\n * stop; the row is a `<nav>` named by `label`.\n *\n * Place it right under the page's title (`PageHeader`); the AppShell's own row of a\n * group's pages above the bar (`mobileSubNav`) is the shell-wide alternative — use one,\n * not both, for the same pages.\n *\n * @deprecated 0.52 — `<NavPills layout=\"scroll\" itemClassName=\"min-h-11 focus-visible:ring-inset\"\n * className=\"md:hidden\">`, the same row; removed in 0.53. kastlan, whose section pills it\n * was made for (0.49 KIT-6), never adopted it: no app renders it (refactor audit\n * 2026-10-30, MOD-5).\n */\nexport function SectionNav<T extends string>({\n items,\n current,\n label,\n onSelect,\n renderLink,\n phoneOnly = true,\n className,\n ref,\n}: SectionNavProps<T>) {\n return (\n <NavPills\n ref={ref}\n aria-label={label}\n items={items}\n current={current}\n onSelect={onSelect}\n renderLink={renderLink}\n layout=\"scroll\"\n // The ring inside the pill: the row scrolls, so it clips what leaves its box.\n itemClassName=\"min-h-11 focus-visible:ring-inset\"\n className={cn(\"py-0.5 large:py-0\", phoneOnly && \"md:hidden\", className)}\n />\n );\n}\n"],"mappings":";AA0DI;AA1DJ,SAAS,UAAU;AACnB,SAAS,gBAAgB;AA8ClB,SAAS,WAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAuB;AACrB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAO;AAAA,MAEP,eAAc;AAAA,MACd,WAAW,GAAG,qBAAqB,aAAa,aAAa,SAAS;AAAA;AAAA,EACxE;AAEJ;","names":[]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
const DEFAULT_SIGN_CHIP_LABELS = {
|
|
3
|
-
outflow: "Outflow",
|
|
4
|
-
inflow: "Inflow",
|
|
5
|
-
direction: (current, next) => `Direction: ${current} \u2014 switch to ${next}`,
|
|
6
|
-
switchTo: (next) => `Switch to ${next}`
|
|
7
|
-
};
|
|
8
|
-
export {
|
|
9
|
-
DEFAULT_SIGN_CHIP_LABELS
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=sign-chip-labels.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/sign-chip-labels.ts"],"sourcesContent":["// The English words of ./sign-chip, in a module with no component code: the English\n// catalogue (src/i18n/defaults.ts) imports them without the component, so `en` loads as one\n// chunk like the other languages (0.34, KS U8). ./sign-chip re-exports them.\n\nimport type { SignChipLabels } from \"./sign-chip\";\n\n/** @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53. */\nexport const DEFAULT_SIGN_CHIP_LABELS: SignChipLabels = {\n outflow: \"Outflow\",\n inflow: \"Inflow\",\n direction: (current, next) => `Direction: ${current} — switch to ${next}`,\n switchTo: (next) => `Switch to ${next}`,\n};\n"],"mappings":";AAOO,MAAM,2BAA2C;AAAA,EACtD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW,CAAC,SAAS,SAAS,cAAc,OAAO,qBAAgB,IAAI;AAAA,EACvE,UAAU,CAAC,SAAS,aAAa,IAAI;AACvC;","names":[]}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { type LabelOverride } from "../i18n/kit-labels.js";
|
|
2
|
-
import { DEFAULT_SIGN_CHIP_LABELS } from "./sign-chip-labels.js";
|
|
3
|
-
/** The words a {@link SignChip} says — the `signChip` namespace of
|
|
4
|
-
* `<UiKitProvider labels>`, and per chip through its `labels` prop.
|
|
5
|
-
*
|
|
6
|
-
* @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53. */
|
|
7
|
-
export interface SignChipLabels {
|
|
8
|
-
/** The chip's text while the figure is negative — money going out. */
|
|
9
|
-
outflow: string;
|
|
10
|
-
/** The chip's text while the figure is positive — money coming in. */
|
|
11
|
-
inflow: string;
|
|
12
|
-
/** The chip's accessible name and its tooltip: the state it is in, and what a press
|
|
13
|
-
* turns it into — `current` and `next` are the two words above. The other option is
|
|
14
|
-
* nowhere on screen to be read, so the name has to say both. */
|
|
15
|
-
direction: (current: string, next: string) => string;
|
|
16
|
-
/** 0.32: what a press does, in words under the chip at Large and Extra large —
|
|
17
|
-
* `next` is the other direction's word ("Switch to Inflow"). The tooltip's half of
|
|
18
|
-
* `direction`, shown, since a fact may not live only in a tooltip there (§4). */
|
|
19
|
-
switchTo: (next: string) => string;
|
|
20
|
-
}
|
|
21
|
-
export { DEFAULT_SIGN_CHIP_LABELS };
|
|
22
|
-
/** @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53. */
|
|
23
|
-
export interface SignChipProps {
|
|
24
|
-
/** The figure is negative (an outflow) — the same flag {@link AmountInput}'s
|
|
25
|
-
* `negative` is, so one state feeds both: `<AmountInput negative={neg}
|
|
26
|
-
* onNegativeChange={setNeg} />` and `<SignChip negative={neg}
|
|
27
|
-
* onNegativeChange={setNeg} />`. */
|
|
28
|
-
negative: boolean;
|
|
29
|
-
/** A press: the other direction. */
|
|
30
|
-
onNegativeChange: (negative: boolean) => void;
|
|
31
|
-
disabled?: boolean;
|
|
32
|
-
/** Per-chip overrides of the `signChip` namespace. */
|
|
33
|
-
labels?: LabelOverride<SignChipLabels>;
|
|
34
|
-
/** On the chip. Place it in its row from here — keksdose's form passes `self-end`
|
|
35
|
-
* to sit it on the display figure's baseline strip. */
|
|
36
|
-
className?: string;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* The SIGN of an amount, as one chip that shows only the state it is in — keksdose
|
|
40
|
-
* K15, its `DirectionToggle` (feedback #417, #430, #431, dev#434) made a kit piece,
|
|
41
|
-
* so the figure and its direction come from one package and wear one palette.
|
|
42
|
-
*
|
|
43
|
-
* WHY A CHIP, NOT A SEGMENTED CONTROL. A two-option toggle spends half its width
|
|
44
|
-
* telling you what the figure is NOT, and at a phone's width it was the widest thing on
|
|
45
|
-
* the form after the figure itself (#417). This sits beside the amount and reads as one
|
|
46
|
-
* phrase with it — "−42,50 Outflow" — and a press flips it. Hiding the alternative costs
|
|
47
|
-
* discoverability, so two things pay it back: the ± glyph and the swap arrows say "this
|
|
48
|
-
* is a control", not a badge; and the name and tooltip spell out both the current state
|
|
49
|
-
* and what a press will do.
|
|
50
|
-
*
|
|
51
|
-
* NOT A PRESSED/UNPRESSED TOGGLE. A {@link Chip} toggle has a FIXED label naming its
|
|
52
|
-
* on-state, with `aria-pressed` carrying the state. Here the word IS the state, so it
|
|
53
|
-
* is an action button with no `aria-pressed`: "Direction: Inflow, not pressed" would
|
|
54
|
-
* name no on-state for "not pressed" to be the absence of. The name changing under
|
|
55
|
-
* focus is the echo.
|
|
56
|
-
*
|
|
57
|
-
* THE COLOUR is the money pairing — the chip's `expense` / `income` tones, the same
|
|
58
|
-
* `--money-expense` / `--money-income` that {@link AmountInput}'s `tone` paints the
|
|
59
|
-
* figure with (dev#434): only the text and the border carry the tint, on a neutral
|
|
60
|
-
* surface, because a filled pill next to a tinted figure reads as one smear.
|
|
61
|
-
*
|
|
62
|
-
* SIZE. `lg` on a phone — the 44px touch target beside a display figure that has no
|
|
63
|
-
* field height to match — and from `md` up exactly a labelled field's 42px, so it never
|
|
64
|
-
* stands taller than the fields in its grid row (#431).
|
|
65
|
-
*
|
|
66
|
-
* An exported chip rather than an `AmountInput signControl` slot: the slot would have
|
|
67
|
-
* to own the row the two share, and every form lays that row out differently (beside
|
|
68
|
-
* the figure on a phone, in a grid column on a desktop). The chip is the part that was
|
|
69
|
-
* being re-written; the row stays the form's.
|
|
70
|
-
*
|
|
71
|
-
* Deprecated in 0.52, removed in 0.53 (decision 4 of the refactor audit 2026-10-30,
|
|
72
|
-
* MOD-5): keksdose, whose chip this was (K15), kept its own `DirectionToggle` on `Chip`
|
|
73
|
-
* and `Tooltip`, and no other app renders one.
|
|
74
|
-
*
|
|
75
|
-
* @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53.
|
|
76
|
-
*/
|
|
77
|
-
export declare function SignChip({ negative, onNegativeChange, disabled, labels: labelsProp, className }: SignChipProps): import("react").JSX.Element;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ArrowUpDown, Minus, Plus } from "lucide-react";
|
|
4
|
-
import { Chip } from "./chip.js";
|
|
5
|
-
import { Tooltip } from "./tooltip.js";
|
|
6
|
-
import { cn } from "../lib/cn.js";
|
|
7
|
-
import { useKitLabels } from "../i18n/kit-labels.js";
|
|
8
|
-
import { useLargeText } from "../hooks/use-large-text.js";
|
|
9
|
-
import { DEFAULT_SIGN_CHIP_LABELS } from "./sign-chip-labels.js";
|
|
10
|
-
function SignChip({ negative, onNegativeChange, disabled, labels: labelsProp, className }) {
|
|
11
|
-
const labels = useKitLabels("signChip", DEFAULT_SIGN_CHIP_LABELS, labelsProp);
|
|
12
|
-
const current = negative ? labels.outflow : labels.inflow;
|
|
13
|
-
const next = negative ? labels.inflow : labels.outflow;
|
|
14
|
-
const name = labels.direction(current, next);
|
|
15
|
-
const large = useLargeText();
|
|
16
|
-
const chip = /* @__PURE__ */ jsxs(
|
|
17
|
-
Chip,
|
|
18
|
-
{
|
|
19
|
-
size: "lg",
|
|
20
|
-
tone: negative ? "expense" : "income",
|
|
21
|
-
icon: negative ? Minus : Plus,
|
|
22
|
-
onClick: () => onNegativeChange(!negative),
|
|
23
|
-
disabled,
|
|
24
|
-
"aria-label": name,
|
|
25
|
-
className: cn("shrink-0 gap-1.5 whitespace-nowrap px-3 font-medium md:min-h-0 md:py-2.5", !large && className),
|
|
26
|
-
children: [
|
|
27
|
-
current,
|
|
28
|
-
/* @__PURE__ */ jsx(ArrowUpDown, { className: "ms-1.5 inline size-3.5 align-[-0.2em] opacity-60", "aria-hidden": true })
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
if (!large) {
|
|
33
|
-
return /* @__PURE__ */ jsx(Tooltip, { label: name, lazy: true, children: chip });
|
|
34
|
-
}
|
|
35
|
-
return (
|
|
36
|
-
// The caller's placement (`self-end`…) moves to the pair, which is now the row's item.
|
|
37
|
-
/* @__PURE__ */ jsxs("span", { "data-slot": "sign-chip", className: cn("inline-flex shrink-0 flex-col items-start gap-0.5", className), children: [
|
|
38
|
-
chip,
|
|
39
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-xs text-muted", children: labels.switchTo(next) })
|
|
40
|
-
] })
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
export {
|
|
44
|
-
DEFAULT_SIGN_CHIP_LABELS,
|
|
45
|
-
SignChip
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=sign-chip.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/sign-chip.tsx"],"sourcesContent":["import { ArrowUpDown, Minus, Plus } from \"lucide-react\";\nimport { Chip } from \"./chip\";\nimport { Tooltip } from \"./tooltip\";\nimport { cn } from \"../lib/cn\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { useLargeText } from \"../hooks/use-large-text\";\nimport { DEFAULT_SIGN_CHIP_LABELS } from \"./sign-chip-labels\";\n\n// ── Labels ────────────────────────────────────────────────────────────────────\n\n/** The words a {@link SignChip} says — the `signChip` namespace of\n * `<UiKitProvider labels>`, and per chip through its `labels` prop.\n *\n * @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53. */\nexport interface SignChipLabels {\n /** The chip's text while the figure is negative — money going out. */\n outflow: string;\n /** The chip's text while the figure is positive — money coming in. */\n inflow: string;\n /** The chip's accessible name and its tooltip: the state it is in, and what a press\n * turns it into — `current` and `next` are the two words above. The other option is\n * nowhere on screen to be read, so the name has to say both. */\n direction: (current: string, next: string) => string;\n /** 0.32: what a press does, in words under the chip at Large and Extra large —\n * `next` is the other direction's word (\"Switch to Inflow\"). The tooltip's half of\n * `direction`, shown, since a fact may not live only in a tooltip there (§4). */\n switchTo: (next: string) => string;\n}\n\n// Defined in ./sign-chip-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_SIGN_CHIP_LABELS };\n\n/** @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53. */\nexport interface SignChipProps {\n /** The figure is negative (an outflow) — the same flag {@link AmountInput}'s\n * `negative` is, so one state feeds both: `<AmountInput negative={neg}\n * onNegativeChange={setNeg} />` and `<SignChip negative={neg}\n * onNegativeChange={setNeg} />`. */\n negative: boolean;\n /** A press: the other direction. */\n onNegativeChange: (negative: boolean) => void;\n disabled?: boolean;\n /** Per-chip overrides of the `signChip` namespace. */\n labels?: LabelOverride<SignChipLabels>;\n /** On the chip. Place it in its row from here — keksdose's form passes `self-end`\n * to sit it on the display figure's baseline strip. */\n className?: string;\n}\n\n/**\n * The SIGN of an amount, as one chip that shows only the state it is in — keksdose\n * K15, its `DirectionToggle` (feedback #417, #430, #431, dev#434) made a kit piece,\n * so the figure and its direction come from one package and wear one palette.\n *\n * WHY A CHIP, NOT A SEGMENTED CONTROL. A two-option toggle spends half its width\n * telling you what the figure is NOT, and at a phone's width it was the widest thing on\n * the form after the figure itself (#417). This sits beside the amount and reads as one\n * phrase with it — \"−42,50 Outflow\" — and a press flips it. Hiding the alternative costs\n * discoverability, so two things pay it back: the ± glyph and the swap arrows say \"this\n * is a control\", not a badge; and the name and tooltip spell out both the current state\n * and what a press will do.\n *\n * NOT A PRESSED/UNPRESSED TOGGLE. A {@link Chip} toggle has a FIXED label naming its\n * on-state, with `aria-pressed` carrying the state. Here the word IS the state, so it\n * is an action button with no `aria-pressed`: \"Direction: Inflow, not pressed\" would\n * name no on-state for \"not pressed\" to be the absence of. The name changing under\n * focus is the echo.\n *\n * THE COLOUR is the money pairing — the chip's `expense` / `income` tones, the same\n * `--money-expense` / `--money-income` that {@link AmountInput}'s `tone` paints the\n * figure with (dev#434): only the text and the border carry the tint, on a neutral\n * surface, because a filled pill next to a tinted figure reads as one smear.\n *\n * SIZE. `lg` on a phone — the 44px touch target beside a display figure that has no\n * field height to match — and from `md` up exactly a labelled field's 42px, so it never\n * stands taller than the fields in its grid row (#431).\n *\n * An exported chip rather than an `AmountInput signControl` slot: the slot would have\n * to own the row the two share, and every form lays that row out differently (beside\n * the figure on a phone, in a grid column on a desktop). The chip is the part that was\n * being re-written; the row stays the form's.\n *\n * Deprecated in 0.52, removed in 0.53 (decision 4 of the refactor audit 2026-10-30,\n * MOD-5): keksdose, whose chip this was (K15), kept its own `DirectionToggle` on `Chip`\n * and `Tooltip`, and no other app renders one.\n *\n * @deprecated 0.52 — a `Chip` of your own, as keksdose's DirectionToggle is (MOD-5); removed in 0.53.\n */\nexport function SignChip({ negative, onNegativeChange, disabled, labels: labelsProp, className }: SignChipProps) {\n const labels = useKitLabels(\"signChip\", DEFAULT_SIGN_CHIP_LABELS, labelsProp);\n const current = negative ? labels.outflow : labels.inflow;\n const next = negative ? labels.inflow : labels.outflow;\n const name = labels.direction(current, next);\n // §4: at Large the chip's word is joined by what a press does, as a line under it —\n // and the bubble that said only that goes.\n const large = useLargeText();\n const chip = (\n <Chip\n size=\"lg\"\n tone={negative ? \"expense\" : \"income\"}\n icon={negative ? Minus : Plus}\n onClick={() => onNegativeChange(!negative)}\n disabled={disabled}\n aria-label={name}\n // `gap-1.5 px-3`: the chip's narrower body, because on a 375px screen every\n // pixel of it is taken from the figure beside it (#430). `md:py-2.5` with the\n // hairline border is the 42px a labelled field measures (#431).\n className={cn(\"shrink-0 gap-1.5 whitespace-nowrap px-3 font-medium md:min-h-0 md:py-2.5\", !large && className)}\n >\n {current}\n {/* The swap arrows after the word, in the label's own row: decoration (the\n name already says what a press does), set a little apart from the word. */}\n <ArrowUpDown className=\"ms-1.5 inline size-3.5 align-[-0.2em] opacity-60\" aria-hidden />\n </Chip>\n );\n if (!large) {\n return (\n <Tooltip label={name} lazy>\n {chip}\n </Tooltip>\n );\n }\n return (\n // The caller's placement (`self-end`…) moves to the pair, which is now the row's item.\n <span data-slot=\"sign-chip\" className={cn(\"inline-flex shrink-0 flex-col items-start gap-0.5\", className)}>\n {chip}\n {/* Hidden from the accessibility tree: the chip's name already says it. */}\n <span aria-hidden className=\"text-xs text-muted\">\n {labels.switchTo(next)}\n </span>\n </span>\n );\n}\n"],"mappings":";AAiGI,SAeE,KAfF;AAjGJ,SAAS,aAAa,OAAO,YAAY;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,UAAU;AACnB,SAAS,oBAAwC;AACjD,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AAkFlC,SAAS,SAAS,EAAE,UAAU,kBAAkB,UAAU,QAAQ,YAAY,UAAU,GAAkB;AAC/G,QAAM,SAAS,aAAa,YAAY,0BAA0B,UAAU;AAC5E,QAAM,UAAU,WAAW,OAAO,UAAU,OAAO;AACnD,QAAM,OAAO,WAAW,OAAO,SAAS,OAAO;AAC/C,QAAM,OAAO,OAAO,UAAU,SAAS,IAAI;AAG3C,QAAM,QAAQ,aAAa;AAC3B,QAAM,OACJ;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAM,WAAW,YAAY;AAAA,MAC7B,MAAM,WAAW,QAAQ;AAAA,MACzB,SAAS,MAAM,iBAAiB,CAAC,QAAQ;AAAA,MACzC;AAAA,MACA,cAAY;AAAA,MAIZ,WAAW,GAAG,4EAA4E,CAAC,SAAS,SAAS;AAAA,MAE5G;AAAA;AAAA,QAGD,oBAAC,eAAY,WAAU,oDAAmD,eAAW,MAAC;AAAA;AAAA;AAAA,EACxF;AAEF,MAAI,CAAC,OAAO;AACV,WACE,oBAAC,WAAQ,OAAO,MAAM,MAAI,MACvB,gBACH;AAAA,EAEJ;AACA;AAAA;AAAA,IAEE,qBAAC,UAAK,aAAU,aAAY,WAAW,GAAG,qDAAqD,SAAS,GACrG;AAAA;AAAA,MAED,oBAAC,UAAK,eAAW,MAAC,WAAU,sBACzB,iBAAO,SAAS,IAAI,GACvB;AAAA,OACF;AAAA;AAEJ;","names":[]}
|
package/dist/lib/isin.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ISIN helpers — ISO 6166 — for keksdose, whose securities holdings are keyed by ISIN
|
|
3
|
-
* and whose import and edit forms checked only the shape (two letters, ten
|
|
4
|
-
* characters): a mistyped ISIN saved happily and then matched no price feed.
|
|
5
|
-
*
|
|
6
|
-
* An ISIN is twelve characters: a two-letter prefix (the issuing country, or a
|
|
7
|
-
* supranational code such as `XS`), nine letters or digits (the national number) and
|
|
8
|
-
* one check digit. The check digit is the Luhn digit of the first eleven characters
|
|
9
|
-
* with every letter expanded to two digits (A = 10 … Z = 35) — which is all that is
|
|
10
|
-
* checked here. Whether the prefix is an assigned code, and whether the security
|
|
11
|
-
* exists, is a lookup, not a checksum.
|
|
12
|
-
*
|
|
13
|
-
* Deprecated in 0.52, removed in 0.53 (decision 4 of the refactor audit 2026-10-30,
|
|
14
|
-
* MOD-5): keksdose, which they were written for, never imported them, and no other app
|
|
15
|
-
* does.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* The ISIN as it is printed: upper case, unbroken (ISO 6166 has no grouping), with
|
|
19
|
-
* the spaces and hyphens a paste from a broker's statement carries dropped.
|
|
20
|
-
*
|
|
21
|
-
* @deprecated 0.52 — none: no app checks an ISIN with it; copy lib/isin.ts (MOD-5); removed in 0.53.
|
|
22
|
-
*/
|
|
23
|
-
export declare function formatIsin(value: string): string;
|
|
24
|
-
/**
|
|
25
|
-
* The check digit for the first eleven characters of an ISIN (prefix and national
|
|
26
|
-
* number). For BUILDING one — a test fixture, a demo — rather than checking it.
|
|
27
|
-
*
|
|
28
|
-
* @deprecated 0.52 — none: no app checks an ISIN with it; copy lib/isin.ts (MOD-5); removed in 0.53.
|
|
29
|
-
*/
|
|
30
|
-
export declare function isinCheckDigit(body: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Whether `value` is a valid ISIN: two letters, nine letters or digits, one digit,
|
|
33
|
-
* and a matching Luhn check digit over the letter-expanded form. Spaces, hyphens and
|
|
34
|
-
* lower case are fine (see {@link formatIsin}).
|
|
35
|
-
*
|
|
36
|
-
* @deprecated 0.52 — none: no app checks an ISIN with it; copy lib/isin.ts (MOD-5); removed in 0.53.
|
|
37
|
-
*/
|
|
38
|
-
export declare function isValidIsin(value: string): boolean;
|