@inkra/react 0.4.3 → 0.5.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 (75) hide show
  1. package/README.md +98 -0
  2. package/dist/audio-player/index.js +6 -4
  3. package/dist/breadcrumb/index.js +4 -1
  4. package/dist/calendar/index.js +10 -4
  5. package/dist/calendar/locales.d.ts +7 -0
  6. package/dist/calendar/locales.js +2 -0
  7. package/dist/carousel/index.js +8 -5
  8. package/dist/choice-grid/index.js +3 -1
  9. package/dist/color-picker/GradientEditor.d.ts +2 -1
  10. package/dist/color-picker/GradientEditor.js +10 -9
  11. package/dist/color-picker/index.js +12 -10
  12. package/dist/content/index.js +4 -1
  13. package/dist/data-table/index.d.ts +1 -1
  14. package/dist/data-table/index.js +4 -1
  15. package/dist/date-picker/index.js +11 -13
  16. package/dist/day-agenda/index.d.ts +1 -1
  17. package/dist/day-agenda/index.js +4 -1
  18. package/dist/dockable-panel/index.js +10 -8
  19. package/dist/drawer/index.js +7 -2
  20. package/dist/editable/index.js +8 -5
  21. package/dist/form/Input.js +4 -1
  22. package/dist/form/Textarea.js +4 -2
  23. package/dist/heatmap/index.js +4 -1
  24. package/dist/hotkey/index.js +4 -1
  25. package/dist/i18n/catalog.d.ts +5 -0
  26. package/dist/i18n/catalog.js +289 -0
  27. package/dist/i18n/index.d.ts +23 -0
  28. package/dist/i18n/index.js +50 -0
  29. package/dist/image-cropper/index.js +17 -15
  30. package/dist/image-viewer/VideoResource.js +14 -12
  31. package/dist/image-viewer/index.js +23 -21
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +2 -0
  34. package/dist/input-number/index.js +4 -1
  35. package/dist/internal/textRenderer.d.ts +1 -0
  36. package/dist/internal/textRenderer.js +10 -2
  37. package/dist/journey-map/index.d.ts +1 -1
  38. package/dist/journey-map/index.js +6 -3
  39. package/dist/keypad/index.js +4 -1
  40. package/dist/log/index.js +13 -11
  41. package/dist/markdown/index.js +11 -9
  42. package/dist/node-graph/index.d.ts +1 -1
  43. package/dist/node-graph/index.js +14 -3
  44. package/dist/number-ticker/index.js +4 -1
  45. package/dist/overflow-list/index.js +4 -4
  46. package/dist/overlay/Overlay.js +7 -2
  47. package/dist/pagination/index.js +7 -5
  48. package/dist/pan-zoom/index.js +6 -3
  49. package/dist/radar/index.js +4 -1
  50. package/dist/range-slider/index.js +4 -3
  51. package/dist/rating/index.js +5 -2
  52. package/dist/resizable/index.d.ts +1 -1
  53. package/dist/resizable/index.js +4 -1
  54. package/dist/select/SearchableSelect.js +3 -1
  55. package/dist/select/Select.js +3 -1
  56. package/dist/select/StepperSelect.js +4 -1
  57. package/dist/sortable/index.js +11 -9
  58. package/dist/sparkline/index.js +7 -7
  59. package/dist/steps/index.js +4 -1
  60. package/dist/styles/select.css +1 -4
  61. package/dist/styles.css +1 -4
  62. package/dist/tabs/index.js +4 -1
  63. package/dist/tag-input/index.js +12 -17
  64. package/dist/theme/index.d.ts +11 -0
  65. package/dist/theme/index.js +23 -0
  66. package/dist/time-picker/index.js +7 -7
  67. package/dist/timeline/index.js +4 -1
  68. package/dist/toast/index.d.ts +1 -1
  69. package/dist/toast/index.js +4 -1
  70. package/dist/transfer/index.js +30 -27
  71. package/dist/tree/index.js +7 -2
  72. package/dist/tree-select/index.js +3 -1
  73. package/dist/upload/index.js +21 -18
  74. package/dist/virtual-list/index.js +4 -1
  75. package/package.json +11 -1
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export { Button, ButtonGroup, ButtonLink } from './Button.js';
2
+ export * from './i18n/index.js';
3
+ export * from './theme/index.js';
2
4
  export * from './overlay/index.js';
3
5
  export * from './form/index.js';
4
6
  export * from './input-number/index.js';
@@ -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, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
4
  import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
4
5
  import { Button } from '../Button.js';
@@ -13,7 +14,9 @@ const minusPaths = [{ d: 'M5 12h14' }];
13
14
  const finiteValue = (value) => typeof value === 'number' && Number.isFinite(value) ? value : null;
14
15
  const format = (value) => String(finiteValue(value) ?? '');
15
16
  const numberFromDraft = (draft) => draft === '' ? null : finiteValue(Number(draft));
16
- export const InputNumber = forwardRef(function InputNumber({ classNames, controls = true, decrementButtonLabel = 'Decrease value', defaultValue, disabled, incrementButtonLabel = 'Increase value', inputMode = 'decimal', max, min, onChange, onBlur, onKeyDown, onValueChange, pressAndHold = true, readOnly, repeatDelay = 400, repeatInterval = 80, size = 'md', step = 1, stepModifiers, textAlign = 'center', value, ...props }, forwardedRef) {
17
+ export const InputNumber = forwardRef(function InputNumber(inkraProps, forwardedRef) {
18
+ const { t } = useInkraLocale();
19
+ const { classNames, controls = true, decrementButtonLabel = t('inputNumber.decrease'), defaultValue, disabled, incrementButtonLabel = t('inputNumber.increase'), inputMode = 'decimal', max, min, onChange, onBlur, onKeyDown, onValueChange, pressAndHold = true, readOnly, repeatDelay = 400, repeatInterval = 80, size = 'md', step = 1, stepModifiers, textAlign = 'center', value, ...props } = inkraProps;
17
20
  const inputRef = useRef(null);
18
21
  const isControlled = value !== undefined;
19
22
  const [draft, setDraft] = useState(() => format(isControlled ? value : defaultValue));
@@ -16,6 +16,7 @@ export interface PencilTextSprite {
16
16
  }
17
17
  export declare const pencilLettering: PencilLetteringSettings;
18
18
  export declare const pencilHandwriting = "\"Trip Map Hand\",\"HanziPen SC\",\"Hannotate SC\",\"Kaiti SC\",\"STKaiti\",cursive";
19
+ export declare function boundedTextResolution(width: number, height: number, dpr: number): number;
19
20
  export declare function renderPencilText(text: string, options: {
20
21
  size: number;
21
22
  family?: string;
@@ -9,6 +9,11 @@ export const pencilLettering = Object.freeze({
9
9
  });
10
10
  export const pencilHandwriting = '"Trip Map Hand","HanziPen SC","Hannotate SC","Kaiti SC","STKaiti",cursive';
11
11
  const TEXT_PIXEL_LIMIT = 524288;
12
+ export function boundedTextResolution(width, height, dpr) {
13
+ if (![width, height, dpr].every(value => Number.isFinite(value) && value > 0))
14
+ return 0;
15
+ return Math.floor(Math.min(Math.max(2, Math.min(4, Math.ceil(dpr * 2))), Math.sqrt(TEXT_PIXEL_LIMIT / (width * height)), 8192 / width, 8192 / height));
16
+ }
12
17
  const textCache = new BoundedCache(160, 16 * 1024 * 1024);
13
18
  const glyphCache = new BoundedCache(512, 8 * 1024 * 1024);
14
19
  const clamp = (value) => Math.max(0, Math.min(1, value));
@@ -75,11 +80,12 @@ export function renderPencilText(text, options) {
75
80
  if (text.length > 256 || !Number.isFinite(size) || size <= 0 || size > 128) {
76
81
  throw new RangeError('Pencil text exceeds the bounded renderer input');
77
82
  }
78
- const resolution = Math.max(2, Math.min(4, Math.ceil(dpr * 2)));
83
+ let resolution = Math.max(2, Math.min(4, Math.ceil(dpr * 2)));
79
84
  const font = `${options.style ?? 'normal'} ${options.weight ?? '400'} ${size}px ${family}`;
80
85
  const fontReady = document.fonts?.check(font, text) ?? false;
81
86
  const key = cache ? JSON.stringify([
82
87
  PENCIL_RENDERER_VERSION,
88
+ 'adaptive-text-raster-v1',
83
89
  text,
84
90
  size,
85
91
  font,
@@ -113,7 +119,9 @@ export function renderPencilText(text, options) {
113
119
  const descent = Math.ceil(size * 0.35);
114
120
  const width = advance + padding * 2;
115
121
  const height = ascent + descent + padding * 2;
116
- if (width * height * resolution ** 2 > TEXT_PIXEL_LIMIT) {
122
+ // Wide lines on Retina displays can retain pencil ink at a lower sampling rate.
123
+ resolution = boundedTextResolution(width, height, dpr);
124
+ if (resolution < 1) {
117
125
  throw new RangeError('Pencil text exceeds the bounded pixel budget');
118
126
  }
119
127
  mask.width = width * resolution;
@@ -36,4 +36,4 @@ export interface JourneyMapProps extends HTMLAttributes<HTMLDivElement> {
36
36
  attribution?: string;
37
37
  };
38
38
  }
39
- export declare function JourneyMap({ places, routes, geography, landCover, water, selectedPlace, onPlaceSelect, attribution, attributionDisplay, height, interactive, scrollWheelZoom, showControls, showPlaceList, focusSelectedPlace, labels, className, ...props }: JourneyMapProps): import("react").JSX.Element;
39
+ export declare function JourneyMap({ places, routes, geography, landCover, water, selectedPlace, onPlaceSelect, attribution, attributionDisplay, height, interactive, scrollWheelZoom, showControls, showPlaceList, focusSelectedPlace, labels: labelOverrides, className, ...props }: JourneyMapProps): import("react").JSX.Element;
@@ -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 { useEffect, useRef, useState } from 'react';
3
4
  import { createPortal } from 'react-dom';
4
5
  import { Button } from '../Button.js';
@@ -9,10 +10,12 @@ import { TravelIcon } from '../internal/travelIcons.js';
9
10
  import { paintTerrain, paintStop, paintSea } from './terrain.js';
10
11
  import { MapScale } from './MapScale.js';
11
12
  import { seedToNumber } from '../internal/random.js';
12
- const defaults = { zoomIn: 'Zoom in', zoomOut: 'Zoom out', reset: 'Reset view', unavailable: 'Map unavailable. Choose a place below.' };
13
+ const defaults = (t) => ({ zoomIn: t('common.zoomIn'), zoomOut: t('common.zoomOut'), reset: t('common.resetView'), unavailable: t('map.unavailable') });
13
14
  const noRoutes = [];
14
15
  const valid = (place) => Number.isFinite(place.latitude) && Number.isFinite(place.longitude) && Math.abs(place.latitude) <= 85 && Math.abs(place.longitude) <= 180;
15
- export function JourneyMap({ places, routes = noRoutes, geography, landCover, water, selectedPlace, onPlaceSelect, attribution, attributionDisplay = 'inline', height = 480, interactive = true, scrollWheelZoom = false, showControls = true, showPlaceList = false, focusSelectedPlace = false, labels = defaults, className, ...props }) {
16
+ export function JourneyMap({ places, routes = noRoutes, geography, landCover, water, selectedPlace, onPlaceSelect, attribution, attributionDisplay = 'inline', height = 480, interactive = true, scrollWheelZoom = false, showControls = true, showPlaceList = false, focusSelectedPlace = false, labels: labelOverrides, className, ...props }) {
17
+ const { t } = useInkraLocale();
18
+ const labels = { ...defaults(t), ...labelOverrides };
16
19
  const root = useRef(null);
17
20
  const host = useRef(null);
18
21
  const mapRef = useRef(null);
@@ -193,5 +196,5 @@ export function JourneyMap({ places, routes = noRoutes, geography, landCover, wa
193
196
  else
194
197
  map.scrollWheelZoom.disable();
195
198
  }, [interactive, scrollWheelZoom, ready]);
196
- return _jsxs("div", { ...props, ref: root, className: ['inkra-journey-map', className].filter(Boolean).join(' '), children: [placeLabels.map(({ place, node }) => createPortal(_jsx(PencilText, { children: place.label }), node, place.id)), _jsxs("div", { className: "inkra-journey-map__viewport", style: { height: Math.max(240, Math.min(800, height)) }, children: [attributionDisplay === 'popover' && _jsx("div", { className: "inkra-journey-map__credits", children: _jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { size: "icon", variant: "ghost", "aria-label": labels.attribution ?? 'Map credits', title: labels.attribution ?? 'Map credits', children: _jsx(TravelIcon, { name: "info" }) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "end", sideOffset: 8, collisionPadding: 20, className: "inkra-journey-map__credits-content", children: [_jsx(Popover.Title, { children: labels.attribution ?? 'Map credits' }), _jsx(Popover.Description, { children: attribution })] }) })] }) }), _jsx("div", { ref: host, className: "inkra-journey-map__host", "aria-label": props['aria-label'] ?? 'Map' }), showControls && _jsxs("div", { className: "inkra-journey-map__tools", children: [_jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomIn, title: labels.zoomIn, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomIn(); }, children: _jsx(TravelIcon, { name: "plus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomOut, title: labels.zoomOut, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomOut(); }, children: _jsx(TravelIcon, { name: "minus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.reset, title: labels.reset, onClick: () => { mapRef.current?.stop(); fitRef.current(); }, children: _jsx(TravelIcon, { name: "reset" }) })] }), ready && _jsx(MapScale, { map: mapRef.current }), failed && _jsx("p", { role: "status", children: labels.unavailable })] }), attributionDisplay === 'inline' && _jsx("p", { className: "inkra-journey-map__attribution", children: attribution }), (showPlaceList || !ready || failed) && _jsx("ol", { className: "inkra-journey-map__places", children: places.filter(valid).map((place, index) => _jsx("li", { children: _jsxs("button", { type: "button", "aria-pressed": selectedPlace === place.id, onClick: () => { mapRef.current?.panTo([place.latitude, place.longitude], { animate: false }); onPlaceSelect?.(place); }, children: [_jsx("span", { "aria-hidden": "true", children: index + 1 }), _jsx(PencilText, { children: place.label })] }) }, place.id)) })] });
199
+ return _jsxs("div", { ...props, ref: root, className: ['inkra-journey-map', className].filter(Boolean).join(' '), children: [placeLabels.map(({ place, node }) => createPortal(_jsx(PencilText, { children: place.label }), node, place.id)), _jsxs("div", { className: "inkra-journey-map__viewport", style: { height: Math.max(240, Math.min(800, height)) }, children: [attributionDisplay === 'popover' && _jsx("div", { className: "inkra-journey-map__credits", children: _jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { size: "icon", variant: "ghost", "aria-label": labels.attribution ?? t('map.credits'), title: labels.attribution ?? t('map.credits'), children: _jsx(TravelIcon, { name: "info" }) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "end", sideOffset: 8, collisionPadding: 20, className: "inkra-journey-map__credits-content", children: [_jsx(Popover.Title, { children: labels.attribution ?? t('map.credits') }), _jsx(Popover.Description, { children: attribution })] }) })] }) }), _jsx("div", { ref: host, className: "inkra-journey-map__host", "aria-label": props['aria-label'] ?? t('map.label') }), showControls && _jsxs("div", { className: "inkra-journey-map__tools", children: [_jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomIn, title: labels.zoomIn, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomIn(); }, children: _jsx(TravelIcon, { name: "plus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomOut, title: labels.zoomOut, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomOut(); }, children: _jsx(TravelIcon, { name: "minus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.reset, title: labels.reset, onClick: () => { mapRef.current?.stop(); fitRef.current(); }, children: _jsx(TravelIcon, { name: "reset" }) })] }), ready && _jsx(MapScale, { map: mapRef.current }), failed && _jsx("p", { role: "status", children: labels.unavailable })] }), attributionDisplay === 'inline' && _jsx("p", { className: "inkra-journey-map__attribution", children: attribution }), (showPlaceList || !ready || failed) && _jsx("ol", { className: "inkra-journey-map__places", children: places.filter(valid).map((place, index) => _jsx("li", { children: _jsxs("button", { type: "button", "aria-pressed": selectedPlace === place.id, onClick: () => { mapRef.current?.panTo([place.latitude, place.longitude], { animate: false }); onPlaceSelect?.(place); }, children: [_jsx("span", { "aria-hidden": "true", children: index + 1 }), _jsx(PencilText, { children: place.label })] }) }, place.id)) })] });
197
200
  }
@@ -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, useId, useRef, useState } from 'react';
3
4
  import { PencilSurface } from '../PencilSurface.js';
4
5
  import { PencilText } from '../PencilText.js';
@@ -20,7 +21,9 @@ function keyVariant(key) {
20
21
  return 'secondary';
21
22
  return 'default';
22
23
  }
23
- export const Keypad = forwardRef(function Keypad({ 'aria-label': ariaLabel, className, defaultValue = '', disabled = false, emptyContent = null, label = 'Keypad', layout, maxLength, onConfirm, onKeyPress, onValueChange, value, ...props }, ref) {
24
+ export const Keypad = forwardRef(function Keypad(inkraProps, ref) {
25
+ const { t } = useInkraLocale();
26
+ const { 'aria-label': ariaLabel, className, defaultValue = '', disabled = false, emptyContent = null, label = t('keypad.label'), layout, maxLength, onConfirm, onKeyPress, onValueChange, value, ...props } = inkraProps;
24
27
  const id = useId();
25
28
  const controlled = value !== undefined;
26
29
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
package/dist/log/index.js CHANGED
@@ -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, useCallback, useMemo, useRef, useState, } from 'react';
3
4
  import { Button } from '../Button.js';
4
5
  import { PencilIcon } from '../PencilIcon.js';
@@ -7,16 +8,16 @@ import { PencilSurface } from '../PencilSurface.js';
7
8
  import { PencilText } from '../PencilText.js';
8
9
  import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
9
10
  import { isNearLogBottom, shouldFollowLogUpdate } from './model.js';
10
- const defaultLabels = {
11
- filters: 'Filter log levels',
12
- allLevels: 'All',
13
- pause: 'Pause automatic follow',
14
- resume: 'Resume automatic follow',
15
- latest: 'Go to latest record',
16
- empty: 'No records yet',
17
- noMatches: 'No records match the selected levels',
18
- list: 'Log records',
19
- };
11
+ const defaultLabels = (t) => ({
12
+ filters: t('log.filters'),
13
+ allLevels: t('common.all'),
14
+ pause: t('log.pause'),
15
+ resume: t('log.resume'),
16
+ latest: t('log.latest'),
17
+ empty: t('log.empty'),
18
+ noMatches: t('log.noMatches'),
19
+ list: t('log.list'),
20
+ });
20
21
  const defaultTones = {
21
22
  debug: 'neutral',
22
23
  info: 'info',
@@ -42,7 +43,8 @@ function normalizedThreshold(value) {
42
43
  return Number.isFinite(value) ? Math.max(0, value) : 32;
43
44
  }
44
45
  export const Log = forwardRef(function Log({ 'aria-label': ariaLabel, className, defaultFilteredLevels, defaultPaused = false, empty, filteredLevels, items, labels: labelOverrides, levels, nearBottomThreshold, noMatches, onFilteredLevelsChange, onPausedChange, paused, showFollowControl = true, showLevelFilter = true, viewportProps, ...props }, ref) {
45
- const labels = { ...defaultLabels, ...labelOverrides };
46
+ const { t } = useInkraLocale();
47
+ const labels = { ...defaultLabels(t), ...labelOverrides };
46
48
  const resolvedLevels = useMemo(() => {
47
49
  if (levels)
48
50
  return levels;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import ReactMarkdown from 'react-markdown';
3
4
  import remarkGfm from 'remark-gfm';
4
5
  import { Children, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useMemo, } from 'react';
@@ -153,17 +154,18 @@ function createMarkdownComponents(headingLevelOffset, labels) {
153
154
  };
154
155
  }
155
156
  export const Markdown = forwardRef(function Markdown({ children, className, components, headingLevelOffset = 0, labels, transformUrl, ...props }, ref) {
157
+ const { t } = useInkraLocale();
156
158
  const offset = Number.isFinite(headingLevelOffset) ? Math.trunc(headingLevelOffset) : 0;
157
159
  const resolvedLabels = useMemo(() => ({
158
- code: labels?.code ?? 'Code block',
159
- plainText: labels?.plainText ?? 'Plain text',
160
- copy: labels?.copy ?? 'Copy code',
161
- copied: labels?.copied ?? 'Code copied',
162
- copyFailed: labels?.copyFailed ?? 'Copy failed. Select the code to copy it manually.',
163
- table: labels?.table ?? 'Markdown table',
164
- taskChecked: labels?.taskChecked ?? 'Completed task',
165
- taskUnchecked: labels?.taskUnchecked ?? 'Incomplete task',
166
- }), [labels?.code, labels?.plainText, labels?.copy, labels?.copied, labels?.copyFailed, labels?.table, labels?.taskChecked, labels?.taskUnchecked]);
160
+ code: labels?.code ?? t('markdown.code'),
161
+ plainText: labels?.plainText ?? t('markdown.plain'),
162
+ copy: labels?.copy ?? t('markdown.copy'),
163
+ copied: labels?.copied ?? t('markdown.copied'),
164
+ copyFailed: labels?.copyFailed ?? t('markdown.copyFailed'),
165
+ table: labels?.table ?? t('markdown.table'),
166
+ taskChecked: labels?.taskChecked ?? t('markdown.checked'),
167
+ taskUnchecked: labels?.taskUnchecked ?? t('markdown.unchecked'),
168
+ }), [t, labels?.code, labels?.plainText, labels?.copy, labels?.copied, labels?.copyFailed, labels?.table, labels?.taskChecked, labels?.taskUnchecked]);
167
169
  const builtIns = useMemo(() => createMarkdownComponents(offset, resolvedLabels), [offset, resolvedLabels]);
168
170
  const resolvedComponents = useMemo(() => ({ ...builtIns, ...components }), [builtIns, components]);
169
171
  const urlTransform = useCallback((url, key) => {
@@ -111,7 +111,7 @@ export interface NodeGraphControlsProps extends HTMLAttributes<HTMLDivElement> {
111
111
  zoomOutLabel?: string;
112
112
  fitViewLabel?: string;
113
113
  }
114
- declare function NodeGraphControls({ className, fitViewOptions, zoomInLabel, zoomOutLabel, fitViewLabel, ...props }: NodeGraphControlsProps): import("react").JSX.Element;
114
+ declare function NodeGraphControls(inkraProps: NodeGraphControlsProps): import("react").JSX.Element;
115
115
  export declare function applyNodeGraphNodeChanges<NodeType extends NodeGraphNode>(changes: NodeGraphNodeChange<NodeType>[], nodes: NodeType[]): NodeType[];
116
116
  export declare function applyNodeGraphEdgeChanges<EdgeType extends NodeGraphEdge>(changes: NodeGraphEdgeChange<EdgeType>[], edges: EdgeType[]): EdgeType[];
117
117
  export declare function addNodeGraphEdge<EdgeType extends NodeGraphEdge>(connection: NodeGraphConnection | EdgeType, edges: EdgeType[]): EdgeType[];
@@ -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 { EdgeLabelRenderer, Handle, Panel, Position, ReactFlow, ReactFlowProvider, addEdge as addFlowEdge, applyEdgeChanges as applyFlowEdgeChanges, applyNodeChanges as applyFlowNodeChanges, getBezierPath, useReactFlow, useViewport, } from '@xyflow/react';
3
4
  import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
4
5
  import { PencilIcon } from '../PencilIcon.js';
@@ -238,7 +239,9 @@ function NodeGraphConnectionLine({ fromX, fromY, toX, toY, fromPosition, toPosit
238
239
  });
239
240
  return _jsx(PencilEdgeCanvas, { id: "connection-preview", path: path, className: "inkra-node-graph__connection-line" });
240
241
  }
241
- function NodeGraphControls({ className, fitViewOptions, zoomInLabel = 'Zoom in', zoomOutLabel = 'Zoom out', fitViewLabel = 'Fit graph to view', ...props }) {
242
+ function NodeGraphControls(inkraProps) {
243
+ const { t } = useInkraLocale();
244
+ const { className, fitViewOptions, zoomInLabel = t('common.zoomIn'), zoomOutLabel = t('common.zoomOut'), fitViewLabel = t('graph.fit'), ...props } = inkraProps;
242
245
  const flow = useReactFlow();
243
246
  const actions = [
244
247
  { label: zoomInLabel, paths: zoomInPaths, run: () => flow.zoomIn() },
@@ -250,7 +253,9 @@ function NodeGraphControls({ className, fitViewOptions, zoomInLabel = 'Zoom in',
250
253
  function SsrFallback({ nodes, emptyState, label, }) {
251
254
  return _jsx("div", { className: "inkra-node-graph__ssr", role: "group", "aria-label": label, children: nodes.length === 0 ? _jsx("div", { className: "inkra-node-graph__empty", children: emptyState }) : _jsx("ul", { children: nodes.map(node => _jsx("li", { children: readableLabel(node.data.label, node.id) }, node.id)) }) });
252
255
  }
253
- function NodeGraphRootInner({ nodes, edges, onNodesChange, onEdgesChange, onConnect, isValidConnection, renderNode, renderEdge, renderEdgeLabel, readOnly = false, emptyState = 'No nodes yet', controls = true, controlLabels, fitView = false, fitViewOptions, defaultViewport = { x: 0, y: 0, zoom: 1 }, viewport, onViewportChange, minZoom = 0.25, maxZoom = 2, panOnDrag = true, zoomOnScroll = true, zoomOnPinch = true, connectOnClick = true, onlyRenderVisibleElements = false, className, style, 'aria-label': ariaLabel = 'Node graph', ...props }, forwardedRef) {
256
+ function NodeGraphRootInner(inkraProps, forwardedRef) {
257
+ const { t } = useInkraLocale();
258
+ const { nodes, edges, onNodesChange, onEdgesChange, onConnect, isValidConnection, renderNode, renderEdge, renderEdgeLabel, readOnly = false, emptyState = t('graph.empty'), controls = true, controlLabels, fitView = false, fitViewOptions, defaultViewport = { x: 0, y: 0, zoom: 1 }, viewport, onViewportChange, minZoom = 0.25, maxZoom = 2, panOnDrag = true, zoomOnScroll = true, zoomOnPinch = true, connectOnClick = true, onlyRenderVisibleElements = false, className, style, 'aria-label': ariaLabel = t('graph.label'), ...props } = inkraProps;
254
259
  const [mounted, setMounted] = useState(false);
255
260
  const instanceRef = useRef(null);
256
261
  useEffect(() => setMounted(true), []);
@@ -307,7 +312,13 @@ function NodeGraphRootInner({ nodes, edges, onNodesChange, onEdgesChange, onConn
307
312
  const internalNodes = useMemo(() => nodes.map(adaptNode), [nodes, adaptNode]);
308
313
  const internalEdges = useMemo(() => edges.map(adaptEdge), [edges, adaptEdge]);
309
314
  const rootStyle = { minHeight: 320, ...style };
310
- return _jsx("div", { ...props, "aria-label": ariaLabel, className: ['inkra-node-graph', className].filter(Boolean).join(' '), "data-empty": nodes.length === 0 || undefined, "data-read-only": readOnly || undefined, style: rootStyle, children: !mounted ? _jsx(SsrFallback, { nodes: nodes, emptyState: emptyState, label: ariaLabel }) : _jsxs(ReactFlowProvider, { children: [_jsx(ReactFlow, { "aria-label": ariaLabel, nodes: internalNodes, edges: internalEdges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onInit: instance => { instanceRef.current = instance; }, onNodesChange: changes => onNodesChange?.(changes), onEdgesChange: changes => onEdgesChange?.(changes), onConnect: readOnly ? undefined : onConnect, isValidConnection: isValidConnection, nodesDraggable: !readOnly, nodesConnectable: !readOnly, elementsSelectable: !readOnly, edgesReconnectable: !readOnly, nodesFocusable: true, edgesFocusable: true, deleteKeyCode: readOnly ? null : ['Backspace', 'Delete'], fitView: fitView, fitViewOptions: fitViewOptions, defaultViewport: defaultViewport, viewport: viewport, onViewportChange: onViewportChange, minZoom: minZoom, maxZoom: maxZoom, panOnDrag: panOnDrag, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, connectOnClick: connectOnClick && !readOnly, connectionLineComponent: NodeGraphConnectionLine, onlyRenderVisibleElements: onlyRenderVisibleElements, noDragClassName: "nodrag", noPanClassName: "nopan", children: controls && _jsx(Panel, { position: "bottom-left", children: _jsx(NodeGraphControls, { fitViewLabel: controlLabels?.fitView, fitViewOptions: fitViewOptions, zoomInLabel: controlLabels?.zoomIn, zoomOutLabel: controlLabels?.zoomOut }) }) }), nodes.length === 0 && _jsx("div", { className: "inkra-node-graph__empty", role: "status", children: emptyState })] }) });
315
+ return _jsx("div", { ...props, "aria-label": ariaLabel, className: ['inkra-node-graph', className].filter(Boolean).join(' '), "data-empty": nodes.length === 0 || undefined, "data-read-only": readOnly || undefined, style: rootStyle, children: !mounted ? _jsx(SsrFallback, { nodes: nodes, emptyState: emptyState, label: ariaLabel }) : _jsxs(ReactFlowProvider, { children: [_jsx(ReactFlow, { "aria-label": ariaLabel, ariaLabelConfig: {
316
+ 'node.a11yDescription.default': t('graph.nodeInstructions'),
317
+ 'node.a11yDescription.keyboardDisabled': t('graph.nodeMoveInstructions'),
318
+ 'node.a11yDescription.ariaLiveMessage': ({ x, y }) => t('graph.nodeMoved', { x, y }),
319
+ 'edge.a11yDescription.default': t('graph.edgeInstructions'),
320
+ 'handle.ariaLabel': t('graph.handle'),
321
+ }, nodes: internalNodes, edges: internalEdges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onInit: instance => { instanceRef.current = instance; }, onNodesChange: changes => onNodesChange?.(changes), onEdgesChange: changes => onEdgesChange?.(changes), onConnect: readOnly ? undefined : onConnect, isValidConnection: isValidConnection, nodesDraggable: !readOnly, nodesConnectable: !readOnly, elementsSelectable: !readOnly, edgesReconnectable: !readOnly, nodesFocusable: true, edgesFocusable: true, deleteKeyCode: readOnly ? null : ['Backspace', 'Delete'], fitView: fitView, fitViewOptions: fitViewOptions, defaultViewport: defaultViewport, viewport: viewport, onViewportChange: onViewportChange, minZoom: minZoom, maxZoom: maxZoom, panOnDrag: panOnDrag, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, connectOnClick: connectOnClick && !readOnly, connectionLineComponent: NodeGraphConnectionLine, onlyRenderVisibleElements: onlyRenderVisibleElements, noDragClassName: "nodrag", noPanClassName: "nopan", children: controls && _jsx(Panel, { position: "bottom-left", children: _jsx(NodeGraphControls, { fitViewLabel: controlLabels?.fitView, fitViewOptions: fitViewOptions, zoomInLabel: controlLabels?.zoomIn, zoomOutLabel: controlLabels?.zoomOut }) }) }), nodes.length === 0 && _jsx("div", { className: "inkra-node-graph__empty", role: "status", children: emptyState })] }) });
311
322
  }
312
323
  const NodeGraphRoot = forwardRef(NodeGraphRootInner);
313
324
  export function applyNodeGraphNodeChanges(changes, nodes) {
@@ -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, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
3
4
  import { PencilText } from '../PencilText.js';
4
5
  import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
@@ -23,7 +24,9 @@ function lettering(content, seed) {
23
24
  function accessibleAffix(content) {
24
25
  return typeof content === 'string' || typeof content === 'number' ? String(content) : '';
25
26
  }
26
- export const NumberTicker = forwardRef(function NumberTicker({ animate = true, className, compact = false, compactOptions, deltaDuration = 1600, deltaFormatOptions, duration = 420, easing = easeOutCubic, formatOptions, getValueLabel, locale, prefix, renderDelta, renderValue, showDelta = false, suffix, value, ...props }, forwardedRef) {
27
+ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, className, compact = false, compactOptions, deltaDuration = 1600, deltaFormatOptions, duration = 420, easing = easeOutCubic, formatOptions, getValueLabel, locale: localeProp, prefix, renderDelta, renderValue, showDelta = false, suffix, value, ...props }, forwardedRef) {
28
+ const { locale: inheritedLocale } = useInkraLocale();
29
+ const locale = localeProp ?? inheritedLocale;
27
30
  const rootRef = useRef(null);
28
31
  const frameRef = useRef(null);
29
32
  const deltaTimerRef = useRef(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, useCallback, useEffect, useId, useMemo, useRef, useState, } from 'react';
3
4
  import { Button } from '../Button.js';
4
5
  import { DropdownMenu } from '../dropdown-menu/index.js';
@@ -27,10 +28,9 @@ function assignRef(ref, value) {
27
28
  else if (ref)
28
29
  ref.current = value;
29
30
  }
30
- function defaultOverflowLabel(count) {
31
- return `More items (${count})`;
32
- }
33
- function OverflowListInner({ 'aria-label': ariaLabel, className, gap, getOverflowItemIcon, items, measurementKey = '', onOverflowChange, onOverflowItemSelect, overflowButtonProps, overflowLabel = defaultOverflowLabel, renderItem, renderOverflow, renderOverflowItem, role, strategy = 'end', style, ...props }, forwardedRef) {
31
+ function OverflowListInner({ 'aria-label': ariaLabel, className, gap, getOverflowItemIcon, items, measurementKey = '', onOverflowChange, onOverflowItemSelect, overflowButtonProps, overflowLabel: overflowLabelProp, renderItem, renderOverflow, renderOverflowItem, role, strategy = 'end', style, ...props }, forwardedRef) {
32
+ const { t } = useInkraLocale();
33
+ const overflowLabel = overflowLabelProp ?? ((count) => t('overflow.more', { count }));
34
34
  const generatedId = useId();
35
35
  const rootRef = useRef(null);
36
36
  const triggerRef = useRef(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 * as DialogPrimitive from '@radix-ui/react-dialog';
3
4
  import { forwardRef, useId } from 'react';
4
5
  import { __iconData as closeIcon } from 'lucide-react/dist/esm/icons/x.mjs';
@@ -43,7 +44,9 @@ export const OverlayClose = forwardRef(function OverlayClose(props, ref) {
43
44
  return _jsx(DialogPrimitive.Close, { ...props, ref: ref });
44
45
  });
45
46
  const closePaths = closeIcon.node.map(([, attributes]) => ({ d: attributes.d }));
46
- export const OverlayCloseButton = forwardRef(function OverlayCloseButton({ label = 'Close', className, onFocus, ...props }, ref) {
47
+ export const OverlayCloseButton = forwardRef(function OverlayCloseButton(inkraProps, ref) {
48
+ const { t } = useInkraLocale();
49
+ const { label = t('common.close'), className, onFocus, ...props } = inkraProps;
47
50
  return _jsx(Tooltip.Provider, { children: _jsxs(Tooltip.Root, { children: [_jsx(Tooltip.Trigger, { asChild: true, children: _jsx(DialogPrimitive.Close, { asChild: true, children: _jsx(Button, { ...props, ref: ref, type: "button", size: "icon", variant: "ghost", className: classes('inkra-overlay__close-button', className), "aria-label": label, onFocus: event => {
48
51
  onFocus?.(event);
49
52
  const dialog = event.currentTarget.closest('[role="dialog"]');
@@ -52,7 +55,9 @@ export const OverlayCloseButton = forwardRef(function OverlayCloseButton({ label
52
55
  event.preventDefault();
53
56
  }, children: _jsx(PencilIcon, { paths: closePaths, size: 30, sourceSize: 24, decorative: true }) }) }) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Content, { side: "bottom", align: "end", children: label }) })] }) });
54
57
  });
55
- export const OverlayHeader = forwardRef(function OverlayHeader({ children, className, divided = true, showClose = true, closeLabel = 'Close', action, ...props }, ref) {
58
+ export const OverlayHeader = forwardRef(function OverlayHeader(inkraProps, ref) {
59
+ const { t } = useInkraLocale();
60
+ const { children, className, divided = true, showClose = true, closeLabel = t('common.close'), action, ...props } = inkraProps;
56
61
  const id = useId();
57
62
  return _jsxs("div", { ...props, ref: ref, className: classes('inkra-overlay__header', className), "data-divided": divided || undefined, children: [_jsx("div", { className: "inkra-overlay__heading", children: children }), (action || showClose) && _jsxs("div", { className: "inkra-overlay__header-actions", children: [action, showClose && _jsx(OverlayCloseButton, { label: closeLabel })] }), divided && _jsx(PencilLine, { className: "inkra-overlay__section-divider", seed: `overlay-header-${id}` })] });
58
63
  });
@@ -1,19 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { forwardRef, useEffect, useState } from 'react';
3
4
  import { Button } from '../Button.js';
4
5
  import { PencilIcon } from '../PencilIcon.js';
5
6
  import { PencilSurface } from '../PencilSurface.js';
6
7
  import { PencilText } from '../PencilText.js';
7
8
  import { clampPage, getPaginationEntries, normalizeTotal } from './model.js';
8
- const defaultLabels = {
9
- nav: 'Pagination', first: 'First page', previous: 'Previous page', next: 'Next page', last: 'Last page',
10
- page: page => `Page ${page}`, ellipsis: 'More pages',
11
- };
9
+ const defaultLabels = (t) => ({
10
+ nav: t('pagination.nav'), first: t('pagination.first'), previous: t('common.previousPage'), next: t('common.nextPage'), last: t('pagination.last'),
11
+ page: page => t('pagination.page', { page }), ellipsis: t('pagination.more'),
12
+ });
12
13
  export const Pagination = forwardRef(function Pagination({ totalPages, page, defaultPage = 1, onPageChange, siblingCount = 1, boundaryCount = 1, showFirstLast = false, showPreviousNext = true, hideOnSinglePage = false, simple = false, renderPage, renderSummary, disabled = false, size = 'md', labels, className, ...props }, ref) {
14
+ const { t } = useInkraLocale();
13
15
  const total = normalizeTotal(totalPages);
14
16
  const [internalPage, setInternalPage] = useState(() => clampPage(defaultPage, total));
15
17
  const current = clampPage(page ?? internalPage, total);
16
- const text = { ...defaultLabels, ...labels };
18
+ const text = { ...defaultLabels(t), ...labels };
17
19
  useEffect(() => {
18
20
  if (page === undefined)
19
21
  setInternalPage(value => clampPage(value, total));
@@ -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 { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
3
4
  import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useRef, } from 'react';
4
5
  import { PencilSurface } from '../PencilSurface.js';
@@ -26,7 +27,9 @@ function transformsEqual(left, right) {
26
27
  function prefersReducedMotion(element) {
27
28
  return element?.ownerDocument.defaultView?.matchMedia('(prefers-reduced-motion: reduce)').matches ?? false;
28
29
  }
29
- function PanZoomRoot({ children, value, defaultValue = defaultTransform, onTransformChange, minScale = 0.25, maxScale = 8, wheel = 'modifier', wheelStep = 0.001, pinch = true, pan = true, keyboard = true, keyboardPanStep = 48, keyboardZoomStep = 0.25, doubleClickZoom = false, disabled = false, readOnly = false, limitToBounds = true, centerZoomedOut = true, centerOnInit = false, fitOnInit = false, className, viewportClassName, contentClassName, style, viewportStyle, contentStyle, viewportProps, contentProps, instructions, 'aria-label': ariaLabel = 'Pan and zoom content', ...props }, forwardedRef) {
30
+ function PanZoomRoot(inkraProps, forwardedRef) {
31
+ const { t } = useInkraLocale();
32
+ const { children, value, defaultValue = defaultTransform, onTransformChange, minScale = 0.25, maxScale = 8, wheel = 'modifier', wheelStep = 0.001, pinch = true, pan = true, keyboard = true, keyboardPanStep = 48, keyboardZoomStep = 0.25, doubleClickZoom = false, disabled = false, readOnly = false, limitToBounds = true, centerZoomedOut = true, centerOnInit = false, fitOnInit = false, className, viewportClassName, contentClassName, style, viewportStyle, contentStyle, viewportProps, contentProps, instructions, 'aria-label': ariaLabel = t('panZoom.label'), ...props } = inkraProps;
30
33
  const resolvedMinScale = Math.max(Number.isFinite(minScale) ? minScale : 0.25, 0.0001);
31
34
  const resolvedMaxScale = Math.max(Number.isFinite(maxScale) ? maxScale : 8, resolvedMinScale);
32
35
  const resolvedWheelStep = Number.isFinite(wheelStep) && wheelStep > 0 ? wheelStep : 0.001;
@@ -122,8 +125,8 @@ function PanZoomRoot({ children, value, defaultValue = defaultTransform, onTrans
122
125
  ? (keys) => !wheelTargetBlocksZoomRef.current && (keys.includes('Control') || keys.includes('Meta'))
123
126
  : () => !wheelTargetBlocksZoomRef.current;
124
127
  const defaultInstructions = wheel === 'modifier'
125
- ? 'Use Control or Command with the mouse wheel to zoom. Use arrow keys to pan, plus or minus to zoom, and 0 to reset.'
126
- : 'Use arrow keys to pan, plus or minus to zoom, and 0 to reset.';
128
+ ? t('panZoom.modifierInstructions')
129
+ : t('panZoom.instructions');
127
130
  const wheelHint = instructions ?? defaultInstructions;
128
131
  const mergedViewportLabel = viewportProps?.['aria-label'] ?? ariaLabel;
129
132
  const describedBy = [viewportProps?.['aria-describedby'], keyboard && !disabled ? instructionsId : null]
@@ -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 { scaleLinear } from 'd3-scale';
3
4
  import { curveLinearClosed, lineRadial } from 'd3-shape';
4
5
  import { forwardRef, useEffect, useId, useMemo, useRef, useState } from 'react';
@@ -179,7 +180,9 @@ function domainForAxis(axis, values) {
179
180
  function paintStyle(color) {
180
181
  return { '--inkra-accent': color, '--inkra-accent-soft': color, '--inkra-accent-strong': color };
181
182
  }
182
- export const Radar = forwardRef(function Radar({ axes, series, label, description, size = 'min(100%, 34rem)', maxRadius, levels = 4, showAxes = true, showLabels = true, showLegend = true, showValues = true, showTable = true, legendLabel = 'Series', tableCaption = 'Radar data', seriesLabel = 'Series', missingLabel = 'Missing', emptyMessage = 'No radar data', selectedSeries, defaultSelectedSeries = null, onSelectedSeriesChange, renderAxisLabel, renderValueLabel, getValueLabel, className, style, ...props }, forwardedRef) {
183
+ export const Radar = forwardRef(function Radar(inkraProps, forwardedRef) {
184
+ const { t } = useInkraLocale();
185
+ const { axes, series, label, description, size = 'min(100%, 34rem)', maxRadius, levels = 4, showAxes = true, showLabels = true, showLegend = true, showValues = true, showTable = true, legendLabel = t('radar.series'), tableCaption = t('radar.caption'), seriesLabel = t('radar.series'), missingLabel = t('common.missing'), emptyMessage = t('radar.empty'), selectedSeries, defaultSelectedSeries = null, onSelectedSeriesChange, renderAxisLabel, renderValueLabel, getValueLabel, className, style, ...props } = inkraProps;
183
186
  const id = useId();
184
187
  const labelId = `inkra-radar-${id}-label`;
185
188
  const descriptionId = description == null ? undefined : `inkra-radar-${id}-description`;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef } from 'react';
3
+ import { useInkraLocale } from '../i18n/index.js';
3
4
  import { Slider } from '../slider/index.js';
4
- const DEFAULT_LABELS = ['Minimum value', 'Maximum value'];
5
5
  function asRangeValue(value) {
6
6
  return [value[0], value[1]];
7
7
  }
@@ -15,8 +15,9 @@ function getMinimumSteps(minDistance, step) {
15
15
  return 0;
16
16
  return Math.ceil(minDistance / step);
17
17
  }
18
- export const RangeSlider = forwardRef(function RangeSlider({ allowCross = false, defaultValue, max = 100, min = 0, minDistance = 0, onValueChange, onValueCommit, step = 1, thumbLabels = DEFAULT_LABELS, value, ...props }, forwardedRef) {
18
+ export const RangeSlider = forwardRef(function RangeSlider({ allowCross = false, defaultValue, max = 100, min = 0, minDistance = 0, onValueChange, onValueCommit, step = 1, thumbLabels, value, ...props }, forwardedRef) {
19
+ const { t } = useInkraLocale();
19
20
  const controlledValue = value === undefined ? undefined : normalizeRangeValue(value);
20
21
  const uncontrolledDefault = value === undefined ? normalizeRangeValue(defaultValue ?? [min, max]) : undefined;
21
- return _jsx(Slider, { ...props, defaultValue: uncontrolledDefault, max: max, min: min, minStepsBetweenThumbs: allowCross ? 0 : getMinimumSteps(minDistance, step), onValueChange: next => onValueChange?.(asRangeValue(next)), onValueCommit: next => onValueCommit?.(asRangeValue(next)), preserveThumbOrder: !allowCross, ref: forwardedRef, step: step, thumbLabels: thumbLabels, value: controlledValue });
22
+ return _jsx(Slider, { ...props, defaultValue: uncontrolledDefault, max: max, min: min, minStepsBetweenThumbs: allowCross ? 0 : getMinimumSteps(minDistance, step), onValueChange: next => onValueChange?.(asRangeValue(next)), onValueCommit: next => onValueCommit?.(asRangeValue(next)), preserveThumbOrder: !allowCross, ref: forwardedRef, step: step, thumbLabels: thumbLabels ?? [t('rangeSlider.minimum'), t('rangeSlider.maximum')], value: controlledValue });
22
23
  });
@@ -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, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
3
4
  import { PencilIcon } from '../PencilIcon.js';
4
5
  import { PencilSurface } from '../PencilSurface.js';
@@ -39,7 +40,9 @@ function previousRatingValue(value, count, step) {
39
40
  }
40
41
  return value - step;
41
42
  }
42
- export const Rating = forwardRef(function Rating({ value, defaultValue = 0, onValueChange, name, form, hoverPreview = true, onPreviewChange, count: countProp = 5, step: stepProp = 1, clearable = false, disabled = false, readOnly = false, size = 'md', dir, symbol, renderSymbol, getValueLabel = (nextValue, maximum) => `${nextValue} of ${maximum}`, className = '', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, onBlur, onKeyDown, onPointerDown, onPointerLeave, ...props }, forwardedRef) {
43
+ export const Rating = forwardRef(function Rating({ value, defaultValue = 0, onValueChange, name, form, hoverPreview = true, onPreviewChange, count: countProp = 5, step: stepProp = 1, clearable = false, disabled = false, readOnly = false, size = 'md', dir, symbol, renderSymbol, getValueLabel: valueLabelProp, className = '', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, onBlur, onKeyDown, onPointerDown, onPointerLeave, ...props }, forwardedRef) {
44
+ const { t } = useInkraLocale();
45
+ const getValueLabel = valueLabelProp ?? ((value, count) => t('rating.value', { value, count }));
43
46
  const count = Math.max(1, Math.floor(finitePositive(countProp, 5)));
44
47
  const step = Math.min(count, finitePositive(stepProp, 1));
45
48
  const controlled = value !== undefined;
@@ -207,7 +210,7 @@ export const Rating = forwardRef(function Rating({ value, defaultValue = 0, onVa
207
210
  return symbol;
208
211
  return _jsx(PencilIcon, { paths: defaultSymbolPaths, sourceSize: 24, size: defaultIconSize, active: filled, seed: `${symbolId}-${index}` });
209
212
  };
210
- return (_jsxs("div", { ...props, "aria-disabled": disabled || undefined, "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : 'Rating'), "aria-labelledby": ariaLabelledBy, "aria-readonly": readOnly || undefined, "aria-valuemax": count, "aria-valuemin": 0, "aria-valuenow": committedValue, "aria-valuetext": getValueLabel(committedValue, count), className: ['inkra-rating', className].filter(Boolean).join(' '), "data-disabled": disabled || undefined, "data-previewing": previewing || undefined, "data-readonly": readOnly || undefined, "data-size": size, dir: dir, onBlur: (event) => {
213
+ return (_jsxs("div", { ...props, "aria-disabled": disabled || undefined, "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : t('rating.label')), "aria-labelledby": ariaLabelledBy, "aria-readonly": readOnly || undefined, "aria-valuemax": count, "aria-valuemin": 0, "aria-valuenow": committedValue, "aria-valuetext": getValueLabel(committedValue, count), className: ['inkra-rating', className].filter(Boolean).join(' '), "data-disabled": disabled || undefined, "data-previewing": previewing || undefined, "data-readonly": readOnly || undefined, "data-size": size, dir: dir, onBlur: (event) => {
211
214
  clearPendingKeyboard();
212
215
  changePreview(null);
213
216
  onBlur?.(event);
@@ -23,7 +23,7 @@ export interface ResizableHandleProps extends SeparatorProps {
23
23
  label?: string;
24
24
  lineSeed?: string | number;
25
25
  }
26
- declare function ResizableHandle({ 'aria-label': ariaLabel, children, className, label, lineSeed, ...props }: ResizableHandleProps): import("react").JSX.Element;
26
+ declare function ResizableHandle(inkraProps: ResizableHandleProps): import("react").JSX.Element;
27
27
  export declare const Resizable: Readonly<{
28
28
  Root: typeof ResizableRoot;
29
29
  Panel: typeof ResizablePanel;
@@ -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 { Group as ResizableGroupPrimitive, Panel as ResizablePanelPrimitive, Separator as ResizableSeparatorPrimitive, useGroupCallbackRef, useGroupRef, usePanelCallbackRef, usePanelRef, } from 'react-resizable-panels';
3
4
  import { useCallback, useReducer } from 'react';
4
5
  import { PencilLine } from '../PencilLine.js';
@@ -39,7 +40,9 @@ function ResizableRoot({ apiRef, className, defaultLayout, layout, onLayoutChang
39
40
  function ResizablePanel({ className, ...props }) {
40
41
  return _jsx(ResizablePanelPrimitive, { ...props, className: classes('inkra-resizable__panel', className) });
41
42
  }
42
- function ResizableHandle({ 'aria-label': ariaLabel, children, className, label = 'Resize panels', lineSeed = 'inkra-resizable-handle', ...props }) {
43
+ function ResizableHandle(inkraProps) {
44
+ const { t } = useInkraLocale();
45
+ const { 'aria-label': ariaLabel, children, className, label = t('resizable.label'), lineSeed = 'inkra-resizable-handle', ...props } = inkraProps;
43
46
  return _jsxs(ResizableSeparatorPrimitive, { ...props, "aria-label": ariaLabel ?? label, className: classes('inkra-resizable__handle', className), children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-resizable__handle-focus", seed: `${lineSeed}-focus`, variant: "outline" }), _jsxs("span", { "aria-hidden": "true", className: "inkra-resizable__handle-mark", children: [_jsx(PencilLine, { className: "inkra-resizable__handle-line inkra-resizable__handle-line--horizontal", seed: `${lineSeed}-horizontal` }), _jsx(PencilLine, { className: "inkra-resizable__handle-line inkra-resizable__handle-line--vertical", orientation: "vertical", seed: `${lineSeed}-vertical` }), children] })] });
44
47
  }
45
48
  export const Resizable = /* @__PURE__ */ Object.freeze({
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useInkraLocale } from '../i18n/index.js';
2
3
  import { Combobox } from '@base-ui/react/combobox';
3
4
  import { DirectionProvider } from '@base-ui/react/direction-provider';
4
5
  import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
@@ -15,7 +16,8 @@ const chevronPaths = [{ d: 'M6 9l6 6 6-6' }];
15
16
  const checkPaths = [{ d: 'M5 12l4 4L19 6' }];
16
17
  const clearPaths = [{ d: 'M6 6l12 12M18 6L6 18' }];
17
18
  const SearchableControl = forwardRef(function SearchableControl(props, forwardedRef) {
18
- const { items, searchPlacement = 'trigger', placeholder = 'Select an option', searchPlaceholder = 'Search options', emptyContent = 'No matching options', clearable = true, clearLabel = 'Clear selection', openLabel = 'Show options', size = 'md', multiple = false, value, defaultValue, onValueChange: _onValueChange, name, form, dir, disabled, readOnly, required, invalid, id, className, children: _children, open, defaultOpen, onOpenChange, side = 'bottom', align = 'start', sideOffset = 8, collisionPadding = 8, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...rest } = props;
19
+ const { t } = useInkraLocale();
20
+ const { items, searchPlacement = 'trigger', placeholder = t('select.placeholder'), searchPlaceholder = t('select.search'), emptyContent = t('select.empty'), clearable = true, clearLabel = t('select.clear'), openLabel = t('select.open'), size = 'md', multiple = false, value, defaultValue, onValueChange: _onValueChange, name, form, dir, disabled, readOnly, required, invalid, id, className, children: _children, open, defaultOpen, onOpenChange, side = 'bottom', align = 'start', sideOffset = 8, collisionPadding = 8, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...rest } = props;
19
21
  const indicatorSize = size === 'sm' ? 24 : size === 'lg' ? 32 : 28;
20
22
  const actionSize = size === 'sm' ? 'icon-sm' : size === 'lg' ? 'icon-lg' : 'icon';
21
23
  const field = useFieldContext();
@@ -176,6 +176,7 @@ export const SelectSeparator = forwardRef(function SelectSeparator({ className,
176
176
  });
177
177
  export const SelectClear = forwardRef(function SelectClear({ children, className, disabled, onClick, ...props }, ref) {
178
178
  const state = useContext(MultipleSelectContext);
179
+ const { t } = useInkraLocale();
179
180
  if (!state)
180
181
  throw new Error('Select.Clear must be used inside Select.Root with multiple.');
181
182
  return _jsx(Button, { ...props, className: classes('inkra-select__clear', className), ref: ref, type: "button", variant: "ghost", disabled: disabled || state.disabled || state.readOnly || state.values.length === 0, onClick: event => {
@@ -184,7 +185,7 @@ export const SelectClear = forwardRef(function SelectClear({ children, className
184
185
  return;
185
186
  state.change([]);
186
187
  state.triggerRef.current?.focus({ preventScroll: true });
187
- }, children: children ?? 'Clear' });
188
+ }, children: children ?? t('common.clear') });
188
189
  });
189
190
  export const Select = /* @__PURE__ */ Object.freeze({
190
191
  Searchable: SearchableSelect,
@@ -205,3 +206,4 @@ export const Select = /* @__PURE__ */ Object.freeze({
205
206
  Separator: SelectSeparator,
206
207
  Clear: SelectClear,
207
208
  });
209
+ import { useInkraLocale } from '../i18n/index.js';
@@ -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, useEffect, useId, useRef, useState } from 'react';
3
4
  import { Button } from '../Button.js';
4
5
  import { InputAction } from '../form/InputAction.js';
@@ -9,7 +10,9 @@ import { PencilText } from '../PencilText.js';
9
10
  import { findAdjacentStepperItem, findEdgeStepperItem, resolveStepperNativeValue } from './stepperModel.js';
10
11
  const previousPaths = [{ d: 'M15 18l-6-6 6-6' }];
11
12
  const nextPaths = [{ d: 'M9 18l6-6-6-6' }];
12
- export const SelectStepper = forwardRef(function SelectStepper({ 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, className, defaultValue = null, dir, disabled, form, fullWidth = true, id, invalid, items, loop = false, name, nextLabel = 'Select next option', onKeyDown, onValueChange, placeholder = 'Select an option', previousLabel = 'Select previous option', readOnly, required, size = 'md', value, ...props }, forwardedRef) {
13
+ export const SelectStepper = forwardRef(function SelectStepper(inkraProps, forwardedRef) {
14
+ const { t } = useInkraLocale();
15
+ const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, className, defaultValue = null, dir, disabled, form, fullWidth = true, id, invalid, items, loop = false, name, nextLabel = t('select.next'), onKeyDown, onValueChange, placeholder = t('select.placeholder'), previousLabel = t('select.previous'), readOnly, required, size = 'md', value, ...props } = inkraProps;
13
16
  const generatedId = useId();
14
17
  const controlRef = useRef(null);
15
18
  const selectRef = useRef(null);