@eifi1/ui-kit 0.16.1 → 0.18.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 +34 -9
- package/dist/chart.d.ts +10 -1
- package/dist/components/account-chips.d.ts +153 -0
- package/dist/components/account-chips.js +135 -0
- package/dist/components/account-chips.js.map +1 -0
- package/dist/components/account-settings-labels.d.ts +10 -2
- package/dist/components/account-settings-labels.js +3 -0
- package/dist/components/account-settings-labels.js.map +1 -1
- package/dist/components/amount-input.d.ts +18 -1
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/button-group.d.ts +10 -1
- package/dist/components/calculator.d.ts +10 -1
- package/dist/components/chip.d.ts +25 -1
- package/dist/components/chip.js +143 -6
- package/dist/components/chip.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +42 -1
- package/dist/components/confirm-dialog.js +36 -5
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +10 -1
- package/dist/components/danger-confirm.d.ts +55 -4
- package/dist/components/danger-confirm.js +67 -30
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +1 -1
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +93 -3
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/facing-pair.d.ts +10 -1
- package/dist/components/file-button.d.ts +10 -1
- package/dist/components/file-dropzone.d.ts +10 -1
- package/dist/components/form-actions.d.ts +10 -1
- package/dist/components/form-actions.js +35 -1
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/line-items.d.ts +30 -6
- package/dist/components/line-items.js +77 -33
- package/dist/components/line-items.js.map +1 -1
- package/dist/components/list.d.ts +13 -1
- package/dist/components/list.js.map +1 -1
- package/dist/components/money-field.d.ts +10 -1
- package/dist/components/number-field.d.ts +11 -2
- package/dist/components/number-field.js +23 -22
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +10 -1
- package/dist/components/numpad-sheet.d.ts +10 -1
- package/dist/components/passkeys-setting.d.ts +27 -1
- package/dist/components/passkeys-setting.js +31 -5
- package/dist/components/passkeys-setting.js.map +1 -1
- package/dist/components/progress-bar.d.ts +25 -1
- package/dist/components/progress-bar.js +21 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/reauth-dialog.d.ts +90 -0
- package/dist/components/reauth-dialog.js +158 -0
- package/dist/components/reauth-dialog.js.map +1 -0
- package/dist/components/series-chart.d.ts +10 -1
- package/dist/components/server-wake.d.ts +58 -0
- package/dist/components/server-wake.js +58 -0
- package/dist/components/server-wake.js.map +1 -0
- package/dist/components/settings-fields.d.ts +10 -1
- package/dist/components/share-card.d.ts +61 -0
- package/dist/components/share-card.js +350 -0
- package/dist/components/share-card.js.map +1 -0
- package/dist/components/signed-amount.d.ts +12 -3
- package/dist/components/signed-amount.js +6 -4
- package/dist/components/signed-amount.js.map +1 -1
- package/dist/components/status-dot.d.ts +2 -0
- package/dist/components/text-link.d.ts +10 -1
- package/dist/components/time-input.d.ts +10 -1
- package/dist/components/toast.d.ts +10 -1
- package/dist/components/toast.js +42 -2
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.js +5 -2
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/ui.d.ts +10 -1
- package/dist/components/ui.js +67 -30
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/user-avatar.d.ts +2 -0
- package/dist/components/write-lock.d.ts +68 -0
- package/dist/components/write-lock.js +33 -0
- package/dist/components/write-lock.js.map +1 -0
- package/dist/{data-table-CfVXtCeG.d.ts → data-table-3mi2R3so.d.ts} +59 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/hooks/use-file-drop.d.ts +10 -1
- package/dist/hooks/use-overlay-history.js +16 -0
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.d.ts +10 -1
- package/dist/i18n/defaults.js +11 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +78 -0
- package/dist/i18n/{locales/de.js → german.js} +59 -9
- package/dist/i18n/german.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +10 -1
- package/dist/i18n/kit-labels.js +5 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/de-CH.d.ts +23 -8
- package/dist/i18n/locales/de-CH.js +7 -3
- package/dist/i18n/locales/de-CH.js.map +1 -1
- package/dist/i18n/locales/{de.d.ts → en.d.ts} +24 -10
- package/dist/i18n/locales/en.js +95 -0
- package/dist/i18n/locales/en.js.map +1 -0
- package/dist/i18n/locales/es.d.ts +10 -1
- package/dist/i18n/locales/es.js +51 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +10 -1
- package/dist/i18n/locales/fr.js +51 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +10 -1
- package/dist/i18n/locales/hu.js +54 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +10 -1
- package/dist/i18n/locales/it.js +51 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +10 -1
- package/dist/i18n/locales/zh.js +50 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/swiss.d.ts +3 -3
- package/dist/i18n/swiss.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/dist/{kit-labels-DE--LmCk.d.ts → kit-labels-CPbsjlXr.d.ts} +282 -10
- package/dist/lib/server-wake.d.ts +130 -0
- package/dist/lib/server-wake.js +121 -0
- package/dist/lib/server-wake.js.map +1 -0
- package/dist/rhf/fields.d.ts +10 -1
- package/dist/rhf/form.d.ts +10 -1
- package/dist/rhf/line-items.js.map +1 -1
- package/dist/rhf.d.ts +10 -1
- package/dist/search/global-search.d.ts +24 -4
- package/dist/search/global-search.js +31 -3
- package/dist/search/global-search.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/shell/app-shell.d.ts +10 -1
- package/dist/shell/top-bar-brand.d.ts +10 -1
- package/dist/shell/topbar-action-menu.d.ts +2 -0
- package/dist/shell.d.ts +10 -1
- package/dist/wizard/stepper-nav.d.ts +10 -1
- package/dist/wizard.d.ts +10 -1
- package/package.json +1 -1
- package/src/components/account-chips.tsx +302 -0
- package/src/components/account-settings-labels.ts +16 -2
- package/src/components/amount-input.tsx +8 -0
- package/src/components/chip.tsx +239 -6
- package/src/components/confirm-dialog.tsx +82 -7
- package/src/components/danger-confirm.tsx +118 -33
- package/src/components/data-table.tsx +185 -3
- package/src/components/form-actions.tsx +93 -4
- package/src/components/line-items.tsx +116 -40
- package/src/components/list.tsx +13 -1
- package/src/components/number-field.tsx +32 -26
- package/src/components/passkeys-setting.tsx +68 -7
- package/src/components/progress-bar.tsx +40 -4
- package/src/components/reauth-dialog.tsx +273 -0
- package/src/components/server-wake.tsx +124 -0
- package/src/components/share-card.tsx +615 -0
- package/src/components/signed-amount.tsx +16 -5
- package/src/components/toast.tsx +72 -2
- package/src/components/toggle-group.tsx +5 -2
- package/src/components/ui.tsx +145 -34
- package/src/components/write-lock.tsx +96 -0
- package/src/hooks/use-overlay-history.ts +51 -0
- package/src/i18n/defaults.ts +10 -0
- package/src/i18n/{locales/de.ts → german.ts} +68 -13
- package/src/i18n/kit-labels.tsx +22 -2
- package/src/i18n/locales/de-CH.ts +17 -8
- package/src/i18n/locales/en.ts +116 -0
- package/src/i18n/locales/es.ts +51 -0
- package/src/i18n/locales/fr.ts +52 -0
- package/src/i18n/locales/hu.ts +55 -0
- package/src/i18n/locales/it.ts +51 -0
- package/src/i18n/locales/zh.ts +50 -0
- package/src/i18n/swiss.ts +3 -3
- package/src/index.ts +23 -1
- package/src/lib/server-wake.ts +270 -0
- package/src/rhf/line-items.tsx +2 -1
- package/src/search/global-search.tsx +59 -6
- package/dist/i18n/locales/de-CH-informal.d.ts +0 -61
- package/dist/i18n/locales/de-CH-informal.js +0 -8
- package/dist/i18n/locales/de-CH-informal.js.map +0 -1
- package/dist/i18n/locales/de-informal.d.ts +0 -82
- package/dist/i18n/locales/de-informal.js +0 -37
- package/dist/i18n/locales/de-informal.js.map +0 -1
- package/dist/i18n/locales/de.js.map +0 -1
- package/src/i18n/locales/de-CH-informal.ts +0 -10
- package/src/i18n/locales/de-informal.ts +0 -61
package/src/i18n/locales/it.ts
CHANGED
|
@@ -89,6 +89,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
89
89
|
passkeys: {
|
|
90
90
|
title: "Passkey",
|
|
91
91
|
description: "Accedi con impronta, volto o PIN del dispositivo invece che con una password.",
|
|
92
|
+
descriptionAlongside: "Accedi con impronta, volto o PIN del dispositivo. La tua password continua a funzionare.",
|
|
92
93
|
empty: "Ancora nessuna passkey",
|
|
93
94
|
loading: "Caricamento delle passkey…",
|
|
94
95
|
list: "Le tue passkey",
|
|
@@ -498,6 +499,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
498
499
|
confirmDialog: {
|
|
499
500
|
confirm: "Conferma",
|
|
500
501
|
cancel: "Annulla",
|
|
502
|
+
typed: (text) => `Digita «${text}» per confermare`,
|
|
501
503
|
},
|
|
502
504
|
floatingPanel: {
|
|
503
505
|
close: "Chiudi",
|
|
@@ -583,6 +585,55 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
583
585
|
noPreview: "Nessuna anteprima disponibile per questo file",
|
|
584
586
|
openInNewTab: "Apri in una nuova scheda",
|
|
585
587
|
},
|
|
588
|
+
writeLock: {
|
|
589
|
+
reason: "Puoi visualizzarlo, ma non modificarlo.",
|
|
590
|
+
},
|
|
591
|
+
accountState: {
|
|
592
|
+
// Agreeing with "account" (masculine): the chip is the state of an account.
|
|
593
|
+
active: "Attivo",
|
|
594
|
+
inactive: "Non attivo",
|
|
595
|
+
invited: "Invitato",
|
|
596
|
+
registered: "Registrato",
|
|
597
|
+
unverified: "Non verificato",
|
|
598
|
+
passwordChange: "Deve cambiare la password",
|
|
599
|
+
},
|
|
600
|
+
shareCard: {
|
|
601
|
+
dialogTitle: "Condividi",
|
|
602
|
+
close: "Chiudi",
|
|
603
|
+
email: "Indirizzo email",
|
|
604
|
+
emailOptional: "Indirizzo email (facoltativo)",
|
|
605
|
+
// example.com is reserved for examples (RFC 2606); a localised domain is real.
|
|
606
|
+
emailPlaceholder: "nome@example.com",
|
|
607
|
+
invalidEmail: "Inserisci un indirizzo email completo.",
|
|
608
|
+
role: "Ruolo",
|
|
609
|
+
roleOf: (name) => `Ruolo di ${name}`,
|
|
610
|
+
add: "Condividi",
|
|
611
|
+
whoHasAccess: "Chi ha accesso",
|
|
612
|
+
nobodyYet: "Nessun altro ha ancora accesso.",
|
|
613
|
+
pending: "In attesa",
|
|
614
|
+
openInvite: "Link di invito aperto",
|
|
615
|
+
copyLink: "Copia link",
|
|
616
|
+
team: "Team",
|
|
617
|
+
teamHint: (name) => `Tutti i membri di ${name} avranno accesso.`,
|
|
618
|
+
remove: "Rimuovi accesso",
|
|
619
|
+
removeConfirm: (name) => `Rimuovere l’accesso di ${name}?`,
|
|
620
|
+
revokePending: "Ritira invito",
|
|
621
|
+
revokePendingConfirm: (name) => `Ritirare l’invito per ${name}?`,
|
|
622
|
+
failed: "Non ha funzionato. Riprova.",
|
|
623
|
+
loading: "Caricamento…",
|
|
624
|
+
},
|
|
625
|
+
reauthDialog: {
|
|
626
|
+
title: "Conferma la tua identità",
|
|
627
|
+
description: "Inserisci la password attuale per continuare.",
|
|
628
|
+
password: "Password attuale",
|
|
629
|
+
submit: "Continua",
|
|
630
|
+
cancel: "Annulla",
|
|
631
|
+
},
|
|
632
|
+
serverWake: {
|
|
633
|
+
slow: "Caricamento in corso: ci vuole più del solito.",
|
|
634
|
+
waking: (appName) =>
|
|
635
|
+
`Il server va in sospensione quando nessuno usa ${appName ?? "l’app"}, quindi la prima richiesta dopo una pausa deve riavviarlo. Può volerci un momento: non si perde nulla, la pagina si completa da sola.`,
|
|
636
|
+
},
|
|
586
637
|
};
|
|
587
638
|
}
|
|
588
639
|
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -87,6 +87,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
87
87
|
passkeys: {
|
|
88
88
|
title: "通行密钥",
|
|
89
89
|
description: "使用指纹、面容或设备 PIN 登录,无需密码。",
|
|
90
|
+
descriptionAlongside: "使用指纹、面容或设备 PIN 登录,密码仍可照常使用。",
|
|
90
91
|
empty: "暂无通行密钥",
|
|
91
92
|
loading: "正在加载通行密钥…",
|
|
92
93
|
list: "已注册的通行密钥",
|
|
@@ -485,6 +486,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
485
486
|
confirmDialog: {
|
|
486
487
|
confirm: "确认",
|
|
487
488
|
cancel: "取消",
|
|
489
|
+
typed: (text) => `输入“${text}”以确认`,
|
|
488
490
|
},
|
|
489
491
|
floatingPanel: {
|
|
490
492
|
close: "关闭",
|
|
@@ -570,6 +572,54 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
570
572
|
noPreview: "此文件无法预览",
|
|
571
573
|
openInNewTab: "在新标签页中打开",
|
|
572
574
|
},
|
|
575
|
+
writeLock: {
|
|
576
|
+
reason: "仅可查看,无法修改。",
|
|
577
|
+
},
|
|
578
|
+
accountState: {
|
|
579
|
+
active: "已启用",
|
|
580
|
+
inactive: "已停用",
|
|
581
|
+
invited: "已邀请",
|
|
582
|
+
registered: "已注册",
|
|
583
|
+
unverified: "未验证",
|
|
584
|
+
passwordChange: "需修改密码",
|
|
585
|
+
},
|
|
586
|
+
shareCard: {
|
|
587
|
+
dialogTitle: "共享",
|
|
588
|
+
close: "关闭",
|
|
589
|
+
email: "电子邮箱地址",
|
|
590
|
+
emailOptional: "电子邮箱地址(可选)",
|
|
591
|
+
emailPlaceholder: "name@example.com",
|
|
592
|
+
invalidEmail: "请输入完整的电子邮箱地址。",
|
|
593
|
+
role: "角色",
|
|
594
|
+
roleOf: (name) => `${name} 的角色`,
|
|
595
|
+
add: "共享",
|
|
596
|
+
whoHasAccess: "有权访问的人",
|
|
597
|
+
nobodyYet: "目前还没有其他人有权访问。",
|
|
598
|
+
pending: "待接受",
|
|
599
|
+
openInvite: "公开邀请链接",
|
|
600
|
+
copyLink: "复制链接",
|
|
601
|
+
team: "团队",
|
|
602
|
+
teamHint: (name) => `${name} 的所有成员都将获得访问权限。`,
|
|
603
|
+
remove: "移除访问权限",
|
|
604
|
+
removeConfirm: (name) => `移除 ${name} 的访问权限?`,
|
|
605
|
+
revokePending: "撤回邀请",
|
|
606
|
+
revokePendingConfirm: (name) => `撤回对 ${name} 的邀请?`,
|
|
607
|
+
failed: "操作未成功,请重试。",
|
|
608
|
+
loading: "加载中…",
|
|
609
|
+
},
|
|
610
|
+
reauthDialog: {
|
|
611
|
+
title: "验证身份",
|
|
612
|
+
description: "请输入当前密码以继续。",
|
|
613
|
+
password: "当前密码",
|
|
614
|
+
submit: "继续",
|
|
615
|
+
cancel: "取消",
|
|
616
|
+
},
|
|
617
|
+
serverWake: {
|
|
618
|
+
slow: "仍在加载,比平时耗时更长。",
|
|
619
|
+
// A Latin product name gets a space either side, as everywhere in this catalogue.
|
|
620
|
+
waking: (appName) =>
|
|
621
|
+
`无人使用${appName ? ` ${appName} ` : "本应用"}时,服务器会进入休眠,因此暂停后的第一个请求需要重新启动它。这可能需要一点时间——不会丢失任何内容,页面会自动加载完成。`,
|
|
622
|
+
},
|
|
573
623
|
};
|
|
574
624
|
}
|
|
575
625
|
|
package/src/i18n/swiss.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swiss Standard German respelling,
|
|
3
|
-
*
|
|
4
|
-
* becomes a public `@eifi1/ui-kit/i18n/<code>` subpath.
|
|
2
|
+
* Swiss Standard German respelling, applied to the internal German source (`german.ts`)
|
|
3
|
+
* to make the `de-CH` catalogue. Internal: it lives outside `src/i18n/locales/` because
|
|
4
|
+
* every file there becomes a public `@eifi1/ui-kit/i18n/<code>` subpath.
|
|
5
5
|
*
|
|
6
6
|
* "ss" for every "ß" (Schliessen, Grösse) is the one systematic difference in UI text.
|
|
7
7
|
* Function labels are wrapped: their RESULT is respelled, so an argument the app passes
|
package/src/index.ts
CHANGED
|
@@ -109,7 +109,13 @@ export * from "./components/password-strength";
|
|
|
109
109
|
export * from "./components/page-contents";
|
|
110
110
|
export * from "./components/disclosure";
|
|
111
111
|
export * from "./components/dialog-frame";
|
|
112
|
-
|
|
112
|
+
// Named, not `*`: danger-confirm also holds the password field and pending hook that
|
|
113
|
+
// ReauthDialog shares with it, which are kit-internal.
|
|
114
|
+
export { DangerConfirm, DEFAULT_DANGER_CONFIRM_LABELS, typedMatches } from "./components/danger-confirm";
|
|
115
|
+
export type { DangerConfirmLabels, DangerConfirmProps, TypedMatch } from "./components/danger-confirm";
|
|
116
|
+
// 0.18: re-authentication before a sensitive action (Kurvenschmiede 4).
|
|
117
|
+
export { ReauthDialog, DEFAULT_REAUTH_DIALOG_LABELS } from "./components/reauth-dialog";
|
|
118
|
+
export type { ReauthDialogProps, ReauthDialogLabels } from "./components/reauth-dialog";
|
|
113
119
|
// `useConfirm()` — the promise-based replacement for `window.confirm`, one host per app.
|
|
114
120
|
export * from "./components/confirm-dialog";
|
|
115
121
|
// A non-modal corner panel and its round trigger (keksdose's assistant launcher).
|
|
@@ -174,6 +180,22 @@ export * from "./components/description-list";
|
|
|
174
180
|
// The Save / Cancel row forms and dialogs end in, and the editable line-item repeater
|
|
175
181
|
// (journal lines, lease components, invoice positions). 0.12.0.
|
|
176
182
|
export * from "./components/form-actions";
|
|
183
|
+
// 0.18: lock every opted-in commit under one provider, the reason in its tooltip
|
|
184
|
+
// (Kurvenschmiede 2). `useCommitReason` stays internal.
|
|
185
|
+
export { WriteLockProvider, useWriteLock, DEFAULT_WRITE_LOCK_LABELS } from "./components/write-lock";
|
|
186
|
+
export type { WriteLock, WriteLockLabels, WriteLockProviderProps } from "./components/write-lock";
|
|
187
|
+
// 0.18: the parts both apps' admin rosters repeat (Kurvenschmiede 6).
|
|
188
|
+
export { RoleChip, AccountStateChip, DateMark, dateColumn, DEFAULT_ACCOUNT_STATE_LABELS, ACCOUNT_STATE_TONES } from "./components/account-chips";
|
|
189
|
+
export type { RoleChipProps, RoleDefinition, RoleVocabulary, AccountState, AccountStateLabels, AccountStateChipProps, DateMarkProps, DateColumnOptions } from "./components/account-chips";
|
|
190
|
+
// 0.18: the cold-start notice keksdose built (#199), for every app: a framework-free
|
|
191
|
+
// watchdog over the app's requests and the corner notice that explains the wait.
|
|
192
|
+
export { createServerWake, serverWake, watchReadsAnd, attachServerWake, wrapFetch } from "./lib/server-wake";
|
|
193
|
+
export type { ServerWakeStage, ServerWakeFilter, ServerWakeOptions, ServerWakeRequest, ServerWakeWatcher, ServerWakeAxiosConfig, AxiosLikeInstance } from "./lib/server-wake";
|
|
194
|
+
export { ServerWakeNotice, useServerWakeStage, DEFAULT_SERVER_WAKE_LABELS } from "./components/server-wake";
|
|
195
|
+
export type { ServerWakeLabels, ServerWakeNoticeProps } from "./components/server-wake";
|
|
196
|
+
// 0.18: sharing — grantees, roles, pending grants, candidates (Kurvenschmiede 1).
|
|
197
|
+
export { ShareCard, ShareDialog, SharePanel, DEFAULT_SHARE_CARD_LABELS } from "./components/share-card";
|
|
198
|
+
export type { ShareCardProps, ShareDialogProps, SharePanelProps, ShareCardLabels, ShareRole, ShareGrantee, SharePendingGrant, ShareCandidate, ShareAddRequest } from "./components/share-card";
|
|
177
199
|
export * from "./components/line-items";
|
|
178
200
|
export * from "./components/progress-bar";
|
|
179
201
|
// Named: skeleton.tsx also holds SKELETON_CLASS, the look StatTile shares — internal.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Is the server asleep, or is the app broken?" — the cold-start watchdog, lifted
|
|
3
|
+
* from keksdose (feedback #199) so kastlan and Kurvenschmiede get it too.
|
|
4
|
+
*
|
|
5
|
+
* All three backends run on Cloud Run with `--min-instances=0`: after an idle
|
|
6
|
+
* period the container is gone and the first request has to start it, which takes
|
|
7
|
+
* several seconds and occasionally more. Nothing in the UI said so, and none of the
|
|
8
|
+
* axios instances has a timeout, so the honest experience was a spinner that looked
|
|
9
|
+
* stuck. This counts the in-flight requests a cold start would actually delay and
|
|
10
|
+
* escalates in two steps, so a merely slow response says "still loading" and only a
|
|
11
|
+
* genuinely long wait blames the sleeping server.
|
|
12
|
+
*
|
|
13
|
+
* Framework-free on purpose (no zustand, no axios, no React): a counter, two timers
|
|
14
|
+
* and a listener set, read by `useServerWakeStage` through `useSyncExternalStore`.
|
|
15
|
+
* The adapters below take the app's axios instance / fetch as an ARGUMENT, so the
|
|
16
|
+
* kit never imports either.
|
|
17
|
+
*
|
|
18
|
+
* WHAT ARMS IT, and why the default is narrow:
|
|
19
|
+
* - reads (GET) — what every page does on open — always;
|
|
20
|
+
* - writes only when the app says so (`watchReadsAnd(/\/auth\/login\b/)`): the
|
|
21
|
+
* auth POSTs that gate the app are the only writes a user waits on before the
|
|
22
|
+
* app exists at all, so a cold start there looks exactly like a broken login.
|
|
23
|
+
* An UPLOAD must never be watched — a statement PDF or a photo takes seconds on a
|
|
24
|
+
* perfectly warm server, and "the server is waking up" during a 4 s upload would
|
|
25
|
+
* simply be false. With the default that holds by itself (an upload is a POST);
|
|
26
|
+
* an app widening `shouldWatch` keeps its upload routes out of it.
|
|
27
|
+
*
|
|
28
|
+
* It never arms while the browser is offline, whatever `shouldWatch` says: that state
|
|
29
|
+
* has its own, truthful indicator, and a request that never left the device is not
|
|
30
|
+
* evidence of anything about the server.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** `slow` = longer than a warm server ever takes; `waking` = long enough that a cold
|
|
34
|
+
* start is the only sensible explanation. */
|
|
35
|
+
export type ServerWakeStage = "idle" | "slow" | "waking";
|
|
36
|
+
|
|
37
|
+
/** Decides which requests count. `method` arrives lower-cased (`"get"` when the
|
|
38
|
+
* request named none — axios's and fetch's default); `url` as the client had it. */
|
|
39
|
+
export type ServerWakeFilter = (method: string, url: string) => boolean;
|
|
40
|
+
|
|
41
|
+
export interface ServerWakeOptions {
|
|
42
|
+
/** Past this, a warm round trip is out of the question (a cold database query on a
|
|
43
|
+
* free tier can still take a second, so it is not tighter). Default 2000 ms. */
|
|
44
|
+
slowMs?: number;
|
|
45
|
+
/** Past this, the container was almost certainly scaled to zero. Default 7000 ms. */
|
|
46
|
+
wakingMs?: number;
|
|
47
|
+
/** Which requests count. Default {@link watchReadsAnd}`()` — GETs only. The offline
|
|
48
|
+
* guard runs before it and cannot be overridden. */
|
|
49
|
+
shouldWatch?: ServerWakeFilter;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** What a request is, for {@link ServerWakeWatcher.track}. */
|
|
53
|
+
export interface ServerWakeRequest {
|
|
54
|
+
method?: string;
|
|
55
|
+
url?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ServerWakeWatcher {
|
|
59
|
+
/**
|
|
60
|
+
* Count a request that is going out, and arm the escalation if it is the first.
|
|
61
|
+
* Returns whether it was counted — the caller MUST call {@link end} exactly once
|
|
62
|
+
* for every `true`, or the counter never returns to zero and the notice sticks.
|
|
63
|
+
* (The axios adapter stashes it on the request config for that reason.)
|
|
64
|
+
*/
|
|
65
|
+
start(method?: string, url?: string): boolean;
|
|
66
|
+
/** A counted request settled — either way: an error still proves the server
|
|
67
|
+
* answered or gave up. When the last one does, the notice goes away. Never takes
|
|
68
|
+
* the counter below zero. */
|
|
69
|
+
end(): void;
|
|
70
|
+
/** `start` + `end` around a promise (or a function returning one), for a request
|
|
71
|
+
* that does not go through an adapter. Resolves and rejects as the work does. */
|
|
72
|
+
track<T>(work: PromiseLike<T> | (() => PromiseLike<T>), request?: ServerWakeRequest): Promise<T>;
|
|
73
|
+
/** Back to a pristine watchdog — for tests, which share an instance across cases. */
|
|
74
|
+
reset(): void;
|
|
75
|
+
/** `useSyncExternalStore`'s pair. Listeners hear stage changes only. */
|
|
76
|
+
subscribe(listener: () => void): () => void;
|
|
77
|
+
getStage(): ServerWakeStage;
|
|
78
|
+
/** Counted requests currently outstanding. */
|
|
79
|
+
getInFlight(): number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The usual filter: every GET, plus any other request whose URL matches `writes` —
|
|
84
|
+
* keksdose's `/\/auth\/(login|register|demo-session)\b/`. Keep upload routes out of
|
|
85
|
+
* the pattern (see the module comment).
|
|
86
|
+
*/
|
|
87
|
+
export function watchReadsAnd(writes?: RegExp): ServerWakeFilter {
|
|
88
|
+
return (method, url) => method === "get" || (writes !== undefined && writes.test(url));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isOffline(): boolean {
|
|
92
|
+
return typeof navigator !== "undefined" && navigator.onLine === false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createServerWake({
|
|
96
|
+
slowMs = 2000,
|
|
97
|
+
wakingMs = 7000,
|
|
98
|
+
shouldWatch = watchReadsAnd(),
|
|
99
|
+
}: ServerWakeOptions = {}): ServerWakeWatcher {
|
|
100
|
+
let inFlight = 0;
|
|
101
|
+
let stage: ServerWakeStage = "idle";
|
|
102
|
+
let slowTimer: ReturnType<typeof setTimeout> | null = null;
|
|
103
|
+
let wakingTimer: ReturnType<typeof setTimeout> | null = null;
|
|
104
|
+
const listeners = new Set<() => void>();
|
|
105
|
+
|
|
106
|
+
const setStage = (next: ServerWakeStage) => {
|
|
107
|
+
if (next === stage) return;
|
|
108
|
+
stage = next;
|
|
109
|
+
for (const listener of listeners) listener();
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const clearTimers = () => {
|
|
113
|
+
if (slowTimer !== null) clearTimeout(slowTimer);
|
|
114
|
+
if (wakingTimer !== null) clearTimeout(wakingTimer);
|
|
115
|
+
slowTimer = null;
|
|
116
|
+
wakingTimer = null;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const start = (method?: string, url?: string): boolean => {
|
|
120
|
+
if (isOffline()) return false;
|
|
121
|
+
if (!shouldWatch((method ?? "get").toLowerCase(), url ?? "")) return false;
|
|
122
|
+
inFlight += 1;
|
|
123
|
+
// The clock runs from the FIRST outstanding request: a page firing a second read
|
|
124
|
+
// at 1.9 s is still waiting on the same sleeping server, not starting afresh.
|
|
125
|
+
if (inFlight === 1) {
|
|
126
|
+
clearTimers();
|
|
127
|
+
slowTimer = setTimeout(() => setStage("slow"), slowMs);
|
|
128
|
+
wakingTimer = setTimeout(() => setStage("waking"), wakingMs);
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const end = () => {
|
|
134
|
+
inFlight = Math.max(0, inFlight - 1);
|
|
135
|
+
if (inFlight > 0) return;
|
|
136
|
+
clearTimers();
|
|
137
|
+
setStage("idle");
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
start,
|
|
142
|
+
end,
|
|
143
|
+
track<T>(work: PromiseLike<T> | (() => PromiseLike<T>), request: ServerWakeRequest = {}) {
|
|
144
|
+
const counted = start(request.method, request.url);
|
|
145
|
+
let promise: Promise<T>;
|
|
146
|
+
try {
|
|
147
|
+
promise = Promise.resolve(typeof work === "function" ? work() : work);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
// A thunk that throws before it returns a promise settled too.
|
|
150
|
+
promise = Promise.reject(error);
|
|
151
|
+
}
|
|
152
|
+
return counted ? promise.finally(end) : promise;
|
|
153
|
+
},
|
|
154
|
+
reset() {
|
|
155
|
+
inFlight = 0;
|
|
156
|
+
clearTimers();
|
|
157
|
+
setStage("idle");
|
|
158
|
+
},
|
|
159
|
+
subscribe(listener) {
|
|
160
|
+
listeners.add(listener);
|
|
161
|
+
return () => {
|
|
162
|
+
listeners.delete(listener);
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
getStage: () => stage,
|
|
166
|
+
getInFlight: () => inFlight,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The shared instance: what `<ServerWakeNotice />`, `useServerWakeStage()`,
|
|
172
|
+
* {@link attachServerWake} and {@link wrapFetch} use when no watcher is passed. GETs
|
|
173
|
+
* only, default timings — an app that also watches its login POST creates its own
|
|
174
|
+
* with {@link createServerWake} and passes it to both ends.
|
|
175
|
+
*/
|
|
176
|
+
export const serverWake: ServerWakeWatcher = createServerWake();
|
|
177
|
+
|
|
178
|
+
// ── axios ─────────────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
/** The two config fields the watchdog reads — what axios's request config has. */
|
|
181
|
+
export interface ServerWakeAxiosConfig {
|
|
182
|
+
method?: string;
|
|
183
|
+
url?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The slice of an interceptor manager the adapter uses. A method (not a function
|
|
187
|
+
* property) so axios's wider `use(onFulfilled?, onRejected?, options?)` fits it. */
|
|
188
|
+
interface InterceptorManagerLike<V> {
|
|
189
|
+
use(onFulfilled: (value: V) => V | Promise<V>, onRejected: (error: unknown) => unknown): number;
|
|
190
|
+
eject(id: number): void;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** An axios instance, structurally — so the kit types the adapter without importing
|
|
194
|
+
* axios. `axios.create()`'s result fits it as is. */
|
|
195
|
+
export interface AxiosLikeInstance<C extends ServerWakeAxiosConfig, R extends { config: C }> {
|
|
196
|
+
interceptors: {
|
|
197
|
+
request: InterceptorManagerLike<C>;
|
|
198
|
+
response: InterceptorManagerLike<R>;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Whether the watchdog counted this request — the response side owes a matching
|
|
203
|
+
* `end()` only then. On the config, as keksdose's `_wake` was: it travels with the
|
|
204
|
+
* request onto the response and onto an error. Cleared once settled, so a 401 replay
|
|
205
|
+
* of the same config re-enters the request interceptor and is counted afresh — the
|
|
206
|
+
* pairing stays balanced. */
|
|
207
|
+
const COUNTED = "_serverWake";
|
|
208
|
+
type Marked = { [COUNTED]?: boolean };
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Drive a watcher from an axios instance's interceptors: `attachServerWake(api)`.
|
|
212
|
+
* Returns a function that ejects both interceptors again.
|
|
213
|
+
*
|
|
214
|
+
* Attach it BEFORE the app's own response interceptors: axios runs those in the order
|
|
215
|
+
* they were added, so the wake one ends the wait before a 401 handler awaits a token
|
|
216
|
+
* refresh — otherwise the notice would keep counting a request that has already been
|
|
217
|
+
* answered. (Request-side order does not matter.)
|
|
218
|
+
*/
|
|
219
|
+
export function attachServerWake<C extends ServerWakeAxiosConfig, R extends { config: C }>(
|
|
220
|
+
instance: AxiosLikeInstance<C, R>,
|
|
221
|
+
watcher: ServerWakeWatcher = serverWake,
|
|
222
|
+
): () => void {
|
|
223
|
+
const settle = (config: unknown) => {
|
|
224
|
+
const marked = config as Marked | undefined;
|
|
225
|
+
if (marked?.[COUNTED]) {
|
|
226
|
+
marked[COUNTED] = false;
|
|
227
|
+
watcher.end();
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const request = instance.interceptors.request.use(
|
|
231
|
+
(config) => {
|
|
232
|
+
(config as C & Marked)[COUNTED] = watcher.start(config.method, config.url);
|
|
233
|
+
return config;
|
|
234
|
+
},
|
|
235
|
+
(error) => Promise.reject(error),
|
|
236
|
+
);
|
|
237
|
+
const response = instance.interceptors.response.use(
|
|
238
|
+
(res) => {
|
|
239
|
+
settle(res.config);
|
|
240
|
+
return res;
|
|
241
|
+
},
|
|
242
|
+
(error) => {
|
|
243
|
+
settle((error as { config?: unknown } | null)?.config);
|
|
244
|
+
return Promise.reject(error);
|
|
245
|
+
},
|
|
246
|
+
);
|
|
247
|
+
return () => {
|
|
248
|
+
instance.interceptors.request.eject(request);
|
|
249
|
+
instance.interceptors.response.eject(response);
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ── fetch ─────────────────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A `fetch` that drives a watcher: `const apiFetch = wrapFetch(fetch)`. The wait ends
|
|
257
|
+
* when the response's HEADERS arrive (or the call fails) — the server is awake by then,
|
|
258
|
+
* however long the body takes.
|
|
259
|
+
*/
|
|
260
|
+
export function wrapFetch(
|
|
261
|
+
fetchImpl: typeof fetch = globalThis.fetch,
|
|
262
|
+
watcher: ServerWakeWatcher = serverWake,
|
|
263
|
+
): typeof fetch {
|
|
264
|
+
return (input, init) => {
|
|
265
|
+
const request = typeof Request !== "undefined" && input instanceof Request ? input : undefined;
|
|
266
|
+
const method = init?.method ?? request?.method;
|
|
267
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : (request?.url ?? "");
|
|
268
|
+
return watcher.track(() => fetchImpl(input, init), { method, url });
|
|
269
|
+
};
|
|
270
|
+
}
|
package/src/rhf/line-items.tsx
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
* list's `error`. Each cell's `render` gets `name`, the row's path
|
|
25
25
|
* (`"components.2"`), to bind its field to.
|
|
26
26
|
*
|
|
27
|
-
* `summary`, `fieldLabels` and `
|
|
27
|
+
* `summary`, `fieldLabels`, `narrowColumns`, `removePlacement` and `removeAlign` pass
|
|
28
|
+
* through to {@link LineItems}. With
|
|
28
29
|
* `fieldLabels="floating"` a cell's `fieldLabel` is the column's name; the bound
|
|
29
30
|
* fields' own `label` is a `FormLabel` ABOVE the control, so for the floating
|
|
30
31
|
* one render the kit field through `RhfField`:
|
|
@@ -42,8 +42,22 @@ export const DEFAULT_GLOBAL_SEARCH_LABELS: GlobalSearchLabels = {
|
|
|
42
42
|
export interface GlobalSearchSource {
|
|
43
43
|
/** Stable key — the effect that runs the source is keyed by it, not by identity. */
|
|
44
44
|
id: string;
|
|
45
|
-
/** The group its results are shown under (an entry's own `group` is ignored)
|
|
45
|
+
/** The group its results are shown under (an entry's own `group` is ignored) — unless
|
|
46
|
+
* `groups` is set, where it is only the home of entries that name no declared group.
|
|
47
|
+
* If every entry names a declared group, it is never shown: a catch-all heading such
|
|
48
|
+
* as "Other" is the honest value, so a stray type would still surface, labelled. */
|
|
46
49
|
group: string;
|
|
50
|
+
/**
|
|
51
|
+
* Split ONE request into several groups — kastlan's record search answers with
|
|
52
|
+
* properties, units, contacts, leases and invoices at once, and one source per group
|
|
53
|
+
* meant a dedupe layer so the network still saw one request. Each entry is then shown
|
|
54
|
+
* under the group its own `group` names: a declared `key` (shown by its `label`) or a
|
|
55
|
+
* plain string (key and heading at once). The groups appear in THIS order whatever the
|
|
56
|
+
* answer's order, each kept to `limit` rows; an entry naming no declared group falls
|
|
57
|
+
* under `group`. While the request runs, or when it fails, the source shows one status
|
|
58
|
+
* line under its first group, not one per group. Unset: one group, as before.
|
|
59
|
+
*/
|
|
60
|
+
groups?: readonly (string | GlobalSearchSourceGroup)[];
|
|
47
61
|
/** Called with the trimmed query. Its results are shown AS GIVEN — the source ranked
|
|
48
62
|
* them — and never re-filtered by the index's matcher. `signal` aborts when the query
|
|
49
63
|
* moves on or the palette closes; a stale answer is dropped either way. */
|
|
@@ -52,7 +66,7 @@ export interface GlobalSearchSource {
|
|
|
52
66
|
minChars?: number;
|
|
53
67
|
/** Quiet period after the last keystroke before it is asked. Default 200ms. */
|
|
54
68
|
debounceMs?: number;
|
|
55
|
-
/** Rows kept. Default: `indexOptions.groupLimit` (8). */
|
|
69
|
+
/** Rows kept — per group, when `groups` is set. Default: `indexOptions.groupLimit` (8). */
|
|
56
70
|
limit?: number;
|
|
57
71
|
/** Mask every row of this source for session replay, hints (amounts) included. An
|
|
58
72
|
* entry's own `redact` still wins. */
|
|
@@ -62,6 +76,13 @@ export interface GlobalSearchSource {
|
|
|
62
76
|
onError?: (error: unknown) => void;
|
|
63
77
|
}
|
|
64
78
|
|
|
79
|
+
/** One of a multi-group source's groups: the `key` its entries name in `group`, and the
|
|
80
|
+
* heading shown — so a server's type ("unit") need not be translated per entry. */
|
|
81
|
+
export interface GlobalSearchSourceGroup {
|
|
82
|
+
key: string;
|
|
83
|
+
label: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
/** What a custom trigger gets: everything the default button uses. */
|
|
66
87
|
export interface GlobalSearchTriggerProps {
|
|
67
88
|
open: () => void;
|
|
@@ -90,7 +111,7 @@ export interface GlobalSearchProps {
|
|
|
90
111
|
* neither re-indexes nor loops, and the icons and handlers used are always the latest.
|
|
91
112
|
*/
|
|
92
113
|
entries?: readonly SearchEntry[];
|
|
93
|
-
/** The async sources, each streamed into its own group. */
|
|
114
|
+
/** The async sources, each streamed into its own group — or several (`groups`). */
|
|
94
115
|
sources?: readonly GlobalSearchSource[];
|
|
95
116
|
/** What the empty query shows, under the `suggestions` heading. */
|
|
96
117
|
suggestions?: readonly GlobalSearchSuggestion[];
|
|
@@ -311,7 +332,17 @@ function GlobalSearchImpl({
|
|
|
311
332
|
const byId = useMemo(() => new Map(list.map((e) => [e.id, e])), [sig]);
|
|
312
333
|
|
|
313
334
|
const sourceKey = (sources ?? [])
|
|
314
|
-
.map((s) =>
|
|
335
|
+
.map((s) =>
|
|
336
|
+
[
|
|
337
|
+
s.id,
|
|
338
|
+
s.group,
|
|
339
|
+
(s.groups ?? []).map((g) => (typeof g === "string" ? g : `${g.key}\u0003${g.label}`)).join("\u0004"),
|
|
340
|
+
s.minChars ?? 2,
|
|
341
|
+
s.debounceMs ?? 200,
|
|
342
|
+
s.limit,
|
|
343
|
+
s.redact,
|
|
344
|
+
].join("\u0001"),
|
|
345
|
+
)
|
|
315
346
|
.join("\u0002");
|
|
316
347
|
const sourceList = useMemo(() => sources ?? [], [sourceKey]);
|
|
317
348
|
|
|
@@ -434,10 +465,12 @@ function GlobalSearchImpl({
|
|
|
434
465
|
for (const source of sourceList) {
|
|
435
466
|
if (q.length < (source.minChars ?? 2)) continue;
|
|
436
467
|
const result = sourceResults[source.id];
|
|
468
|
+
const declared = (source.groups ?? []).map((g) => (typeof g === "string" ? { key: g, label: g } : g));
|
|
437
469
|
const status = (label: string, icon: ReactNode): CommandItem => ({
|
|
438
470
|
id: `src:${source.id}:status`,
|
|
439
471
|
label,
|
|
440
|
-
group
|
|
472
|
+
// One line for the one request, under the group that will head its answer.
|
|
473
|
+
group: declared[0]?.label ?? source.group,
|
|
441
474
|
icon,
|
|
442
475
|
kind: "status",
|
|
443
476
|
onSelect: noop,
|
|
@@ -448,10 +481,30 @@ function GlobalSearchImpl({
|
|
|
448
481
|
out.push(status(pl.loading, <Loader2 className="size-3.5 animate-spin" />));
|
|
449
482
|
} else if (result.status === "error") {
|
|
450
483
|
out.push(status(pl.error ?? DEFAULT_COMMAND_PALETTE_LABELS.error!, <AlertCircle className="size-3.5" />));
|
|
451
|
-
} else {
|
|
484
|
+
} else if (!declared.length) {
|
|
452
485
|
for (const entry of result.entries.slice(0, source.limit ?? groupLimit)) {
|
|
453
486
|
out.push(toItem(`src:${source.id}:`, entry, source.group, source.redact ?? redactLabels));
|
|
454
487
|
}
|
|
488
|
+
} else {
|
|
489
|
+
// Bucket by the entry's own group, then emit in the DECLARED order — the answer's
|
|
490
|
+
// order must not reshuffle the headings between keystrokes. Within a group the
|
|
491
|
+
// source's ranking stands. The group key is in the row id: two record types may
|
|
492
|
+
// share an id ("1"), and the palette keys rows by it.
|
|
493
|
+
const buckets = new Map<string, { label: string; entries: SearchEntry[] }>();
|
|
494
|
+
for (const g of declared) buckets.set(g.key, { label: g.label, entries: [] });
|
|
495
|
+
const rest: SearchEntry[] = [];
|
|
496
|
+
for (const entry of result.entries) {
|
|
497
|
+
const bucket = entry.group === undefined ? undefined : buckets.get(entry.group);
|
|
498
|
+
(bucket ? bucket.entries : rest).push(entry);
|
|
499
|
+
}
|
|
500
|
+
const limit = source.limit ?? groupLimit;
|
|
501
|
+
const emit = (key: string, label: string, list: SearchEntry[]) => {
|
|
502
|
+
for (const entry of list.slice(0, limit)) {
|
|
503
|
+
out.push(toItem(`src:${source.id}:${key}:`, entry, label, source.redact ?? redactLabels));
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
for (const [key, { label, entries: list }] of buckets) emit(key, label, list);
|
|
507
|
+
emit("~", source.group, rest);
|
|
455
508
|
}
|
|
456
509
|
}
|
|
457
510
|
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { aZ as UiKitLabels } from '../../kit-labels-DE--LmCk.js';
|
|
2
|
-
import 'react';
|
|
3
|
-
import '../../components/data-table-labels.js';
|
|
4
|
-
import '../../components/mini-calendar.js';
|
|
5
|
-
import '../../components/calendar-heatmap.js';
|
|
6
|
-
import '../../components/popover.js';
|
|
7
|
-
import '../../components/chip.js';
|
|
8
|
-
import 'lucide-react';
|
|
9
|
-
import '../../components/field-sync.js';
|
|
10
|
-
import '../../components/tooltip.js';
|
|
11
|
-
import '../../hooks/use-anchored-rect.js';
|
|
12
|
-
import '../../lib/clipping.js';
|
|
13
|
-
import '../../wizard/types.js';
|
|
14
|
-
import '../../tour/tour.js';
|
|
15
|
-
import '../../search/command-palette.js';
|
|
16
|
-
import '../../search/global-search.js';
|
|
17
|
-
import '../../search/search-index.js';
|
|
18
|
-
import '../../components/month-picker.js';
|
|
19
|
-
import '../../components/page-contents.js';
|
|
20
|
-
import '../../components/series-chart-labels.js';
|
|
21
|
-
import '../../components/pie-chart-labels.js';
|
|
22
|
-
import '../../components/sparkline.js';
|
|
23
|
-
import '../../components/stat-tile.js';
|
|
24
|
-
import '../../components/signature-pad.js';
|
|
25
|
-
import '../../components/password-strength.js';
|
|
26
|
-
import '../../components/danger-confirm.js';
|
|
27
|
-
import '../../components/swatch-picker.js';
|
|
28
|
-
import '../../components/tile-radio.js';
|
|
29
|
-
import '../../components/icon-picker.js';
|
|
30
|
-
import '../../components/dialog-frame.js';
|
|
31
|
-
import '../../components/modal.js';
|
|
32
|
-
import '../../hooks/use-search-param-state.js';
|
|
33
|
-
import '../../components/measured-grid.js';
|
|
34
|
-
import '../../feedback-attachment-WFiXJ8We.js';
|
|
35
|
-
import '../../feedback/feedback-thread.js';
|
|
36
|
-
import '../../feedback/feedback-inbox.js';
|
|
37
|
-
import '../../components/account-settings-labels.js';
|
|
38
|
-
import '../../components/confirm-dialog.js';
|
|
39
|
-
import '../../components/floating-panel.js';
|
|
40
|
-
import '../../components/bulk-action-bar.js';
|
|
41
|
-
import '../../components/list.js';
|
|
42
|
-
import '../../components/signed-amount.js';
|
|
43
|
-
import '../../components/breadcrumbs.js';
|
|
44
|
-
import '../../components/toast.js';
|
|
45
|
-
import '../../components/description-list.js';
|
|
46
|
-
import '../../components/line-items.js';
|
|
47
|
-
import '../../components/progress-bar.js';
|
|
48
|
-
import '../../components/error-boundary.js';
|
|
49
|
-
import '../../components/authed-image.js';
|
|
50
|
-
import '../../hooks/use-authed-src.js';
|
|
51
|
-
import '../../components/image-grid.js';
|
|
52
|
-
import '../../components/lightbox.js';
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Swiss Standard German, informal ("du"): the informal German labels with Swiss
|
|
56
|
-
* spelling ("ss" for every "ß") and `de-CH` number formatting (1’234.5) — derived the
|
|
57
|
-
* same way `de-CH` is from `de`, so both stay in sync with their source.
|
|
58
|
-
*/
|
|
59
|
-
declare const UI_KIT_LABELS_DE_CH_INFORMAL: UiKitLabels;
|
|
60
|
-
|
|
61
|
-
export { UI_KIT_LABELS_DE_CH_INFORMAL };
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { swiss } from "../swiss.js";
|
|
3
|
-
import { uiKitLabelsDeInformal } from "./de-informal.js";
|
|
4
|
-
const UI_KIT_LABELS_DE_CH_INFORMAL = swiss(uiKitLabelsDeInformal("de-CH"));
|
|
5
|
-
export {
|
|
6
|
-
UI_KIT_LABELS_DE_CH_INFORMAL
|
|
7
|
-
};
|
|
8
|
-
//# sourceMappingURL=de-CH-informal.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/i18n/locales/de-CH-informal.ts"],"sourcesContent":["import type { UiKitLabels } from \"../kit-labels\";\nimport { swiss } from \"../swiss\";\nimport { uiKitLabelsDeInformal } from \"./de-informal\";\n\n/**\n * Swiss Standard German, informal (\"du\"): the informal German labels with Swiss\n * spelling (\"ss\" for every \"ß\") and `de-CH` number formatting (1’234.5) — derived the\n * same way `de-CH` is from `de`, so both stay in sync with their source.\n */\nexport const UI_KIT_LABELS_DE_CH_INFORMAL: UiKitLabels = swiss(uiKitLabelsDeInformal(\"de-CH\"));\n"],"mappings":";AACA,SAAS,aAAa;AACtB,SAAS,6BAA6B;AAO/B,MAAM,+BAA4C,MAAM,sBAAsB,OAAO,CAAC;","names":[]}
|