@gntik-ai/blocks 0.5.0 → 0.6.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 (36) hide show
  1. package/dist/index.d.ts +163 -31
  2. package/dist/index.js +663 -157
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/auth/SignUpForm/SignUpForm.configured.test.tsx +316 -0
  6. package/src/auth/SignUpForm/SignUpForm.tsx +194 -50
  7. package/src/auth/SignUpForm/block.meta.ts +15 -2
  8. package/src/auth/SignUpForm/examples/CustomFields.tsx +56 -0
  9. package/src/auth/SignUpForm/examples/Error.tsx +5 -0
  10. package/src/auth/SignUpForm/examples/Loading.tsx +6 -0
  11. package/src/auth/SignUpForm/examples/NoTerms.tsx +6 -0
  12. package/src/auth/SignUpForm/examples/Submitting.tsx +5 -0
  13. package/src/auth/SignUpForm/examples/Unavailable.tsx +15 -0
  14. package/src/auth/index.ts +1 -0
  15. package/src/auth/shared.tsx +34 -0
  16. package/src/meta.ts +2 -0
  17. package/src/tables/DataTable/DataTable.labels.test.tsx +291 -0
  18. package/src/tables/DataTable/DataTable.overflow-labels.test.tsx +59 -0
  19. package/src/tables/DataTable/DataTable.tsx +20 -9
  20. package/src/tables/DataTable/block.meta.ts +102 -0
  21. package/src/tables/DataTable/column-menu.tsx +10 -7
  22. package/src/tables/DataTable/data-table-body.tsx +3 -2
  23. package/src/tables/DataTable/data-table-props.ts +3 -0
  24. package/src/tables/DataTable/data-table-row.tsx +15 -4
  25. package/src/tables/DataTable/density-toggle.tsx +7 -4
  26. package/src/tables/DataTable/editable-cell.tsx +21 -8
  27. package/src/tables/DataTable/header-cell.tsx +4 -0
  28. package/src/tables/DataTable/labels.ts +54 -0
  29. package/src/tables/DataTable/resize-handle.tsx +6 -3
  30. package/src/tables/DataTable/views.tsx +21 -14
  31. package/src/tables/FilterBar/FilterBar.labels.test.tsx +69 -0
  32. package/src/tables/FilterBar/FilterBar.tsx +45 -12
  33. package/src/tables/FilterBar/block.meta.ts +22 -0
  34. package/src/tables/PaginationFooter/PaginationFooter.labels.test.tsx +57 -0
  35. package/src/tables/PaginationFooter/PaginationFooter.tsx +31 -7
  36. package/src/tables/PaginationFooter/block.meta.ts +23 -0
package/dist/index.js CHANGED
@@ -1671,6 +1671,12 @@ function authIdentifierError(value, config, messages) {
1671
1671
  if ((config?.type ?? "email") === "email" && !EMAIL_RE$1.test(trimmed) || config?.maxLength !== void 0 && trimmed.length > config.maxLength) return messages?.identifierInvalid ?? fallback;
1672
1672
  return null;
1673
1673
  }
1674
+ function authSignUpFieldError(value, field) {
1675
+ const trimmed = value.trim();
1676
+ if (!trimmed) return field.required ? field.messages?.required ?? `Enter ${field.label}.` : null;
1677
+ if (field.type === "email" && !EMAIL_RE$1.test(trimmed)) return field.messages?.invalid ?? "Enter a valid email address.";
1678
+ return null;
1679
+ }
1674
1680
  //#endregion
1675
1681
  //#region src/auth/ForgotPasswordForm/ForgotPasswordForm.tsx
1676
1682
  /** Request a reset link by email, then a "check your inbox" state with resend and a way back. */
@@ -2351,10 +2357,85 @@ const meta$59 = {
2351
2357
  ]
2352
2358
  };
2353
2359
  //#endregion
2360
+ //#region src/forms/ValidationSummary/fixtures.ts
2361
+ const validationIssues = [
2362
+ {
2363
+ fieldId: "project-name",
2364
+ label: "Project name",
2365
+ message: "Enter a project name."
2366
+ },
2367
+ {
2368
+ fieldId: "billing-email",
2369
+ label: "Billing email",
2370
+ message: "Enter a valid email address, like name@company.com."
2371
+ },
2372
+ {
2373
+ fieldId: "region",
2374
+ label: "Region",
2375
+ message: "Choose a region."
2376
+ }
2377
+ ];
2378
+ //#endregion
2379
+ //#region src/forms/ValidationSummary/ValidationSummary.tsx
2380
+ const focusField = (fieldId) => {
2381
+ const el = document.getElementById(fieldId);
2382
+ if (!el) return;
2383
+ el.focus();
2384
+ el.scrollIntoView?.({ block: "center" });
2385
+ };
2386
+ /**
2387
+ * Error summary for long forms: lists every problem with a link that moves focus to the field.
2388
+ * After a failed submit it takes focus, so screen readers announce it.
2389
+ */
2390
+ function ValidationSummary({ errors = validationIssues, submitCount = 0, title, className }) {
2391
+ const ref = useRef(null);
2392
+ const hasErrors = errors.length > 0;
2393
+ useEffect(() => {
2394
+ if (submitCount > 0 && hasErrors) ref.current?.focus();
2395
+ }, [submitCount, hasErrors]);
2396
+ if (!hasErrors) return null;
2397
+ const heading = title ?? `There ${errors.length === 1 ? "is 1 problem" : `are ${errors.length} problems`} with this form`;
2398
+ return /* @__PURE__ */ jsx(Alert, {
2399
+ ref,
2400
+ tabIndex: -1,
2401
+ role: "region",
2402
+ tone: "destructive",
2403
+ title: heading,
2404
+ className: cn("focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring", className),
2405
+ children: /* @__PURE__ */ jsx("ul", {
2406
+ className: "mt-1 list-disc space-y-1 ps-4",
2407
+ children: errors.map((e) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
2408
+ href: `#${e.fieldId}`,
2409
+ tone: "inherit",
2410
+ underline: "always",
2411
+ onClick: (ev) => {
2412
+ ev.preventDefault();
2413
+ focusField(e.fieldId);
2414
+ },
2415
+ children: [e.label ? `${e.label}: ` : "", e.message]
2416
+ }) }, e.fieldId))
2417
+ })
2418
+ });
2419
+ }
2420
+ //#endregion
2354
2421
  //#region src/auth/SignUpForm/SignUpForm.tsx
2355
2422
  /** Account creation: name, work email, password with a strength meter and a required terms checkbox. */
2356
- function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPasswordLength = 8, termsHref = "#", privacyHref = "#", signInHref = "#", onSignIn, sso = /* @__PURE__ */ jsx(SsoButtons, {}), eyebrow = "Get started", title = "Create your account", description = "Set up a workspace for your team in a couple of minutes.", className }) {
2423
+ function SignUpForm({ fields, messages, unavailable, policyLoading = false, onSubmit, error: errorProp, loading: loadingProp, minPasswordLength = 8, passwordHelp, terms: termsProp, termsHref = "#", privacyHref = "#", signInHref = "#", onSignIn, sso = /* @__PURE__ */ jsx(SsoButtons, {}), eyebrow = "Get started", title = "Create your account", description = "Set up a workspace for your team in a couple of minutes.", className }) {
2357
2424
  const termsErrorId = useId();
2425
+ const fieldPrefix = useId();
2426
+ const passwordId = `${fieldPrefix}-password`;
2427
+ const termsId = `${fieldPrefix}-terms`;
2428
+ const [fieldValues, setFieldValues] = useState({});
2429
+ const [submitCount, setSubmitCount] = useState(0);
2430
+ const fieldValue = (id) => Object.hasOwn(fieldValues, id) ? fieldValues[id] ?? "" : "";
2431
+ const configuredErrors = (fields ?? []).flatMap((field) => {
2432
+ const message = authSignUpFieldError(fieldValue(field.id), field);
2433
+ return message ? [{
2434
+ fieldId: `${fieldPrefix}-field-${field.id}`,
2435
+ label: field.label,
2436
+ message
2437
+ }] : [];
2438
+ });
2358
2439
  const [name, setName] = useState("");
2359
2440
  const [email, setEmail] = useState("");
2360
2441
  const [password, setPassword] = useState("");
@@ -2365,8 +2446,62 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2365
2446
  const alert = errorProp ?? error;
2366
2447
  const nameInvalid = submitted && name.trim() === "";
2367
2448
  const emailInvalid = submitted && !EMAIL_RE$1.test(email.trim());
2368
- const passwordInvalid = submitted && password.length < minPasswordLength;
2369
- const termsInvalid = submitted && !terms;
2449
+ const passwordInvalid = (submitted || fields !== void 0 && password.length > 0) && password.length < minPasswordLength;
2450
+ const termsInvalid = submitted && termsProp !== false && !terms;
2451
+ const passwordError = messages?.passwordTooShort?.(minPasswordLength) ?? `Use at least ${minPasswordLength} characters.`;
2452
+ const termsError = messages?.termsRequired ?? "Accept the terms to continue.";
2453
+ const summaryErrors = [
2454
+ ...configuredErrors,
2455
+ ...password.length < minPasswordLength ? [{
2456
+ fieldId: passwordId,
2457
+ label: "Password",
2458
+ message: passwordError
2459
+ }] : [],
2460
+ ...termsProp !== false && !terms ? [{
2461
+ fieldId: termsId,
2462
+ label: "Terms",
2463
+ message: termsError
2464
+ }] : []
2465
+ ];
2466
+ if (unavailable) return /* @__PURE__ */ jsx("div", {
2467
+ className: cn(authFormClass, className),
2468
+ children: /* @__PURE__ */ jsx(Alert, {
2469
+ tone: "info",
2470
+ title: unavailable.title,
2471
+ description: unavailable.message,
2472
+ actions: unavailable.action
2473
+ })
2474
+ });
2475
+ if (policyLoading) return /* @__PURE__ */ jsxs("div", {
2476
+ className: cn(authFormClass, "flex flex-col gap-4", className),
2477
+ children: [
2478
+ /* @__PURE__ */ jsx("span", {
2479
+ role: "status",
2480
+ className: "sr-only",
2481
+ children: "Loading sign-up policy…"
2482
+ }),
2483
+ /* @__PURE__ */ jsx(Skeleton, {
2484
+ "aria-hidden": true,
2485
+ className: "h-8 w-3/4"
2486
+ }),
2487
+ /* @__PURE__ */ jsx(Skeleton, {
2488
+ "aria-hidden": true,
2489
+ className: "h-4 w-full"
2490
+ }),
2491
+ /* @__PURE__ */ jsx(Skeleton, {
2492
+ "aria-hidden": true,
2493
+ className: "h-12 w-full"
2494
+ }),
2495
+ /* @__PURE__ */ jsx(Skeleton, {
2496
+ "aria-hidden": true,
2497
+ className: "h-12 w-full"
2498
+ }),
2499
+ /* @__PURE__ */ jsx(Skeleton, {
2500
+ "aria-hidden": true,
2501
+ className: "h-12 w-full"
2502
+ })
2503
+ ]
2504
+ });
2370
2505
  return /* @__PURE__ */ jsxs("div", {
2371
2506
  className: cn(authFormClass, className),
2372
2507
  children: [
@@ -2382,7 +2517,19 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2382
2517
  onSubmit: (e) => {
2383
2518
  e.preventDefault();
2384
2519
  setSubmitted(true);
2385
- if (name.trim() === "" || !EMAIL_RE$1.test(email.trim()) || password.length < minPasswordLength || !terms) return;
2520
+ if (fields !== void 0) {
2521
+ setSubmitCount((count) => count + 1);
2522
+ if (summaryErrors.length > 0) return;
2523
+ const values = Object.fromEntries(fields.map((field) => [field.id, fieldValue(field.id).trim()]));
2524
+ run(() => onSubmit?.({
2525
+ name: values.name ?? "",
2526
+ email: values.email ?? "",
2527
+ password,
2528
+ fields: values
2529
+ }));
2530
+ return;
2531
+ }
2532
+ if (name.trim() === "" || !EMAIL_RE$1.test(email.trim()) || password.length < minPasswordLength || termsProp !== false && !terms) return;
2386
2533
  run(() => onSubmit?.({
2387
2534
  name: name.trim(),
2388
2535
  email: email.trim(),
@@ -2394,7 +2541,38 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2394
2541
  tone: "destructive",
2395
2542
  description: alert
2396
2543
  }),
2397
- /* @__PURE__ */ jsxs(Field, {
2544
+ fields !== void 0 && submitted && /* @__PURE__ */ jsx(ValidationSummary, {
2545
+ errors: summaryErrors,
2546
+ submitCount,
2547
+ className: "bg-background"
2548
+ }),
2549
+ fields !== void 0 ? fields.map((field) => {
2550
+ const message = authSignUpFieldError(fieldValue(field.id), field);
2551
+ const invalid = submitted && message !== null;
2552
+ return /* @__PURE__ */ jsxs(Field, {
2553
+ invalid,
2554
+ children: [
2555
+ /* @__PURE__ */ jsx(FieldLabel, { children: field.label }),
2556
+ /* @__PURE__ */ jsx(Input, {
2557
+ id: `${fieldPrefix}-field-${field.id}`,
2558
+ type: field.type ?? "text",
2559
+ autoComplete: field.autoComplete,
2560
+ placeholder: field.placeholder,
2561
+ required: field.required,
2562
+ value: fieldValue(field.id),
2563
+ onValueChange: (value) => setFieldValues((values) => ({
2564
+ ...values,
2565
+ [field.id]: value
2566
+ }))
2567
+ }),
2568
+ field.help != null && /* @__PURE__ */ jsx(FieldDescription, { children: field.help }),
2569
+ /* @__PURE__ */ jsx(FieldError, {
2570
+ match: invalid,
2571
+ children: message
2572
+ })
2573
+ ]
2574
+ }, field.id);
2575
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Field, {
2398
2576
  invalid: nameInvalid,
2399
2577
  children: [
2400
2578
  /* @__PURE__ */ jsx(FieldLabel, { children: "Full name" }),
@@ -2411,8 +2589,7 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2411
2589
  children: "Enter your name."
2412
2590
  })
2413
2591
  ]
2414
- }),
2415
- /* @__PURE__ */ jsxs(Field, {
2592
+ }), /* @__PURE__ */ jsxs(Field, {
2416
2593
  invalid: emailInvalid,
2417
2594
  children: [
2418
2595
  /* @__PURE__ */ jsx(FieldLabel, { children: "Work email" }),
@@ -2430,28 +2607,28 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2430
2607
  children: "Enter a valid email address."
2431
2608
  })
2432
2609
  ]
2433
- }),
2610
+ })] }),
2434
2611
  /* @__PURE__ */ jsxs(Field, {
2435
2612
  invalid: passwordInvalid,
2436
2613
  children: [
2437
2614
  /* @__PURE__ */ jsx(FieldLabel, { children: "Password" }),
2438
2615
  /* @__PURE__ */ jsx(PasswordInput, {
2616
+ id: fields !== void 0 ? passwordId : void 0,
2439
2617
  showStrength: true,
2618
+ strength: password.length > 0 && password.length < minPasswordLength ? 0 : void 0,
2440
2619
  value: password,
2441
2620
  onValueChange: setPassword,
2442
2621
  required: true
2443
2622
  }),
2444
- /* @__PURE__ */ jsxs(FieldError, {
2623
+ passwordHelp != null && /* @__PURE__ */ jsx(FieldDescription, { children: typeof passwordHelp === "function" ? passwordHelp(minPasswordLength) : passwordHelp }),
2624
+ /* @__PURE__ */ jsx(FieldError, {
2445
2625
  match: passwordInvalid,
2446
- children: [
2447
- "Use at least ",
2448
- minPasswordLength,
2449
- " characters."
2450
- ]
2626
+ children: passwordError
2451
2627
  })
2452
2628
  ]
2453
2629
  }),
2454
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Checkbox, {
2630
+ termsProp !== false && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Checkbox, {
2631
+ render: fields !== void 0 ? /* @__PURE__ */ jsx("span", { id: termsId }) : void 0,
2455
2632
  checked: terms,
2456
2633
  onCheckedChange: setTerms,
2457
2634
  "aria-invalid": termsInvalid || void 0,
@@ -2472,7 +2649,7 @@ function SignUpForm({ onSubmit, error: errorProp, loading: loadingProp, minPassw
2472
2649
  }), termsInvalid && /* @__PURE__ */ jsx("p", {
2473
2650
  id: termsErrorId,
2474
2651
  className: "mt-1.5 text-[12.5px] text-destructive-text",
2475
- children: "Accept the terms to continue."
2652
+ children: termsError
2476
2653
  })] }),
2477
2654
  /* @__PURE__ */ jsx(Button, {
2478
2655
  type: "submit",
@@ -2508,7 +2685,7 @@ const meta$60 = {
2508
2685
  name: "Sign-up form",
2509
2686
  family: "auth",
2510
2687
  status: "stable",
2511
- description: "Account creation with name, email, a password strength meter and a required terms checkbox.",
2688
+ description: "Account creation with default name/email fields or ordered text/email fields (values.fields), a password strength meter and required consent (terms=false opts out). minPasswordLength defaults to 8 and drives length validation and the insufficient-length meter; optional passwordHelp can receive the minimum. Configured fields support help, required, autoComplete and validation messages, with a linked validation summary on invalid submit. policyLoading renders skeletons without a form; unavailable replaces the form with consumer title, message and optional action. loading remains submitting; error is form-level feedback. Examples: custom-fields, no-terms, unavailable, loading, submitting and error.",
2512
2689
  uses: [
2513
2690
  "Field",
2514
2691
  "Input",
@@ -2517,7 +2694,10 @@ const meta$60 = {
2517
2694
  "Button",
2518
2695
  "Alert",
2519
2696
  "Link",
2520
- "SsoButtons"
2697
+ "SsoButtons",
2698
+ "Skeleton",
2699
+ "FieldDescription",
2700
+ "ValidationSummary"
2521
2701
  ]
2522
2702
  };
2523
2703
  //#endregion
@@ -8486,67 +8666,6 @@ const meta$58 = {
8486
8666
  uses: ["Switch"]
8487
8667
  };
8488
8668
  //#endregion
8489
- //#region src/forms/ValidationSummary/fixtures.ts
8490
- const validationIssues = [
8491
- {
8492
- fieldId: "project-name",
8493
- label: "Project name",
8494
- message: "Enter a project name."
8495
- },
8496
- {
8497
- fieldId: "billing-email",
8498
- label: "Billing email",
8499
- message: "Enter a valid email address, like name@company.com."
8500
- },
8501
- {
8502
- fieldId: "region",
8503
- label: "Region",
8504
- message: "Choose a region."
8505
- }
8506
- ];
8507
- //#endregion
8508
- //#region src/forms/ValidationSummary/ValidationSummary.tsx
8509
- const focusField = (fieldId) => {
8510
- const el = document.getElementById(fieldId);
8511
- if (!el) return;
8512
- el.focus();
8513
- el.scrollIntoView?.({ block: "center" });
8514
- };
8515
- /**
8516
- * Error summary for long forms: lists every problem with a link that moves focus to the field.
8517
- * After a failed submit it takes focus, so screen readers announce it.
8518
- */
8519
- function ValidationSummary({ errors = validationIssues, submitCount = 0, title, className }) {
8520
- const ref = useRef(null);
8521
- const hasErrors = errors.length > 0;
8522
- useEffect(() => {
8523
- if (submitCount > 0 && hasErrors) ref.current?.focus();
8524
- }, [submitCount, hasErrors]);
8525
- if (!hasErrors) return null;
8526
- const heading = title ?? `There ${errors.length === 1 ? "is 1 problem" : `are ${errors.length} problems`} with this form`;
8527
- return /* @__PURE__ */ jsx(Alert, {
8528
- ref,
8529
- tabIndex: -1,
8530
- role: "region",
8531
- tone: "destructive",
8532
- title: heading,
8533
- className: cn("focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring", className),
8534
- children: /* @__PURE__ */ jsx("ul", {
8535
- className: "mt-1 list-disc space-y-1 ps-4",
8536
- children: errors.map((e) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
8537
- href: `#${e.fieldId}`,
8538
- tone: "inherit",
8539
- underline: "always",
8540
- onClick: (ev) => {
8541
- ev.preventDefault();
8542
- focusField(e.fieldId);
8543
- },
8544
- children: [e.label ? `${e.label}: ` : "", e.message]
8545
- }) }, e.fieldId))
8546
- })
8547
- });
8548
- }
8549
- //#endregion
8550
8669
  //#region src/forms/DialogWizard/DialogWizard.tsx
8551
8670
  /** Modal variant of create-wizard for a flow over its parent page. State stays consumer-owned. */
8552
8671
  function DialogWizard({ steps, current = 0, size = "dialog", stepError, reviewStep, children, title, ...props }) {
@@ -11652,7 +11771,7 @@ function PaginationFooter({ total = 97, page: pageProp, defaultPage = 1, onPageC
11652
11771
  25,
11653
11772
  50,
11654
11773
  100
11655
- ], noun: nounProp, locale: localeProp, className }) {
11774
+ ], noun: nounProp, locale: localeProp, className, labels }) {
11656
11775
  const { t, locale: providerLocale } = useI18n();
11657
11776
  const locale = localeProp ?? providerLocale;
11658
11777
  const noun = nounProp ?? t("table.rows");
@@ -11683,9 +11802,9 @@ function PaginationFooter({ total = 97, page: pageProp, defaultPage = 1, onPageC
11683
11802
  children: [/* @__PURE__ */ jsx("span", {
11684
11803
  className: "text-[12.5px] text-muted-foreground",
11685
11804
  "aria-hidden": true,
11686
- children: t("table.rowsPerPage")
11805
+ children: labels?.rowsPerPage ?? t("table.rowsPerPage")
11687
11806
  }), /* @__PURE__ */ jsx(SimpleSelect, {
11688
- "aria-label": t("table.rowsPerPage"),
11807
+ "aria-label": labels?.rowsPerPageLabel ?? t("table.rowsPerPage"),
11689
11808
  size: "sm",
11690
11809
  className: "w-[76px] font-mono",
11691
11810
  value: String(pageSize),
@@ -11697,10 +11816,10 @@ function PaginationFooter({ total = 97, page: pageProp, defaultPage = 1, onPageC
11697
11816
  })]
11698
11817
  }), /* @__PURE__ */ jsxs("div", {
11699
11818
  className: "flex flex-col gap-3 sm:flex-row sm:items-center",
11700
- children: [/* @__PURE__ */ jsxs("p", {
11819
+ children: [/* @__PURE__ */ jsx("p", {
11701
11820
  className: "text-[12.5px] text-muted-foreground tabular-nums",
11702
11821
  "aria-live": "polite",
11703
- children: [
11822
+ children: labels?.rangeSummary ? labels.rangeSummary(from, to, total) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
11704
11823
  /* @__PURE__ */ jsxs("span", {
11705
11824
  className: "font-mono text-foreground",
11706
11825
  children: [
@@ -11716,13 +11835,18 @@ function PaginationFooter({ total = 97, page: pageProp, defaultPage = 1, onPageC
11716
11835
  className: "font-mono text-foreground",
11717
11836
  children: fmt(total)
11718
11837
  })
11719
- ]
11838
+ ] })
11720
11839
  }), /* @__PURE__ */ jsx(Pagination, {
11721
11840
  page,
11722
11841
  pageCount,
11723
11842
  onPageChange: goTo,
11724
11843
  size: "sm",
11725
- labels: { nav: t("table.pagination", { noun }) }
11844
+ labels: {
11845
+ nav: labels?.paginationNav?.(noun) ?? t("table.pagination", { noun }),
11846
+ previous: labels?.previous,
11847
+ next: labels?.next,
11848
+ page: labels?.page
11849
+ }
11726
11850
  })]
11727
11851
  })]
11728
11852
  });
@@ -11734,9 +11858,9 @@ const PIN_NONE = "none";
11734
11858
  * "Columns" menu: one checkbox item per hideable column (the last visible column cannot be
11735
11859
  * hidden) and, when pinning is on, a submenu per visible column to pin it left, right or unpin it.
11736
11860
  */
11737
- function ColumnVisibilityMenu({ columns, hidden, onToggle, label: labelProp, pinOf, onPin }) {
11861
+ function ColumnVisibilityMenu({ columns, hidden, onToggle, label: labelProp, labels, pinOf, onPin }) {
11738
11862
  const { t } = useI18n();
11739
- const label = labelProp ?? t("table.columns");
11863
+ const label = labels?.columns ?? labelProp ?? t("table.columns");
11740
11864
  const visibleCount = columns.filter((c) => !hidden.has(c.id)).length;
11741
11865
  const pinnable = onPin && pinOf ? columns.filter((c) => c.pinnable !== false && !hidden.has(c.id)) : [];
11742
11866
  return /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, {
@@ -11750,7 +11874,7 @@ function ColumnVisibilityMenu({ columns, hidden, onToggle, label: labelProp, pin
11750
11874
  }), /* @__PURE__ */ jsxs(MenuContent, {
11751
11875
  align: "end",
11752
11876
  className: "min-w-[200px]",
11753
- children: [/* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: t("table.visibleColumns") }), columns.filter((c) => c.hideable !== false).map((c) => {
11877
+ children: [/* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: labels?.visibleColumns ?? t("table.visibleColumns") }), columns.filter((c) => c.hideable !== false).map((c) => {
11754
11878
  const visible = !hidden.has(c.id);
11755
11879
  return /* @__PURE__ */ jsx(MenuCheckboxItem, {
11756
11880
  checked: visible,
@@ -11758,26 +11882,26 @@ function ColumnVisibilityMenu({ columns, hidden, onToggle, label: labelProp, pin
11758
11882
  onCheckedChange: (on) => onToggle(c.id, on),
11759
11883
  children: c.header
11760
11884
  }, c.id);
11761
- })] }), pinnable.length > 0 && onPin && pinOf && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: "Pin columns" }), pinnable.map((c) => {
11885
+ })] }), pinnable.length > 0 && onPin && pinOf && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: labels?.pinColumns ?? "Pin columns" }), pinnable.map((c) => {
11762
11886
  const side = pinOf(c.id);
11763
11887
  return /* @__PURE__ */ jsxs(MenuSub, { children: [/* @__PURE__ */ jsxs(MenuSubTrigger, { children: [c.header, side && /* @__PURE__ */ jsx("span", {
11764
11888
  className: "ms-auto ps-3 text-[11.5px] text-muted-foreground",
11765
- children: side === "left" ? "Left" : "Right"
11889
+ children: side === "left" ? labels?.pinnedLeft ?? "Left" : labels?.pinnedRight ?? "Right"
11766
11890
  })] }), /* @__PURE__ */ jsx(MenuSubContent, { children: /* @__PURE__ */ jsxs(MenuRadioGroup, {
11767
11891
  value: side ?? PIN_NONE,
11768
11892
  onValueChange: (v) => onPin(c.id, v === "left" || v === "right" ? v : null),
11769
11893
  children: [
11770
11894
  /* @__PURE__ */ jsx(MenuRadioItem, {
11771
11895
  value: "left",
11772
- children: "Pin left"
11896
+ children: labels?.pinLeft ?? "Pin left"
11773
11897
  }),
11774
11898
  /* @__PURE__ */ jsx(MenuRadioItem, {
11775
11899
  value: "right",
11776
- children: "Pin right"
11900
+ children: labels?.pinRight ?? "Pin right"
11777
11901
  }),
11778
11902
  /* @__PURE__ */ jsx(MenuRadioItem, {
11779
11903
  value: PIN_NONE,
11780
- children: "Unpinned"
11904
+ children: labels?.unpinned ?? "Unpinned"
11781
11905
  })
11782
11906
  ]
11783
11907
  }) })] }, c.id);
@@ -12049,7 +12173,7 @@ function focusSibling(from, dir) {
12049
12173
  * edits); while editing, an input or a select: Enter or Tab commits, Escape cancels. `onCellEdit`
12050
12174
  * can reject the value with a message, shown inline under the editor.
12051
12175
  */
12052
- function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, children }) {
12176
+ function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, children, labels }) {
12053
12177
  const errorId = useId();
12054
12178
  const editor = column.editor ?? { type: "text" };
12055
12179
  const [editing, setEditing] = useState(false);
@@ -12097,15 +12221,15 @@ function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, childr
12097
12221
  const n = Number(text);
12098
12222
  if (!Number.isFinite(n)) return {
12099
12223
  ok: false,
12100
- message: "Enter a number."
12224
+ message: labels?.enterNumber ?? "Enter a number."
12101
12225
  };
12102
12226
  if (editor.min != null && n < editor.min) return {
12103
12227
  ok: false,
12104
- message: `Enter ${editor.min} or more.`
12228
+ message: labels?.minimumValue?.(editor.min) ?? `Enter ${editor.min} or more.`
12105
12229
  };
12106
12230
  if (editor.max != null && n > editor.max) return {
12107
12231
  ok: false,
12108
- message: `Enter ${editor.max} or less.`
12232
+ message: labels?.maximumValue?.(editor.max) ?? `Enter ${editor.max} or less.`
12109
12233
  };
12110
12234
  return {
12111
12235
  ok: true,
@@ -12139,11 +12263,11 @@ function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, childr
12139
12263
  previous
12140
12264
  });
12141
12265
  } catch (err) {
12142
- result = err instanceof Error && err.message ? err.message : GENERIC_ERROR;
12266
+ result = err instanceof Error && err.message ? err.message : labels?.editError ?? GENERIC_ERROR;
12143
12267
  }
12144
12268
  busy.current = false;
12145
12269
  setPending(false);
12146
- if (result === false) result = GENERIC_ERROR;
12270
+ if (result === false) result = labels?.editError ?? GENERIC_ERROR;
12147
12271
  if (typeof result === "string") {
12148
12272
  setError(result);
12149
12273
  if (after) field.current?.focus();
@@ -12186,7 +12310,7 @@ function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, childr
12186
12310
  children
12187
12311
  });
12188
12312
  const common = {
12189
- "aria-label": `${column.header} for ${rowLabel}`,
12313
+ "aria-label": labels?.editLabel?.(column.header, rowLabel) ?? `${column.header} for ${rowLabel}`,
12190
12314
  "aria-invalid": error ? true : void 0,
12191
12315
  "aria-describedby": error ? errorId : void 0,
12192
12316
  "aria-busy": pending || void 0,
@@ -12226,7 +12350,7 @@ function EditableCell({ row, rowId, rowLabel, column, onCellEdit, hintId, childr
12226
12350
  }), error && /* @__PURE__ */ jsx("p", {
12227
12351
  id: errorId,
12228
12352
  role: "alert",
12229
- className: "mt-1 text-start text-[11.5px] font-medium whitespace-normal text-destructive-text",
12353
+ className: "mt-1 rounded-sm border border-border bg-popover px-1.5 py-1 text-start text-[11.5px] font-medium whitespace-normal text-destructive-text",
12230
12354
  children: error
12231
12355
  })]
12232
12356
  });
@@ -12237,7 +12361,7 @@ function isCellEditable(column, row) {
12237
12361
  }
12238
12362
  //#endregion
12239
12363
  //#region src/tables/DataTable/data-table-row.tsx
12240
- function DataTableRow({ row, columns, getRowId, getRowLabel, selectable, selected, singleSelection, onToggleRow, rowActions, layout, onCellEdit, editHintId, ariaRowIndex, style }) {
12364
+ function DataTableRow({ labels, row, columns, getRowId, getRowLabel, selectable, selected, singleSelection, onToggleRow, rowActions, layout, onCellEdit, editHintId, ariaRowIndex, style }) {
12241
12365
  const { t } = useI18n();
12242
12366
  const id = getRowId(row);
12243
12367
  const selectionColumn = selectable || singleSelection != null;
@@ -12261,7 +12385,7 @@ function DataTableRow({ row, columns, getRowId, getRowLabel, selectable, selecte
12261
12385
  className: "size-4 accent-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring disabled:cursor-not-allowed"
12262
12386
  })
12263
12387
  }) : /* @__PURE__ */ jsx(Checkbox, {
12264
- "aria-label": t("table.selectRow", { label }),
12388
+ "aria-label": labels?.selectRow?.(label) ?? t("table.selectRow", { label }),
12265
12389
  checked: isSelected,
12266
12390
  onCheckedChange: () => onToggleRow(id)
12267
12391
  })
@@ -12275,6 +12399,7 @@ function DataTableRow({ row, columns, getRowId, getRowLabel, selectable, selecte
12275
12399
  style: cl?.style,
12276
12400
  className: cn("whitespace-nowrap", cl?.style && "overflow-hidden text-ellipsis", COLUMN_VARIANT_CLASSES[c.variant ?? "default"], c.className, cl?.className, tint(cl)),
12277
12401
  children: editable ? /* @__PURE__ */ jsx(EditableCell, {
12402
+ labels,
12278
12403
  row,
12279
12404
  rowId: id,
12280
12405
  rowLabel: label,
@@ -12291,7 +12416,7 @@ function DataTableRow({ row, columns, getRowId, getRowLabel, selectable, selecte
12291
12416
  style: layout?.trail.style,
12292
12417
  children: /* @__PURE__ */ jsx(MoreMenu, {
12293
12418
  items: rowActions(row),
12294
- label: t("table.rowActions", { label }),
12419
+ label: labels?.rowActions?.(label) ?? t("table.rowActions", { label }),
12295
12420
  variant: "ghost",
12296
12421
  size: "sm"
12297
12422
  })
@@ -12301,7 +12426,7 @@ function DataTableRow({ row, columns, getRowId, getRowLabel, selectable, selecte
12301
12426
  }
12302
12427
  const groupHeadClass = "h-9 bg-secondary/50 text-[12.5px] font-semibold tracking-normal normal-case text-foreground";
12303
12428
  /** The header row of a group: group checkbox, collapse button, label and count. */
12304
- function GroupHeaderRow({ group, open, selectable, selected, span, collapsible, onToggle, onToggleRows, ariaRowIndex, style }) {
12429
+ function GroupHeaderRow({ labels, group, open, selectable, selected, span, collapsible, onToggle, onToggleRows, ariaRowIndex, style }) {
12305
12430
  const { t } = useI18n();
12306
12431
  const state = selectionState(group.ids, selected);
12307
12432
  const count = group.ids.length;
@@ -12318,7 +12443,7 @@ function GroupHeaderRow({ group, open, selectable, selected, span, collapsible,
12318
12443
  " ",
12319
12444
  /* @__PURE__ */ jsx("span", {
12320
12445
  className: "sr-only",
12321
- children: count === 1 ? "row" : "rows"
12446
+ children: labels?.groupRows?.(count) ?? (count === 1 ? "row" : "rows")
12322
12447
  })
12323
12448
  ] });
12324
12449
  return /* @__PURE__ */ jsxs(TableRow, {
@@ -12329,7 +12454,7 @@ function GroupHeaderRow({ group, open, selectable, selected, span, collapsible,
12329
12454
  children: [selectable && /* @__PURE__ */ jsx(TableCell, {
12330
12455
  className: cn("w-10 pe-0", "h-9 bg-secondary/50 text-[12.5px] font-semibold tracking-normal normal-case text-foreground"),
12331
12456
  children: /* @__PURE__ */ jsx(Checkbox, {
12332
- "aria-label": t("table.selectRow", { label: `all in ${group.name}` }),
12457
+ "aria-label": labels?.selectGroup?.(group.name) ?? t("table.selectRow", { label: `all in ${group.name}` }),
12333
12458
  checked: state.checked,
12334
12459
  indeterminate: state.indeterminate,
12335
12460
  disabled: count === 0,
@@ -12423,8 +12548,8 @@ function DataTableBody(props) {
12423
12548
  children: emptyState ?? /* @__PURE__ */ jsx(EmptyState, {
12424
12549
  icon: Inbox,
12425
12550
  size: "sm",
12426
- title: t("common.noResults"),
12427
- description: t("table.emptyDescription")
12551
+ title: props.labels?.emptyTitle ?? t("common.noResults"),
12552
+ description: props.labels?.emptyDescription ?? t("table.emptyDescription")
12428
12553
  })
12429
12554
  })
12430
12555
  }) });
@@ -12436,6 +12561,7 @@ function DataTableBody(props) {
12436
12561
  }, props.getRowId(r));
12437
12562
  const header = (group, open, extra) => /* @__PURE__ */ jsx(GroupHeaderRow, {
12438
12563
  group,
12564
+ labels: props.labels,
12439
12565
  open,
12440
12566
  selectable,
12441
12567
  selected,
@@ -12501,10 +12627,10 @@ function DataTableBody(props) {
12501
12627
  //#endregion
12502
12628
  //#region src/tables/DataTable/density-toggle.tsx
12503
12629
  /** Two-icon segmented control: comfortable or compact rows. */
12504
- function DensityToggle({ value, onChange }) {
12630
+ function DensityToggle({ value, onChange, labels }) {
12505
12631
  const { t } = useI18n();
12506
12632
  return /* @__PURE__ */ jsxs(ToggleGroup, {
12507
- "aria-label": t("table.density"),
12633
+ "aria-label": labels?.density ?? t("table.density"),
12508
12634
  size: "sm",
12509
12635
  value: [value],
12510
12636
  onValueChange: (v) => {
@@ -12514,7 +12640,7 @@ function DensityToggle({ value, onChange }) {
12514
12640
  children: [/* @__PURE__ */ jsx(Toggle, {
12515
12641
  value: "comfortable",
12516
12642
  iconOnly: true,
12517
- "aria-label": t("table.comfortable"),
12643
+ "aria-label": labels?.comfortable ?? t("table.comfortable"),
12518
12644
  children: /* @__PURE__ */ jsx(Rows2, {
12519
12645
  size: 15,
12520
12646
  "aria-hidden": true
@@ -12522,7 +12648,7 @@ function DensityToggle({ value, onChange }) {
12522
12648
  }), /* @__PURE__ */ jsx(Toggle, {
12523
12649
  value: "compact",
12524
12650
  iconOnly: true,
12525
- "aria-label": t("table.compact"),
12651
+ "aria-label": labels?.compact ?? t("table.compact"),
12526
12652
  children: /* @__PURE__ */ jsx(Rows3, {
12527
12653
  size: 15,
12528
12654
  "aria-hidden": true
@@ -12657,7 +12783,7 @@ const BIG_STEP = 50;
12657
12783
  * aria-valuenow/min/max (the width in px). Drag it with a pointer; from the keyboard,
12658
12784
  * ArrowLeft/ArrowRight change the width by 10px (Shift: 50px), Home/End jump to the limits.
12659
12785
  */
12660
- function ColumnResizeHandle({ label, width, min, max, onResize, onResizeEnd }) {
12786
+ function ColumnResizeHandle({ label, width, min, max, onResize, onResizeEnd, labels }) {
12661
12787
  const drag = useRef(null);
12662
12788
  const clamp = (w) => Math.round(Math.min(max, Math.max(min, w)));
12663
12789
  const onPointerDown = (e) => {
@@ -12701,11 +12827,11 @@ function ColumnResizeHandle({ label, width, min, max, onResize, onResizeEnd }) {
12701
12827
  role: "separator",
12702
12828
  tabIndex: 0,
12703
12829
  "aria-orientation": "vertical",
12704
- "aria-label": `Resize ${label} column`,
12830
+ "aria-label": labels?.resizeColumn?.(label) ?? `Resize ${label} column`,
12705
12831
  "aria-valuenow": width,
12706
12832
  "aria-valuemin": min,
12707
12833
  "aria-valuemax": max,
12708
- "aria-valuetext": `${width} pixels`,
12834
+ "aria-valuetext": labels?.columnWidth?.(width) ?? `${width} pixels`,
12709
12835
  "data-column-resizer": "",
12710
12836
  onPointerDown,
12711
12837
  onPointerMove,
@@ -12725,7 +12851,7 @@ function ColumnResizeHandle({ label, width, min, max, onResize, onResizeEnd }) {
12725
12851
  * Column header of the DataTable. Without a resize handle it is the kit TableHead; with one, the
12726
12852
  * sort button and the handle sit side by side (the handle never nests inside the button).
12727
12853
  */
12728
- function DataTableHeadCell({ column, sortDirection, onSortChange, layout, resize }) {
12854
+ function DataTableHeadCell({ column, sortDirection, onSortChange, layout, resize, labels }) {
12729
12855
  const align = columnAlign(column);
12730
12856
  const pinnedClass = layout?.pinned ? cn(layout.className, "z-[11]", stickyTint(layout, { head: true }), "bg-card") : void 0;
12731
12857
  if (!resize) return /* @__PURE__ */ jsx(TableHead, {
@@ -12765,6 +12891,7 @@ function DataTableHeadCell({ column, sortDirection, onSortChange, layout, resize
12765
12891
  })]
12766
12892
  }) : column.header
12767
12893
  }), /* @__PURE__ */ jsx(ColumnResizeHandle, {
12894
+ labels,
12768
12895
  label: column.header,
12769
12896
  width: resize.width,
12770
12897
  min: resize.min,
@@ -12898,7 +13025,7 @@ function newViewId(name, views) {
12898
13025
  }
12899
13026
  const DEFAULT_VALUE = "__default__";
12900
13027
  /** "Views" menu: switch between saved views, save the current table as a view, rename, delete, reset. */
12901
- function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRename, onDelete, onReset }) {
13028
+ function ViewSwitcher({ labels, views, activeId, dirty, onSelect, onSaveAs, onSave, onRename, onDelete, onReset }) {
12902
13029
  const inputId = useId();
12903
13030
  const active = views.find((v) => v.id === activeId) ?? null;
12904
13031
  const [dialog, setDialog] = useState(null);
@@ -12923,7 +13050,7 @@ function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRe
12923
13050
  }),
12924
13051
  children: [/* @__PURE__ */ jsx("span", {
12925
13052
  className: "max-w-40 truncate",
12926
- children: active ? active.name : "Default view"
13053
+ children: active ? active.name : labels?.defaultView ?? "Default view"
12927
13054
  }), dirty && /* @__PURE__ */ jsxs(Fragment$1, { children: [
12928
13055
  " ",
12929
13056
  /* @__PURE__ */ jsx("span", {
@@ -12933,19 +13060,19 @@ function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRe
12933
13060
  " ",
12934
13061
  /* @__PURE__ */ jsx("span", {
12935
13062
  className: "sr-only",
12936
- children: "(modified)"
13063
+ children: labels?.modified ?? "(modified)"
12937
13064
  })
12938
13065
  ] })]
12939
13066
  }), /* @__PURE__ */ jsxs(MenuContent, {
12940
13067
  align: "end",
12941
13068
  className: "min-w-[220px]",
12942
13069
  children: [
12943
- /* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: "Views" }), /* @__PURE__ */ jsxs(MenuRadioGroup, {
13070
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: labels?.views ?? "Views" }), /* @__PURE__ */ jsxs(MenuRadioGroup, {
12944
13071
  value: activeId ?? DEFAULT_VALUE,
12945
13072
  onValueChange: (v) => onSelect(v === DEFAULT_VALUE ? null : String(v)),
12946
13073
  children: [/* @__PURE__ */ jsx(MenuRadioItem, {
12947
13074
  value: DEFAULT_VALUE,
12948
- children: "Default view"
13075
+ children: labels?.defaultViewOption ?? "Default view"
12949
13076
  }), views.map((v) => /* @__PURE__ */ jsx(MenuRadioItem, {
12950
13077
  value: v.id,
12951
13078
  children: v.name
@@ -12955,26 +13082,26 @@ function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRe
12955
13082
  /* @__PURE__ */ jsx(MenuItem, {
12956
13083
  disabled: !active || !dirty,
12957
13084
  onClick: onSave,
12958
- children: "Save changes"
13085
+ children: labels?.saveChanges ?? "Save changes"
12959
13086
  }),
12960
13087
  /* @__PURE__ */ jsx(MenuItem, {
12961
13088
  onClick: () => open("create"),
12962
- children: "Save as new view…"
13089
+ children: labels?.saveAs ?? "Save as new view…"
12963
13090
  }),
12964
13091
  /* @__PURE__ */ jsx(MenuItem, {
12965
13092
  disabled: !active,
12966
13093
  onClick: () => open("rename"),
12967
- children: "Rename view…"
13094
+ children: labels?.renameView ?? "Rename view…"
12968
13095
  }),
12969
13096
  /* @__PURE__ */ jsx(MenuItem, {
12970
13097
  disabled: !active,
12971
13098
  onClick: () => active && onDelete(active.id),
12972
- children: "Delete view"
13099
+ children: labels?.deleteView ?? "Delete view"
12973
13100
  }),
12974
13101
  /* @__PURE__ */ jsx(MenuItem, {
12975
13102
  disabled: !dirty,
12976
13103
  onClick: onReset,
12977
- children: "Reset changes"
13104
+ children: labels?.resetChanges ?? "Reset changes"
12978
13105
  })
12979
13106
  ]
12980
13107
  })] }), /* @__PURE__ */ jsx(Dialog, {
@@ -12982,17 +13109,18 @@ function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRe
12982
13109
  onOpenChange: (o) => !o && setDialog(null),
12983
13110
  children: /* @__PURE__ */ jsx(DialogContent, {
12984
13111
  size: "sm",
13112
+ closeLabel: labels?.closeDialog,
12985
13113
  children: /* @__PURE__ */ jsxs("form", {
12986
13114
  onSubmit: (e) => {
12987
13115
  e.preventDefault();
12988
13116
  submit();
12989
13117
  },
12990
13118
  children: [
12991
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: dialog === "rename" ? "Rename view" : "Save view" }) }),
13119
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: dialog === "rename" ? labels?.renameViewTitle ?? "Rename view" : labels?.saveViewTitle ?? "Save view" }) }),
12992
13120
  /* @__PURE__ */ jsxs(DialogBody, { children: [/* @__PURE__ */ jsx("label", {
12993
13121
  htmlFor: inputId,
12994
13122
  className: "mb-1.5 block text-[12.5px] font-medium text-foreground",
12995
- children: "View name"
13123
+ children: labels?.viewName ?? "View name"
12996
13124
  }), /* @__PURE__ */ jsx(Input, {
12997
13125
  id: inputId,
12998
13126
  value: name,
@@ -13002,11 +13130,11 @@ function ViewSwitcher({ views, activeId, dirty, onSelect, onSaveAs, onSave, onRe
13002
13130
  })] }),
13003
13131
  /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(DialogClose, {
13004
13132
  render: /* @__PURE__ */ jsx(Button, { variant: "ghost" }),
13005
- children: "Cancel"
13133
+ children: labels?.cancel ?? "Cancel"
13006
13134
  }), /* @__PURE__ */ jsx(Button, {
13007
13135
  type: "submit",
13008
13136
  disabled: !name.trim(),
13009
- children: dialog === "rename" ? "Rename" : "Save view"
13137
+ children: dialog === "rename" ? labels?.rename ?? "Rename" : labels?.saveView ?? "Save view"
13010
13138
  })] })
13011
13139
  ]
13012
13140
  })
@@ -13147,7 +13275,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13147
13275
  10,
13148
13276
  25,
13149
13277
  50
13150
- ], density: densityProp, defaultDensity, onDensityChange, showDensityToggle = true, showColumnMenu = true, loading = false, loadingRows = 5, emptyState, groupBy, groupLabel, collapsibleGroups = true, collapsedGroups: collapsedProp, defaultCollapsedGroups = [], onCollapsedGroupsChange, onGroupByChange, hiddenColumns: hiddenProp, onHiddenColumnsChange, resizable = false, columnWidths: widthsProp, defaultColumnWidths = EMPTY_WIDTHS, onColumnWidthsChange, pinnedColumns: pinnedProp, defaultPinnedColumns = EMPTY_PINNED, onPinnedColumnsChange, showPinMenu, virtualize = "auto", virtualizeThreshold = 100, rowHeight: rowHeightProp, virtualContainerClassName = "max-h-[32rem]", onCellEdit, views: viewsProp, defaultViews, onViewsChange, activeViewId, defaultActiveViewId, onActiveViewChange, showViewSwitcher, filters, onFiltersChange, toolbar, className }) {
13278
+ ], density: densityProp, defaultDensity, onDensityChange, showDensityToggle = true, showColumnMenu = true, loading = false, loadingRows = 5, emptyState, groupBy, groupLabel, collapsibleGroups = true, collapsedGroups: collapsedProp, defaultCollapsedGroups = [], onCollapsedGroupsChange, onGroupByChange, hiddenColumns: hiddenProp, onHiddenColumnsChange, resizable = false, columnWidths: widthsProp, defaultColumnWidths = EMPTY_WIDTHS, onColumnWidthsChange, pinnedColumns: pinnedProp, defaultPinnedColumns = EMPTY_PINNED, onPinnedColumnsChange, showPinMenu, virtualize = "auto", virtualizeThreshold = 100, rowHeight: rowHeightProp, virtualContainerClassName = "max-h-[32rem]", onCellEdit, views: viewsProp, defaultViews, onViewsChange, activeViewId, defaultActiveViewId, onActiveViewChange, showViewSwitcher, filters, onFiltersChange, toolbar, className, labels }) {
13151
13279
  const { t } = useI18n();
13152
13280
  const singleMode = selectionMode === "single";
13153
13281
  const selectable = selectionMode === void 0 ? selectableProp : selectionMode === "multiple";
@@ -13208,7 +13336,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13208
13336
  setInnerCollapsed(next);
13209
13337
  onCollapsedGroupsChange?.([...next]);
13210
13338
  };
13211
- const nameOf = (key) => key || "No group";
13339
+ const nameOf = (key) => key || (labels?.noGroup ?? "No group");
13212
13340
  const bodyGroups = effectiveGroupBy && allGroups ? allGroups.map((g) => ({
13213
13341
  key: g.key,
13214
13342
  label: groupLabel ? groupLabel(g.key, g.rows) : nameOf(g.key),
@@ -13313,10 +13441,10 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13313
13441
  children: [/* @__PURE__ */ jsx("div", {
13314
13442
  className: "flex min-w-0 flex-1 flex-wrap items-center gap-2",
13315
13443
  children: hasSelection ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
13316
- selectionSummary && /* @__PURE__ */ jsxs("span", {
13444
+ selectionSummary && /* @__PURE__ */ jsx("span", {
13317
13445
  className: "text-[12.5px] font-semibold text-foreground",
13318
13446
  "aria-live": "polite",
13319
- children: [selectedList.length, " selected"]
13447
+ children: labels?.selectionCount ? labels.selectionCount(selectedList.length) : /* @__PURE__ */ jsxs(Fragment$1, { children: [selectedList.length, " selected"] })
13320
13448
  }),
13321
13449
  selectionSummary && bulkActions && /* @__PURE__ */ jsx("span", {
13322
13450
  "aria-hidden": true,
@@ -13328,6 +13456,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13328
13456
  className: "flex items-center gap-2",
13329
13457
  children: [
13330
13458
  showViews && /* @__PURE__ */ jsx(ViewSwitcher, {
13459
+ labels,
13331
13460
  views: viewsApi.views,
13332
13461
  activeId: viewsApi.activeId,
13333
13462
  dirty: viewsApi.dirty,
@@ -13339,10 +13468,12 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13339
13468
  onReset: viewsApi.reset
13340
13469
  }),
13341
13470
  showDensityToggle && /* @__PURE__ */ jsx(DensityToggle, {
13471
+ labels,
13342
13472
  value: density,
13343
13473
  onChange: changeDensity
13344
13474
  }),
13345
13475
  showColumnMenu && /* @__PURE__ */ jsx(ColumnVisibilityMenu, {
13476
+ labels,
13346
13477
  columns,
13347
13478
  hidden,
13348
13479
  onToggle: toggleColumn,
@@ -13355,7 +13486,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13355
13486
  anyEditable && /* @__PURE__ */ jsx("span", {
13356
13487
  id: editHintId,
13357
13488
  className: "sr-only",
13358
- children: "Press Enter or F2 to edit."
13489
+ children: labels?.editHint ?? "Press Enter or F2 to edit."
13359
13490
  }),
13360
13491
  /* @__PURE__ */ jsx(SelectionGroup, {
13361
13492
  single: singleMode,
@@ -13370,7 +13501,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13370
13501
  "aria-rowcount": virtual ? items + 1 : void 0,
13371
13502
  stickyHeader: virtualOn,
13372
13503
  containerClassName: virtualOn ? virtualContainerClassName : void 0,
13373
- containerLabel: virtualOn ? caption : void 0,
13504
+ containerLabel: virtualOn ? caption : labels?.scrollRegion,
13374
13505
  className: cn(layout.tableClassName, singleMode && "[&_[data-column-resizer]]:end-0 [&_[data-column-resizer]]:justify-end"),
13375
13506
  style: layout.tableStyle,
13376
13507
  "data-virtualized": virtualOn || void 0,
@@ -13388,9 +13519,9 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13388
13519
  style: layout.lead.style,
13389
13520
  children: [singleMode && /* @__PURE__ */ jsx("span", {
13390
13521
  className: "sr-only",
13391
- children: t("table.selection")
13522
+ children: labels?.selection ?? t("table.selection")
13392
13523
  }), selectable && /* @__PURE__ */ jsx(Checkbox, {
13393
- "aria-label": t("table.selectAllPage"),
13524
+ "aria-label": labels?.selectAll ?? t("table.selectAllPage"),
13394
13525
  checked: header.checked,
13395
13526
  indeterminate: header.indeterminate,
13396
13527
  disabled: loading || pageIds.length === 0,
@@ -13400,6 +13531,7 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13400
13531
  visibleColumns.map((c) => {
13401
13532
  const bounds = widthBounds(c);
13402
13533
  return /* @__PURE__ */ jsx(DataTableHeadCell, {
13534
+ labels,
13403
13535
  column: c,
13404
13536
  layout: layout.columns.get(c.id),
13405
13537
  sortDirection: sort?.columnId === c.id ? sort.direction : "none",
@@ -13417,12 +13549,13 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13417
13549
  style: layout.trail.style,
13418
13550
  children: /* @__PURE__ */ jsx("span", {
13419
13551
  className: "sr-only",
13420
- children: t("table.actions")
13552
+ children: labels?.actions ?? t("table.actions")
13421
13553
  })
13422
13554
  })
13423
13555
  ]
13424
13556
  }) }),
13425
13557
  /* @__PURE__ */ jsx(DataTableBody, {
13558
+ labels,
13426
13559
  rows: pageRows,
13427
13560
  columns: visibleColumns,
13428
13561
  getRowId,
@@ -13449,16 +13582,17 @@ function DataTable({ rows = DEPLOYMENT_ROWS, columns = DEPLOYMENT_COLUMNS, getRo
13449
13582
  })
13450
13583
  })
13451
13584
  }),
13452
- loading && /* @__PURE__ */ jsxs("p", {
13585
+ loading && /* @__PURE__ */ jsx("p", {
13453
13586
  role: "status",
13454
13587
  className: "sr-only",
13455
- children: [
13588
+ children: labels?.loading ? labels.loading(caption) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
13456
13589
  "Loading ",
13457
13590
  caption.toLowerCase(),
13458
13591
  "…"
13459
- ]
13592
+ ] })
13460
13593
  }),
13461
13594
  paginated && !loading && rows.length > 0 && /* @__PURE__ */ jsx(PaginationFooter, {
13595
+ labels,
13462
13596
  className: "border-t border-border px-4 py-3",
13463
13597
  total: rows.length,
13464
13598
  page,
@@ -13478,6 +13612,283 @@ const meta$15 = {
13478
13612
  family: "tables",
13479
13613
  status: "beta",
13480
13614
  description: "Generic typed table: sortable headers with aria-sort, row selection with select-all and indeterminate by default (selectionMode=\"multiple\"), selectionMode=\"none\" to hide selection, column visibility menu, density toggle, row actions, loading and empty states, client-side sort and pagination, optional row groups (groupBy) with rowgroup header rows, counts, keyboard-accessible collapse buttons and group selection; column resize (pointer drag or a focusable role=\"separator\" handle with arrow keys, min/max widths, controlled columnWidths), column pinning to either edge with sticky cells (column menu or pinnedColumns), row virtualization for large datasets (aria-rowcount/aria-rowindex, works with selection and grouping), inline cell editing (text, number and select editors; Enter/F2 to edit, Enter/Tab to commit, Escape to cancel, onCellEdit can reject with an inline error) and saved views (filters, sort, visible columns, widths, pinning and grouping) with a view switcher to save as, rename, delete and reset. Opt-in selectionMode=\"single\" uses native radios in a labelled fieldset (never paginated or virtualized; paginated defaults to false and paginated=true is a type error). Single mode uses selectedId/onSelectedIdChange or defaultSelectedId; isRowDisabled skips unavailable rows and preserves a checked disabled choice without callbacks. Supply legend, aria-label or aria-labelledby for the group and getRowLabel for each radio (otherwise the first column labels it). selectionName is used verbatim and must be unique per page; omitted names use an instance-unique useId name, and duplicate supplied names or missing group labels warn in development. Tab enters once on the checked enabled row or first enabled row; arrows wrap and skip disabled rows, Space selects, Shift+Tab exits, and all-disabled groups have no tab stop. No select-all, group checkboxes, bulk actions or selection summary appear in single mode. Examples in examples/single-selection.tsx: an uncontrolled non-paginated list with legend=\"Available items\" and a controlled versions table with legend=\"Available versions\", selectedId=\"v2\" and a checked disabled version.",
13615
+ labels: [
13616
+ {
13617
+ key: "scrollRegion",
13618
+ type: "string",
13619
+ state: "Non-virtual table overflow: focusable scroll region name; virtualized regions use the caller caption"
13620
+ },
13621
+ {
13622
+ key: "selectionCount",
13623
+ type: "(count: number) => string",
13624
+ state: "Multiple selection: toolbar count and polite live region"
13625
+ },
13626
+ {
13627
+ key: "columns",
13628
+ type: "string",
13629
+ state: "Column visibility trigger text and name"
13630
+ },
13631
+ {
13632
+ key: "visibleColumns",
13633
+ type: "string",
13634
+ state: "Open column menu: visibility heading"
13635
+ },
13636
+ {
13637
+ key: "pinColumns",
13638
+ type: "string",
13639
+ state: "Open column menu with pinning: pin heading"
13640
+ },
13641
+ {
13642
+ key: "pinnedLeft",
13643
+ type: "string",
13644
+ state: "Open pin submenu trigger: left side suffix"
13645
+ },
13646
+ {
13647
+ key: "pinnedRight",
13648
+ type: "string",
13649
+ state: "Open pin submenu trigger: right side suffix"
13650
+ },
13651
+ {
13652
+ key: "pinLeft",
13653
+ type: "string",
13654
+ state: "Open pin submenu: left option"
13655
+ },
13656
+ {
13657
+ key: "pinRight",
13658
+ type: "string",
13659
+ state: "Open pin submenu: right option"
13660
+ },
13661
+ {
13662
+ key: "unpinned",
13663
+ type: "string",
13664
+ state: "Open pin submenu: unpinned option"
13665
+ },
13666
+ {
13667
+ key: "density",
13668
+ type: "string",
13669
+ state: "Density group accessible name"
13670
+ },
13671
+ {
13672
+ key: "comfortable",
13673
+ type: "string",
13674
+ state: "Comfortable density button name"
13675
+ },
13676
+ {
13677
+ key: "compact",
13678
+ type: "string",
13679
+ state: "Compact density button name"
13680
+ },
13681
+ {
13682
+ key: "resizeColumn",
13683
+ type: "(label: string) => string",
13684
+ state: "Resizable header: separator name; label is caller column header"
13685
+ },
13686
+ {
13687
+ key: "columnWidth",
13688
+ type: "(width: number) => string",
13689
+ state: "Resizable header: separator aria-valuetext in pixels"
13690
+ },
13691
+ {
13692
+ key: "defaultView",
13693
+ type: "string",
13694
+ state: "View trigger when no active view"
13695
+ },
13696
+ {
13697
+ key: "defaultViewOption",
13698
+ type: "string",
13699
+ state: "Open views menu: default option"
13700
+ },
13701
+ {
13702
+ key: "modified",
13703
+ type: "string",
13704
+ state: "Dirty view trigger: screen reader suffix"
13705
+ },
13706
+ {
13707
+ key: "views",
13708
+ type: "string",
13709
+ state: "Open views menu: heading"
13710
+ },
13711
+ {
13712
+ key: "saveChanges",
13713
+ type: "string",
13714
+ state: "Open views menu: save current view action"
13715
+ },
13716
+ {
13717
+ key: "saveAs",
13718
+ type: "string",
13719
+ state: "Open views menu: save as action"
13720
+ },
13721
+ {
13722
+ key: "renameView",
13723
+ type: "string",
13724
+ state: "Open views menu: rename action"
13725
+ },
13726
+ {
13727
+ key: "deleteView",
13728
+ type: "string",
13729
+ state: "Open views menu: immediate delete action; no confirmation dialog"
13730
+ },
13731
+ {
13732
+ key: "resetChanges",
13733
+ type: "string",
13734
+ state: "Open views menu: reset action"
13735
+ },
13736
+ {
13737
+ key: "saveViewTitle",
13738
+ type: "string",
13739
+ state: "Save as dialog: title and accessible name"
13740
+ },
13741
+ {
13742
+ key: "renameViewTitle",
13743
+ type: "string",
13744
+ state: "Rename dialog: title and accessible name"
13745
+ },
13746
+ {
13747
+ key: "viewName",
13748
+ type: "string",
13749
+ state: "Save as and Rename dialogs: input label"
13750
+ },
13751
+ {
13752
+ key: "cancel",
13753
+ type: "string",
13754
+ state: "Save as and Rename dialogs: cancel button"
13755
+ },
13756
+ {
13757
+ key: "saveView",
13758
+ type: "string",
13759
+ state: "Save as dialog: submit button"
13760
+ },
13761
+ {
13762
+ key: "rename",
13763
+ type: "string",
13764
+ state: "Rename dialog: submit button"
13765
+ },
13766
+ {
13767
+ key: "closeDialog",
13768
+ type: "string",
13769
+ state: "Save as and Rename dialogs: close button name"
13770
+ },
13771
+ {
13772
+ key: "selection",
13773
+ type: "string",
13774
+ state: "Single selection: screen reader column heading"
13775
+ },
13776
+ {
13777
+ key: "selectAll",
13778
+ type: "string",
13779
+ state: "Multiple selection: header checkbox name"
13780
+ },
13781
+ {
13782
+ key: "selectRow",
13783
+ type: "(label: string) => string",
13784
+ state: "Multiple selection: row checkbox name; label is caller row name"
13785
+ },
13786
+ {
13787
+ key: "selectGroup",
13788
+ type: "(group: string) => string",
13789
+ state: "Grouped multiple selection: group checkbox name"
13790
+ },
13791
+ {
13792
+ key: "groupRows",
13793
+ type: "(count: number) => string",
13794
+ state: "Grouped header: screen reader row-count unit; numeric count stays unchanged"
13795
+ },
13796
+ {
13797
+ key: "noGroup",
13798
+ type: "string",
13799
+ state: "Grouped header: fallback when group key is empty"
13800
+ },
13801
+ {
13802
+ key: "actions",
13803
+ type: "string",
13804
+ state: "Row actions present: screen reader column heading"
13805
+ },
13806
+ {
13807
+ key: "rowActions",
13808
+ type: "(label: string) => string",
13809
+ state: "Row action menu trigger name; label is caller row name"
13810
+ },
13811
+ {
13812
+ key: "emptyTitle",
13813
+ type: "string",
13814
+ state: "Empty rows: default empty-state title"
13815
+ },
13816
+ {
13817
+ key: "emptyDescription",
13818
+ type: "string",
13819
+ state: "Empty rows: default empty-state description"
13820
+ },
13821
+ {
13822
+ key: "loading",
13823
+ type: "(caption: string) => string",
13824
+ state: "Loading: status announcement; receives original caller caption"
13825
+ },
13826
+ {
13827
+ key: "editHint",
13828
+ type: "string",
13829
+ state: "Editable table: shared keyboard instruction description"
13830
+ },
13831
+ {
13832
+ key: "editLabel",
13833
+ type: "(column: string, row: string) => string",
13834
+ state: "Editing cell: input accessible name"
13835
+ },
13836
+ {
13837
+ key: "editError",
13838
+ type: "string",
13839
+ state: "Rejected cell edit without caller error message: alert"
13840
+ },
13841
+ {
13842
+ key: "enterNumber",
13843
+ type: "string",
13844
+ state: "Editing number: invalid numeric value alert"
13845
+ },
13846
+ {
13847
+ key: "minimumValue",
13848
+ type: "(min: number) => string",
13849
+ state: "Editing number below minimum: alert"
13850
+ },
13851
+ {
13852
+ key: "maximumValue",
13853
+ type: "(max: number) => string",
13854
+ state: "Editing number above maximum: alert"
13855
+ },
13856
+ {
13857
+ key: "rowsPerPage",
13858
+ type: "string",
13859
+ state: "Always: visible page-size text"
13860
+ },
13861
+ {
13862
+ key: "rowsPerPageLabel",
13863
+ type: "string",
13864
+ state: "Always: page-size Select accessible name"
13865
+ },
13866
+ {
13867
+ key: "rangeSummary",
13868
+ type: "(from: number, to: number, total: number) => string",
13869
+ state: "Every page and page-size change: visible summary and polite live region"
13870
+ },
13871
+ {
13872
+ key: "paginationNav",
13873
+ type: "(noun: string) => string",
13874
+ state: "Always: navigation accessible name; noun is the caller caption"
13875
+ },
13876
+ {
13877
+ key: "previous",
13878
+ type: "string",
13879
+ state: "Always: previous-page button name"
13880
+ },
13881
+ {
13882
+ key: "next",
13883
+ type: "string",
13884
+ state: "Always: next-page button name"
13885
+ },
13886
+ {
13887
+ key: "page",
13888
+ type: "(page: number) => string",
13889
+ state: "Always: numbered-page button name; number stays caller content"
13890
+ }
13891
+ ],
13481
13892
  uses: [
13482
13893
  "Table",
13483
13894
  "Checkbox",
@@ -13571,9 +13982,10 @@ const same = (a, b) => a.field === b.field && a.value === b.value;
13571
13982
  * Filter bar: search Input, saved views Select, an "Add filter" Menu (multi-select per field),
13572
13983
  * removable filter chips (Token) and Clear all.
13573
13984
  */
13574
- function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaultQuery = "", onQueryChange, filters: filtersProp, defaultFilters = DEPLOYMENT_DEFAULT_FILTERS, onFiltersChange, views = DEPLOYMENT_SAVED_VIEWS, defaultView = null, onViewChange, searchLabel: searchLabelProp, placeholder = "Search by name…", resultCount, className }) {
13985
+ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaultQuery = "", onQueryChange, filters: filtersProp, defaultFilters = DEPLOYMENT_DEFAULT_FILTERS, onFiltersChange, views = DEPLOYMENT_SAVED_VIEWS, defaultView = null, onViewChange, searchLabel: searchLabelProp, placeholder: placeholderProp, resultCount, className, labels }) {
13575
13986
  const { t } = useI18n();
13576
- const searchLabel = searchLabelProp ?? t("common.search");
13987
+ const searchLabel = labels?.searchLabel ?? searchLabelProp ?? t("common.search");
13988
+ const placeholder = labels?.searchPlaceholder ?? placeholderProp ?? "Search by name…";
13577
13989
  const [innerQuery, setInnerQuery] = useState(defaultQuery);
13578
13990
  const [innerFilters, setInnerFilters] = useState(defaultFilters);
13579
13991
  const [view, setView] = useState(defaultView);
@@ -13605,7 +14017,7 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13605
14017
  const hasAny = filters.length > 0 || query !== "";
13606
14018
  return /* @__PURE__ */ jsxs("div", {
13607
14019
  role: "search",
13608
- "aria-label": t("filters.label"),
14020
+ "aria-label": labels?.searchLandmark ?? t("filters.label"),
13609
14021
  className: cn("flex flex-col gap-3", className),
13610
14022
  children: [/* @__PURE__ */ jsxs("div", {
13611
14023
  className: "flex flex-col gap-2 sm:flex-row sm:items-center",
@@ -13623,9 +14035,9 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13623
14035
  /* @__PURE__ */ jsxs("div", {
13624
14036
  className: "flex flex-wrap items-center gap-2",
13625
14037
  children: [views.length > 0 && /* @__PURE__ */ jsx(SimpleSelect, {
13626
- "aria-label": t("filters.savedView"),
14038
+ "aria-label": labels?.savedViewLabel ?? t("filters.savedView"),
13627
14039
  size: "sm",
13628
- placeholder: t("filters.savedViews"),
14040
+ placeholder: labels?.savedViewPlaceholder ?? t("filters.savedViews"),
13629
14041
  value: view,
13630
14042
  onValueChange: applyView,
13631
14043
  items: views.map((v) => ({
@@ -13639,7 +14051,7 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13639
14051
  size: "sm",
13640
14052
  icon: Plus
13641
14053
  }),
13642
- children: t("filters.add")
14054
+ children: labels?.addFilter ?? t("filters.add")
13643
14055
  }), /* @__PURE__ */ jsx(MenuContent, {
13644
14056
  className: "max-h-80 min-w-[220px] overflow-y-auto",
13645
14057
  children: fields.map((field, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: field.label }), field.options.map((option) => {
@@ -13655,17 +14067,17 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13655
14067
  })] })] }, field.id))
13656
14068
  })] })]
13657
14069
  }),
13658
- resultCount !== void 0 && /* @__PURE__ */ jsxs("p", {
14070
+ resultCount !== void 0 && /* @__PURE__ */ jsx("p", {
13659
14071
  className: "text-[12.5px] text-muted-foreground sm:ms-auto",
13660
14072
  "aria-live": "polite",
13661
- children: [
14073
+ children: labels?.resultCount ? labels.resultCount(resultCount) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
13662
14074
  /* @__PURE__ */ jsx("span", {
13663
14075
  className: "font-mono text-foreground tabular-nums",
13664
14076
  children: resultCount.toLocaleString("en-US")
13665
14077
  }),
13666
14078
  " ",
13667
14079
  resultCount === 1 ? "result" : "results"
13668
- ]
14080
+ ] })
13669
14081
  })
13670
14082
  ]
13671
14083
  }), hasAny && /* @__PURE__ */ jsxs("div", {
@@ -13676,10 +14088,10 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13676
14088
  children: [/* @__PURE__ */ jsx(ListFilter, {
13677
14089
  size: 12,
13678
14090
  "aria-hidden": true
13679
- }), "Filters"]
14091
+ }), labels?.filters ?? "Filters"]
13680
14092
  }),
13681
14093
  /* @__PURE__ */ jsx("ul", {
13682
- "aria-label": t("filters.active"),
14094
+ "aria-label": labels?.activeFilters ?? t("filters.active"),
13683
14095
  className: "flex flex-wrap items-center gap-2",
13684
14096
  children: filters.map((f) => /* @__PURE__ */ jsx("li", {
13685
14097
  className: "flex",
@@ -13687,7 +14099,7 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13687
14099
  size: "sm",
13688
14100
  prefix: `${labelOf(f.field)}:`,
13689
14101
  label: f.value,
13690
- removeLabel: t("filters.remove", {
14102
+ removeLabel: labels?.removeFilter?.(labelOf(f.field), f.value) ?? t("filters.remove", {
13691
14103
  field: labelOf(f.field),
13692
14104
  value: f.value
13693
14105
  }),
@@ -13700,7 +14112,7 @@ function FilterBar({ fields = DEPLOYMENT_FILTER_FIELDS, query: queryProp, defaul
13700
14112
  size: "sm",
13701
14113
  className: "h-7 px-2 hover:text-destructive-text",
13702
14114
  onClick: clearAll,
13703
- children: "Clear all"
14115
+ children: labels?.clearAll ?? "Clear all"
13704
14116
  })
13705
14117
  ]
13706
14118
  })]
@@ -13713,6 +14125,63 @@ const meta$26 = {
13713
14125
  family: "tables",
13714
14126
  status: "beta",
13715
14127
  description: "Search Input, saved views Select, an Add filter Menu with multi-select per field, removable filter chips and Clear all.",
14128
+ labels: [
14129
+ {
14130
+ key: "searchLandmark",
14131
+ type: "string",
14132
+ state: "Always: search landmark name"
14133
+ },
14134
+ {
14135
+ key: "searchLabel",
14136
+ type: "string",
14137
+ state: "Always: search input accessible name"
14138
+ },
14139
+ {
14140
+ key: "searchPlaceholder",
14141
+ type: "string",
14142
+ state: "Always: search input placeholder"
14143
+ },
14144
+ {
14145
+ key: "savedViewLabel",
14146
+ type: "string",
14147
+ state: "When views exist: Select accessible name"
14148
+ },
14149
+ {
14150
+ key: "savedViewPlaceholder",
14151
+ type: "string",
14152
+ state: "When no view selected: Select placeholder"
14153
+ },
14154
+ {
14155
+ key: "addFilter",
14156
+ type: "string",
14157
+ state: "Always: Add filter trigger text and name"
14158
+ },
14159
+ {
14160
+ key: "activeFilters",
14161
+ type: "string",
14162
+ state: "When filters or search exist: active-filters list name"
14163
+ },
14164
+ {
14165
+ key: "filters",
14166
+ type: "string",
14167
+ state: "When filters or search exist: chip section heading"
14168
+ },
14169
+ {
14170
+ key: "clearAll",
14171
+ type: "string",
14172
+ state: "When filters or search exist: clear button text and name"
14173
+ },
14174
+ {
14175
+ key: "removeFilter",
14176
+ type: "(field: string, value: string) => string",
14177
+ state: "When chip exists: remove button name; field and value are caller content"
14178
+ },
14179
+ {
14180
+ key: "resultCount",
14181
+ type: "(count: number) => string",
14182
+ state: "When resultCount supplied: visible count and polite live region"
14183
+ }
14184
+ ],
13716
14185
  uses: [
13717
14186
  "Input",
13718
14187
  "SimpleSelect",
@@ -13729,6 +14198,43 @@ const meta$45 = {
13729
14198
  family: "tables",
13730
14199
  status: "beta",
13731
14200
  description: "Table footer with a rows-per-page Select, a \"1–10 of 97\" summary and numbered Pagination; controlled or uncontrolled.",
14201
+ labels: [
14202
+ {
14203
+ key: "rowsPerPage",
14204
+ type: "string",
14205
+ state: "Always: visible page-size text"
14206
+ },
14207
+ {
14208
+ key: "rowsPerPageLabel",
14209
+ type: "string",
14210
+ state: "Always: page-size Select accessible name"
14211
+ },
14212
+ {
14213
+ key: "rangeSummary",
14214
+ type: "(from: number, to: number, total: number) => string",
14215
+ state: "Every page and page-size change: visible summary and polite live region"
14216
+ },
14217
+ {
14218
+ key: "paginationNav",
14219
+ type: "(noun: string) => string",
14220
+ state: "Always: navigation accessible name; noun is caller content or defaults to the provider translation of rows"
14221
+ },
14222
+ {
14223
+ key: "previous",
14224
+ type: "string",
14225
+ state: "Always: previous-page button name"
14226
+ },
14227
+ {
14228
+ key: "next",
14229
+ type: "string",
14230
+ state: "Always: next-page button name"
14231
+ },
14232
+ {
14233
+ key: "page",
14234
+ type: "(page: number) => string",
14235
+ state: "Always: numbered-page button name; number stays caller content"
14236
+ }
14237
+ ],
13732
14238
  uses: ["SimpleSelect", "Pagination"]
13733
14239
  };
13734
14240
  //#endregion