@inkra/react 0.4.3 → 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 (77) hide show
  1. package/README.md +109 -0
  2. package/dist/audio-player/index.js +6 -4
  3. package/dist/breadcrumb/index.d.ts +16 -4
  4. package/dist/breadcrumb/index.js +24 -8
  5. package/dist/calendar/index.js +10 -4
  6. package/dist/calendar/locales.d.ts +7 -0
  7. package/dist/calendar/locales.js +2 -0
  8. package/dist/carousel/index.js +8 -5
  9. package/dist/choice-grid/index.js +3 -1
  10. package/dist/color-picker/GradientEditor.d.ts +2 -1
  11. package/dist/color-picker/GradientEditor.js +10 -9
  12. package/dist/color-picker/index.js +12 -10
  13. package/dist/content/index.js +4 -1
  14. package/dist/data-table/index.d.ts +1 -1
  15. package/dist/data-table/index.js +4 -1
  16. package/dist/date-picker/index.js +11 -13
  17. package/dist/day-agenda/index.d.ts +1 -1
  18. package/dist/day-agenda/index.js +4 -1
  19. package/dist/dockable-panel/index.js +10 -8
  20. package/dist/drawer/index.js +7 -2
  21. package/dist/editable/index.js +8 -5
  22. package/dist/form/Input.js +4 -1
  23. package/dist/form/Textarea.js +4 -2
  24. package/dist/heatmap/index.js +4 -1
  25. package/dist/hotkey/index.js +4 -1
  26. package/dist/i18n/catalog.d.ts +5 -0
  27. package/dist/i18n/catalog.js +289 -0
  28. package/dist/i18n/index.d.ts +23 -0
  29. package/dist/i18n/index.js +50 -0
  30. package/dist/image-cropper/index.js +17 -15
  31. package/dist/image-viewer/VideoResource.js +14 -12
  32. package/dist/image-viewer/index.js +23 -21
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +2 -0
  35. package/dist/input-number/index.js +4 -1
  36. package/dist/internal/textRenderer.d.ts +1 -0
  37. package/dist/internal/textRenderer.js +10 -2
  38. package/dist/journey-map/index.d.ts +1 -1
  39. package/dist/journey-map/index.js +6 -3
  40. package/dist/keypad/index.js +4 -1
  41. package/dist/log/index.js +13 -11
  42. package/dist/markdown/index.js +11 -9
  43. package/dist/node-graph/index.d.ts +1 -1
  44. package/dist/node-graph/index.js +14 -3
  45. package/dist/number-ticker/index.js +4 -1
  46. package/dist/overflow-list/index.js +4 -4
  47. package/dist/overlay/Overlay.js +7 -2
  48. package/dist/pagination/index.js +7 -5
  49. package/dist/pan-zoom/index.js +6 -3
  50. package/dist/radar/index.js +4 -1
  51. package/dist/range-slider/index.js +4 -3
  52. package/dist/rating/index.js +5 -2
  53. package/dist/resizable/index.d.ts +1 -1
  54. package/dist/resizable/index.js +4 -1
  55. package/dist/select/SearchableSelect.js +3 -1
  56. package/dist/select/Select.js +3 -1
  57. package/dist/select/StepperSelect.js +5 -2
  58. package/dist/sortable/index.js +11 -9
  59. package/dist/sparkline/index.js +7 -7
  60. package/dist/steps/index.js +4 -1
  61. package/dist/styles/breadcrumb.css +96 -5
  62. package/dist/styles/select.css +8 -6
  63. package/dist/styles.css +104 -11
  64. package/dist/tabs/index.js +4 -1
  65. package/dist/tag-input/index.js +12 -17
  66. package/dist/theme/index.d.ts +11 -0
  67. package/dist/theme/index.js +23 -0
  68. package/dist/time-picker/index.js +7 -7
  69. package/dist/timeline/index.js +4 -1
  70. package/dist/toast/index.d.ts +1 -1
  71. package/dist/toast/index.js +4 -1
  72. package/dist/transfer/index.js +30 -27
  73. package/dist/tree/index.js +7 -2
  74. package/dist/tree-select/index.js +3 -1
  75. package/dist/upload/index.js +21 -18
  76. package/dist/virtual-list/index.js +4 -1
  77. package/package.json +11 -1
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { __iconData as gripIconData } from 'lucide-react/dist/esm/icons/grip-vertical.mjs';
3
4
  import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, TouchSensor, closestCenter, pointerWithin, rectIntersection, useDroppable, useSensor, useSensors, } from '@dnd-kit/core';
4
5
  import { SortableContext, rectSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable';
@@ -42,14 +43,14 @@ function filterItemActivatorProps(props) {
42
43
  },
43
44
  };
44
45
  }
45
- const defaultLabels = {
46
- handle: 'Drag item',
47
- screenReaderInstructions: 'Press Space or Enter to pick up an item. Use the arrow keys to move it, Space or Enter to drop it, and Escape to cancel.',
48
- pickedUp: (item, container, position, count) => `Picked up ${item}, item ${position} of ${count} in ${container}.`,
49
- movedOver: (item, container, position, count) => `${item} is over position ${position} of ${count} in ${container}.`,
50
- dropped: (item, container, position, count) => `Dropped ${item} at position ${position} of ${count} in ${container}.`,
51
- cancelled: (item) => `Sorting cancelled. ${item} returned to its original position.`,
52
- };
46
+ const defaultLabels = (t) => ({
47
+ handle: t('sortable.handle'),
48
+ screenReaderInstructions: t('sortable.instructions'),
49
+ pickedUp: (item, container, position, count) => t('sortable.pickedUp', { item, container, position, count }),
50
+ movedOver: (item, container, position, count) => t('sortable.movedOver', { item, container, position, count }),
51
+ dropped: (item, container, position, count) => t('sortable.dropped', { item, container, position, count }),
52
+ cancelled: item => t('sortable.cancelled', { item }),
53
+ });
53
54
  function getEngineData(value) {
54
55
  const data = value?.data.current;
55
56
  return data?.type === 'inkra-sortable-item' || data?.type === 'inkra-sortable-container' ? data : null;
@@ -138,7 +139,8 @@ function SortableItem({ activeId, container, disabled, handle, handleLabel, id,
138
139
  : _jsx("button", { ...activatorProps, "aria-label": handleLabel, className: "inkra-sortable__handle", "data-sortable-handle": "", disabled: disabled, ref: setActivatorNodeRef, type: "button", children: _jsx(PencilIcon, { "aria-hidden": "true", paths: gripIcon, size: 24, sourceSize: 24 }) }) })] });
139
140
  }
140
141
  export function Sortable({ autoScroll = true, className, containerClassName, containers, getContainerLabel = (container) => container.label ?? String(container.id), getItemId, getItemLabel = (item) => String(getItemId(item)), handle = false, isItemDisabled, itemClassName, labels: labelsProp, onChange, onMove, onReorder, renderContainerHeader, renderItem, renderOverlay, }) {
141
- const labels = { ...defaultLabels, ...labelsProp };
142
+ const { t } = useInkraLocale();
143
+ const labels = useMemo(() => ({ ...defaultLabels(t), ...labelsProp }), [t, labelsProp]);
142
144
  const [activeId, setActiveId] = useState(null);
143
145
  const [activeWidth, setActiveWidth] = useState(null);
144
146
  const [projected, setProjected] = useState(null);
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { forwardRef, useMemo, useRef, useState } from 'react';
3
4
  import { PencilText } from '../PencilText.js';
4
5
  import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
@@ -9,9 +10,6 @@ import { pencilStroke } from '../internal/stroke.js';
9
10
  import { drawPencilWash } from '../internal/wash.js';
10
11
  import { pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
11
12
  import { createSparklineGeometry, normalizeSparklineData, sparklineDomain } from './model.js';
12
- function defaultLabel(value, index) {
13
- return value === null ? `Point ${index + 1}: missing` : `Point ${index + 1}: ${value}`;
14
- }
15
13
  function resolveCssColor(host, style, token, fallback) {
16
14
  const value = style.getPropertyValue(token).trim();
17
15
  if (!value || !value.includes('var('))
@@ -23,7 +21,9 @@ function resolveCssColor(host, style, token, fallback) {
23
21
  probe.remove();
24
22
  return resolved || fallback;
25
23
  }
26
- export const Sparkline = forwardRef(function Sparkline({ data, type = 'line', width = 180, height = 56, min, max, label = 'Sparkline', showPoints = true, showArea = true, showSummary = true, getLabel = defaultLabel, className, ...props }, ref) {
24
+ export const Sparkline = forwardRef(function Sparkline(inkraProps, ref) {
25
+ const { t } = useInkraLocale();
26
+ const { data, type = 'line', width = 180, height = 56, min, max, label = t('sparkline.label'), showPoints = true, showArea = true, showSummary = true, getLabel = (value, index) => t(value === null ? 'sparkline.missing' : 'sparkline.point', { index: index + 1, value: value ?? '' }), className, ...props } = inkraProps;
27
27
  const canvasRef = useRef(null);
28
28
  const interactionCanvasRef = useRef(null);
29
29
  const [pencilReady, setPencilReady] = useState(false);
@@ -46,15 +46,15 @@ export const Sparkline = forwardRef(function Sparkline({ data, type = 'line', wi
46
46
  const effectiveShowPoints = showPoints || values.length === 1;
47
47
  const summary = useMemo(() => {
48
48
  if (!values.length)
49
- return 'No data';
49
+ return t('sparkline.empty');
50
50
  let minimum = values[0];
51
51
  let maximum = values[0];
52
52
  for (let index = 1; index < values.length; index++) {
53
53
  minimum = Math.min(minimum, values[index]);
54
54
  maximum = Math.max(maximum, values[index]);
55
55
  }
56
- return `Current ${values[values.length - 1]}, min ${minimum}, max ${maximum}`;
57
- }, [values]);
56
+ return t('sparkline.summary', { current: values[values.length - 1], minimum, maximum });
57
+ }, [t, values]);
58
58
  const onKeyDown = (event) => {
59
59
  if (!points.length || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key))
60
60
  return;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { forwardRef } from 'react';
3
4
  import { PencilIcon } from '../PencilIcon.js';
4
5
  import { PencilLine } from '../PencilLine.js';
@@ -20,7 +21,9 @@ function resolvedStatus(item, index, current) {
20
21
  return 'current';
21
22
  return 'upcoming';
22
23
  }
23
- export const Steps = forwardRef(function Steps({ 'aria-label': ariaLabel = 'Progress', className, current = 0, items, onStepChange, orientation = 'horizontal', ...props }, ref) {
24
+ export const Steps = forwardRef(function Steps(inkraProps, ref) {
25
+ const { t } = useInkraLocale();
26
+ const { 'aria-label': ariaLabel = t('steps.label'), className, current = 0, items, onStepChange, orientation = 'horizontal', ...props } = inkraProps;
24
27
  return _jsx("ol", { ...props, ref: ref, "aria-label": ariaLabel, className: ['inkra-steps', className].filter(Boolean).join(' '), "data-orientation": orientation, children: items.map((item, index) => {
25
28
  const status = resolvedStatus(item, index, current);
26
29
  const interactive = onStepChange !== undefined;
@@ -1,7 +1,98 @@
1
- .inkra-breadcrumb { min-width: 0; color: var(--inkra-ink); }
2
- .inkra-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 0; padding: 0; list-style: none; }
1
+ .inkra-breadcrumb {
2
+ --inkra-breadcrumb-item-height: var(--inkra-control-md);
3
+ --inkra-breadcrumb-item-padding: var(--inkra-space-3);
4
+ --inkra-breadcrumb-item-font-size: var(--inkra-text-md);
5
+ --inkra-breadcrumb-item-icon-size: var(--inkra-icon-md);
6
+ --inkra-breadcrumb-item-gap: var(--inkra-space-2);
7
+ color: var(--inkra-ink);
8
+ max-width: 100%;
9
+ min-width: 0;
10
+ }
11
+ .inkra-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; max-width: 100%; margin: 0; padding: 0; list-style: none; }
3
12
  .inkra-breadcrumb__item { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
4
- .inkra-breadcrumb__link { max-width: 100%; color: var(--inkra-ink-muted); }
5
- .inkra-breadcrumb__link .inkra-button__label { white-space: normal; overflow-wrap: anywhere; }
6
- .inkra-breadcrumb__page { min-height: 44px; display: inline-flex; align-items: center; padding-inline: 12px; overflow-wrap: anywhere; }
13
+ .inkra-breadcrumb__link,
14
+ .inkra-breadcrumb__trigger {
15
+ --inkra-button-visual-height: var(--inkra-breadcrumb-item-height);
16
+ --inkra-button-visual-padding: var(--inkra-breadcrumb-item-padding);
17
+ --inkra-button-font-size: var(--inkra-breadcrumb-item-font-size);
18
+ --inkra-button-icon-size: var(--inkra-breadcrumb-item-icon-size);
19
+ --inkra-button-content-gap: var(--inkra-breadcrumb-item-gap);
20
+ color: var(--inkra-accent-strong);
21
+ max-width: 100%;
22
+ }
23
+ .inkra-breadcrumb__link .inkra-button__label,
24
+ .inkra-breadcrumb__link .inkra-button__label > .inkra-pencil-text,
25
+ .inkra-breadcrumb__link .inkra-button__label > .inkra-pencil-text > .inkra-pencil-text__source,
26
+ .inkra-breadcrumb__trigger .inkra-button__label,
27
+ .inkra-breadcrumb__trigger .inkra-button__label > .inkra-pencil-text,
28
+ .inkra-breadcrumb__trigger .inkra-button__label > .inkra-pencil-text > .inkra-pencil-text__source {
29
+ max-width: 100%;
30
+ min-width: 0;
31
+ overflow-wrap: anywhere;
32
+ white-space: normal;
33
+ }
34
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__surface {
35
+ height: auto;
36
+ max-width: 100%;
37
+ min-height: var(--inkra-breadcrumb-item-height);
38
+ min-width: var(--inkra-breadcrumb-item-height);
39
+ }
40
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__content {
41
+ max-width: 100%;
42
+ min-width: 0;
43
+ }
44
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__focus-ring {
45
+ height: 100%;
46
+ inset-inline: 0;
47
+ top: 0;
48
+ transform: none;
49
+ }
50
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger):hover:not(:disabled):not([aria-disabled="true"]) .inkra-button__surface,
51
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger):active:not(:disabled):not([aria-disabled="true"]) .inkra-button__surface {
52
+ transform: none;
53
+ }
54
+ .inkra-breadcrumb__page {
55
+ align-items: center;
56
+ box-sizing: border-box;
57
+ display: inline-flex;
58
+ font-family: "Trip Map Hand", "HanziPen SC", "Hannotate SC", "Kaiti SC", "STKaiti", cursive;
59
+ font-size: var(--inkra-breadcrumb-item-font-size);
60
+ gap: var(--inkra-breadcrumb-item-gap);
61
+ justify-content: center;
62
+ letter-spacing: var(--inkra-letter-spacing);
63
+ line-height: var(--inkra-line-ui);
64
+ max-width: 100%;
65
+ min-height: var(--inkra-breadcrumb-item-height);
66
+ min-width: var(--inkra-breadcrumb-item-height);
67
+ overflow-wrap: anywhere;
68
+ padding-inline: var(--inkra-breadcrumb-item-padding);
69
+ }
70
+ .inkra-breadcrumb__label,
71
+ .inkra-breadcrumb__label > .inkra-pencil-text {
72
+ align-items: center;
73
+ display: inline-flex;
74
+ min-width: 0;
75
+ overflow: visible;
76
+ overflow-wrap: anywhere;
77
+ white-space: normal;
78
+ }
79
+ .inkra-breadcrumb__label > .inkra-pencil-text > .inkra-pencil-text__source {
80
+ display: block;
81
+ min-width: 0;
82
+ white-space: inherit;
83
+ }
84
+ .inkra-breadcrumb__icon {
85
+ align-items: center;
86
+ display: inline-flex;
87
+ flex: 0 0 var(--inkra-breadcrumb-item-icon-size);
88
+ height: var(--inkra-breadcrumb-item-icon-size);
89
+ justify-content: center;
90
+ width: var(--inkra-breadcrumb-item-icon-size);
91
+ }
92
+ .inkra-breadcrumb__icon > :is(svg, canvas) {
93
+ height: 100% !important;
94
+ max-height: 100%;
95
+ max-width: 100%;
96
+ width: 100% !important;
97
+ }
7
98
  .inkra-breadcrumb__separator { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; min-width: 16px; color: var(--inkra-ink-subtle); pointer-events: none; }
@@ -23,11 +23,8 @@
23
23
  .inkra-search-select__input .inkra-search-select__input-control > .inkra-pencil-surface__border { display: none; }
24
24
  .inkra-search-select__anchor > .inkra-input__end-action { flex: 0 0 auto; }
25
25
  .inkra-search-select__anchor > .inkra-input__end-action:has(.inkra-search-select__clear:not([data-visible])) {
26
- pointer-events: none;
26
+ display: none;
27
27
  }
28
- .inkra-search-select__anchor > .inkra-input__end-action:has(.inkra-search-select__clear:not([data-visible]))
29
- > :is(.inkra-input__action-divider, .inkra-input__action-wash) { display: none; }
30
- .inkra-search-select__clear:not([data-visible]) { visibility: hidden; }
31
28
  .inkra-search-select__trigger .inkra-button__content,
32
29
  .inkra-search-select__trigger .inkra-button__label,
33
30
  .inkra-search-select__trigger .inkra-button__label > span { min-width: 0; width: 100%; }
@@ -61,13 +58,18 @@
61
58
  inline-size: 100%;
62
59
  }
63
60
 
64
- .inkra-select-stepper .inkra-select-stepper__control,
65
- .inkra-select-stepper .inkra-select-stepper__control > .inkra-pencil-surface__content {
61
+ .inkra-select-stepper .inkra-select-stepper__control {
66
62
  flex-wrap: nowrap;
67
63
  gap: 0;
68
64
  padding-inline: 0;
69
65
  }
70
66
 
67
+ .inkra-select-stepper .inkra-select-stepper__control > .inkra-pencil-surface__content {
68
+ flex-wrap: nowrap;
69
+ gap: 0;
70
+ padding-inline: var(--inkra-field-control-padding);
71
+ }
72
+
71
73
  .inkra-select-stepper__control {
72
74
  outline: 0;
73
75
  }
package/dist/styles.css CHANGED
@@ -2015,11 +2015,8 @@
2015
2015
  .inkra-search-select__input .inkra-search-select__input-control > .inkra-pencil-surface__border { display: none; }
2016
2016
  .inkra-search-select__anchor > .inkra-input__end-action { flex: 0 0 auto; }
2017
2017
  .inkra-search-select__anchor > .inkra-input__end-action:has(.inkra-search-select__clear:not([data-visible])) {
2018
- pointer-events: none;
2018
+ display: none;
2019
2019
  }
2020
- .inkra-search-select__anchor > .inkra-input__end-action:has(.inkra-search-select__clear:not([data-visible]))
2021
- > :is(.inkra-input__action-divider, .inkra-input__action-wash) { display: none; }
2022
- .inkra-search-select__clear:not([data-visible]) { visibility: hidden; }
2023
2020
  .inkra-search-select__trigger .inkra-button__content,
2024
2021
  .inkra-search-select__trigger .inkra-button__label,
2025
2022
  .inkra-search-select__trigger .inkra-button__label > span { min-width: 0; width: 100%; }
@@ -2053,13 +2050,18 @@
2053
2050
  inline-size: 100%;
2054
2051
  }
2055
2052
 
2056
- .inkra-select-stepper .inkra-select-stepper__control,
2057
- .inkra-select-stepper .inkra-select-stepper__control > .inkra-pencil-surface__content {
2053
+ .inkra-select-stepper .inkra-select-stepper__control {
2058
2054
  flex-wrap: nowrap;
2059
2055
  gap: 0;
2060
2056
  padding-inline: 0;
2061
2057
  }
2062
2058
 
2059
+ .inkra-select-stepper .inkra-select-stepper__control > .inkra-pencil-surface__content {
2060
+ flex-wrap: nowrap;
2061
+ gap: 0;
2062
+ padding-inline: var(--inkra-field-control-padding);
2063
+ }
2064
+
2063
2065
  .inkra-select-stepper__control {
2064
2066
  outline: 0;
2065
2067
  }
@@ -8720,12 +8722,103 @@ svg.leaflet-image-layer.leaflet-interactive path {
8720
8722
  }
8721
8723
  }
8722
8724
 
8723
- .inkra-breadcrumb { min-width: 0; color: var(--inkra-ink); }
8724
- .inkra-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 0; padding: 0; list-style: none; }
8725
+ .inkra-breadcrumb {
8726
+ --inkra-breadcrumb-item-height: var(--inkra-control-md);
8727
+ --inkra-breadcrumb-item-padding: var(--inkra-space-3);
8728
+ --inkra-breadcrumb-item-font-size: var(--inkra-text-md);
8729
+ --inkra-breadcrumb-item-icon-size: var(--inkra-icon-md);
8730
+ --inkra-breadcrumb-item-gap: var(--inkra-space-2);
8731
+ color: var(--inkra-ink);
8732
+ max-width: 100%;
8733
+ min-width: 0;
8734
+ }
8735
+ .inkra-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; max-width: 100%; margin: 0; padding: 0; list-style: none; }
8725
8736
  .inkra-breadcrumb__item { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
8726
- .inkra-breadcrumb__link { max-width: 100%; color: var(--inkra-ink-muted); }
8727
- .inkra-breadcrumb__link .inkra-button__label { white-space: normal; overflow-wrap: anywhere; }
8728
- .inkra-breadcrumb__page { min-height: 44px; display: inline-flex; align-items: center; padding-inline: 12px; overflow-wrap: anywhere; }
8737
+ .inkra-breadcrumb__link,
8738
+ .inkra-breadcrumb__trigger {
8739
+ --inkra-button-visual-height: var(--inkra-breadcrumb-item-height);
8740
+ --inkra-button-visual-padding: var(--inkra-breadcrumb-item-padding);
8741
+ --inkra-button-font-size: var(--inkra-breadcrumb-item-font-size);
8742
+ --inkra-button-icon-size: var(--inkra-breadcrumb-item-icon-size);
8743
+ --inkra-button-content-gap: var(--inkra-breadcrumb-item-gap);
8744
+ color: var(--inkra-accent-strong);
8745
+ max-width: 100%;
8746
+ }
8747
+ .inkra-breadcrumb__link .inkra-button__label,
8748
+ .inkra-breadcrumb__link .inkra-button__label > .inkra-pencil-text,
8749
+ .inkra-breadcrumb__link .inkra-button__label > .inkra-pencil-text > .inkra-pencil-text__source,
8750
+ .inkra-breadcrumb__trigger .inkra-button__label,
8751
+ .inkra-breadcrumb__trigger .inkra-button__label > .inkra-pencil-text,
8752
+ .inkra-breadcrumb__trigger .inkra-button__label > .inkra-pencil-text > .inkra-pencil-text__source {
8753
+ max-width: 100%;
8754
+ min-width: 0;
8755
+ overflow-wrap: anywhere;
8756
+ white-space: normal;
8757
+ }
8758
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__surface {
8759
+ height: auto;
8760
+ max-width: 100%;
8761
+ min-height: var(--inkra-breadcrumb-item-height);
8762
+ min-width: var(--inkra-breadcrumb-item-height);
8763
+ }
8764
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__content {
8765
+ max-width: 100%;
8766
+ min-width: 0;
8767
+ }
8768
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger) .inkra-button__focus-ring {
8769
+ height: 100%;
8770
+ inset-inline: 0;
8771
+ top: 0;
8772
+ transform: none;
8773
+ }
8774
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger):hover:not(:disabled):not([aria-disabled="true"]) .inkra-button__surface,
8775
+ .inkra-breadcrumb :is(.inkra-breadcrumb__link, .inkra-breadcrumb__trigger):active:not(:disabled):not([aria-disabled="true"]) .inkra-button__surface {
8776
+ transform: none;
8777
+ }
8778
+ .inkra-breadcrumb__page {
8779
+ align-items: center;
8780
+ box-sizing: border-box;
8781
+ display: inline-flex;
8782
+ font-family: "Trip Map Hand", "HanziPen SC", "Hannotate SC", "Kaiti SC", "STKaiti", cursive;
8783
+ font-size: var(--inkra-breadcrumb-item-font-size);
8784
+ gap: var(--inkra-breadcrumb-item-gap);
8785
+ justify-content: center;
8786
+ letter-spacing: var(--inkra-letter-spacing);
8787
+ line-height: var(--inkra-line-ui);
8788
+ max-width: 100%;
8789
+ min-height: var(--inkra-breadcrumb-item-height);
8790
+ min-width: var(--inkra-breadcrumb-item-height);
8791
+ overflow-wrap: anywhere;
8792
+ padding-inline: var(--inkra-breadcrumb-item-padding);
8793
+ }
8794
+ .inkra-breadcrumb__label,
8795
+ .inkra-breadcrumb__label > .inkra-pencil-text {
8796
+ align-items: center;
8797
+ display: inline-flex;
8798
+ min-width: 0;
8799
+ overflow: visible;
8800
+ overflow-wrap: anywhere;
8801
+ white-space: normal;
8802
+ }
8803
+ .inkra-breadcrumb__label > .inkra-pencil-text > .inkra-pencil-text__source {
8804
+ display: block;
8805
+ min-width: 0;
8806
+ white-space: inherit;
8807
+ }
8808
+ .inkra-breadcrumb__icon {
8809
+ align-items: center;
8810
+ display: inline-flex;
8811
+ flex: 0 0 var(--inkra-breadcrumb-item-icon-size);
8812
+ height: var(--inkra-breadcrumb-item-icon-size);
8813
+ justify-content: center;
8814
+ width: var(--inkra-breadcrumb-item-icon-size);
8815
+ }
8816
+ .inkra-breadcrumb__icon > :is(svg, canvas) {
8817
+ height: 100% !important;
8818
+ max-height: 100%;
8819
+ max-width: 100%;
8820
+ width: 100% !important;
8821
+ }
8729
8822
  .inkra-breadcrumb__separator { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; min-width: 16px; color: var(--inkra-ink-subtle); pointer-events: none; }
8730
8823
 
8731
8824
  .inkra-calendar-scroll { max-width: 100%; min-width: 0; overflow-x: auto; }
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import * as Primitive from '@radix-ui/react-tabs';
3
4
  import { createContext, forwardRef, useCallback, useContext, useMemo, useRef, useState } from 'react';
4
5
  import { __iconData as chevronLeft } from 'lucide-react/dist/esm/icons/chevron-left.mjs';
@@ -43,7 +44,9 @@ const Root = forwardRef(function TabsRoot({ value, defaultValue, onValueChange,
43
44
  };
44
45
  return _jsx(ValueContext.Provider, { value: current, children: _jsx(ChangeContext.Provider, { value: change, children: _jsx(AnimatedContext.Provider, { value: animated, children: _jsx(OrientationContext.Provider, { value: orientation, children: _jsx(Primitive.Root, { ...props, orientation: orientation, ref: ref, "data-size": size, className: ['inkra-tabs', className].filter(Boolean).join(' '), value: current ?? '', onValueChange: change }) }) }) }) });
45
46
  });
46
- const List = forwardRef(function TabsList({ className, align = 'start', fullWidth = false, divided = false, overflow = 'scroll', moreLabel = 'More tabs', scrollPreviousLabel = 'Scroll to previous tabs', scrollNextLabel = 'Scroll to next tabs', onFocusCapture, ...props }, ref) {
47
+ const List = forwardRef(function TabsList(inkraProps, ref) {
48
+ const { t } = useInkraLocale();
49
+ const { className, align = 'start', fullWidth = false, divided = false, overflow = 'scroll', moreLabel = t('tabs.more'), scrollPreviousLabel = t('tabs.previous'), scrollNextLabel = t('tabs.next'), onFocusCapture, ...props } = inkraProps;
47
50
  const current = useContext(ValueContext);
48
51
  const change = useContext(ChangeContext);
49
52
  const mode = useContext(OrientationContext) === 'horizontal' ? overflow : 'scroll';
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { forwardRef, memo, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
3
4
  import { PencilIcon } from '../PencilIcon.js';
4
5
  import { Button } from '../Button.js';
@@ -10,26 +11,18 @@ import { createDefaultTag, defaultTagInputDelimiters, defaultTagValueKey, normal
10
11
  const removePaths = [{ d: 'M18 6 6 18' }, { d: 'm6 6 12 12' }];
11
12
  const clearPaths = [{ d: 'M4 7h16' }, { d: 'M9 7V4h6v3' }, { d: 'm7 7 1 13h8l1-13' }];
12
13
  const classes = (...values) => values.filter(Boolean).join(' ');
13
- const defaultLabels = {
14
- tagList: 'Selected tags',
15
- removeTag: tag => `Remove ${tag.label ?? tag.value}`,
16
- clearAll: 'Clear all tags',
17
- duplicate: candidate => `${candidate} is already added.`,
18
- maxTags: maximum => `You can add up to ${maximum} tags.`,
19
- invalid: candidate => `${candidate} is not a valid tag.`,
20
- };
21
14
  function joinAriaIds(...values) {
22
15
  const ids = values.flatMap(value => value?.split(/\s+/).filter(Boolean) ?? []);
23
16
  return [...new Set(ids)].join(' ') || undefined;
24
17
  }
25
- function resolvedLabels(labels) {
18
+ function resolvedLabels(labels, t) {
26
19
  return {
27
- tagList: labels?.tagList ?? defaultLabels.tagList,
28
- removeTag: labels?.removeTag ?? defaultLabels.removeTag,
29
- clearAll: labels?.clearAll ?? defaultLabels.clearAll,
30
- duplicate: labels?.duplicate ?? defaultLabels.duplicate,
31
- maxTags: labels?.maxTags ?? defaultLabels.maxTags,
32
- invalid: labels?.invalid ?? defaultLabels.invalid,
20
+ tagList: labels?.tagList ?? t('tagInput.list'),
21
+ removeTag: labels?.removeTag ?? (tag => t('tagInput.remove', { label: tag.label ?? tag.value })),
22
+ clearAll: labels?.clearAll ?? t('tagInput.clear'),
23
+ duplicate: labels?.duplicate ?? (label => t('tagInput.duplicate', { label })),
24
+ maxTags: labels?.maxTags ?? (count => t('tagInput.maximum', { count })),
25
+ invalid: labels?.invalid ?? (label => t('tagInput.invalid', { label })),
33
26
  };
34
27
  }
35
28
  function validationText(result, fallback) {
@@ -60,7 +53,9 @@ function tagRemoveTarget(event, length) {
60
53
  const index = Number(button.getAttribute('data-inkra-tag-index'));
61
54
  return Number.isInteger(index) && index >= 0 && index < length ? { button, index } : undefined;
62
55
  }
63
- export const TagInput = forwardRef(function TagInput({ 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, classNames = {}, clearable = true, createTag, defaultInputValue = '', defaultValue = [], delimiters = defaultTagInputDelimiters, disabled, form, fullWidth = true, getTagValueKey = defaultTagValueKey, id, inputProps = {}, inputValue, invalid = false, labels, maxTags, maxVisibleTags, showAllTagsLabel = count => `Show all ${count} tags`, showFewerTagsLabel = 'Show fewer tags', name, onClick, onInputValueChange, onValidationError, onValueChange, placeholder, readOnly, required, seed, size = 'md', validationMessage, validateTag, value, variant = 'outline', ...rootProps }, forwardedRef) {
56
+ export const TagInput = forwardRef(function TagInput(inkraProps, forwardedRef) {
57
+ const { t } = useInkraLocale();
58
+ const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, classNames = {}, clearable = true, createTag, defaultInputValue = '', defaultValue = [], delimiters = defaultTagInputDelimiters, disabled, form, fullWidth = true, getTagValueKey = defaultTagValueKey, id, inputProps = {}, inputValue, invalid = false, labels, maxTags, maxVisibleTags, showAllTagsLabel = count => t('tagInput.all', { count }), showFewerTagsLabel = t('tagInput.fewer'), name, onClick, onInputValueChange, onValidationError, onValueChange, placeholder, readOnly, required, seed, size = 'md', validationMessage, validateTag, value, variant = 'outline', ...rootProps } = inkraProps;
64
59
  const generatedId = useId();
65
60
  const inputRef = useRef(null);
66
61
  const removeRefs = useRef(new Map());
@@ -78,7 +73,7 @@ export const TagInput = forwardRef(function TagInput({ 'aria-describedby': ariaD
78
73
  const draftControlled = inputValue !== undefined;
79
74
  const currentTags = tagsControlled ? value : uncontrolledTags;
80
75
  const currentDraft = draftControlled ? inputValue : uncontrolledDraft;
81
- const copy = useMemo(() => resolvedLabels(labels), [labels]);
76
+ const copy = useMemo(() => resolvedLabels(labels, t), [labels, t]);
82
77
  const activeValidationMessage = validationMessage ?? localValidationMessage;
83
78
  const messageId = activeValidationMessage ? `inkra-tag-input-${generatedId}-message` : undefined;
84
79
  const state = useResolvedControlProps({
@@ -0,0 +1,11 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export type InkraTheme = 'light' | 'dark' | 'auto';
3
+ export type InkraThemeTokens = Partial<Record<`--inkra-${string}`, string | number>>;
4
+ export interface ThemeProviderProps extends HTMLAttributes<HTMLDivElement> {
5
+ /** Omit to inherit the surrounding theme. auto follows the system through CSS. */
6
+ theme?: InkraTheme;
7
+ /** Scoped CSS variables. Lengths must include units, for example "1.5px". */
8
+ tokens?: InkraThemeTokens;
9
+ }
10
+ /** A CSS scope: never modifies document attributes, storage or sibling themes. */
11
+ export declare const ThemeProvider: import("react").ForwardRefExoticComponent<ThemeProviderProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useSyncExternalStore } from 'react';
3
+ const forcedColorsQuery = '(forced-colors: active)';
4
+ function forcedColorsSnapshot() {
5
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
6
+ && window.matchMedia(forcedColorsQuery).matches;
7
+ }
8
+ function subscribeForcedColors(update) {
9
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
10
+ return () => { };
11
+ const query = window.matchMedia(forcedColorsQuery);
12
+ query.addEventListener('change', update);
13
+ return () => query.removeEventListener('change', update);
14
+ }
15
+ const serverSnapshot = () => false;
16
+ /** A CSS scope: never modifies document attributes, storage or sibling themes. */
17
+ export const ThemeProvider = forwardRef(function ThemeProvider({ theme, tokens, style, className, children, ...props }, ref) {
18
+ const forcedColors = useSyncExternalStore(subscribeForcedColors, forcedColorsSnapshot, serverSnapshot);
19
+ const variables = Object.fromEntries(Object.entries(tokens ?? {}).filter(([key, value]) => key.startsWith('--inkra-') && (typeof value === 'string' || typeof value === 'number' && Number.isFinite(value))));
20
+ // Let the existing system-color tokens win over user pigments in high contrast.
21
+ const scopedStyle = Object.fromEntries(Object.entries({ ...variables, ...style }).filter(([key]) => !forcedColors || !key.startsWith('--inkra-')));
22
+ return _jsx("div", { ...props, ref: ref, className: ['inkra-theme-provider', className].filter(Boolean).join(' '), "data-theme": theme, "data-inkra-theme": theme, style: { color: 'var(--inkra-ink)', fontFamily: 'var(--inkra-font-body)', ...scopedStyle }, children: children });
23
+ });
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, memo, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
+ import { useInkraLocale } from '../i18n/index.js';
3
4
  import { Button, ButtonGroup } from '../Button.js';
4
5
  import { useResolvedControlProps } from '../form/controlProps.js';
5
6
  import { mergeAriaIds, useFieldContext } from '../form/context.js';
@@ -57,13 +58,12 @@ export const TimePicker = forwardRef(function TimePicker(allProps, forwardedRef)
57
58
  const secondsVisible = showSeconds || (step !== undefined && step !== 'any' && Number(step) % 60 !== 0);
58
59
  const resolvedStep = step ?? (secondsVisible ? 1 : 60);
59
60
  const stepBase = String(defaultValue ?? initialValue.current);
60
- const resolvedLocale = locale ?? lang ?? (hourCycle === 'h12' ? 'en-US' : 'en-GB');
61
- const zh = resolvedLocale.toLowerCase().startsWith('zh');
62
- const copy = zh
63
- ? { title: '选择时间', hour: '时', minute: '分', second: '秒', period: '时段', am: '上午', pm: '下午',
64
- done: '确定', cancel: '取消', clear: '清空时间', invalid: '请选择符合时间范围和步进的时间。' }
65
- : { title: 'Choose time', hour: 'Hour', minute: 'Minute', second: 'Second', period: 'Period', am: 'AM', pm: 'PM',
66
- done: 'Apply', cancel: 'Cancel', clear: 'Clear time', invalid: 'Choose a time within the allowed range and step.' };
61
+ const { locale: resolvedLocale, t } = useInkraLocale(locale ?? lang, hourCycle === 'h12' ? 'en-US' : 'en-GB');
62
+ const copy = {
63
+ title: t('timePicker.title'), hour: t('timePicker.hour'), minute: t('timePicker.minute'),
64
+ second: t('timePicker.second'), period: t('timePicker.period'), am: t('timePicker.am'), pm: t('timePicker.pm'),
65
+ done: t('common.apply'), cancel: t('common.cancel'), clear: t('timePicker.clear'), invalid: t('timePicker.invalid'),
66
+ };
67
67
  const currentParts = partsOf(currentValue);
68
68
  const hasCurrentParts = currentParts !== undefined;
69
69
  const formatter = useMemo(() => hasCurrentParts ? new Intl.DateTimeFormat(resolvedLocale, {
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { forwardRef } from 'react';
3
4
  import { PencilIcon } from '../PencilIcon.js';
4
5
  import { PencilLine } from '../PencilLine.js';
@@ -9,7 +10,9 @@ const markerPaths = [
9
10
  { d: 'M16 11v6l4 3' },
10
11
  ];
11
12
  const copy = (value) => typeof value === 'string' ? _jsx(PencilText, { children: value }) : value;
12
- export const Timeline = forwardRef(function Timeline({ 'aria-label': ariaLabel = 'Timeline', className, items, orientation = 'vertical', ...props }, ref) {
13
+ export const Timeline = forwardRef(function Timeline(inkraProps, ref) {
14
+ const { t } = useInkraLocale();
15
+ const { 'aria-label': ariaLabel = t('timeline.label'), className, items, orientation = 'vertical', ...props } = inkraProps;
13
16
  return _jsx("ol", { ...props, ref: ref, "aria-label": ariaLabel, className: ['inkra-timeline', className].filter(Boolean).join(' '), "data-orientation": orientation, children: items.map((item, index) => _jsxs("li", { className: "inkra-timeline__item", children: [_jsxs("span", { className: "inkra-timeline__rail", "aria-hidden": "true", children: [_jsx(PencilSurface, { as: "span", className: "inkra-timeline__marker", variant: "quiet", radius: 8, seed: `timeline-${item.id}`, children: _jsx(PencilIcon, { paths: markerPaths, size: 24, sourceSize: 32 }) }), index < items.length - 1 && _jsx(PencilLine, { className: "inkra-timeline__connector", orientation: orientation === 'vertical' ? 'vertical' : 'horizontal', seed: `timeline-line-${item.id}` })] }), _jsxs("div", { className: "inkra-timeline__body", children: [item.dateTime
14
17
  ? _jsx("time", { className: "inkra-timeline__date", dateTime: item.dateTime, children: copy(item.date) })
15
18
  : _jsx("span", { className: "inkra-timeline__date", children: copy(item.date) }), _jsx("div", { className: "inkra-timeline__title", children: copy(item.title) }), item.description != null && _jsx("div", { className: "inkra-timeline__description", children: copy(item.description) }), item.content != null && _jsx("div", { className: "inkra-timeline__content", children: item.content })] })] }, item.id)) });
@@ -19,7 +19,7 @@ export interface ToastProviderProps {
19
19
  viewportLabel?: string | ((placement: ToastPlacement) => string);
20
20
  viewportClassName?: string;
21
21
  }
22
- declare function Provider({ children, closeLabel, limit, limits, placement, variant, swipeDirection, timeout, toastManager, viewportClassName, viewportLabel, }: ToastProviderProps): import("react").JSX.Element;
22
+ declare function Provider(inkraProps: ToastProviderProps): import("react").JSX.Element;
23
23
  export declare function useToastManager<Data extends object = any>(): {
24
24
  toasts: ToastObject<Data>[];
25
25
  add: <T extends Data = Data>(options: import("./manager.js").ToastAddOptions<T>) => string;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { Toast as BaseToast } from '@base-ui/react/toast';
3
4
  import { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
4
5
  import { Button } from '../Button.js';
@@ -58,7 +59,9 @@ function ToastQueue({ closeLabel, placement, swipeDirection, viewportClassName,
58
59
  return (_jsxs(BaseToast.Root, { className: "inkra-toast__root", "data-tone": tone, "data-variant": variant, "data-toast-id": toastItem.id, swipeDirection: directions, toast: toastItem, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-toast__surface", seed: `toast-${toastItem.id}`, variant: variant === 'compact' ? 'full' : tone === 'neutral' ? 'paper' : 'quiet' }), variant === 'compact' && _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-toast__wash", seed: `toast-wash-${toastItem.id}`, variant: "selection" }), _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-toast__focus", seed: `toast-focus-${toastItem.id}`, variant: "outline" }), _jsxs(BaseToast.Content, { className: "inkra-toast__content", children: [_jsxs("div", { className: "inkra-toast__copy", children: [toastItem.title != null && (_jsx(BaseToast.Title, { className: "inkra-toast__title", children: text(toastItem.title) })), toastItem.description != null && (_jsx(BaseToast.Description, { className: "inkra-toast__description", children: text(toastItem.description) }))] }), _jsxs("div", { className: "inkra-toast__actions", children: [toastItem.actionProps?.children != null && (_jsx(BaseToast.Action, { className: "inkra-toast__action", render: _jsx(Button, { size: "sm", variant: "outline" }) })), _jsx(BaseToast.Close, { "aria-label": closeLabel, className: "inkra-toast__close", render: _jsx(Button, { size: "icon-sm", variant: "ghost" }), children: _jsx(PencilIcon, { paths: closePaths, size: 20, sourceSize: 24, decorative: true }) })] })] })] }, toastItem.id));
59
60
  }) }) }) }));
60
61
  }
61
- function Provider({ children, closeLabel = 'Close notification', limit, limits, placement = 'bottom-end', variant = 'auto', swipeDirection, timeout, toastManager, viewportClassName, viewportLabel = 'Notifications', }) {
62
+ function Provider(inkraProps) {
63
+ const { t } = useInkraLocale();
64
+ const { children, closeLabel = t('toast.close'), limit, limits, placement = 'bottom-end', variant = 'auto', swipeDirection, timeout, toastManager, viewportClassName, viewportLabel = t('toast.viewport'), } = inkraProps;
62
65
  const [localManager] = useState(() => createToastManager());
63
66
  const manager = toastManager ?? localManager;
64
67
  const router = getToastRouter(manager);