@inkra/react 0.1.0 → 0.3.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 (177) hide show
  1. package/README.md +186 -1
  2. package/dist/PencilIcon.js +14 -16
  3. package/dist/PencilLine.js +18 -12
  4. package/dist/PencilText.js +192 -59
  5. package/dist/alert-dialog/index.d.ts +64 -0
  6. package/dist/alert-dialog/index.js +111 -0
  7. package/dist/audio-player/index.d.ts +60 -0
  8. package/dist/audio-player/index.js +101 -0
  9. package/dist/carousel/index.d.ts +51 -0
  10. package/dist/carousel/index.js +290 -0
  11. package/dist/choice-grid/index.d.ts +38 -0
  12. package/dist/choice-grid/index.js +85 -0
  13. package/dist/color-picker/color.d.ts +15 -0
  14. package/dist/color-picker/color.js +55 -0
  15. package/dist/color-picker/index.d.ts +31 -0
  16. package/dist/color-picker/index.js +69 -0
  17. package/dist/command/index.d.ts +45 -0
  18. package/dist/command/index.js +39 -0
  19. package/dist/context-menu/index.d.ts +76 -0
  20. package/dist/context-menu/index.js +241 -0
  21. package/dist/data-table/index.d.ts +47 -0
  22. package/dist/data-table/index.js +91 -0
  23. package/dist/date-picker/index.d.ts +50 -0
  24. package/dist/date-picker/index.js +180 -0
  25. package/dist/display/ProgressRing.d.ts +6 -0
  26. package/dist/display/ProgressRing.js +146 -0
  27. package/dist/display/index.d.ts +27 -0
  28. package/dist/display/index.js +18 -2
  29. package/dist/dockable-panel/index.d.ts +41 -0
  30. package/dist/dockable-panel/index.js +253 -0
  31. package/dist/dockable-panel/model.d.ts +21 -0
  32. package/dist/dockable-panel/model.js +61 -0
  33. package/dist/drawer/index.d.ts +63 -0
  34. package/dist/drawer/index.js +88 -0
  35. package/dist/dropdown-menu/index.d.ts +1 -1
  36. package/dist/dropdown-menu/index.js +3 -2
  37. package/dist/editable/index.d.ts +53 -0
  38. package/dist/editable/index.js +162 -0
  39. package/dist/empty/index.d.ts +21 -0
  40. package/dist/empty/index.js +23 -0
  41. package/dist/flip/index.d.ts +49 -0
  42. package/dist/flip/index.js +138 -0
  43. package/dist/form/EditablePencilText.js +188 -143
  44. package/dist/form/Input.js +1 -1
  45. package/dist/form/Textarea.d.ts +12 -0
  46. package/dist/form/Textarea.js +83 -4
  47. package/dist/form/index.d.ts +1 -1
  48. package/dist/heatmap/index.d.ts +59 -0
  49. package/dist/heatmap/index.js +248 -0
  50. package/dist/hotkey/index.d.ts +37 -0
  51. package/dist/hotkey/index.js +130 -0
  52. package/dist/hotkey/model.d.ts +27 -0
  53. package/dist/hotkey/model.js +97 -0
  54. package/dist/hover-card/index.d.ts +33 -0
  55. package/dist/hover-card/index.js +141 -0
  56. package/dist/image-cropper/index.d.ts +82 -0
  57. package/dist/image-cropper/index.js +261 -0
  58. package/dist/image-cropper/model.d.ts +42 -0
  59. package/dist/image-cropper/model.js +94 -0
  60. package/dist/image-viewer/index.d.ts +79 -0
  61. package/dist/image-viewer/index.js +220 -0
  62. package/dist/image-viewer/model.d.ts +9 -0
  63. package/dist/image-viewer/model.js +18 -0
  64. package/dist/index.d.ts +47 -0
  65. package/dist/index.js +47 -0
  66. package/dist/input-number/index.d.ts +6 -0
  67. package/dist/input-number/index.js +126 -15
  68. package/dist/input-number/model.d.ts +13 -0
  69. package/dist/input-number/model.js +50 -0
  70. package/dist/internal/cache.d.ts +1 -1
  71. package/dist/internal/cache.js +1 -1
  72. package/dist/internal/renderTokens.d.ts +10 -0
  73. package/dist/internal/renderTokens.js +13 -0
  74. package/dist/internal/surfaceRenderer.d.ts +14 -6
  75. package/dist/internal/surfaceRenderer.js +15 -12
  76. package/dist/internal/textRenderer.d.ts +1 -0
  77. package/dist/internal/textRenderer.js +33 -7
  78. package/dist/internal/usePencilSurface.js +16 -8
  79. package/dist/internal/useSelectionDrawing.js +15 -11
  80. package/dist/journey-map/MapScale.js +3 -1
  81. package/dist/journey-map/index.js +3 -1
  82. package/dist/keypad/index.d.ts +23 -0
  83. package/dist/keypad/index.js +63 -0
  84. package/dist/keypad/model.d.ts +20 -0
  85. package/dist/keypad/model.js +33 -0
  86. package/dist/layout/index.d.ts +63 -0
  87. package/dist/layout/index.js +62 -0
  88. package/dist/log/index.d.ts +45 -0
  89. package/dist/log/index.js +159 -0
  90. package/dist/log/model.d.ts +16 -0
  91. package/dist/log/model.js +13 -0
  92. package/dist/node-graph/index.d.ts +123 -0
  93. package/dist/node-graph/index.js +304 -0
  94. package/dist/number-ticker/index.d.ts +31 -0
  95. package/dist/number-ticker/index.js +135 -0
  96. package/dist/number-ticker/model.d.ts +41 -0
  97. package/dist/number-ticker/model.js +163 -0
  98. package/dist/number-ticker/precision.d.ts +7 -0
  99. package/dist/number-ticker/precision.js +134 -0
  100. package/dist/overflow-list/index.d.ts +39 -0
  101. package/dist/overflow-list/index.js +230 -0
  102. package/dist/overflow-list/layout.d.ts +19 -0
  103. package/dist/overflow-list/layout.js +44 -0
  104. package/dist/overlay/Overlay.d.ts +1 -1
  105. package/dist/overlay/Overlay.js +4 -2
  106. package/dist/overlay/Popover.d.ts +1 -1
  107. package/dist/overlay/Popover.js +3 -2
  108. package/dist/overlay/themeBridge.d.ts +12 -1
  109. package/dist/overlay/themeBridge.js +27 -1
  110. package/dist/pagination/index.d.ts +23 -0
  111. package/dist/pagination/index.js +41 -0
  112. package/dist/pagination/model.d.ts +4 -0
  113. package/dist/pagination/model.js +42 -0
  114. package/dist/pan-zoom/index.d.ts +71 -0
  115. package/dist/pan-zoom/index.js +175 -0
  116. package/dist/radar/index.d.ts +56 -0
  117. package/dist/radar/index.js +220 -0
  118. package/dist/range-slider/index.d.ts +16 -0
  119. package/dist/range-slider/index.js +22 -0
  120. package/dist/rating/index.d.ts +28 -0
  121. package/dist/rating/index.js +127 -0
  122. package/dist/resizable/index.d.ts +32 -0
  123. package/dist/resizable/index.js +50 -0
  124. package/dist/scroll-area/index.d.ts +36 -0
  125. package/dist/scroll-area/index.js +44 -0
  126. package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
  127. package/dist/segmented-progress/SegmentedProgress.js +42 -0
  128. package/dist/slider/index.js +1 -1
  129. package/dist/slot/index.d.ts +24 -0
  130. package/dist/slot/index.js +29 -0
  131. package/dist/sortable/index.d.ts +50 -0
  132. package/dist/sortable/index.js +283 -0
  133. package/dist/sortable/model.d.ts +30 -0
  134. package/dist/sortable/model.js +64 -0
  135. package/dist/sparkline/index.d.ts +19 -0
  136. package/dist/sparkline/index.js +240 -0
  137. package/dist/sparkline/model.d.ts +16 -0
  138. package/dist/sparkline/model.js +28 -0
  139. package/dist/steps/index.d.ts +16 -0
  140. package/dist/steps/index.js +37 -0
  141. package/dist/styles.css +4957 -269
  142. package/dist/tag-input/index.d.ts +69 -0
  143. package/dist/tag-input/index.js +325 -0
  144. package/dist/tag-input/model.d.ts +14 -0
  145. package/dist/tag-input/model.js +38 -0
  146. package/dist/time-picker/index.d.ts +15 -0
  147. package/dist/time-picker/index.js +168 -0
  148. package/dist/timeline/index.d.ts +14 -0
  149. package/dist/timeline/index.js +16 -0
  150. package/dist/toast/index.d.ts +27 -0
  151. package/dist/toast/index.js +36 -0
  152. package/dist/toggle-group/index.d.ts +17 -0
  153. package/dist/toggle-group/index.js +26 -0
  154. package/dist/tokens.css +10 -9
  155. package/dist/tooltip/Tooltip.d.ts +1 -1
  156. package/dist/tooltip/Tooltip.js +3 -2
  157. package/dist/transfer/index.d.ts +64 -0
  158. package/dist/transfer/index.js +174 -0
  159. package/dist/transfer/model.d.ts +12 -0
  160. package/dist/transfer/model.js +33 -0
  161. package/dist/tree/index.d.ts +39 -0
  162. package/dist/tree/index.js +36 -0
  163. package/dist/tree-select/index.d.ts +59 -0
  164. package/dist/tree-select/index.js +142 -0
  165. package/dist/tree-select/model.d.ts +31 -0
  166. package/dist/tree-select/model.js +135 -0
  167. package/dist/typewriter/index.d.ts +21 -0
  168. package/dist/typewriter/index.js +101 -0
  169. package/dist/typewriter/model.d.ts +7 -0
  170. package/dist/typewriter/model.js +45 -0
  171. package/dist/upload/index.d.ts +59 -0
  172. package/dist/upload/index.js +230 -0
  173. package/dist/virtual-list/index.d.ts +44 -0
  174. package/dist/virtual-list/index.js +129 -0
  175. package/dist/virtual-list/model.d.ts +7 -0
  176. package/dist/virtual-list/model.js +27 -0
  177. package/package.json +23 -3
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PencilSurface } from '../PencilSurface.js';
3
+ function segmentFill(index, count, percent, policy) {
4
+ if (percent === null)
5
+ return 1;
6
+ const raw = percent / 100 * count - index;
7
+ if (policy === 'complete')
8
+ return raw >= 1 ? 1 : 0;
9
+ if (policy === 'next')
10
+ return raw > 0 ? 1 : 0;
11
+ return Math.max(0, Math.min(1, raw));
12
+ }
13
+ function thresholdColor(index, count, max, thresholds) {
14
+ if (!thresholds?.length)
15
+ return undefined;
16
+ const segmentValue = index / count * max;
17
+ let color;
18
+ for (const threshold of thresholds) {
19
+ if (!Number.isFinite(threshold.value) || !threshold.color || threshold.value > segmentValue)
20
+ continue;
21
+ color = threshold.color;
22
+ }
23
+ return color;
24
+ }
25
+ export function SegmentedProgress({ count, gap, max, partialSegment, percent, seed, segmentColors, thresholds, }) {
26
+ const normalizedThresholds = thresholds
27
+ ?.filter(threshold => Number.isFinite(threshold.value) && threshold.color)
28
+ .map(threshold => ({ ...threshold, value: Math.max(0, Math.min(max, threshold.value)) }))
29
+ .sort((left, right) => left.value - right.value);
30
+ return (_jsx("span", { "aria-hidden": "true", className: "inkra-progress__segments", style: {
31
+ '--inkra-progress-segment-count': count,
32
+ '--inkra-progress-segment-gap': `${gap}px`,
33
+ }, children: Array.from({ length: count }, (_, index) => {
34
+ const fill = segmentFill(index, count, percent, partialSegment);
35
+ const color = segmentColors?.[index] || thresholdColor(index, count, max, normalizedThresholds);
36
+ const style = {
37
+ '--inkra-progress-segment-fill': `${fill * 100}%`,
38
+ ...(color ? { '--inkra-selection-pigment': color } : {}),
39
+ };
40
+ return (_jsxs("span", { className: "inkra-progress__segment", "data-fill": fill === 0 ? 'empty' : fill === 1 ? 'full' : 'partial', children: [_jsx(PencilSurface, { as: "span", className: "inkra-progress__segment-outline", radius: 3, seed: `${seed}-segment-${index}-outline`, variant: "outline" }), _jsx("span", { className: "inkra-progress__segment-fill-clip", style: style, children: _jsx(PencilSurface, { as: "span", className: "inkra-progress__segment-fill", radius: 3, seed: `${seed}-segment-${index}-fill`, variant: "selection" }) })] }, index));
41
+ }) }));
42
+ }
@@ -40,6 +40,6 @@ export const Slider = forwardRef(function Slider({ 'aria-label': ariaLabel, 'ari
40
40
  const thumbLabel = thumbLabels?.[index];
41
41
  const labelledBy = thumbLabel ? undefined : ariaLabelledBy;
42
42
  const label = thumbLabel ?? (ariaLabel ? `${ariaLabel}${values.length > 1 ? ` ${index + 1}` : ''}` : labelledBy ? undefined : `Value${values.length > 1 ? ` ${index + 1}` : ''}`);
43
- return _jsx(SliderPrimitive.Thumb, { "aria-describedby": ariaDescribedBy, "aria-label": label, "aria-labelledby": labelledBy, "aria-valuetext": getValueLabel?.(current, index) ?? ariaValueText, className: "inkra-slider__thumb", children: _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slider__thumb-surface", variant: "paper", radius: 8, seed: `${resolvedSeed}-thumb-${index}`, children: _jsx(PencilSurface, { as: "span", className: "inkra-slider__focus-ring", variant: "outline", radius: 8, seed: `${resolvedSeed}-focus-${index}` }) }) }, index);
43
+ return _jsx(SliderPrimitive.Thumb, { "aria-describedby": ariaDescribedBy, "aria-label": label, "aria-labelledby": labelledBy, "aria-valuenow": current, "aria-valuetext": getValueLabel?.(current, index) ?? ariaValueText, className: "inkra-slider__thumb", children: _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slider__thumb-surface", variant: "paper", radius: 8, seed: `${resolvedSeed}-thumb-${index}`, children: _jsx(PencilSurface, { as: "span", className: "inkra-slider__focus-ring", variant: "outline", radius: 8, seed: `${resolvedSeed}-focus-${index}` }) }) }, index);
44
44
  })] });
45
45
  });
@@ -0,0 +1,24 @@
1
+ import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
2
+ export type SlotSize = 'sm' | 'md' | 'lg';
3
+ interface SlotSharedProps {
4
+ children?: ReactNode;
5
+ /** Shows emptyContent instead of children while preserving the slot geometry. */
6
+ empty?: boolean;
7
+ emptyContent?: ReactNode;
8
+ selected?: boolean;
9
+ size?: SlotSize;
10
+ count?: number;
11
+ /** Replaces the default Badge while keeping the count anchored to the slot. */
12
+ renderCount?: (count: number) => ReactNode;
13
+ seed?: string | number;
14
+ }
15
+ export interface StaticSlotProps extends SlotSharedProps, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
16
+ interactive?: false;
17
+ disabled?: boolean;
18
+ }
19
+ export interface InteractiveSlotProps extends SlotSharedProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
20
+ interactive: true;
21
+ }
22
+ export type SlotProps = StaticSlotProps | InteractiveSlotProps;
23
+ export declare const Slot: import("react").ForwardRefExoticComponent<SlotProps & import("react").RefAttributes<HTMLButtonElement | HTMLDivElement>>;
24
+ export {};
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId } from 'react';
3
+ import { Badge } from '../display/index.js';
4
+ import { PencilSurface } from '../PencilSurface.js';
5
+ const classes = (...values) => values.filter(Boolean).join(' ');
6
+ export const Slot = forwardRef(function Slot({ children, className, count, disabled = false, empty = false, emptyContent, interactive = false, renderCount, seed, selected = false, size = 'md', ...props }, ref) {
7
+ const generatedId = useId();
8
+ const resolvedSeed = seed ?? `inkra-slot-${generatedId}`;
9
+ const content = empty
10
+ ? (emptyContent ?? _jsx("span", { className: "inkra-slot__empty-mark", "aria-hidden": "true", children: "+" }))
11
+ : children;
12
+ const countContent = count == null
13
+ ? null
14
+ : renderCount?.(count) ?? _jsx(Badge, { size: "sm", "aria-label": `${count}`, children: count });
15
+ const commonProps = {
16
+ ...props,
17
+ className: classes('inkra-slot', className),
18
+ 'data-disabled': disabled || undefined,
19
+ 'data-empty': empty || undefined,
20
+ 'data-interactive': interactive || undefined,
21
+ 'data-selected': selected || undefined,
22
+ 'data-size': size,
23
+ };
24
+ const body = _jsxs(_Fragment, { children: [selected && _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slot__selected-wash", seed: `${resolvedSeed}-wash`, variant: "selection" }), _jsx("span", { className: "inkra-slot__content", children: content }), selected && _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slot__selected-outline", radius: 8, seed: `${resolvedSeed}-outline`, variant: "outline" }), countContent != null && _jsx("span", { className: "inkra-slot__count", children: countContent })] });
25
+ if (interactive) {
26
+ return _jsx(PencilSurface, { ...commonProps, as: "button", "aria-pressed": selected, disabled: disabled, radius: 8, ref: ref, seed: resolvedSeed, variant: "paper", children: body });
27
+ }
28
+ return _jsx(PencilSurface, { ...commonProps, as: "div", "aria-disabled": disabled || undefined, radius: 8, ref: ref, seed: resolvedSeed, variant: "paper", children: body });
29
+ });
@@ -0,0 +1,50 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ import type { SortableChangeDetails, SortableContainerData, SortableIdentifier } from './model.js';
3
+ export type { SortableChangeDetails, SortableContainerData, SortableIdentifier, SortableLayout, SortableLocation, } from './model.js';
4
+ export interface SortableLabels {
5
+ handle?: string;
6
+ screenReaderInstructions?: string;
7
+ pickedUp?: (item: string, container: string, position: number, count: number) => string;
8
+ movedOver?: (item: string, container: string, position: number, count: number) => string;
9
+ dropped?: (item: string, container: string, position: number, count: number) => string;
10
+ cancelled?: (item: string) => string;
11
+ }
12
+ export interface SortableItemRenderState<Item> {
13
+ item: Item;
14
+ id: SortableIdentifier;
15
+ containerId: SortableIdentifier;
16
+ index: number;
17
+ disabled: boolean;
18
+ isDragging: boolean;
19
+ isOverlay: boolean;
20
+ }
21
+ export interface SortableHandleRenderState<Item> extends SortableItemRenderState<Item> {
22
+ props: ButtonHTMLAttributes<HTMLButtonElement> & {
23
+ ref: (element: HTMLButtonElement | null) => void;
24
+ };
25
+ }
26
+ export interface SortableContainerRenderState<Item> {
27
+ container: SortableContainerData<Item>;
28
+ isOver: boolean;
29
+ itemCount: number;
30
+ }
31
+ export interface SortableProps<Item> {
32
+ containers: readonly SortableContainerData<Item>[];
33
+ getItemId: (item: Item) => SortableIdentifier;
34
+ renderItem: (state: SortableItemRenderState<Item>) => ReactNode;
35
+ onChange: (containers: SortableContainerData<Item>[], details: SortableChangeDetails<Item>) => void;
36
+ onReorder?: (details: SortableChangeDetails<Item>) => void;
37
+ onMove?: (details: SortableChangeDetails<Item>) => void;
38
+ renderContainerHeader?: (state: SortableContainerRenderState<Item>) => ReactNode;
39
+ renderOverlay?: (state: SortableItemRenderState<Item>) => ReactNode;
40
+ handle?: boolean | ((state: SortableHandleRenderState<Item>) => ReactNode);
41
+ isItemDisabled?: (item: Item, container: SortableContainerData<Item>) => boolean;
42
+ getItemLabel?: (item: Item) => string;
43
+ getContainerLabel?: (container: SortableContainerData<Item>) => string;
44
+ labels?: SortableLabels;
45
+ className?: string;
46
+ containerClassName?: string;
47
+ itemClassName?: string;
48
+ autoScroll?: boolean;
49
+ }
50
+ export declare function Sortable<Item>({ autoScroll, className, containerClassName, containers, getContainerLabel, getItemId, getItemLabel, handle, isItemDisabled, itemClassName, labels: labelsProp, onChange, onMove, onReorder, renderContainerHeader, renderItem, renderOverlay, }: SortableProps<Item>): import("react").JSX.Element;
@@ -0,0 +1,283 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { __iconData as gripIconData } from 'lucide-react/dist/esm/icons/grip-vertical.mjs';
3
+ import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, TouchSensor, closestCenter, pointerWithin, rectIntersection, useDroppable, useSensor, useSensors, } from '@dnd-kit/core';
4
+ import { SortableContext, rectSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable';
5
+ import { CSS } from '@dnd-kit/utilities';
6
+ import { useMemo, useRef, useState } from 'react';
7
+ import { PencilIcon } from '../PencilIcon.js';
8
+ import { PencilLine } from '../PencilLine.js';
9
+ import { PencilSurface } from '../PencilSurface.js';
10
+ import { PencilText } from '../PencilText.js';
11
+ import { cloneSortableContainers, findSortableLocation, projectSortableMove, sortableContainersEqual, } from './model.js';
12
+ const gripIcon = gripIconData.node.map(([, attributes]) => {
13
+ const { cx, cy, r } = attributes;
14
+ const x = Number(cx);
15
+ const y = Number(cy);
16
+ const radius = Number(r);
17
+ return { d: `M ${x - radius} ${y} A ${radius} ${radius} 0 1 0 ${x + radius} ${y} A ${radius} ${radius} 0 1 0 ${x - radius} ${y}` };
18
+ });
19
+ function engineItemId(id) {
20
+ return `inkra-sortable-item:${typeof id}:${String(id)}`;
21
+ }
22
+ function engineContainerId(id) {
23
+ return `inkra-sortable-container:${typeof id}:${String(id)}`;
24
+ }
25
+ function isInteractiveTarget(target) {
26
+ return target instanceof Element && Boolean(target.closest('button, a, input, select, textarea, [contenteditable="true"], [data-sortable-no-drag]'));
27
+ }
28
+ function filterItemActivatorProps(props) {
29
+ return {
30
+ ...props,
31
+ onKeyDown: (event) => {
32
+ if (!isInteractiveTarget(event.target))
33
+ props.onKeyDown?.(event);
34
+ },
35
+ onMouseDown: (event) => {
36
+ if (!isInteractiveTarget(event.target))
37
+ props.onMouseDown?.(event);
38
+ },
39
+ onTouchStart: (event) => {
40
+ if (!isInteractiveTarget(event.target))
41
+ props.onTouchStart?.(event);
42
+ },
43
+ };
44
+ }
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
+ };
53
+ function getEngineData(value) {
54
+ const data = value?.data.current;
55
+ return data?.type === 'inkra-sortable-item' || data?.type === 'inkra-sortable-container' ? data : null;
56
+ }
57
+ function getCollisionEngineData(collision) {
58
+ return collision.data?.droppableContainer
59
+ ? getEngineData(collision.data.droppableContainer)
60
+ : null;
61
+ }
62
+ function getDropProjection(containers, event, getItemId) {
63
+ const activeData = getEngineData(event.active);
64
+ const overData = getEngineData(event.over);
65
+ if (activeData?.itemId === undefined || !overData || overData.disabled)
66
+ return cloneSortableContainers(containers);
67
+ const targetContainer = containers.find((container) => container.id === overData.containerId);
68
+ const translated = event.active.rect.current.translated;
69
+ const source = findSortableLocation(containers, activeData.itemId, getItemId);
70
+ const target = overData.itemId === undefined ? null : findSortableLocation(containers, overData.itemId, getItemId);
71
+ const keyboardDrag = typeof KeyboardEvent !== 'undefined' && event.activatorEvent instanceof KeyboardEvent;
72
+ const insertAfter = keyboardDrag
73
+ ? source !== null && target !== null && source.containerId === target.containerId && source.index < target.index
74
+ : overData.itemId !== undefined && translated !== null && targetContainer?.layout === 'grid'
75
+ ? translated.left + translated.width / 2 > event.over.rect.left + event.over.rect.width / 2
76
+ : overData.itemId !== undefined && translated !== null
77
+ && translated.top + translated.height / 2 > event.over.rect.top + event.over.rect.height / 2;
78
+ return projectSortableMove(containers, activeData.itemId, getItemId, {
79
+ overContainerId: overData.containerId,
80
+ overItemId: overData.itemId,
81
+ insertAfter,
82
+ });
83
+ }
84
+ const collisionDetection = (args) => {
85
+ const pointer = pointerWithin(args);
86
+ const pointerItems = pointer.filter((collision) => getCollisionEngineData(collision)?.type === 'inkra-sortable-item');
87
+ if (pointerItems.length > 0)
88
+ return pointerItems;
89
+ if (pointer.length > 0)
90
+ return pointer;
91
+ const intersections = rectIntersection(args);
92
+ const intersectingItems = intersections.filter((collision) => getCollisionEngineData(collision)?.type === 'inkra-sortable-item');
93
+ if (intersectingItems.length > 0)
94
+ return intersectingItems;
95
+ return intersections.length > 0 ? intersections : closestCenter(args);
96
+ };
97
+ function SortableContainer({ container, getItemId, renderItem, renderContainerHeader, handle, isItemDisabled, itemClassName, containerClassName, activeId, handleLabel, getItemLabel, }) {
98
+ const disabled = container.disabled === true;
99
+ const { isOver, setNodeRef } = useDroppable({
100
+ id: engineContainerId(container.id),
101
+ data: { type: 'inkra-sortable-container', containerId: container.id, disabled },
102
+ });
103
+ const ids = container.items.map((item) => engineItemId(getItemId(item)));
104
+ const layout = container.layout ?? 'list';
105
+ const acceptingOver = isOver && !disabled;
106
+ return _jsxs(PencilSurface, { as: "section", className: ['inkra-sortable__container', containerClassName].filter(Boolean).join(' '), "data-disabled": disabled || undefined, "data-layout": layout, "data-over": acceptingOver || undefined, "aria-disabled": disabled || undefined, "aria-label": container.label ?? String(container.id), ref: setNodeRef, seed: `sortable-container-${String(container.id)}`, variant: "paper", children: [acceptingOver && _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-sortable__container-target", seed: `sortable-container-${String(container.id)}-target`, variant: "selection" }), (renderContainerHeader || container.label) && _jsxs("div", { className: "inkra-sortable__container-header", children: [_jsx("div", { className: "inkra-sortable__container-title", children: renderContainerHeader
107
+ ? renderContainerHeader({ container, isOver: acceptingOver, itemCount: container.items.length })
108
+ : container.label ? _jsx(PencilText, { children: container.label }) : null }), _jsx(PencilLine, { "aria-hidden": "true", className: "inkra-sortable__container-divider", seed: `sortable-container-${String(container.id)}-divider` })] }), _jsx(SortableContext, { items: ids, strategy: layout === 'grid' ? rectSortingStrategy : verticalListSortingStrategy, children: _jsxs("div", { className: "inkra-sortable__items", children: [container.items.map((item, index) => {
109
+ const id = getItemId(item);
110
+ return _jsx(SortableItem, { activeId: activeId, container: container, disabled: disabled || isItemDisabled?.(item, container) === true, handle: handle, handleLabel: `${handleLabel}: ${getItemLabel(item)}`, id: id, index: index, item: item, itemClassName: itemClassName, renderItem: renderItem }, engineItemId(id));
111
+ }), container.items.length === 0 && _jsx("div", { className: "inkra-sortable__empty", "aria-hidden": "true" })] }) })] });
112
+ }
113
+ function SortableItem({ activeId, container, disabled, handle, handleLabel, id, index, item, itemClassName, renderItem, }) {
114
+ const hasHandle = handle === true || typeof handle === 'function';
115
+ const { attributes, isDragging, listeners, setActivatorNodeRef, setNodeRef, transform, transition, } = useSortable({
116
+ id: engineItemId(id),
117
+ disabled: disabled ? { draggable: true, droppable: false } : false,
118
+ data: { type: 'inkra-sortable-item', containerId: container.id, itemId: id, disabled },
119
+ });
120
+ const state = {
121
+ item,
122
+ id,
123
+ containerId: container.id,
124
+ index,
125
+ disabled,
126
+ isDragging,
127
+ isOverlay: false,
128
+ };
129
+ const activatorProps = { ...attributes, ...listeners };
130
+ const itemActivatorProps = filterItemActivatorProps({ ...attributes, ...listeners });
131
+ const setItemRef = (element) => {
132
+ setNodeRef(element);
133
+ if (!hasHandle)
134
+ setActivatorNodeRef(element);
135
+ };
136
+ return _jsxs("div", { ...(!hasHandle ? itemActivatorProps : {}), "aria-disabled": disabled || undefined, "aria-label": !hasHandle ? handleLabel : undefined, className: ['inkra-sortable__item', itemClassName].filter(Boolean).join(' '), "data-disabled": disabled || undefined, "data-dragging": isDragging || undefined, "data-handle": hasHandle || undefined, "data-sortable-id": String(id), ref: setItemRef, style: { transform: CSS.Transform.toString(transform), transition }, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-sortable__item-surface", seed: `sortable-item-${String(id)}`, variant: "paper" }), _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-sortable__focus", seed: `sortable-focus-${String(id)}`, variant: "outline" }), activeId === id && _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-sortable__placeholder", seed: `sortable-placeholder-${String(id)}`, variant: "selection" }), _jsx("div", { className: "inkra-sortable__item-content", children: renderItem(state) }), hasHandle && _jsx("div", { className: "inkra-sortable__handle-slot", children: typeof handle === 'function'
137
+ ? handle({ ...state, props: { ...activatorProps, 'aria-label': handleLabel, disabled, ref: setActivatorNodeRef } })
138
+ : _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
+ 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 [activeId, setActiveId] = useState(null);
143
+ const [activeWidth, setActiveWidth] = useState(null);
144
+ const [projected, setProjected] = useState(null);
145
+ const initialRef = useRef([]);
146
+ const projectedRef = useRef([]);
147
+ const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 6 } }), useSensor(TouchSensor, { activationConstraint: { delay: 180, tolerance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
148
+ const visibleContainers = projected ?? cloneSortableContainers(containers);
149
+ const activeLocation = activeId === null ? null : findSortableLocation(visibleContainers, activeId, getItemId);
150
+ const activeContainer = activeLocation === null
151
+ ? null
152
+ : visibleContainers.find((container) => container.id === activeLocation.containerId) ?? null;
153
+ const activeItem = activeContainer && activeLocation
154
+ ? activeContainer.items[activeLocation.index]
155
+ : undefined;
156
+ const announcements = useMemo(() => ({
157
+ onDragStart({ active }) {
158
+ const data = getEngineData(active);
159
+ if (data?.itemId === undefined)
160
+ return undefined;
161
+ const location = findSortableLocation(containers, data.itemId, getItemId);
162
+ const container = location && containers.find((entry) => entry.id === location.containerId);
163
+ const item = container && location ? container.items[location.index] : undefined;
164
+ return container && location && item !== undefined
165
+ ? labels.pickedUp(getItemLabel(item), getContainerLabel(container), location.index + 1, container.items.length)
166
+ : undefined;
167
+ },
168
+ onDragOver({ active, over }) {
169
+ const data = getEngineData(active);
170
+ const overData = getEngineData(over);
171
+ if (data?.itemId === undefined || !overData || overData.disabled)
172
+ return undefined;
173
+ const current = projectedRef.current.length > 0 ? projectedRef.current : containers;
174
+ const location = findSortableLocation(current, data.itemId, getItemId);
175
+ const container = location && current.find((entry) => entry.id === location.containerId);
176
+ const item = container && location ? container.items[location.index] : undefined;
177
+ return container && location && item !== undefined
178
+ ? labels.movedOver(getItemLabel(item), getContainerLabel(container), location.index + 1, container.items.length)
179
+ : undefined;
180
+ },
181
+ onDragEnd({ active, over }) {
182
+ const data = getEngineData(active);
183
+ const overData = getEngineData(over);
184
+ if (data?.itemId === undefined || !over)
185
+ return undefined;
186
+ if (overData?.disabled) {
187
+ const location = findSortableLocation(initialRef.current, data.itemId, getItemId);
188
+ const container = location && initialRef.current.find((entry) => entry.id === location.containerId);
189
+ const item = container && location ? container.items[location.index] : undefined;
190
+ return item === undefined ? undefined : labels.cancelled(getItemLabel(item));
191
+ }
192
+ const current = projectedRef.current;
193
+ const location = findSortableLocation(current, data.itemId, getItemId);
194
+ const container = location && current.find((entry) => entry.id === location.containerId);
195
+ const item = container && location ? container.items[location.index] : undefined;
196
+ return container && location && item !== undefined
197
+ ? labels.dropped(getItemLabel(item), getContainerLabel(container), location.index + 1, container.items.length)
198
+ : undefined;
199
+ },
200
+ onDragCancel({ active }) {
201
+ const data = getEngineData(active);
202
+ if (data?.itemId === undefined)
203
+ return undefined;
204
+ const location = findSortableLocation(initialRef.current, data.itemId, getItemId);
205
+ const container = location && initialRef.current.find((entry) => entry.id === location.containerId);
206
+ const item = container && location ? container.items[location.index] : undefined;
207
+ return item === undefined ? undefined : labels.cancelled(getItemLabel(item));
208
+ },
209
+ }), [containers, getContainerLabel, getItemId, getItemLabel, labels]);
210
+ function resetDrag() {
211
+ setActiveId(null);
212
+ setActiveWidth(null);
213
+ setProjected(null);
214
+ projectedRef.current = [];
215
+ }
216
+ function handleDragStart(event) {
217
+ const data = getEngineData(event.active);
218
+ if (data?.itemId === undefined)
219
+ return;
220
+ const snapshot = cloneSortableContainers(containers);
221
+ initialRef.current = snapshot;
222
+ projectedRef.current = snapshot;
223
+ setProjected(snapshot);
224
+ setActiveId(data.itemId);
225
+ setActiveWidth(event.active.rect.current.initial?.width ?? null);
226
+ }
227
+ function handleDragOver(event) {
228
+ const overData = getEngineData(event.over);
229
+ if (!event.over || overData?.disabled) {
230
+ const restored = cloneSortableContainers(initialRef.current);
231
+ projectedRef.current = restored;
232
+ setProjected(restored);
233
+ return;
234
+ }
235
+ const next = getDropProjection(projectedRef.current, event, getItemId);
236
+ projectedRef.current = next;
237
+ setProjected(next);
238
+ }
239
+ function handleDragEnd(event) {
240
+ const data = getEngineData(event.active);
241
+ const overData = getEngineData(event.over);
242
+ if (data?.itemId === undefined || !event.over || overData?.disabled) {
243
+ resetDrag();
244
+ return;
245
+ }
246
+ const next = projectedRef.current;
247
+ const from = findSortableLocation(initialRef.current, data.itemId, getItemId);
248
+ const to = findSortableLocation(next, data.itemId, getItemId);
249
+ const sourceContainer = from && initialRef.current.find((container) => container.id === from.containerId);
250
+ const item = sourceContainer && from ? sourceContainer.items[from.index] : undefined;
251
+ if (from && to && item !== undefined && !sortableContainersEqual(initialRef.current, next, getItemId)) {
252
+ const details = {
253
+ activeId: data.itemId,
254
+ overId: overData?.itemId ?? null,
255
+ from,
256
+ to,
257
+ item,
258
+ type: from.containerId === to.containerId ? 'reorder' : 'move',
259
+ };
260
+ onChange(next, details);
261
+ if (details.type === 'reorder')
262
+ onReorder?.(details);
263
+ else
264
+ onMove?.(details);
265
+ }
266
+ resetDrag();
267
+ }
268
+ function handleDragCancel(_event) {
269
+ resetDrag();
270
+ }
271
+ return _jsxs(DndContext, { accessibility: {
272
+ announcements,
273
+ screenReaderInstructions: { draggable: labels.screenReaderInstructions },
274
+ }, autoScroll: autoScroll, collisionDetection: collisionDetection, onDragCancel: handleDragCancel, onDragEnd: handleDragEnd, onDragOver: handleDragOver, onDragStart: handleDragStart, sensors: sensors, children: [_jsx("div", { className: ['inkra-sortable', className].filter(Boolean).join(' '), "data-dragging": activeId !== null || undefined, children: visibleContainers.map((container) => _jsx(SortableContainer, { activeId: activeId, container: container, containerClassName: containerClassName, getItemId: getItemId, getItemLabel: getItemLabel, handle: handle, handleLabel: labels.handle, isItemDisabled: isItemDisabled, itemClassName: itemClassName, renderContainerHeader: renderContainerHeader, renderItem: renderItem }, container.id)) }), _jsx(DragOverlay, { dropAnimation: null, children: activeItem !== undefined && activeContainer && activeLocation ? _jsxs("div", { className: "inkra-sortable__overlay", style: activeWidth === null ? undefined : { '--inkra-sortable-overlay-width': `${activeWidth}px` }, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-sortable__overlay-surface", seed: `sortable-overlay-${String(activeId)}`, variant: "paper" }), _jsx("div", { className: "inkra-sortable__item-content", children: (renderOverlay ?? renderItem)({
275
+ item: activeItem,
276
+ id: activeId,
277
+ containerId: activeContainer.id,
278
+ index: activeLocation.index,
279
+ disabled: false,
280
+ isDragging: true,
281
+ isOverlay: true,
282
+ }) })] }) : null })] });
283
+ }
@@ -0,0 +1,30 @@
1
+ export type SortableIdentifier = string | number;
2
+ export type SortableLayout = 'list' | 'grid';
3
+ export interface SortableContainerData<Item> {
4
+ id: SortableIdentifier;
5
+ items: readonly Item[];
6
+ label?: string;
7
+ disabled?: boolean;
8
+ layout?: SortableLayout;
9
+ }
10
+ export interface SortableLocation {
11
+ containerId: SortableIdentifier;
12
+ index: number;
13
+ }
14
+ export interface SortableChangeDetails<Item> {
15
+ activeId: SortableIdentifier;
16
+ overId: SortableIdentifier | null;
17
+ from: SortableLocation;
18
+ to: SortableLocation;
19
+ item: Item;
20
+ type: 'reorder' | 'move';
21
+ }
22
+ export interface SortableProjectionOptions {
23
+ overContainerId: SortableIdentifier;
24
+ overItemId?: SortableIdentifier;
25
+ insertAfter?: boolean;
26
+ }
27
+ export declare function cloneSortableContainers<Item>(containers: readonly SortableContainerData<Item>[]): SortableContainerData<Item>[];
28
+ export declare function findSortableLocation<Item>(containers: readonly SortableContainerData<Item>[], itemId: SortableIdentifier, getItemId: (item: Item) => SortableIdentifier): SortableLocation | null;
29
+ export declare function projectSortableMove<Item>(containers: readonly SortableContainerData<Item>[], activeId: SortableIdentifier, getItemId: (item: Item) => SortableIdentifier, options: SortableProjectionOptions): SortableContainerData<Item>[];
30
+ export declare function sortableContainersEqual<Item>(left: readonly SortableContainerData<Item>[], right: readonly SortableContainerData<Item>[], getItemId: (item: Item) => SortableIdentifier): boolean;
@@ -0,0 +1,64 @@
1
+ export function cloneSortableContainers(containers) {
2
+ return containers.map((container) => ({ ...container, items: [...container.items] }));
3
+ }
4
+ export function findSortableLocation(containers, itemId, getItemId) {
5
+ for (const container of containers) {
6
+ const index = container.items.findIndex((item) => getItemId(item) === itemId);
7
+ if (index >= 0)
8
+ return { containerId: container.id, index };
9
+ }
10
+ return null;
11
+ }
12
+ export function projectSortableMove(containers, activeId, getItemId, options) {
13
+ const source = findSortableLocation(containers, activeId, getItemId);
14
+ const targetContainerIndex = containers.findIndex((container) => container.id === options.overContainerId);
15
+ if (!source || targetContainerIndex < 0 || containers[targetContainerIndex]?.disabled) {
16
+ return cloneSortableContainers(containers);
17
+ }
18
+ if (options.overItemId === activeId)
19
+ return cloneSortableContainers(containers);
20
+ const sourceContainerIndex = containers.findIndex((container) => container.id === source.containerId);
21
+ const result = cloneSortableContainers(containers);
22
+ const sourceItems = [...result[sourceContainerIndex].items];
23
+ const [activeItem] = sourceItems.splice(source.index, 1);
24
+ if (activeItem === undefined)
25
+ return result;
26
+ if (sourceContainerIndex === targetContainerIndex) {
27
+ const originalTargetIndex = options.overItemId === undefined
28
+ ? sourceItems.length
29
+ : containers[targetContainerIndex].items.findIndex((item) => getItemId(item) === options.overItemId);
30
+ if (originalTargetIndex < 0)
31
+ return result;
32
+ let targetIndex = originalTargetIndex;
33
+ if (source.index < originalTargetIndex)
34
+ targetIndex -= 1;
35
+ if (options.insertAfter)
36
+ targetIndex += 1;
37
+ targetIndex = Math.max(0, Math.min(targetIndex, sourceItems.length));
38
+ sourceItems.splice(targetIndex, 0, activeItem);
39
+ result[sourceContainerIndex] = { ...result[sourceContainerIndex], items: sourceItems };
40
+ return result;
41
+ }
42
+ const targetItems = [...result[targetContainerIndex].items];
43
+ let targetIndex = options.overItemId === undefined
44
+ ? targetItems.length
45
+ : targetItems.findIndex((item) => getItemId(item) === options.overItemId);
46
+ if (targetIndex < 0)
47
+ targetIndex = targetItems.length;
48
+ if (options.insertAfter)
49
+ targetIndex += 1;
50
+ targetIndex = Math.max(0, Math.min(targetIndex, targetItems.length));
51
+ targetItems.splice(targetIndex, 0, activeItem);
52
+ result[sourceContainerIndex] = { ...result[sourceContainerIndex], items: sourceItems };
53
+ result[targetContainerIndex] = { ...result[targetContainerIndex], items: targetItems };
54
+ return result;
55
+ }
56
+ export function sortableContainersEqual(left, right, getItemId) {
57
+ return left.length === right.length && left.every((container, containerIndex) => {
58
+ const other = right[containerIndex];
59
+ return other !== undefined
60
+ && container.id === other.id
61
+ && container.items.length === other.items.length
62
+ && container.items.every((item, itemIndex) => getItemId(item) === getItemId(other.items[itemIndex]));
63
+ });
64
+ }
@@ -0,0 +1,19 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ import type { SparklineDatum } from './model.js';
3
+ export type SparklineType = 'line' | 'bar';
4
+ export interface SparklineProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ data: readonly SparklineDatum[];
6
+ type?: SparklineType;
7
+ width?: number;
8
+ height?: number;
9
+ min?: number;
10
+ max?: number;
11
+ label?: string;
12
+ showPoints?: boolean;
13
+ showArea?: boolean;
14
+ showSummary?: boolean;
15
+ getLabel?: (value: number | null, index: number) => string;
16
+ }
17
+ export declare const Sparkline: import("react").ForwardRefExoticComponent<SparklineProps & import("react").RefAttributes<HTMLDivElement>>;
18
+ export { createSparklineGeometry, normalizeSparklineData, sparklineDomain } from './model.js';
19
+ export type { SparklineDatum, SparklinePoint } from './model.js';