@eifi1/ui-kit 0.8.0 → 0.9.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.
Files changed (107) hide show
  1. package/README.md +4 -0
  2. package/dist/components/amount-input.d.ts +2 -0
  3. package/dist/components/calculator.d.ts +2 -0
  4. package/dist/components/chip.d.ts +27 -5
  5. package/dist/components/chip.js +6 -2
  6. package/dist/components/chip.js.map +1 -1
  7. package/dist/components/data-table-filter-popover.d.ts +1 -1
  8. package/dist/components/data-table-filters.d.ts +1 -1
  9. package/dist/components/data-table.d.ts +1 -1
  10. package/dist/components/data-table.js +122 -91
  11. package/dist/components/data-table.js.map +1 -1
  12. package/dist/components/date-picker.d.ts +69 -3
  13. package/dist/components/date-picker.js +152 -65
  14. package/dist/components/date-picker.js.map +1 -1
  15. package/dist/components/disclosure.d.ts +11 -2
  16. package/dist/components/disclosure.js +10 -4
  17. package/dist/components/disclosure.js.map +1 -1
  18. package/dist/components/floating-panel.d.ts +29 -2
  19. package/dist/components/floating-panel.js +16 -2
  20. package/dist/components/floating-panel.js.map +1 -1
  21. package/dist/components/full-bleed-dialog.js +3 -1
  22. package/dist/components/full-bleed-dialog.js.map +1 -1
  23. package/dist/components/number-field.d.ts +2 -0
  24. package/dist/components/number-input.d.ts +2 -0
  25. package/dist/components/numpad-sheet.d.ts +2 -0
  26. package/dist/components/progress-bar.d.ts +16 -2
  27. package/dist/components/progress-bar.js +4 -2
  28. package/dist/components/progress-bar.js.map +1 -1
  29. package/dist/components/treemap.js +4 -1
  30. package/dist/components/treemap.js.map +1 -1
  31. package/dist/components/ui.d.ts +44 -14
  32. package/dist/components/ui.js +28 -22
  33. package/dist/components/ui.js.map +1 -1
  34. package/dist/components/use-table-state.d.ts +1 -1
  35. package/dist/{data-table-filters-CF1PXqjQ.d.ts → data-table-filters-Dh9uF_S-.d.ts} +33 -2
  36. package/dist/data-table.d.ts +1 -1
  37. package/dist/hooks/use-overlay-history.js +84 -4
  38. package/dist/hooks/use-overlay-history.js.map +1 -1
  39. package/dist/i18n/defaults.d.ts +2 -0
  40. package/dist/i18n/defaults.js +2 -0
  41. package/dist/i18n/defaults.js.map +1 -1
  42. package/dist/i18n/kit-labels.d.ts +3 -0
  43. package/dist/i18n/kit-labels.js.map +1 -1
  44. package/dist/i18n/locales/de-CH-informal.d.ts +2 -0
  45. package/dist/i18n/locales/de-CH.d.ts +2 -0
  46. package/dist/i18n/locales/de-informal.d.ts +2 -0
  47. package/dist/i18n/locales/de.d.ts +2 -0
  48. package/dist/i18n/locales/de.js +10 -0
  49. package/dist/i18n/locales/de.js.map +1 -1
  50. package/dist/i18n/locales/es.d.ts +2 -0
  51. package/dist/i18n/locales/es.js +10 -0
  52. package/dist/i18n/locales/es.js.map +1 -1
  53. package/dist/i18n/locales/fr.d.ts +2 -0
  54. package/dist/i18n/locales/fr.js +10 -0
  55. package/dist/i18n/locales/fr.js.map +1 -1
  56. package/dist/i18n/locales/hu.d.ts +2 -0
  57. package/dist/i18n/locales/hu.js +10 -0
  58. package/dist/i18n/locales/hu.js.map +1 -1
  59. package/dist/i18n/locales/it.d.ts +2 -0
  60. package/dist/i18n/locales/it.js +10 -0
  61. package/dist/i18n/locales/it.js.map +1 -1
  62. package/dist/i18n/locales/zh.d.ts +2 -0
  63. package/dist/i18n/locales/zh.js +10 -0
  64. package/dist/i18n/locales/zh.js.map +1 -1
  65. package/dist/index.d.ts +5 -3
  66. package/dist/index.js +2 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/search/command-palette.d.ts +49 -1
  69. package/dist/search/command-palette.js +132 -29
  70. package/dist/search/command-palette.js.map +1 -1
  71. package/dist/search/global-search.d.ts +141 -0
  72. package/dist/search/global-search.js +277 -0
  73. package/dist/search/global-search.js.map +1 -0
  74. package/dist/search/search-index.d.ts +105 -0
  75. package/dist/search/search-index.js +142 -0
  76. package/dist/search/search-index.js.map +1 -0
  77. package/dist/search.d.ts +2 -0
  78. package/dist/search.js +2 -0
  79. package/dist/search.js.map +1 -1
  80. package/dist/wizard/stepper-nav.d.ts +10 -1
  81. package/dist/wizard/stepper-nav.js +2 -1
  82. package/dist/wizard/stepper-nav.js.map +1 -1
  83. package/package.json +12 -3
  84. package/src/components/chip.tsx +36 -6
  85. package/src/components/data-table.tsx +81 -5
  86. package/src/components/date-picker.tsx +328 -117
  87. package/src/components/disclosure.tsx +24 -8
  88. package/src/components/floating-panel.tsx +46 -2
  89. package/src/components/full-bleed-dialog.tsx +7 -2
  90. package/src/components/progress-bar.tsx +27 -3
  91. package/src/components/treemap.tsx +3 -0
  92. package/src/components/ui.tsx +95 -37
  93. package/src/hooks/use-overlay-history.ts +181 -4
  94. package/src/i18n/defaults.ts +2 -0
  95. package/src/i18n/kit-labels.tsx +2 -0
  96. package/src/i18n/locales/de.ts +10 -0
  97. package/src/i18n/locales/es.ts +10 -0
  98. package/src/i18n/locales/fr.ts +10 -0
  99. package/src/i18n/locales/hu.ts +10 -0
  100. package/src/i18n/locales/it.ts +10 -0
  101. package/src/i18n/locales/zh.ts +10 -0
  102. package/src/index.ts +4 -0
  103. package/src/search/command-palette.tsx +200 -27
  104. package/src/search/global-search.tsx +484 -0
  105. package/src/search/search-index.ts +295 -0
  106. package/src/search.ts +2 -0
  107. package/src/wizard/stepper-nav.tsx +11 -1
@@ -81,9 +81,48 @@ interface PushedEntry {
81
81
  href: string;
82
82
  /** Its overlay is gone but the entry is not — someone above owes this pop. */
83
83
  dead: boolean;
84
+ /** The router's `idx` on this entry when the marker went on — see {@link locate}. */
85
+ idx: number | undefined;
86
+ /** The router's `key` copied from the page entry beneath ours. */
87
+ key: string | undefined;
88
+ /** {@link routeOf} the address the marker was placed at. */
89
+ route: string;
84
90
  }
85
91
  const pushed: PushedEntry[] = [];
86
92
 
93
+ /**
94
+ * Entries of ours that a ROUTER NAVIGATION made from inside the overlay has left
95
+ * behind — ours, still in the history, but no longer on top and no longer anyone's.
96
+ *
97
+ * Nothing can take an entry out from under a newer one, so they cannot be unwound;
98
+ * what can be done is to not let the user land on one. A traversal that comes to
99
+ * rest on a buried entry is carried straight on over the whole run of them, in the
100
+ * direction it was going (see `skipBuried`): Back from the new page lands on the page
101
+ * the overlay was opened from, Forward from there lands on the new page, and no press
102
+ * is spent on an entry with nothing on screen for it.
103
+ *
104
+ * `below` / `above` count the buried entries of the same run on either side. The
105
+ * router's `idx` tells the direction apart — except after a REPLACE navigation, when
106
+ * the page above the run shares the run's `idx`; the two keys decide there.
107
+ */
108
+ interface BuriedEntry {
109
+ idx: number;
110
+ /** The `idx` of the router entry that buried the run: higher after a push, the
111
+ * same after a replace. */
112
+ aboveIdx: number;
113
+ /** The key of the page entry directly beneath the run. */
114
+ belowKey: string | undefined;
115
+ /** The key of the router entry that buried the run, when it did. */
116
+ aboveKey: string | undefined;
117
+ below: number;
118
+ above: number;
119
+ }
120
+ const buried = new Map<string, BuriedEntry>();
121
+
122
+ /** The router `{idx, key}` of the entry the last traversal (or burial) left us on —
123
+ * what `skipBuried` compares against to know which way the user was going. */
124
+ let lastSeen: { idx: number | undefined; key: string | undefined } | null = null;
125
+
87
126
  /** Entries a real Back press already consumed. Their overlay's cleanup still has to
88
127
  * run, and it must not mistake "my entry is not current" for "my entry is buried"
89
128
  * — it has no entry left at all. */
@@ -140,7 +179,12 @@ function runUnwind(): void {
140
179
  const job = unwind;
141
180
  unwind = null;
142
181
  if (!job) return;
143
- if (currentSentinel() !== job.id || currentHref() !== job.href) return;
182
+ if (currentSentinel() !== job.id || currentHref() !== job.href) {
183
+ // A router navigation landed between the cleanup and this task: the entries
184
+ // are under it (or were replaced by it) rather than simply forgotten.
185
+ if (taggedSentinel() === null) buryUnder(job.entries);
186
+ return;
187
+ }
144
188
  pendingProgrammatic += 1;
145
189
  window.history.go(-job.entries.length);
146
190
  }
@@ -149,6 +193,120 @@ function currentHref(): string {
149
193
  return typeof window === "undefined" ? "" : window.location.href;
150
194
  }
151
195
 
196
+ /** The router's own `{idx, key}` on the current entry, where a router has put them. */
197
+ function routerMark(): { idx: number | undefined; key: string | undefined } {
198
+ const state = typeof window !== "undefined" ? (window.history.state as unknown) : null;
199
+ if (!state || typeof state !== "object") return { idx: undefined, key: undefined };
200
+ const { idx, key } = state as Record<string, unknown>;
201
+ return { idx: typeof idx === "number" ? idx : undefined, key: typeof key === "string" ? key : undefined };
202
+ }
203
+
204
+ /**
205
+ * The part of an address that names the PAGE — the query string left out.
206
+ *
207
+ * A `setSearchParams(…, { replace: true })` rewrites the query on the entry the
208
+ * overlay is standing on; that is a wipe to repair, not a move. Anything else changing
209
+ * is a navigation. Under a hash router the page lives in the hash (`#/table?f.q=x`), so
210
+ * a hash that starts with `#/` counts up to its own `?`; an ordinary `#fragment` does
211
+ * not name a page and is left out.
212
+ */
213
+ function routeOf(href: string): string {
214
+ try {
215
+ const url = new URL(href);
216
+ const hashRoute = url.hash.startsWith("#/") ? url.hash.split("?")[0] : "";
217
+ return url.origin + url.pathname + hashRoute;
218
+ } catch {
219
+ return href;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Is the untagged entry we are standing on still `p`'s — merely wiped by the router —
225
+ * or an entry the router NAVIGATED to from inside the overlay?
226
+ *
227
+ * The router's `idx` is the one per-entry fact that separates the two, because it is
228
+ * the one the router itself keeps straight: a replace leaves it as it was, a push adds
229
+ * one. Our own push copies the page's state, so our entry carries the page's `idx` and
230
+ * a router push on top of it carries one more. The key would not do — every replace
231
+ * mints a new one, so the wipe this repair exists for would already fail it. Neither
232
+ * would the address alone: a push to the address we are already at is still a push.
233
+ *
234
+ * `idx` does not see one move, the router REPLACING our entry with a different page
235
+ * (same `idx`), so the page named by the address must also be unchanged. Where there is
236
+ * no `idx` to compare (no router, or one that keeps none) the address is all there is.
237
+ */
238
+ function isStillOn(p: PushedEntry): boolean {
239
+ if (routeOf(currentHref()) !== p.route) return false;
240
+ const { idx } = routerMark();
241
+ return idx === undefined || p.idx === undefined || idx === p.idx;
242
+ }
243
+
244
+ /**
245
+ * The router has navigated while our entries were on top: record which of `records`
246
+ * (ours, oldest first, ending with the top one) it buried and which it replaced, and
247
+ * drop them from `pushed` — none of them can be unwound any more. Returns whether it
248
+ * recognised a navigation at all; when it cannot tell (no router `idx` to go by), it
249
+ * leaves everything as it was and the caller falls back to marking the entry dead.
250
+ */
251
+ function buryUnder(records: PushedEntry[]): boolean {
252
+ const top = records[records.length - 1];
253
+ const here = routerMark();
254
+ if (!top || top.idx === undefined || here.idx === undefined) return false;
255
+ let run: PushedEntry[];
256
+ if (here.idx > top.idx) {
257
+ run = [];
258
+ for (let i = records.length - 1; i >= 0 && records[i].idx === top.idx; i -= 1) run.unshift(records[i]);
259
+ } else if (here.idx === top.idx && !isStillOn(top)) {
260
+ // Replaced: the top entry IS the new page now, and gone as ours. Those beneath it
261
+ // are buried under it.
262
+ run = [];
263
+ for (let i = records.length - 2; i >= 0 && records[i].idx === top.idx; i -= 1) run.unshift(records[i]);
264
+ } else {
265
+ return false;
266
+ }
267
+ run.forEach((r, i) => {
268
+ buried.set(r.id, {
269
+ idx: top.idx as number,
270
+ aboveIdx: here.idx as number,
271
+ belowKey: run[0].key,
272
+ aboveKey: here.key,
273
+ below: i,
274
+ above: run.length - 1 - i,
275
+ });
276
+ });
277
+ for (const r of records) forgetPushed(r.id);
278
+ lastSeen = here;
279
+ return true;
280
+ }
281
+
282
+ /**
283
+ * A traversal came to rest on one of our buried entries: carry it on over the run, the
284
+ * way it was going. Back from above (a higher `idx`, or the very entry that buried us)
285
+ * continues down to the page beneath; Forward from that page continues up. A landing
286
+ * from anywhere this cannot place is left alone — one idle press is the price of not
287
+ * guessing, and a wrong guess would move the user somewhere they did not ask to go.
288
+ */
289
+ function skipBuried(from: typeof lastSeen): void {
290
+ const tag = taggedSentinel();
291
+ const rec = tag !== null ? buried.get(tag) : undefined;
292
+ if (!rec || !from || stack.some((e) => e.id === tag)) return;
293
+ let steps = 0;
294
+ if ((from.idx !== undefined && from.idx > rec.idx) || (from.key !== undefined && from.key === rec.aboveKey)) {
295
+ steps = -(rec.below + 1);
296
+ } else if (
297
+ from.idx !== undefined &&
298
+ // Below the run, or level with it when nothing above it is (a push buried it),
299
+ // or level with it and provably the page beneath (a replace buried it). The
300
+ // router's first entry has no key at all, so an absent key is a key here.
301
+ (from.idx < rec.idx || (from.idx === rec.idx && (rec.aboveIdx > rec.idx || from.key === rec.belowKey)))
302
+ ) {
303
+ steps = rec.above + 1;
304
+ }
305
+ if (steps === 0) return;
306
+ pendingProgrammatic += 1;
307
+ window.history.go(steps);
308
+ }
309
+
152
310
  function forgetPushed(id: string): void {
153
311
  const at = pushed.findIndex((p) => p.id === id);
154
312
  if (at >= 0) pushed.splice(at, 1);
@@ -210,7 +368,14 @@ function currentSentinel(): string | null {
210
368
  // belief left over from an overlay that is long gone — a tab that has since
211
369
  // navigated, a test file that ran another case — would be stamped onto a
212
370
  // stranger's entry, and the next cleanup would traverse off it.
213
- if (!pushed.some((p) => p.id === standing)) {
371
+ //
372
+ // And only an entry that is still the one the marker was placed on. A row that
373
+ // NAVIGATES (a palette result, a dialog's link) has the router push the new page
374
+ // before the overlay's cleanup runs; that entry carries no tag either, and
375
+ // re-stamping it made the cleanup "unwind" the navigation itself — the page
376
+ // changed and changed straight back. See `isStillOn` for how the two are told apart.
377
+ const record = pushed.find((p) => p.id === standing);
378
+ if (!record || !isStillOn(record)) {
214
379
  standing = null;
215
380
  return null;
216
381
  }
@@ -223,10 +388,17 @@ function handlePop() {
223
388
  // entry we have landed on before anything below consults it, or the repair in
224
389
  // `currentSentinel` would stamp the entry we just left onto the one we are on.
225
390
  standing = taggedSentinel();
391
+ const from = lastSeen;
392
+ lastSeen = routerMark();
226
393
  if (pendingProgrammatic > 0) {
227
394
  pendingProgrammatic -= 1;
228
395
  return;
229
396
  }
397
+ closeOnPop();
398
+ skipBuried(from);
399
+ }
400
+
401
+ function closeOnPop() {
230
402
  // Nothing open → an ordinary navigation, which is none of our business.
231
403
  const top = stack[stack.length - 1];
232
404
  if (!top) return;
@@ -290,7 +462,8 @@ export function useOverlayHistory(open: boolean, onClose: () => void): void {
290
462
  window.history.replaceState(marked, "");
291
463
  // The abandoned ones beneath come back owed; ours takes the top slot.
292
464
  for (const owed of inherited.slice(0, -1)) pushed.push({ ...owed, dead: true });
293
- pushed.push({ id, href: currentHref(), dead: false });
465
+ const was = inherited[inherited.length - 1];
466
+ pushed.push({ id, href: currentHref(), dead: false, idx: was.idx, key: was.key, route: was.route });
294
467
  standing = id;
295
468
  } else {
296
469
  // A push DESTROYS every entry ahead of the one we are on, so anything we
@@ -304,7 +477,8 @@ export function useOverlayHistory(open: boolean, onClose: () => void): void {
304
477
  // Preserve react-router's own `{usr,key,idx}` — we only tack a marker on, and
305
478
  // the URL is unchanged, so the router treats popping this as a no-op re-render.
306
479
  window.history.pushState(marked, "");
307
- pushed.push({ id, href: currentHref(), dead: false });
480
+ const page = routerMark();
481
+ pushed.push({ id, href: currentHref(), dead: false, idx: page.idx, key: page.key, route: routeOf(currentHref()) });
308
482
  standing = id;
309
483
  }
310
484
  return () => {
@@ -320,6 +494,9 @@ export function useOverlayHistory(open: boolean, onClose: () => void): void {
320
494
  // a SIBLING overlay's sentinel landed on top, that overlay is about to unwind
321
495
  // and is the one that can take ours with it.
322
496
  if (currentSentinel() !== id) {
497
+ // A router navigation from inside the overlay: nothing to unwind — the
498
+ // entries it left beneath the new page are skipped over instead.
499
+ if (mine >= 0 && taggedSentinel() === null && buryUnder(pushed.slice(0, pushed.length))) return;
323
500
  if (mine >= 0) pushed[mine].dead = true;
324
501
  return;
325
502
  }
@@ -9,6 +9,7 @@ import { DEFAULT_PASSWORD_REVEAL_LABELS, DEFAULT_TABS_LABELS } from "../componen
9
9
  import { DEFAULT_WIZARD_LABELS } from "../wizard/types";
10
10
  import { DEFAULT_TOUR_LABELS } from "../tour/tour";
11
11
  import { DEFAULT_COMMAND_PALETTE_LABELS } from "../search/command-palette";
12
+ import { DEFAULT_GLOBAL_SEARCH_LABELS } from "../search/global-search";
12
13
  import { DEFAULT_SERIES_CHART_LABELS } from "../components/series-chart-labels";
13
14
  import { DEFAULT_SPARKLINE_LABELS } from "../components/sparkline";
14
15
  import { DEFAULT_STAT_TILE_LABELS } from "../components/stat-tile";
@@ -71,6 +72,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
71
72
  wizard: DEFAULT_WIZARD_LABELS,
72
73
  tour: DEFAULT_TOUR_LABELS,
73
74
  commandPalette: DEFAULT_COMMAND_PALETTE_LABELS,
75
+ globalSearch: DEFAULT_GLOBAL_SEARCH_LABELS,
74
76
  seriesChart: DEFAULT_SERIES_CHART_LABELS,
75
77
  sparkline: DEFAULT_SPARKLINE_LABELS,
76
78
  statTile: DEFAULT_STAT_TILE_LABELS,
@@ -9,6 +9,7 @@ import type { PasswordRevealLabels, TabsLabels } from "../components/ui";
9
9
  import type { WizardLabels } from "../wizard/types";
10
10
  import type { TourLabels } from "../tour/tour";
11
11
  import type { CommandPaletteLabels } from "../search/command-palette";
12
+ import type { GlobalSearchLabels } from "../search/global-search";
12
13
  import type { MonthPickerLabels } from "../components/month-picker";
13
14
  import type { PageContentsLabels } from "../components/page-contents";
14
15
  import type { SeriesChartLabels } from "../components/series-chart-labels";
@@ -212,6 +213,7 @@ export interface UiKitLabels {
212
213
  wizard: WizardLabels;
213
214
  tour: TourLabels;
214
215
  commandPalette: CommandPaletteLabels;
216
+ globalSearch: GlobalSearchLabels;
215
217
  seriesChart: SeriesChartLabels;
216
218
  sparkline: SparklineLabels;
217
219
  statTile: StatTileLabels;
@@ -302,12 +302,22 @@ export function uiKitLabelsDe(numberLocale = "de-DE"): UiKitLabels {
302
302
  step: (current, total) => `${n(current)} / ${n(total)}`,
303
303
  },
304
304
  commandPalette: {
305
+ clear: "Suche löschen",
306
+ submit: "Suchen",
307
+ close: "Schließen",
305
308
  placeholder: "Suchen…",
306
309
  empty: "Keine Ergebnisse",
307
310
  loading: "Wird gesucht…",
308
311
  dialog: "Suche",
309
312
  error: "Die Suche ist fehlgeschlagen. Bitte erneut versuchen.",
310
313
  },
314
+ globalSearch: {
315
+ trigger: "Suche",
316
+ placeholder: "Suchen oder springen zu…",
317
+ shortcut: (keys) => `Suche (${keys})`,
318
+ suggestions: "Vorschläge",
319
+ results: "Ergebnisse",
320
+ },
311
321
  sparkline: {
312
322
  rising: (first, last) => `Steigend von ${first} auf ${last}`,
313
323
  falling: (first, last) => `Fallend von ${first} auf ${last}`,
@@ -295,12 +295,22 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
295
295
  step: (current, total) => `${n(current)} / ${n(total)}`,
296
296
  },
297
297
  commandPalette: {
298
+ clear: "Borrar búsqueda",
299
+ submit: "Buscar",
300
+ close: "Cerrar",
298
301
  placeholder: "Buscar…",
299
302
  empty: "Sin resultados",
300
303
  loading: "Buscando…",
301
304
  dialog: "Búsqueda",
302
305
  error: "La búsqueda ha fallado. Inténtelo de nuevo.",
303
306
  },
307
+ globalSearch: {
308
+ trigger: "Buscar",
309
+ placeholder: "Buscar o ir a…",
310
+ shortcut: (keys) => `Buscar (${keys})`,
311
+ suggestions: "Pruebe",
312
+ results: "Resultados",
313
+ },
304
314
  sparkline: {
305
315
  rising: (first, last) => `Sube de ${first} a ${last}`,
306
316
  falling: (first, last) => `Baja de ${first} a ${last}`,
@@ -307,12 +307,22 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
307
307
  step: (current, total) => `${n(current)} / ${n(total)}`,
308
308
  },
309
309
  commandPalette: {
310
+ clear: "Effacer la recherche",
311
+ submit: "Rechercher",
312
+ close: "Fermer",
310
313
  placeholder: "Rechercher…",
311
314
  empty: "Aucun résultat",
312
315
  loading: "Recherche…",
313
316
  dialog: "Recherche",
314
317
  error: "La recherche a échoué. Réessayez.",
315
318
  },
319
+ globalSearch: {
320
+ trigger: "Rechercher",
321
+ placeholder: "Rechercher ou aller à…",
322
+ shortcut: (keys) => `Rechercher (${keys})`,
323
+ suggestions: "Essayez",
324
+ results: "Résultats",
325
+ },
316
326
  sparkline: {
317
327
  // "En hausse de 12 à 40" would read as "up BY 12", hence the colon.
318
328
  rising: (first, last) => `En hausse\u00a0: de ${first} à ${last}`,
@@ -297,12 +297,22 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
297
297
  step: (current, total) => `${n(current)} / ${n(total)}`,
298
298
  },
299
299
  commandPalette: {
300
+ clear: "Keresés törlése",
301
+ submit: "Keresés",
302
+ close: "Bezárás",
300
303
  placeholder: "Keresés…",
301
304
  empty: "Nincs találat",
302
305
  loading: "Keresés folyamatban…",
303
306
  dialog: "Keresés",
304
307
  error: "A keresés nem sikerült. Próbálja újra.",
305
308
  },
309
+ globalSearch: {
310
+ trigger: "Keresés",
311
+ placeholder: "Keresés vagy ugrás…",
312
+ shortcut: (keys) => `Keresés (${keys})`,
313
+ suggestions: "Próbálja ki",
314
+ results: "Találatok",
315
+ },
306
316
  sparkline: {
307
317
  // Numerals stay bare (see 3. above): "12-ről 40-re" would need vowel harmony.
308
318
  rising: (first, last) => `Emelkedik (első érték: ${first}, utolsó: ${last})`,
@@ -297,12 +297,22 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
297
297
  step: (current, total) => `${n(current)} / ${n(total)}`,
298
298
  },
299
299
  commandPalette: {
300
+ clear: "Cancella ricerca",
301
+ submit: "Cerca",
302
+ close: "Chiudi",
300
303
  placeholder: "Cerca…",
301
304
  empty: "Nessun risultato",
302
305
  loading: "Ricerca in corso…",
303
306
  dialog: "Ricerca",
304
307
  error: "Ricerca non riuscita. Riprova.",
305
308
  },
309
+ globalSearch: {
310
+ trigger: "Cerca",
311
+ placeholder: "Cerca o vai a…",
312
+ shortcut: (keys) => `Cerca (${keys})`,
313
+ suggestions: "Prova",
314
+ results: "Risultati",
315
+ },
306
316
  sparkline: {
307
317
  rising: (first, last) => `In aumento da ${first} a ${last}`,
308
318
  falling: (first, last) => `In calo da ${first} a ${last}`,
@@ -287,12 +287,22 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
287
287
  step: (current, total) => `${n(current)} / ${n(total)}`,
288
288
  },
289
289
  commandPalette: {
290
+ clear: "清除搜索",
291
+ submit: "搜索",
292
+ close: "关闭",
290
293
  placeholder: "搜索…",
291
294
  empty: "无结果",
292
295
  loading: "搜索中…",
293
296
  dialog: "搜索",
294
297
  error: "搜索失败,请重试。",
295
298
  },
299
+ globalSearch: {
300
+ trigger: "搜索",
301
+ placeholder: "搜索或跳转到…",
302
+ shortcut: (keys) => `搜索(${keys})`,
303
+ suggestions: "试试",
304
+ results: "结果",
305
+ },
296
306
  sparkline: {
297
307
  rising: (first, last) => `从 ${first} 上升到 ${last}`,
298
308
  falling: (first, last) => `从 ${first} 下降到 ${last}`,
package/src/index.ts CHANGED
@@ -163,6 +163,8 @@ export { FilterPopover } from "./components/data-table-filter-popover";
163
163
  export { DataTable } from "./components/data-table";
164
164
  export type {
165
165
  DataTableColumn,
166
+ DataTableCellProps,
167
+ DataTableHeadProps,
166
168
  DataTableProps,
167
169
  ServerPagination,
168
170
  FilterState,
@@ -217,6 +219,8 @@ export * from "./tour/tour";
217
219
 
218
220
  // ── command palette / global search ──────────────────────────────────────────
219
221
  export * from "./search/command-palette";
222
+ export * from "./search/search-index";
223
+ export * from "./search/global-search";
220
224
 
221
225
  // ── i18n: one label tree, one optional provider ──────────────────────────────
222
226
  // `UiKitLabels` names every string the kit renders; `<UiKitProvider labels locale>`