@eifi1/ui-kit 0.48.2 → 0.49.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 +26 -12
- package/dist/admin/admin-action-confirm.js +1 -1
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.js +5 -1
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/user-roster.d.ts +11 -5
- package/dist/admin/user-roster.js +15 -4
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/accept-invitation.js +2 -2
- package/dist/auth/accept-invitation.js.map +1 -1
- package/dist/auth/not-found-page.js +1 -1
- package/dist/auth/not-found-page.js.map +1 -1
- package/dist/auth/reset-password-form.js +2 -2
- package/dist/auth/reset-password-form.js.map +1 -1
- package/dist/auth/verify-email.js +2 -2
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/billing/plan-card.js +2 -2
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/components/breadcrumbs.d.ts +19 -1
- package/dist/components/breadcrumbs.js +11 -4
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/button-look.d.ts +37 -7
- package/dist/components/button-look.js +33 -6
- package/dist/components/button-look.js.map +1 -1
- package/dist/components/button.d.ts +4 -1
- package/dist/components/button.js +2 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -1
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart-edge.d.ts +10 -0
- package/dist/components/chart-edge.js +57 -0
- package/dist/components/chart-edge.js.map +1 -0
- package/dist/components/chart.js +2 -1
- package/dist/components/chart.js.map +1 -1
- package/dist/components/chip.d.ts +8 -1
- package/dist/components/chip.js +4 -1
- package/dist/components/chip.js.map +1 -1
- package/dist/components/confirm-context.d.ts +5 -0
- package/dist/components/confirm-context.js +7 -0
- package/dist/components/confirm-context.js.map +1 -0
- package/dist/components/confirm-dialog.js +3 -3
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +2 -2
- package/dist/components/danger-confirm.js +1 -1
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-fit.js +21 -3
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-mobile.d.ts +48 -2
- package/dist/components/data-table-mobile.js +87 -10
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +2 -1
- package/dist/components/data-table-row-actions.js +7 -0
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table-row.d.ts +1 -1
- package/dist/components/data-table-row.js.map +1 -1
- package/dist/components/data-table.d.ts +34 -1
- package/dist/components/data-table.js +34 -9
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/error-boundary.js +2 -2
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/floating-panel.d.ts +36 -13
- package/dist/components/floating-panel.js +27 -7
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +1 -1
- package/dist/components/form-actions.js +1 -1
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/icon-button.d.ts +11 -0
- package/dist/components/icon-button.js +7 -5
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/nav-pills.d.ts +8 -1
- package/dist/components/nav-pills.js +61 -38
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/page-header-context.d.ts +8 -0
- package/dist/components/page-header-context.js +7 -0
- package/dist/components/page-header-context.js.map +1 -0
- package/dist/components/page-header.d.ts +14 -2
- package/dist/components/page-header.js +10 -1
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pie-chart.js +2 -1
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/progress-bar.js +5 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/reauth-dialog.js +1 -1
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/row-actions-labels.js +3 -1
- package/dist/components/row-actions-labels.js.map +1 -1
- package/dist/components/row-actions.d.ts +43 -9
- package/dist/components/row-actions.js +78 -15
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/section-nav.d.ts +37 -0
- package/dist/components/section-nav.js +33 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/series-chart.js +8 -3
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/share-card.js +1 -1
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/signature-pad.js +1 -1
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/sparkline.js +2 -1
- package/dist/components/sparkline.js.map +1 -1
- package/dist/components/stat-tile.js +14 -7
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +5 -1
- package/dist/components/status-dot.js +4 -1
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/translation-review-editor.js +2 -2
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review.js +2 -2
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/treemap.d.ts +5 -2
- package/dist/components/treemap.js +26 -6
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/use-table-state.js +37 -6
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/demo/demo-banner.js +1 -1
- package/dist/demo/demo-banner.js.map +1 -1
- package/dist/demo/demo-ended.js +1 -1
- package/dist/demo/demo-ended.js.map +1 -1
- package/dist/demo/demo-start.js +1 -1
- package/dist/demo/demo-start.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-inbox.js +1 -1
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/hooks/use-dismiss.d.ts +9 -1
- package/dist/hooks/use-dismiss.js +25 -5
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-document-title.d.ts +14 -0
- package/dist/hooks/use-document-title.js +22 -0
- package/dist/hooks/use-document-title.js.map +1 -0
- package/dist/i18n/german.js +6 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +31 -1
- package/dist/i18n/kit-labels.js +43 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +6 -2
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/es.js +6 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +6 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +6 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +6 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +6 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +2 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +1 -0
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.js +2 -2
- package/dist/landing/landing-actions.js.map +1 -1
- package/dist/landing/public-header.js +2 -2
- package/dist/landing/public-header.js.map +1 -1
- package/dist/lib/clipboard-minus.d.ts +27 -0
- package/dist/lib/clipboard-minus.js +63 -0
- package/dist/lib/clipboard-minus.js.map +1 -0
- package/dist/lib/document-title.d.ts +17 -0
- package/dist/lib/document-title.js +29 -0
- package/dist/lib/document-title.js.map +1 -0
- package/dist/lib/float-clearance.js +8 -2
- package/dist/lib/float-clearance.js.map +1 -1
- package/dist/lib/format.d.ts +35 -10
- package/dist/lib/format.js +55 -11
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/minus.d.ts +22 -0
- package/dist/lib/minus.js +14 -0
- package/dist/lib/minus.js.map +1 -0
- package/dist/lib/status-tone.d.ts +30 -0
- package/dist/lib/status-tone.js +17 -0
- package/dist/lib/status-tone.js.map +1 -0
- package/dist/settings/settings-layout.js +3 -2
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +1 -1
- package/dist/shell/account-menu-language.js +9 -1
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/app-shell-labels.js +2 -1
- package/dist/shell/app-shell-labels.js.map +1 -1
- package/dist/shell/app-shell.d.ts +58 -16
- package/dist/shell/app-shell.js +151 -43
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/language-code.d.ts +3 -0
- package/dist/shell/language-code.js +8 -0
- package/dist/shell/language-code.js.map +1 -0
- package/dist/shell/topbar-controls.d.ts +14 -6
- package/dist/shell/topbar-controls.js +14 -17
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +1 -1
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +82 -15
- package/dist/theme/chart-palette.js +51 -24
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/palette-presets.d.ts +9 -0
- package/dist/theme/palette-presets.js +77 -62
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/tour/tour.js +1 -1
- package/dist/tour/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +1 -1
- package/dist/wizard/stepper-nav.js +3 -3
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/admin-action-confirm.tsx +1 -1
- package/src/admin/invitations-panel.tsx +7 -3
- package/src/admin/user-roster.tsx +23 -6
- package/src/auth/accept-invitation.tsx +3 -3
- package/src/auth/not-found-page.tsx +1 -1
- package/src/auth/reset-password-form.tsx +3 -3
- package/src/auth/verify-email.tsx +3 -3
- package/src/billing/plan-card.tsx +2 -2
- package/src/components/breadcrumbs.tsx +32 -4
- package/src/components/button-look.ts +86 -15
- package/src/components/button.tsx +6 -3
- package/src/components/calendar-heatmap.tsx +2 -1
- package/src/components/chart-edge.ts +73 -0
- package/src/components/chart.tsx +3 -1
- package/src/components/chip.tsx +10 -1
- package/src/components/confirm-context.ts +7 -0
- package/src/components/confirm-dialog.tsx +3 -3
- package/src/components/danger-confirm.tsx +3 -3
- package/src/components/data-table-fit.ts +41 -6
- package/src/components/data-table-mobile.tsx +162 -11
- package/src/components/data-table-row-actions.tsx +9 -1
- package/src/components/data-table-row.tsx +2 -0
- package/src/components/data-table.tsx +82 -18
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/floating-panel.tsx +67 -16
- package/src/components/form-actions.tsx +2 -2
- package/src/components/icon-button.tsx +21 -8
- package/src/components/nav-pills.tsx +29 -2
- package/src/components/page-header-context.ts +8 -0
- package/src/components/page-header.tsx +34 -2
- package/src/components/pie-chart.tsx +2 -1
- package/src/components/progress-bar.tsx +6 -3
- package/src/components/reauth-dialog.tsx +1 -1
- package/src/components/row-actions-labels.ts +2 -0
- package/src/components/row-actions.tsx +154 -33
- package/src/components/section-nav.tsx +65 -0
- package/src/components/series-chart.tsx +12 -3
- package/src/components/share-card.tsx +1 -1
- package/src/components/signature-pad.tsx +1 -1
- package/src/components/sparkline.tsx +2 -1
- package/src/components/stat-tile.tsx +19 -7
- package/src/components/status-dot.tsx +7 -1
- package/src/components/tooltip.tsx +3 -1
- package/src/components/translation-review-editor.tsx +2 -2
- package/src/components/translation-review.tsx +2 -2
- package/src/components/treemap.tsx +34 -8
- package/src/components/use-table-state.ts +63 -8
- package/src/data-table.ts +3 -3
- package/src/demo/demo-banner.tsx +1 -1
- package/src/demo/demo-ended.tsx +1 -1
- package/src/demo/demo-start.tsx +1 -1
- package/src/feedback/feedback-inbox.tsx +1 -1
- package/src/hooks/use-dismiss.ts +43 -4
- package/src/hooks/use-document-title.ts +31 -0
- package/src/i18n/german.ts +4 -0
- package/src/i18n/kit-labels.tsx +76 -1
- package/src/i18n/locales/es.ts +4 -0
- package/src/i18n/locales/fr.ts +4 -0
- package/src/i18n/locales/hu.ts +4 -0
- package/src/i18n/locales/it.ts +4 -0
- package/src/i18n/locales/zh.ts +4 -0
- package/src/i18n/review.ts +2 -0
- package/src/i18n/shared-labels.ts +1 -0
- package/src/index.ts +13 -0
- package/src/landing/landing-actions.tsx +2 -2
- package/src/landing/public-header.tsx +2 -2
- package/src/lib/clipboard-minus.ts +94 -0
- package/src/lib/document-title.ts +41 -0
- package/src/lib/float-clearance.ts +19 -3
- package/src/lib/format.ts +115 -30
- package/src/lib/minus.ts +40 -0
- package/src/lib/status-tone.ts +43 -0
- package/src/settings/settings-layout.tsx +3 -2
- package/src/shell/account-menu-language.tsx +14 -2
- package/src/shell/app-shell-labels.ts +1 -0
- package/src/shell/app-shell.tsx +291 -68
- package/src/shell/language-code.ts +5 -0
- package/src/shell/topbar-controls.tsx +35 -15
- package/src/testing/css-variables.generated.ts +6 -6
- package/src/theme/chart-palette.ts +102 -20
- package/src/theme/palette-presets.ts +121 -84
- package/src/tour/tour.tsx +1 -1
- package/src/wizard/stepper-nav.tsx +4 -4
- package/tokens.css +28 -18
|
@@ -12,12 +12,14 @@ import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
|
12
12
|
import type { ComponentProps, ComponentPropsWithoutRef, KeyboardEvent, ReactNode } from "react";
|
|
13
13
|
import { Treemap as RechartsTreemap } from "recharts";
|
|
14
14
|
import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "./chart";
|
|
15
|
-
import { PALETTE_HEX, textOn } from "../theme/chart-palette";
|
|
15
|
+
import { PALETTE_HEX, PALETTE_SIZE, textOn } from "../theme/chart-palette";
|
|
16
|
+
import { resolveChartColor, useChartEdge } from "./chart-edge";
|
|
16
17
|
import { parseHex } from "../theme/color";
|
|
17
18
|
import { dirOf, type Direction } from "../lib/direction";
|
|
18
19
|
import { cn } from "../lib/cn";
|
|
19
20
|
import { useTextSize } from "../theme/text-size";
|
|
20
21
|
import { chartHeightProps, type ChartHeight } from "./chart-height";
|
|
22
|
+
import { formatWithMinus } from "../lib/minus";
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* One tile.
|
|
@@ -39,7 +41,9 @@ export interface TreemapNode {
|
|
|
39
41
|
value: number;
|
|
40
42
|
/** Overrides the by-index palette colour for this tile — e.g. to paint every child
|
|
41
43
|
* in its parent group's colour, so the groups stay legible in one picture. A
|
|
42
|
-
* concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured
|
|
44
|
+
* concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured — so
|
|
45
|
+
* does `CHART_ROLES.other` (`var(--chart-muted)`), the colour for a "rest" bucket,
|
|
46
|
+
* which should not wrap round to a hue the reader matches to a real node;
|
|
43
47
|
* anything else (another var, a `color-mix()`) gets `currentColor` ink, because
|
|
44
48
|
* there is nothing to measure — set `labelColor` then. */
|
|
45
49
|
fill?: string;
|
|
@@ -101,7 +105,8 @@ export interface TreemapProps extends Omit<ComponentPropsWithoutRef<"div">, "chi
|
|
|
101
105
|
maxTiles?: number;
|
|
102
106
|
/**
|
|
103
107
|
* The categorical ramp tiles take by index, as concrete hex. Default: the page's
|
|
104
|
-
* live `--chart-1…
|
|
108
|
+
* live `--chart-1…8` tokens (see `useChartTokenHex`), cycled after eight like
|
|
109
|
+
* `paletteFor` (0.49). Pass one when the host
|
|
105
110
|
* already holds the active palette — e.g. `createPaletteStore`'s `useChartHex()` —
|
|
106
111
|
* so a caller colouring a legend beside the map is guaranteed the same list.
|
|
107
112
|
*/
|
|
@@ -119,7 +124,7 @@ export interface TreemapProps extends Omit<ComponentPropsWithoutRef<"div">, "chi
|
|
|
119
124
|
// re-read whenever <html>'s class or style changes — which is exactly when a theme
|
|
120
125
|
// toggle or a palette switch lands.
|
|
121
126
|
|
|
122
|
-
const CHART_TOKENS =
|
|
127
|
+
const CHART_TOKENS = PALETTE_SIZE;
|
|
123
128
|
|
|
124
129
|
/** The resolved ramp, joined into ONE string so `useSyncExternalStore` compares a
|
|
125
130
|
* primitive: a fresh array per read would never be `Object.is`-equal to the last and
|
|
@@ -163,9 +168,12 @@ const CHART_TOKEN_REF = /^var\(\s*--chart-(\d+)\s*\)$/;
|
|
|
163
168
|
function measurableFill(fill: string, tokens: readonly string[]): string | null {
|
|
164
169
|
if (parseHex(fill)) return fill;
|
|
165
170
|
const m = CHART_TOKEN_REF.exec(fill.trim());
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
const hex = m ? tokens[Number(m[1]) - 1] : undefined;
|
|
172
|
+
if (hex && parseHex(hex)) return hex;
|
|
173
|
+
// Any other bare token the page declares — `var(--chart-muted)` for a rest bucket,
|
|
174
|
+
// `var(--chart-9)`, a money colour — read off <html> as it resolves.
|
|
175
|
+
if (typeof document === "undefined") return null;
|
|
176
|
+
return resolveChartColor(fill, getComputedStyle(document.documentElement));
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
// ── Tile ──────────────────────────────────────────────────────────────────
|
|
@@ -241,6 +249,7 @@ export function TreemapCell({
|
|
|
241
249
|
// Before the early return: a hook has to run on every render of the cell.
|
|
242
250
|
const tokenHex = useChartTokenHex();
|
|
243
251
|
const { scale } = useTextSize();
|
|
252
|
+
const edgeFor = useChartEdge();
|
|
244
253
|
// Depth 0 is recharts' synthetic root, which spans the whole chart.
|
|
245
254
|
if (!depth) return null;
|
|
246
255
|
// Whole pixels: a rect on a half pixel renders its edge across two half-lit
|
|
@@ -252,6 +261,9 @@ export function TreemapCell({
|
|
|
252
261
|
const tileFill = fill ?? "var(--chart-1)";
|
|
253
262
|
const measured = measurableFill(tileFill, tokenHex);
|
|
254
263
|
const ink = labelColor ?? (measured ? textOn(measured) : "currentColor");
|
|
264
|
+
// A tile in a hue under 3:1 against the card gets a 1px ink edge inside the gutter
|
|
265
|
+
// (WCAG 1.4.11, anyplot's rule); a hue that clears it stays bare.
|
|
266
|
+
const edge = measured ? edgeFor(measured) : undefined;
|
|
255
267
|
// The halo only on an OPAQUE fill we could measure. A translucent fill (a
|
|
256
268
|
// `color-mix(… transparent)`) composited twice — once as the tile, once as the halo
|
|
257
269
|
// over it — draws a darker ring round every glyph, which read as doubled text on the
|
|
@@ -310,6 +322,20 @@ export function TreemapCell({
|
|
|
310
322
|
strokeWidth={2}
|
|
311
323
|
rx={3}
|
|
312
324
|
/>
|
|
325
|
+
{edge && pw > 3 && ph > 3 && (
|
|
326
|
+
<rect
|
|
327
|
+
data-edge=""
|
|
328
|
+
x={px + 1.5}
|
|
329
|
+
y={py + 1.5}
|
|
330
|
+
width={pw - 3}
|
|
331
|
+
height={ph - 3}
|
|
332
|
+
fill="none"
|
|
333
|
+
stroke={edge}
|
|
334
|
+
strokeWidth={1}
|
|
335
|
+
rx={2}
|
|
336
|
+
style={{ pointerEvents: "none" }}
|
|
337
|
+
/>
|
|
338
|
+
)}
|
|
313
339
|
{label != null && (
|
|
314
340
|
<text
|
|
315
341
|
// A plain attribute, so the host's `[data-private]` rule reaches an SVG
|
|
@@ -437,7 +463,7 @@ export function Treemap({
|
|
|
437
463
|
const total = nodes.reduce((acc, n) => acc + n.size, 0);
|
|
438
464
|
const format = (v: number): ReactNode => {
|
|
439
465
|
const share = total > 0 ? v / total : undefined;
|
|
440
|
-
return valueFormatter ? valueFormatter(v, share) : DEFAULT_NUMBER
|
|
466
|
+
return valueFormatter ? valueFormatter(v, share) : formatWithMinus(DEFAULT_NUMBER, v);
|
|
441
467
|
};
|
|
442
468
|
const Content = redactNames ? RedactedTooltipContent : ChartTooltipContent;
|
|
443
469
|
return (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import type { SetStateAction } from "react";
|
|
3
|
+
import * as ReactRouter from "react-router";
|
|
3
4
|
import { useLocation, useSearchParams } from "react-router";
|
|
4
5
|
import { useKitTableStorageKeyPrefix } from "../i18n/kit-labels";
|
|
5
6
|
import { readStored, writeStored } from "../lib/safe-storage";
|
|
@@ -103,6 +104,62 @@ function urlKeys(prefix = ""): UrlKeys {
|
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/** What a table reads and writes the URL through: react-router's, or none. */
|
|
108
|
+
interface TableSearch {
|
|
109
|
+
searchParams: URLSearchParams;
|
|
110
|
+
setSearchParams: ReturnType<typeof useSearchParams>[1];
|
|
111
|
+
locationState: unknown;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function useRouterSearch(): TableSearch {
|
|
115
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
116
|
+
const location = useLocation();
|
|
117
|
+
return { searchParams, setSearchParams, locationState: location.state };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const NO_ROUTER_SEARCH: TableSearch = {
|
|
121
|
+
searchParams: new URLSearchParams(),
|
|
122
|
+
setSearchParams: () => {},
|
|
123
|
+
locationState: null,
|
|
124
|
+
};
|
|
125
|
+
function useNoRouterSearch(): TableSearch {
|
|
126
|
+
return NO_ROUTER_SEARCH;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `useInRouterContext`, read off the namespace inside a `try` like
|
|
131
|
+
* use-search-param-state's probe: a test's `vi.mock("react-router", () => ({ … }))`
|
|
132
|
+
* without it throws on the read. Missing, a table assumes a Router, as before 0.49.
|
|
133
|
+
*/
|
|
134
|
+
function readInRouterContext(): () => boolean {
|
|
135
|
+
try {
|
|
136
|
+
return (ReactRouter as unknown as { useInRouterContext?: () => boolean }).useInRouterContext ?? (() => true);
|
|
137
|
+
} catch {
|
|
138
|
+
return () => true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const useInRouterContext = readInRouterContext();
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The URL access a table needs (0.49, KIT-3). Without `urlSync` a table needs no Router:
|
|
145
|
+
* before 0.49 it called `useSearchParams()` unconditionally and threw outside one even
|
|
146
|
+
* with URL sync off. With `urlSync` and no Router, a development build throws a message
|
|
147
|
+
* that names the fix; a production build keeps the view in local state rather than
|
|
148
|
+
* crash the page. Whether a Router is around does not change during a table's life, so
|
|
149
|
+
* which hook runs is stable from render to render.
|
|
150
|
+
*/
|
|
151
|
+
function useTableSearch(urlSync: boolean, owner: string): TableSearch {
|
|
152
|
+
const inRouter = useInRouterContext();
|
|
153
|
+
if (!inRouter && urlSync && import.meta.env?.DEV) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`${owner} urlSync requires a Router: render it inside a react-router Router ` +
|
|
156
|
+
"(BrowserRouter, a data router or a MemoryRouter), or leave urlSync off.",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const useSearch = inRouter ? useRouterSearch : useNoRouterSearch;
|
|
160
|
+
return useSearch();
|
|
161
|
+
}
|
|
162
|
+
|
|
106
163
|
/** `urlSync` split into "is it on" and "under which prefix". */
|
|
107
164
|
function resolveTableUrlSync(urlSync: TableUrlSync | undefined): { enabled: boolean; prefix: string } {
|
|
108
165
|
if (!urlSync) return { enabled: false, prefix: "" };
|
|
@@ -295,10 +352,9 @@ export function useTableState<T>({
|
|
|
295
352
|
}) {
|
|
296
353
|
const { enabled: urlSync, prefix: urlPrefix } = resolveTableUrlSync(urlSyncOption);
|
|
297
354
|
const keys = useMemo(() => urlKeys(urlPrefix), [urlPrefix]);
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
const
|
|
301
|
-
const location = useLocation();
|
|
355
|
+
// The SAME pair is used to read and to write; see `readTableUrlState`. No Router is
|
|
356
|
+
// needed without `urlSync` (0.49, KIT-3; see `useTableSearch`).
|
|
357
|
+
const { searchParams, setSearchParams, locationState } = useTableSearch(urlSync, "DataTable");
|
|
302
358
|
// Read URL once on mount so URL-encoded views (e.g. shared links, deep links from
|
|
303
359
|
// other pages) populate initial state. After mount, state is local.
|
|
304
360
|
const [urlInitial] = useState<TableUrlState>(() =>
|
|
@@ -436,7 +492,7 @@ export function useTableState<T>({
|
|
|
436
492
|
// `columns` is deliberately not a dependency and does not need to be: the
|
|
437
493
|
// signature above is computed from it, so a column set that would produce a
|
|
438
494
|
// different address produces a different signature first.
|
|
439
|
-
writeUrlState(setSearchParams, columns, sorts, filters, pageSize, page, defaultPageSize, undefined,
|
|
495
|
+
writeUrlState(setSearchParams, columns, sorts, filters, pageSize, page, defaultPageSize, undefined, locationState, keys);
|
|
440
496
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
441
497
|
}, [urlSync, urlSig]);
|
|
442
498
|
|
|
@@ -620,8 +676,7 @@ export function useTableUrlState<T>({
|
|
|
620
676
|
const urlSync = resolved.enabled;
|
|
621
677
|
const urlPrefix = urlPrefixOption ?? resolved.prefix;
|
|
622
678
|
const keys = useMemo(() => urlKeys(urlPrefix), [urlPrefix]);
|
|
623
|
-
const
|
|
624
|
-
const location = useLocation();
|
|
679
|
+
const { searchParams, setSearchParams, locationState } = useTableSearch(urlSync, "useTableUrlState");
|
|
625
680
|
const [state, setState] = useState<OwnedState>(() => {
|
|
626
681
|
const url = urlSync ? readTableUrlState(searchParams, columns, urlPrefix) : {};
|
|
627
682
|
return {
|
|
@@ -653,7 +708,7 @@ export function useTableUrlState<T>({
|
|
|
653
708
|
state.page,
|
|
654
709
|
defaultPageSize,
|
|
655
710
|
manageSearch ? state.search : undefined,
|
|
656
|
-
|
|
711
|
+
locationState,
|
|
657
712
|
keys,
|
|
658
713
|
);
|
|
659
714
|
// Keyed on the signature, which is computed from everything the write reads — see
|
package/src/data-table.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// `@eifi1/ui-kit/data-table` — the table suite.
|
|
2
2
|
//
|
|
3
|
-
// A re-slicing of the main barrel, not a new API. This entry point
|
|
4
|
-
// `react-router
|
|
5
|
-
//
|
|
3
|
+
// A re-slicing of the main barrel, not a new API. This entry point imports
|
|
4
|
+
// `react-router` (a peer dependency), but since 0.49 (KIT-3) a Router is needed only for
|
|
5
|
+
// a table with `urlSync`: without it DataTable keeps its view in local state alone.
|
|
6
6
|
export * from "./components/data-table-labels";
|
|
7
7
|
export * from "./components/data-table-sort";
|
|
8
8
|
export * from "./components/data-table-filters";
|
package/src/demo/demo-banner.tsx
CHANGED
|
@@ -116,7 +116,7 @@ export function DemoBanner({
|
|
|
116
116
|
<>
|
|
117
117
|
{!collapsed && (
|
|
118
118
|
<>
|
|
119
|
-
<Button href={accessLink.href} variant="
|
|
119
|
+
<Button href={accessLink.href} variant="accent" size="sm">
|
|
120
120
|
{accessLink.label}
|
|
121
121
|
</Button>
|
|
122
122
|
<Button href={signInHref} variant="secondary" size="sm">
|
package/src/demo/demo-ended.tsx
CHANGED
|
@@ -75,7 +75,7 @@ export function DemoEnded({
|
|
|
75
75
|
</p>
|
|
76
76
|
{children}
|
|
77
77
|
<div className="mt-4 flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap sm:justify-center">
|
|
78
|
-
<Button href={restartHref} variant="
|
|
78
|
+
<Button href={restartHref} variant="accent">
|
|
79
79
|
{labels.restart}
|
|
80
80
|
</Button>
|
|
81
81
|
<Button href={accessLink.href} variant="secondary">
|
package/src/demo/demo-start.tsx
CHANGED
|
@@ -139,7 +139,7 @@ export function DemoStart<T>({
|
|
|
139
139
|
</div>
|
|
140
140
|
{refusal !== null && (
|
|
141
141
|
<div className="flex flex-wrap justify-center gap-3">
|
|
142
|
-
<Button href={accessLink.href} variant="
|
|
142
|
+
<Button href={accessLink.href} variant="accent">
|
|
143
143
|
{accessLink.label}
|
|
144
144
|
</Button>
|
|
145
145
|
<Button href={backHref} variant="secondary">
|
|
@@ -469,7 +469,7 @@ export function FeedbackNoteEditor({
|
|
|
469
469
|
<Button variant="ghost" onClick={onCancel}>
|
|
470
470
|
{cancelLabel}
|
|
471
471
|
</Button>
|
|
472
|
-
<Button variant="
|
|
472
|
+
<Button variant="accent" disabled={pending || blank} commit={commit} onClick={submit}>
|
|
473
473
|
{saveLabel}
|
|
474
474
|
</Button>
|
|
475
475
|
</div>
|
package/src/hooks/use-dismiss.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
|
|
2
2
|
import type { KeyboardEvent as ReactKeyboardEvent, RefObject } from "react";
|
|
3
3
|
import { isComposing } from "../lib/ime";
|
|
4
4
|
|
|
@@ -60,6 +60,9 @@ import { isComposing } from "../lib/ime";
|
|
|
60
60
|
/** A layer's handler, read through a ref so the stack holds one stable entry per open. */
|
|
61
61
|
interface Layer {
|
|
62
62
|
escape: (e: KeyboardEvent) => void;
|
|
63
|
+
/** Whether it covers the page — a dialog, a sheet, a popover, a list — rather than
|
|
64
|
+
* annotating it (a tooltip). Counted by {@link useOverlayOpen}. */
|
|
65
|
+
overlay: boolean;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
/** What {@link useDismissLayer} hands back: its layer while open, for
|
|
@@ -114,11 +117,13 @@ function push(layer: Layer): void {
|
|
|
114
117
|
document.addEventListener("keydown", onBubble);
|
|
115
118
|
}
|
|
116
119
|
layers.push(layer);
|
|
120
|
+
if (layer.overlay) notifyOverlays();
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
function pop(layer: Layer): void {
|
|
120
124
|
const at = layers.indexOf(layer);
|
|
121
125
|
if (at >= 0) layers.splice(at, 1);
|
|
126
|
+
if (at >= 0 && layer.overlay) notifyOverlays();
|
|
122
127
|
if (layers.length > 0) return;
|
|
123
128
|
document.removeEventListener("keydown", onCapture, true);
|
|
124
129
|
document.removeEventListener("keydown", onBubble);
|
|
@@ -135,7 +140,11 @@ function pop(layer: Layer): void {
|
|
|
135
140
|
* read live, so it need not be memoised; the layer is pushed and popped only when
|
|
136
141
|
* `open` flips. The layer it returns is for the owner's {@link runOwnKeyDown}.
|
|
137
142
|
*/
|
|
138
|
-
export function useDismissLayer(
|
|
143
|
+
export function useDismissLayer(
|
|
144
|
+
open: boolean,
|
|
145
|
+
onEscape: (e: KeyboardEvent) => void,
|
|
146
|
+
{ overlay = true }: { overlay?: boolean } = {},
|
|
147
|
+
): DismissLayer {
|
|
139
148
|
const handlerRef = useRef(onEscape);
|
|
140
149
|
const layerRef = useRef<Layer | null>(null);
|
|
141
150
|
// Written in an effect, not during render, so it satisfies react-hooks/refs: a
|
|
@@ -148,17 +157,47 @@ export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => v
|
|
|
148
157
|
});
|
|
149
158
|
useEffect(() => {
|
|
150
159
|
if (!open || typeof document === "undefined") return;
|
|
151
|
-
const layer: Layer = { escape: (e) => handlerRef.current(e) };
|
|
160
|
+
const layer: Layer = { escape: (e) => handlerRef.current(e), overlay };
|
|
152
161
|
layerRef.current = layer;
|
|
153
162
|
push(layer);
|
|
154
163
|
return () => {
|
|
155
164
|
pop(layer);
|
|
156
165
|
layerRef.current = null;
|
|
157
166
|
};
|
|
158
|
-
}, [open]);
|
|
167
|
+
}, [open, overlay]);
|
|
159
168
|
return layerRef;
|
|
160
169
|
}
|
|
161
170
|
|
|
171
|
+
/*
|
|
172
|
+
* Whether an overlay is open (0.49 KIT-11): every layer on the stack but a tooltip's is a
|
|
173
|
+
* dialog, a sheet, a popover, a hover menu or a list covering part of the page, so the
|
|
174
|
+
* stack is also the one place that knows. The floating launchers (`hideWhenOverlay`) step
|
|
175
|
+
* aside while one is open instead of floating over it or beside it.
|
|
176
|
+
*/
|
|
177
|
+
const overlayListeners = new Set<() => void>();
|
|
178
|
+
|
|
179
|
+
function notifyOverlays(): void {
|
|
180
|
+
for (const listener of overlayListeners) listener();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function subscribeOverlays(listener: () => void): () => void {
|
|
184
|
+
overlayListeners.add(listener);
|
|
185
|
+
return () => {
|
|
186
|
+
overlayListeners.delete(listener);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function anyOverlayOpen(): boolean {
|
|
191
|
+
return layers.some((layer) => layer.overlay);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** True while a kit dialog, sheet, popover, hover menu or list is open (any layer of the
|
|
195
|
+
* Escape stack but a tooltip's). Internal. */
|
|
196
|
+
export function useOverlayOpen(enabled = true): boolean {
|
|
197
|
+
const open = useSyncExternalStore(subscribeOverlays, anyOverlayOpen, () => false);
|
|
198
|
+
return enabled && open;
|
|
199
|
+
}
|
|
200
|
+
|
|
162
201
|
/**
|
|
163
202
|
* For a layer's own element, in its `onKeyDown`: hands an Escape on its way up to the
|
|
164
203
|
* topmost layer — this one, unless something opened over it without taking focus (a
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useKitDocumentTitle } from "../i18n/kit-labels";
|
|
3
|
+
import { DEFAULT_DOCUMENT_TITLE_FORMAT, formatDocumentTitle } from "../lib/document-title";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Writes the window's title (`document.title`) while the calling component is mounted,
|
|
7
|
+
* and puts back the title it found when it unmounts (0.49 KIT-9).
|
|
8
|
+
*
|
|
9
|
+
* `parts` are positional: `[page, section, app]`. The app falls back to the provider's
|
|
10
|
+
* `appName`, the format to the provider's `documentTitleFormat` (default
|
|
11
|
+
* "{page} · {section} · {app}"); a missing part drops with its separator
|
|
12
|
+
* ({@link formatDocumentTitle}). Parts that are ALL empty write nothing — the provider's
|
|
13
|
+
* app alone is not a page's title.
|
|
14
|
+
*
|
|
15
|
+
* `PageHeader` calls it for its `h1` (`documentTitle`); call it yourself for a page
|
|
16
|
+
* without one.
|
|
17
|
+
*/
|
|
18
|
+
export function useDocumentTitle(parts: ReadonlyArray<string | null | undefined | false>): void {
|
|
19
|
+
const { format, appName } = useKitDocumentTitle();
|
|
20
|
+
const [page, section, app] = parts.map((p) => (typeof p === "string" && p.trim() !== "" ? p : undefined));
|
|
21
|
+
const any = parts.some((p) => typeof p === "string" && p.trim() !== "");
|
|
22
|
+
const title = any ? formatDocumentTitle(format ?? DEFAULT_DOCUMENT_TITLE_FORMAT, { page, section, app: app ?? appName }) : null;
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (title === null || title === "") return;
|
|
25
|
+
const before = document.title;
|
|
26
|
+
document.title = title;
|
|
27
|
+
return () => {
|
|
28
|
+
document.title = before;
|
|
29
|
+
};
|
|
30
|
+
}, [title]);
|
|
31
|
+
}
|
package/src/i18n/german.ts
CHANGED
|
@@ -556,6 +556,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
556
556
|
theme: "Theme wechseln",
|
|
557
557
|
palette: "Darstellungsvorlage",
|
|
558
558
|
language: "Sprache",
|
|
559
|
+
languageCurrent: (name) => `Sprache wählen, aktuell: ${name}`,
|
|
559
560
|
switchRole: "Rolle wechseln",
|
|
560
561
|
role: (value) => `Rolle: ${value}`,
|
|
561
562
|
},
|
|
@@ -1929,10 +1930,13 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1929
1930
|
rowActions: {
|
|
1930
1931
|
actions: "Aktionen",
|
|
1931
1932
|
actionsFor: (name) => `Aktionen für ${name}`,
|
|
1933
|
+
confirmTitle: (action, name) => (name ? `${action}: «${name}»?` : `${action}?`),
|
|
1934
|
+
confirmBody: "Das lässt sich nicht rückgängig machen.",
|
|
1932
1935
|
},
|
|
1933
1936
|
appShellMore: {
|
|
1934
1937
|
more: "Mehr",
|
|
1935
1938
|
moreTitle: "Weitere Seiten",
|
|
1939
|
+
overview: "Übersicht",
|
|
1936
1940
|
},
|
|
1937
1941
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Verwaltung"
|
|
1938
1942
|
// as settings §5 names the page; "mit Administratorrechten" rather than "für
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -283,6 +283,9 @@ export interface TopBarLabels {
|
|
|
283
283
|
theme: string;
|
|
284
284
|
palette: string;
|
|
285
285
|
language: string;
|
|
286
|
+
/** 0.49 KIT-10: the language menu's trigger, given the current language's name —
|
|
287
|
+
* "Choose language, current: Deutsch". */
|
|
288
|
+
languageCurrent: (name: string) => string;
|
|
286
289
|
switchRole: string;
|
|
287
290
|
/** The role switcher's tooltip, given the active role's name. */
|
|
288
291
|
role: (value: string) => string;
|
|
@@ -557,6 +560,8 @@ interface KitI18n {
|
|
|
557
560
|
formatDate?: KitDateFormatter;
|
|
558
561
|
tableStorageKeyPrefix?: string;
|
|
559
562
|
timeZone?: string;
|
|
563
|
+
documentTitleFormat?: string;
|
|
564
|
+
appName?: string;
|
|
560
565
|
}
|
|
561
566
|
|
|
562
567
|
/** What a {@link KitDateFormatter} is told about the date it is formatting. */
|
|
@@ -699,6 +704,25 @@ export interface UiKitProviderProps {
|
|
|
699
704
|
* it names in every zone). Left out, timestamps follow the device, as before.
|
|
700
705
|
*/
|
|
701
706
|
timeZone?: string;
|
|
707
|
+
/**
|
|
708
|
+
* 0.49 KIT-9: how `useDocumentTitle` and every `PageHeader`'s `h1` write the window's
|
|
709
|
+
* title, from `{page}`, `{section}` and `{app}`. Default
|
|
710
|
+
* `"{page} · {section} · {app}"` (`DEFAULT_DOCUMENT_TITLE_FORMAT`); a missing part
|
|
711
|
+
* drops with its separator.
|
|
712
|
+
*/
|
|
713
|
+
documentTitleFormat?: string;
|
|
714
|
+
/** 0.49 KIT-9: the app's name, the `{app}` of the window's title ("Kastlan"). Left
|
|
715
|
+
* out, titles end at the page or section. */
|
|
716
|
+
appName?: string;
|
|
717
|
+
/**
|
|
718
|
+
* 0.49 KIT-1b: while a person copies a selection in the page, a U+2212 minus standing
|
|
719
|
+
* before a number ("−CHF 1,250.50") goes to the clipboard as "-", in `text/plain` and
|
|
720
|
+
* `text/html` (what Excel pastes), so a spreadsheet reads a number; the page keeps "−".
|
|
721
|
+
* A copy out of an input, a textarea or an editable element is left alone. Installed
|
|
722
|
+
* once per document, by the first provider that has it on. Default `true`; `false` on
|
|
723
|
+
* the outermost provider leaves the clipboard to the browser.
|
|
724
|
+
*/
|
|
725
|
+
copyAsciiMinus?: boolean;
|
|
702
726
|
children: ReactNode;
|
|
703
727
|
}
|
|
704
728
|
|
|
@@ -719,6 +743,9 @@ export function UiKitProvider({
|
|
|
719
743
|
formatDate,
|
|
720
744
|
tableStorageKeyPrefix,
|
|
721
745
|
timeZone,
|
|
746
|
+
documentTitleFormat,
|
|
747
|
+
appName,
|
|
748
|
+
copyAsciiMinus = true,
|
|
722
749
|
children,
|
|
723
750
|
}: UiKitProviderProps) {
|
|
724
751
|
const outer = useContext(KitI18nContext);
|
|
@@ -732,10 +759,25 @@ export function UiKitProvider({
|
|
|
732
759
|
formatDate: formatDate ?? outer.formatDate,
|
|
733
760
|
tableStorageKeyPrefix: tableStorageKeyPrefix ?? outer.tableStorageKeyPrefix,
|
|
734
761
|
timeZone: timeZone ?? outer.timeZone,
|
|
762
|
+
documentTitleFormat: documentTitleFormat ?? outer.documentTitleFormat,
|
|
763
|
+
appName: appName ?? outer.appName,
|
|
735
764
|
}),
|
|
736
|
-
[
|
|
765
|
+
[
|
|
766
|
+
outer,
|
|
767
|
+
labels,
|
|
768
|
+
locale,
|
|
769
|
+
weekStartsOn,
|
|
770
|
+
linkComponent,
|
|
771
|
+
chartTooltipPlacement,
|
|
772
|
+
formatDate,
|
|
773
|
+
tableStorageKeyPrefix,
|
|
774
|
+
timeZone,
|
|
775
|
+
documentTitleFormat,
|
|
776
|
+
appName,
|
|
777
|
+
],
|
|
737
778
|
);
|
|
738
779
|
useTableViewsJoin(value.tableStorageKeyPrefix ?? DEFAULT_TABLE_STORAGE_KEY_PREFIX);
|
|
780
|
+
useClipboardMinus(copyAsciiMinus);
|
|
739
781
|
return <KitI18nContext.Provider value={value}>{children}</KitI18nContext.Provider>;
|
|
740
782
|
}
|
|
741
783
|
|
|
@@ -773,6 +815,30 @@ function useTableViewsJoin(prefix: string): void {
|
|
|
773
815
|
}, [prefix]);
|
|
774
816
|
}
|
|
775
817
|
|
|
818
|
+
/**
|
|
819
|
+
* The copy handler (lib/clipboard-minus, 0.49 KIT-1b), through a literal `import()` for
|
|
820
|
+
* the reason {@link useTableViewsJoin} gives: a catalogue may reach nothing outside i18n/.
|
|
821
|
+
* It is in place a microtask after the mount — long before a person selects and copies.
|
|
822
|
+
*/
|
|
823
|
+
function useClipboardMinus(on: boolean): void {
|
|
824
|
+
useEffect(() => {
|
|
825
|
+
if (!on || typeof document === "undefined") return;
|
|
826
|
+
let uninstall: (() => void) | undefined;
|
|
827
|
+
let gone = false;
|
|
828
|
+
import("../lib/clipboard-minus").then(
|
|
829
|
+
({ installClipboardMinus }) => {
|
|
830
|
+
if (!gone) uninstall = installClipboardMinus(document);
|
|
831
|
+
},
|
|
832
|
+
// Not loaded: copying works as the browser does it.
|
|
833
|
+
() => undefined,
|
|
834
|
+
);
|
|
835
|
+
return () => {
|
|
836
|
+
gone = true;
|
|
837
|
+
uninstall?.();
|
|
838
|
+
};
|
|
839
|
+
}, [on]);
|
|
840
|
+
}
|
|
841
|
+
|
|
776
842
|
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
777
843
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
778
844
|
}
|
|
@@ -892,6 +958,15 @@ export function useKitTimeZone(): string | undefined {
|
|
|
892
958
|
return useContext(KitI18nContext).timeZone;
|
|
893
959
|
}
|
|
894
960
|
|
|
961
|
+
/** The nearest provider's `documentTitleFormat` and `appName`, or `undefined` each —
|
|
962
|
+
* what `useDocumentTitle` writes with (the format's default is
|
|
963
|
+
* `DEFAULT_DOCUMENT_TITLE_FORMAT`, applied there: this module reaches nothing outside
|
|
964
|
+
* i18n/). */
|
|
965
|
+
export function useKitDocumentTitle(): { format: string | undefined; appName: string | undefined } {
|
|
966
|
+
const { documentTitleFormat, appName } = useContext(KitI18nContext);
|
|
967
|
+
return { format: documentTitleFormat, appName };
|
|
968
|
+
}
|
|
969
|
+
|
|
895
970
|
/** {@link DEFAULT_FILE_LABELS}, but formatting in the provider's locale. */
|
|
896
971
|
export function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels {
|
|
897
972
|
const locale = useKitLocale();
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -543,6 +543,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
543
543
|
theme: "Cambiar de tema",
|
|
544
544
|
palette: "Ajuste de apariencia",
|
|
545
545
|
language: "Idioma",
|
|
546
|
+
languageCurrent: (name) => `Elegir idioma, actual: ${name}`,
|
|
546
547
|
switchRole: "Cambiar de rol",
|
|
547
548
|
role: (value) => `Rol: ${value}`,
|
|
548
549
|
},
|
|
@@ -1869,10 +1870,13 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
1869
1870
|
rowActions: {
|
|
1870
1871
|
actions: "Acciones",
|
|
1871
1872
|
actionsFor: (name) => `Acciones de ${name}`,
|
|
1873
|
+
confirmTitle: (action, name) => (name ? `¿${action} «${name}»?` : `¿${action}?`),
|
|
1874
|
+
confirmBody: "Esta acción no se puede deshacer.",
|
|
1872
1875
|
},
|
|
1873
1876
|
appShellMore: {
|
|
1874
1877
|
more: "Más",
|
|
1875
1878
|
moreTitle: "Más páginas",
|
|
1879
|
+
overview: "Resumen",
|
|
1876
1880
|
},
|
|
1877
1881
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Usted",
|
|
1878
1882
|
// as the rest of the catalogue.
|
package/src/i18n/locales/fr.ts
CHANGED
|
@@ -551,6 +551,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
551
551
|
theme: "Changer de thème",
|
|
552
552
|
palette: "Préréglage d’apparence",
|
|
553
553
|
language: "Langue",
|
|
554
|
+
languageCurrent: (name) => `Choisir la langue, actuelle\u00a0: ${name}`,
|
|
554
555
|
switchRole: "Changer de rôle",
|
|
555
556
|
role: (value) => `Rôle\u00a0: ${value}`,
|
|
556
557
|
},
|
|
@@ -1909,10 +1910,13 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
1909
1910
|
rowActions: {
|
|
1910
1911
|
actions: "Actions",
|
|
1911
1912
|
actionsFor: (name) => `Actions pour ${name}`,
|
|
1913
|
+
confirmTitle: (action, name) => (name ? `${action} «\u00a0${name}\u00a0»\u00a0?` : `${action}\u00a0?`),
|
|
1914
|
+
confirmBody: "Cette action est irréversible.",
|
|
1912
1915
|
},
|
|
1913
1916
|
appShellMore: {
|
|
1914
1917
|
more: "Plus",
|
|
1915
1918
|
moreTitle: "Autres pages",
|
|
1919
|
+
overview: "Vue d’ensemble",
|
|
1916
1920
|
},
|
|
1917
1921
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words.
|
|
1918
1922
|
mailTestCard: {
|
package/src/i18n/locales/hu.ts
CHANGED
|
@@ -541,6 +541,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
541
541
|
theme: "Téma váltása",
|
|
542
542
|
palette: "Megjelenési séma",
|
|
543
543
|
language: "Nyelv",
|
|
544
|
+
languageCurrent: (name) => `Nyelv kiválasztása, jelenlegi: ${name}`,
|
|
544
545
|
switchRole: "Szerepkör váltása",
|
|
545
546
|
role: (value) => `Szerepkör: ${value}`,
|
|
546
547
|
},
|
|
@@ -1875,10 +1876,13 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
1875
1876
|
rowActions: {
|
|
1876
1877
|
actions: "Műveletek",
|
|
1877
1878
|
actionsFor: (name) => `${name} – műveletek`,
|
|
1879
|
+
confirmTitle: (action, name) => (name ? `${action}: „${name}”?` : `${action}?`),
|
|
1880
|
+
confirmBody: "Ez nem vonható vissza.",
|
|
1878
1881
|
},
|
|
1879
1882
|
appShellMore: {
|
|
1880
1883
|
more: "Továbbiak",
|
|
1881
1884
|
moreTitle: "További oldalak",
|
|
1885
|
+
overview: "Áttekintés",
|
|
1882
1886
|
},
|
|
1883
1887
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Ön", as
|
|
1884
1888
|
// the rest of the catalogue.
|
package/src/i18n/locales/it.ts
CHANGED
|
@@ -546,6 +546,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
546
546
|
theme: "Cambia tema",
|
|
547
547
|
palette: "Preset di aspetto",
|
|
548
548
|
language: "Lingua",
|
|
549
|
+
languageCurrent: (name) => `Scegli la lingua, attuale: ${name}`,
|
|
549
550
|
switchRole: "Cambia ruolo",
|
|
550
551
|
role: (value) => `Ruolo: ${value}`,
|
|
551
552
|
},
|
|
@@ -1888,10 +1889,13 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1888
1889
|
rowActions: {
|
|
1889
1890
|
actions: "Azioni",
|
|
1890
1891
|
actionsFor: (name) => `Azioni per ${name}`,
|
|
1892
|
+
confirmTitle: (action, name) => (name ? `${action} «${name}»?` : `${action}?`),
|
|
1893
|
+
confirmBody: "Questa azione non può essere annullata.",
|
|
1891
1894
|
},
|
|
1892
1895
|
appShellMore: {
|
|
1893
1896
|
more: "Altro",
|
|
1894
1897
|
moreTitle: "Altre pagine",
|
|
1898
|
+
overview: "Panoramica",
|
|
1895
1899
|
},
|
|
1896
1900
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Lei", as
|
|
1897
1901
|
// the rest of the catalogue.
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -529,6 +529,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
529
529
|
theme: "切换主题",
|
|
530
530
|
palette: "外观预设",
|
|
531
531
|
language: "语言",
|
|
532
|
+
languageCurrent: (name) => `选择语言,当前:${name}`,
|
|
532
533
|
switchRole: "切换角色",
|
|
533
534
|
role: (value) => `角色:${value}`,
|
|
534
535
|
},
|
|
@@ -1795,10 +1796,13 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1795
1796
|
rowActions: {
|
|
1796
1797
|
actions: "操作",
|
|
1797
1798
|
actionsFor: (name) => `${name} 的操作`,
|
|
1799
|
+
confirmTitle: (action, name) => (name ? `${action}“${name}”?` : `${action}?`),
|
|
1800
|
+
confirmBody: "此操作无法撤销。",
|
|
1798
1801
|
},
|
|
1799
1802
|
appShellMore: {
|
|
1800
1803
|
more: "更多",
|
|
1801
1804
|
moreTitle: "更多页面",
|
|
1805
|
+
overview: "概览",
|
|
1802
1806
|
},
|
|
1803
1807
|
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words.
|
|
1804
1808
|
mailTestCard: {
|