@inkra/react 0.1.0 → 0.2.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 (160) hide show
  1. package/README.md +104 -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 +109 -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 +23 -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 +240 -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 +86 -0
  35. package/dist/editable/index.d.ts +53 -0
  36. package/dist/editable/index.js +162 -0
  37. package/dist/empty/index.d.ts +21 -0
  38. package/dist/empty/index.js +23 -0
  39. package/dist/flip/index.d.ts +49 -0
  40. package/dist/flip/index.js +138 -0
  41. package/dist/form/EditablePencilText.js +157 -146
  42. package/dist/form/Textarea.d.ts +12 -0
  43. package/dist/form/Textarea.js +83 -4
  44. package/dist/form/index.d.ts +1 -1
  45. package/dist/heatmap/index.d.ts +59 -0
  46. package/dist/heatmap/index.js +248 -0
  47. package/dist/hotkey/index.d.ts +37 -0
  48. package/dist/hotkey/index.js +130 -0
  49. package/dist/hotkey/model.d.ts +27 -0
  50. package/dist/hotkey/model.js +97 -0
  51. package/dist/hover-card/index.d.ts +33 -0
  52. package/dist/hover-card/index.js +141 -0
  53. package/dist/image-cropper/index.d.ts +82 -0
  54. package/dist/image-cropper/index.js +261 -0
  55. package/dist/image-cropper/model.d.ts +42 -0
  56. package/dist/image-cropper/model.js +94 -0
  57. package/dist/image-viewer/index.d.ts +79 -0
  58. package/dist/image-viewer/index.js +220 -0
  59. package/dist/image-viewer/model.d.ts +9 -0
  60. package/dist/image-viewer/model.js +18 -0
  61. package/dist/index.d.ts +47 -0
  62. package/dist/index.js +47 -0
  63. package/dist/internal/cache.d.ts +1 -1
  64. package/dist/internal/cache.js +1 -1
  65. package/dist/internal/renderTokens.d.ts +10 -0
  66. package/dist/internal/renderTokens.js +13 -0
  67. package/dist/internal/surfaceRenderer.d.ts +14 -6
  68. package/dist/internal/surfaceRenderer.js +15 -12
  69. package/dist/internal/textRenderer.d.ts +1 -0
  70. package/dist/internal/textRenderer.js +33 -7
  71. package/dist/internal/usePencilSurface.js +16 -8
  72. package/dist/internal/useSelectionDrawing.js +15 -11
  73. package/dist/journey-map/MapScale.js +3 -1
  74. package/dist/journey-map/index.js +3 -1
  75. package/dist/keypad/index.d.ts +23 -0
  76. package/dist/keypad/index.js +63 -0
  77. package/dist/keypad/model.d.ts +20 -0
  78. package/dist/keypad/model.js +33 -0
  79. package/dist/layout/index.d.ts +63 -0
  80. package/dist/layout/index.js +62 -0
  81. package/dist/log/index.d.ts +45 -0
  82. package/dist/log/index.js +159 -0
  83. package/dist/log/model.d.ts +16 -0
  84. package/dist/log/model.js +13 -0
  85. package/dist/node-graph/index.d.ts +123 -0
  86. package/dist/node-graph/index.js +304 -0
  87. package/dist/number-ticker/index.d.ts +29 -0
  88. package/dist/number-ticker/index.js +132 -0
  89. package/dist/number-ticker/model.d.ts +8 -0
  90. package/dist/number-ticker/model.js +34 -0
  91. package/dist/overflow-list/index.d.ts +39 -0
  92. package/dist/overflow-list/index.js +230 -0
  93. package/dist/overflow-list/layout.d.ts +19 -0
  94. package/dist/overflow-list/layout.js +44 -0
  95. package/dist/pagination/index.d.ts +23 -0
  96. package/dist/pagination/index.js +41 -0
  97. package/dist/pagination/model.d.ts +4 -0
  98. package/dist/pagination/model.js +42 -0
  99. package/dist/pan-zoom/index.d.ts +71 -0
  100. package/dist/pan-zoom/index.js +175 -0
  101. package/dist/radar/index.d.ts +54 -0
  102. package/dist/radar/index.js +220 -0
  103. package/dist/range-slider/index.d.ts +16 -0
  104. package/dist/range-slider/index.js +22 -0
  105. package/dist/rating/index.d.ts +28 -0
  106. package/dist/rating/index.js +127 -0
  107. package/dist/resizable/index.d.ts +32 -0
  108. package/dist/resizable/index.js +50 -0
  109. package/dist/scroll-area/index.d.ts +36 -0
  110. package/dist/scroll-area/index.js +44 -0
  111. package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
  112. package/dist/segmented-progress/SegmentedProgress.js +42 -0
  113. package/dist/slider/index.js +1 -1
  114. package/dist/slot/index.d.ts +24 -0
  115. package/dist/slot/index.js +29 -0
  116. package/dist/sortable/index.d.ts +50 -0
  117. package/dist/sortable/index.js +278 -0
  118. package/dist/sortable/model.d.ts +30 -0
  119. package/dist/sortable/model.js +64 -0
  120. package/dist/sparkline/index.d.ts +19 -0
  121. package/dist/sparkline/index.js +240 -0
  122. package/dist/sparkline/model.d.ts +16 -0
  123. package/dist/sparkline/model.js +28 -0
  124. package/dist/steps/index.d.ts +16 -0
  125. package/dist/steps/index.js +37 -0
  126. package/dist/styles.css +4850 -287
  127. package/dist/tag-input/index.d.ts +69 -0
  128. package/dist/tag-input/index.js +325 -0
  129. package/dist/tag-input/model.d.ts +14 -0
  130. package/dist/tag-input/model.js +38 -0
  131. package/dist/time-picker/index.d.ts +15 -0
  132. package/dist/time-picker/index.js +168 -0
  133. package/dist/timeline/index.d.ts +14 -0
  134. package/dist/timeline/index.js +16 -0
  135. package/dist/toast/index.d.ts +27 -0
  136. package/dist/toast/index.js +36 -0
  137. package/dist/toggle-group/index.d.ts +17 -0
  138. package/dist/toggle-group/index.js +26 -0
  139. package/dist/tokens.css +10 -9
  140. package/dist/transfer/index.d.ts +64 -0
  141. package/dist/transfer/index.js +174 -0
  142. package/dist/transfer/model.d.ts +12 -0
  143. package/dist/transfer/model.js +33 -0
  144. package/dist/tree/index.d.ts +39 -0
  145. package/dist/tree/index.js +36 -0
  146. package/dist/tree-select/index.d.ts +59 -0
  147. package/dist/tree-select/index.js +142 -0
  148. package/dist/tree-select/model.d.ts +31 -0
  149. package/dist/tree-select/model.js +135 -0
  150. package/dist/typewriter/index.d.ts +21 -0
  151. package/dist/typewriter/index.js +101 -0
  152. package/dist/typewriter/model.d.ts +7 -0
  153. package/dist/typewriter/model.js +45 -0
  154. package/dist/upload/index.d.ts +59 -0
  155. package/dist/upload/index.js +230 -0
  156. package/dist/virtual-list/index.d.ts +44 -0
  157. package/dist/virtual-list/index.js +128 -0
  158. package/dist/virtual-list/model.d.ts +7 -0
  159. package/dist/virtual-list/model.js +27 -0
  160. package/package.json +22 -3
@@ -0,0 +1,31 @@
1
+ import type { FieldsetHTMLAttributes, ReactNode } from 'react';
2
+ import type { ColorValue } from './color.js';
3
+ export interface ColorPreset {
4
+ value: string;
5
+ label?: string;
6
+ }
7
+ export interface ColorPickerLabels {
8
+ palette?: string;
9
+ value?: string;
10
+ alpha?: string;
11
+ presets?: string;
12
+ invalid?: string;
13
+ }
14
+ export interface ColorPickerChangeDetails {
15
+ source: 'palette' | 'alpha' | 'input' | 'preset';
16
+ }
17
+ export interface ColorPickerProps extends Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'defaultValue' | 'onChange' | 'value'> {
18
+ value?: string;
19
+ defaultValue?: string;
20
+ onValueChange?: (value: ColorValue, details: ColorPickerChangeDetails) => void;
21
+ onInvalidInput?: (input: string) => void;
22
+ presets?: readonly (string | ColorPreset)[];
23
+ label?: ReactNode;
24
+ labels?: ColorPickerLabels;
25
+ name?: string;
26
+ showAlpha?: boolean;
27
+ seed?: string | number;
28
+ }
29
+ export declare function ColorPicker({ className, defaultValue, disabled, id, label, labels, name, onInvalidInput, onValueChange, presets, seed, showAlpha, value, ...props }: ColorPickerProps): import("react").JSX.Element;
30
+ export { DEFAULT_COLOR_VALUE, colorAlphaPercent, colorRgbValue, formatColorValue, normalizeColorValue, parseColorValue, withColorAlpha, withColorRgb, } from './color.js';
31
+ export type { ColorChannels, ColorValue } from './color.js';
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useId, useMemo, useState } from 'react';
3
+ import { Input } from '../form/Input.js';
4
+ import { PencilLine } from '../PencilLine.js';
5
+ import { PencilSurface } from '../PencilSurface.js';
6
+ import { PencilText } from '../PencilText.js';
7
+ import { Slider } from '../slider/index.js';
8
+ import { DEFAULT_COLOR_VALUE, colorAlphaPercent, colorRgbValue, normalizeColorValue, withColorAlpha, withColorRgb, } from './color.js';
9
+ const defaultLabels = {
10
+ palette: 'Open color palette',
11
+ value: 'Color value',
12
+ alpha: 'Opacity',
13
+ presets: 'Color presets',
14
+ invalid: 'Use #RGB, #RGBA, #RRGGBB, or #RRGGBBAA.',
15
+ };
16
+ function presetData(preset) {
17
+ const input = typeof preset === 'string' ? preset : preset.value;
18
+ const value = normalizeColorValue(input);
19
+ if (!value)
20
+ return null;
21
+ return { value, label: typeof preset === 'string' ? value : preset.label ?? value };
22
+ }
23
+ export function ColorPicker({ className, defaultValue = DEFAULT_COLOR_VALUE, disabled = false, id, label = 'Color', labels, name, onInvalidInput, onValueChange, presets = [], seed, showAlpha = true, value, ...props }) {
24
+ const generatedId = useId();
25
+ const resolvedId = id ?? `inkra-color-picker-${generatedId}`;
26
+ const resolvedSeed = seed ?? resolvedId;
27
+ const controlledValue = value === undefined ? null : normalizeColorValue(value);
28
+ const normalizedDefault = normalizeColorValue(defaultValue) ?? DEFAULT_COLOR_VALUE;
29
+ const [uncontrolledValue, setUncontrolledValue] = useState(normalizedDefault);
30
+ const currentValue = controlledValue ?? (value === undefined ? uncontrolledValue : DEFAULT_COLOR_VALUE);
31
+ const [draft, setDraft] = useState(currentValue);
32
+ const copy = { ...defaultLabels, ...labels };
33
+ const parsedPresets = useMemo(() => presets.map(presetData).filter((preset) => preset !== null), [presets]);
34
+ const inputInvalid = normalizeColorValue(draft) === null;
35
+ const rgbValue = colorRgbValue(currentValue) ?? '#4b96aa';
36
+ const alpha = colorAlphaPercent(currentValue);
37
+ useEffect(() => setDraft(currentValue), [currentValue]);
38
+ const commit = (next, source) => {
39
+ if (value === undefined)
40
+ setUncontrolledValue(next);
41
+ else
42
+ setDraft(currentValue);
43
+ onValueChange?.(next, { source });
44
+ };
45
+ const submitDraft = () => {
46
+ const normalized = normalizeColorValue(draft);
47
+ if (normalized)
48
+ commit(normalized, 'input');
49
+ else
50
+ onInvalidInput?.(draft);
51
+ };
52
+ return _jsxs("fieldset", { ...props, className: ['inkra-color-picker', className].filter(Boolean).join(' '), "data-alpha": showAlpha || undefined, "data-controlled": value !== undefined || undefined, "data-invalid-value": value !== undefined && controlledValue === null || undefined, disabled: disabled, id: resolvedId, children: [_jsx("legend", { className: "inkra-color-picker__legend", children: typeof label === 'string' ? _jsx(PencilText, { seed: `${resolvedSeed}-legend`, children: label }) : label }), _jsxs("div", { className: "inkra-color-picker__main", children: [_jsxs("label", { className: "inkra-color-picker__palette", "aria-label": copy.palette, title: copy.palette, children: [_jsx("span", { className: "inkra-color-picker__checker", "aria-hidden": "true" }), _jsx("span", { className: "inkra-color-picker__current", "aria-hidden": "true", style: { backgroundColor: currentValue } }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-color-picker__palette-outline", variant: "outline", seed: `${resolvedSeed}-palette` }), _jsx("input", { "aria-label": copy.palette, className: "inkra-color-picker__native", disabled: disabled, type: "color", value: rgbValue, onChange: event => {
53
+ const next = withColorRgb(currentValue, event.currentTarget.value);
54
+ if (next)
55
+ commit(next, 'palette');
56
+ } })] }), _jsxs("div", { className: "inkra-color-picker__value-field", children: [_jsx("label", { className: "inkra-color-picker__label", htmlFor: `${resolvedId}-value`, children: _jsx(PencilText, { seed: `${resolvedSeed}-value-label`, children: copy.value }) }), _jsx(Input, { id: `${resolvedId}-value`, "aria-describedby": inputInvalid ? `${resolvedId}-error` : undefined, "aria-invalid": inputInvalid || undefined, autoCapitalize: "off", autoComplete: "off", className: "inkra-color-picker__input", disabled: disabled, inputMode: "text", spellCheck: false, value: draft, onBlur: submitDraft, onKeyDown: event => {
57
+ if (event.key === 'Enter') {
58
+ event.preventDefault();
59
+ submitDraft();
60
+ }
61
+ if (event.key === 'Escape')
62
+ setDraft(currentValue);
63
+ }, onValueChange: setDraft }), _jsx("span", { className: "inkra-color-picker__error", id: `${resolvedId}-error`, "aria-live": "polite", children: inputInvalid ? copy.invalid : '' })] })] }), showAlpha ? _jsxs("div", { className: "inkra-color-picker__alpha", children: [_jsxs("div", { className: "inkra-color-picker__alpha-heading", children: [_jsx("span", { id: `${resolvedId}-alpha-label`, children: _jsx(PencilText, { seed: `${resolvedSeed}-alpha-label`, children: copy.alpha }) }), _jsxs("output", { htmlFor: `${resolvedId}-alpha`, children: [alpha, "%"] })] }), _jsx(Slider, { "aria-labelledby": `${resolvedId}-alpha-label`, disabled: disabled, id: `${resolvedId}-alpha`, max: 100, min: 0, seed: `${resolvedSeed}-alpha`, value: [alpha], getValueLabel: number => `${number}%`, onValueChange: values => {
64
+ const next = withColorAlpha(currentValue, values[0]);
65
+ if (next)
66
+ commit(next, 'alpha');
67
+ } })] }) : null, parsedPresets.length ? _jsxs(_Fragment, { children: [_jsx(PencilLine, { className: "inkra-color-picker__divider", seed: `${resolvedSeed}-divider` }), _jsx("div", { className: "inkra-color-picker__presets", role: "group", "aria-label": copy.presets, children: parsedPresets.map((preset, index) => _jsxs("button", { type: "button", "aria-label": preset.label, "aria-pressed": preset.value === currentValue, className: "inkra-color-picker__preset", disabled: disabled, title: preset.label, onClick: () => commit(preset.value, 'preset'), children: [_jsx("span", { className: "inkra-color-picker__checker", "aria-hidden": "true" }), _jsx("span", { className: "inkra-color-picker__preset-color", "aria-hidden": "true", style: { backgroundColor: preset.value } }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-color-picker__preset-outline", variant: preset.value === currentValue ? 'selection' : 'outline', seed: `${resolvedSeed}-preset-${index}` })] }, `${preset.value}-${index}`)) })] }) : null, name ? _jsx("input", { type: "hidden", name: name, value: currentValue }) : null] });
68
+ }
69
+ export { DEFAULT_COLOR_VALUE, colorAlphaPercent, colorRgbValue, formatColorValue, normalizeColorValue, parseColorValue, withColorAlpha, withColorRgb, } from './color.js';
@@ -0,0 +1,23 @@
1
+ import { Command as Primitive } from 'cmdk';
2
+ import type { ComponentPropsWithoutRef, HTMLAttributes, ReactNode } from 'react';
3
+ export type CommandRootProps = Omit<ComponentPropsWithoutRef<typeof Primitive>, 'asChild'>;
4
+ export type CommandInputProps = Omit<ComponentPropsWithoutRef<typeof Primitive.Input>, 'asChild'>;
5
+ export type CommandListProps = Omit<ComponentPropsWithoutRef<typeof Primitive.List>, 'asChild'>;
6
+ export interface CommandItemProps extends Omit<ComponentPropsWithoutRef<typeof Primitive.Item>, 'asChild'> {
7
+ icon?: ReactNode;
8
+ shortcut?: ReactNode;
9
+ }
10
+ export type CommandGroupProps = Omit<ComponentPropsWithoutRef<typeof Primitive.Group>, 'asChild'>;
11
+ export type CommandEmptyProps = Omit<ComponentPropsWithoutRef<typeof Primitive.Empty>, 'asChild'>;
12
+ export type CommandSeparatorProps = Omit<ComponentPropsWithoutRef<typeof Primitive.Separator>, 'asChild' | 'children'>;
13
+ export type CommandShortcutProps = HTMLAttributes<HTMLElement>;
14
+ export declare const Command: {
15
+ Root: import("react").ForwardRefExoticComponent<CommandRootProps & import("react").RefAttributes<HTMLDivElement>>;
16
+ Input: import("react").ForwardRefExoticComponent<CommandInputProps & import("react").RefAttributes<HTMLInputElement>>;
17
+ List: import("react").ForwardRefExoticComponent<CommandListProps & import("react").RefAttributes<HTMLDivElement>>;
18
+ Group: import("react").ForwardRefExoticComponent<CommandGroupProps & import("react").RefAttributes<HTMLDivElement>>;
19
+ Item: import("react").ForwardRefExoticComponent<CommandItemProps & import("react").RefAttributes<HTMLDivElement>>;
20
+ Empty: import("react").ForwardRefExoticComponent<CommandEmptyProps & import("react").RefAttributes<HTMLDivElement>>;
21
+ Separator: import("react").ForwardRefExoticComponent<CommandSeparatorProps & import("react").RefAttributes<HTMLDivElement>>;
22
+ Shortcut: import("react").ForwardRefExoticComponent<CommandShortcutProps & import("react").RefAttributes<HTMLElement>>;
23
+ };
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Command as Primitive } from 'cmdk';
3
+ import { forwardRef, useRef } from 'react';
4
+ import { PencilSurface } from '../PencilSurface.js';
5
+ import { PencilText } from '../PencilText.js';
6
+ import { PencilLine } from '../PencilLine.js';
7
+ function classes(base, extra) { return [base, extra].filter(Boolean).join(' '); }
8
+ function copy(value) { return typeof value === 'string' ? _jsx(PencilText, { children: value }) : value; }
9
+ const Root = forwardRef(function Root({ className, children, loop = true, onKeyDown, onCompositionStartCapture, onCompositionEndCapture, ...props }, ref) {
10
+ const composing = useRef(false);
11
+ return _jsx(Primitive, { ...props, ref: ref, loop: loop, className: classes('inkra-command', className), onCompositionStartCapture: event => { composing.current = true; onCompositionStartCapture?.(event); }, onCompositionEndCapture: event => { composing.current = false; onCompositionEndCapture?.(event); }, onKeyDown: event => {
12
+ // Capture composition state as well as native flags before cmdk handles Enter.
13
+ if (event.key === 'Enter' && (composing.current || event.nativeEvent.isComposing || event.keyCode === 229))
14
+ event.preventDefault();
15
+ onKeyDown?.(event);
16
+ }, children: _jsx(PencilSurface, { variant: "paper", radius: 8, className: "inkra-command__paper", children: children }) });
17
+ });
18
+ const Input = forwardRef(function Input({ className, ...props }, ref) {
19
+ return _jsxs("div", { className: "inkra-command__input-wrap", children: [_jsx(Primitive.Input, { ...props, ref: ref, className: classes('inkra-command__input', className) }), _jsx(PencilLine, { className: "inkra-command__input-line" })] });
20
+ });
21
+ const List = forwardRef(function List({ className, ...props }, ref) {
22
+ return _jsx(Primitive.List, { ...props, ref: ref, className: classes('inkra-command__list', className) });
23
+ });
24
+ const Item = forwardRef(function Item({ className, children, icon, shortcut, ...props }, ref) {
25
+ return _jsxs(Primitive.Item, { ...props, ref: ref, className: classes('inkra-command__item', className), children: [_jsx(PencilSurface, { as: "span", variant: "selection", "aria-hidden": "true", className: "inkra-command__highlight" }), icon != null && _jsx("span", { "aria-hidden": "true", className: "inkra-command__icon", children: icon }), _jsx("span", { className: "inkra-command__label", children: copy(children) }), shortcut != null && _jsx(Shortcut, { children: shortcut })] });
26
+ });
27
+ const Group = forwardRef(function Group({ className, heading, ...props }, ref) {
28
+ return _jsx(Primitive.Group, { ...props, heading: copy(heading), ref: ref, className: classes('inkra-command__group', className) });
29
+ });
30
+ const Empty = forwardRef(function Empty({ className, children, ...props }, ref) {
31
+ return _jsx(Primitive.Empty, { ...props, ref: ref, className: classes('inkra-command__empty', className), children: copy(children) });
32
+ });
33
+ const Separator = forwardRef(function Separator({ className, ...props }, ref) {
34
+ return _jsx(Primitive.Separator, { ...props, ref: ref, className: classes('inkra-command__separator', className), children: _jsx(PencilLine, {}) });
35
+ });
36
+ const Shortcut = forwardRef(function Shortcut({ className, children, ...props }, ref) {
37
+ return _jsx("kbd", { ...props, ref: ref, className: classes('inkra-command__shortcut', className), children: copy(children) });
38
+ });
39
+ export const Command = { Root, Input, List, Group, Item, Empty, Separator, Shortcut };
@@ -0,0 +1,76 @@
1
+ import * as Primitive from '@radix-ui/react-dropdown-menu';
2
+ import type { ComponentPropsWithoutRef, ElementType, HTMLAttributes, MouseEvent, ReactNode } from 'react';
3
+ import { DropdownMenu } from '../dropdown-menu/index.js';
4
+ import type { DropdownMenuContentProps } from '../dropdown-menu/index.js';
5
+ export type ContextMenuOpenReason = 'pointer' | 'keyboard' | 'long-press' | 'dismiss';
6
+ export interface ContextMenuOpenChangeDetails {
7
+ reason: ContextMenuOpenReason;
8
+ event?: Event;
9
+ }
10
+ export interface ContextMenuRootProps {
11
+ children: ReactNode;
12
+ open?: boolean;
13
+ defaultOpen?: boolean;
14
+ modal?: boolean;
15
+ dir?: 'ltr' | 'rtl';
16
+ onOpenChange?: (open: boolean, details: ContextMenuOpenChangeDetails) => void;
17
+ }
18
+ export declare function ContextMenuRoot({ children, defaultOpen, dir, modal, onOpenChange, open, }: ContextMenuRootProps): import("react").JSX.Element;
19
+ export type ContextMenuNativePolicy = 'editable' | 'always' | 'never';
20
+ export interface ContextMenuLongPressOptions {
21
+ delay?: number;
22
+ movementTolerance?: number;
23
+ }
24
+ export interface ContextMenuTriggerProps extends Omit<HTMLAttributes<HTMLElement>, 'onContextMenu'> {
25
+ as?: ElementType;
26
+ disabled?: boolean;
27
+ exclude?: string | ((target: Element, currentTarget: HTMLElement) => boolean);
28
+ longPress?: boolean | ContextMenuLongPressOptions;
29
+ nativeContextMenu?: ContextMenuNativePolicy;
30
+ onContextMenu?: (event: MouseEvent<HTMLElement>) => void;
31
+ }
32
+ export declare const ContextMenuTrigger: import("react").ForwardRefExoticComponent<ContextMenuTriggerProps & import("react").RefAttributes<HTMLElement>>;
33
+ export type ContextMenuPortalProps = ComponentPropsWithoutRef<typeof DropdownMenu.Portal>;
34
+ export declare const ContextMenuPortal: ({ children, ...props }: import("../dropdown-menu/index.js").DropdownMenuPortalProps) => import("react").JSX.Element;
35
+ export type ContextMenuContentProps = DropdownMenuContentProps;
36
+ export declare const ContextMenuContent: import("react").ForwardRefExoticComponent<DropdownMenuContentProps & import("react").RefAttributes<HTMLDivElement>>;
37
+ export type ContextMenuItemProps = ComponentPropsWithoutRef<typeof DropdownMenu.Item>;
38
+ export declare const ContextMenuItem: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuItemProps & import("react").RefAttributes<HTMLDivElement>>;
39
+ export type ContextMenuCheckboxItemProps = ComponentPropsWithoutRef<typeof DropdownMenu.CheckboxItem>;
40
+ export declare const ContextMenuCheckboxItem: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuCheckboxItemProps & import("react").RefAttributes<HTMLDivElement>>;
41
+ export type ContextMenuRadioItemProps = ComponentPropsWithoutRef<typeof DropdownMenu.RadioItem>;
42
+ export declare const ContextMenuRadioItem: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuRadioItemProps & import("react").RefAttributes<HTMLDivElement>>;
43
+ export type ContextMenuLabelProps = ComponentPropsWithoutRef<typeof DropdownMenu.Label>;
44
+ export declare const ContextMenuLabel: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuLabelProps & import("react").RefAttributes<HTMLDivElement>>;
45
+ export type ContextMenuSeparatorProps = ComponentPropsWithoutRef<typeof DropdownMenu.Separator>;
46
+ export declare const ContextMenuSeparator: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuSeparatorProps & import("react").RefAttributes<HTMLDivElement>>;
47
+ export type ContextMenuGroupProps = ComponentPropsWithoutRef<typeof DropdownMenu.Group>;
48
+ export declare const ContextMenuGroup: import("react").ForwardRefExoticComponent<Primitive.DropdownMenuGroupProps & import("react").RefAttributes<HTMLDivElement>>;
49
+ export type ContextMenuRadioGroupProps = ComponentPropsWithoutRef<typeof DropdownMenu.RadioGroup>;
50
+ export declare const ContextMenuRadioGroup: import("react").ForwardRefExoticComponent<Primitive.DropdownMenuRadioGroupProps & import("react").RefAttributes<HTMLDivElement>>;
51
+ export type ContextMenuSubProps = ComponentPropsWithoutRef<typeof Primitive.Sub>;
52
+ export declare const ContextMenuSub: import("react").FC<Primitive.DropdownMenuSubProps>;
53
+ export interface ContextMenuSubTriggerProps extends Omit<ComponentPropsWithoutRef<typeof Primitive.SubTrigger>, 'asChild'> {
54
+ icon?: ReactNode;
55
+ }
56
+ export declare const ContextMenuSubTrigger: import("react").ForwardRefExoticComponent<ContextMenuSubTriggerProps & import("react").RefAttributes<HTMLDivElement>>;
57
+ export interface ContextMenuSubContentProps extends Omit<ComponentPropsWithoutRef<typeof Primitive.SubContent>, 'asChild'> {
58
+ seed?: string | number;
59
+ }
60
+ export declare const ContextMenuSubContent: import("react").ForwardRefExoticComponent<ContextMenuSubContentProps & import("react").RefAttributes<HTMLDivElement>>;
61
+ export declare const ContextMenu: Readonly<{
62
+ Root: typeof ContextMenuRoot;
63
+ Trigger: import("react").ForwardRefExoticComponent<ContextMenuTriggerProps & import("react").RefAttributes<HTMLElement>>;
64
+ Portal: ({ children, ...props }: import("../dropdown-menu/index.js").DropdownMenuPortalProps) => import("react").JSX.Element;
65
+ Content: import("react").ForwardRefExoticComponent<DropdownMenuContentProps & import("react").RefAttributes<HTMLDivElement>>;
66
+ Item: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuItemProps & import("react").RefAttributes<HTMLDivElement>>;
67
+ CheckboxItem: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuCheckboxItemProps & import("react").RefAttributes<HTMLDivElement>>;
68
+ RadioItem: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuRadioItemProps & import("react").RefAttributes<HTMLDivElement>>;
69
+ Label: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuLabelProps & import("react").RefAttributes<HTMLDivElement>>;
70
+ Separator: import("react").ForwardRefExoticComponent<import("../dropdown-menu/index.js").DropdownMenuSeparatorProps & import("react").RefAttributes<HTMLDivElement>>;
71
+ Group: import("react").ForwardRefExoticComponent<Primitive.DropdownMenuGroupProps & import("react").RefAttributes<HTMLDivElement>>;
72
+ RadioGroup: import("react").ForwardRefExoticComponent<Primitive.DropdownMenuRadioGroupProps & import("react").RefAttributes<HTMLDivElement>>;
73
+ Sub: import("react").FC<Primitive.DropdownMenuSubProps>;
74
+ SubTrigger: import("react").ForwardRefExoticComponent<ContextMenuSubTriggerProps & import("react").RefAttributes<HTMLDivElement>>;
75
+ SubContent: import("react").ForwardRefExoticComponent<ContextMenuSubContentProps & import("react").RefAttributes<HTMLDivElement>>;
76
+ }>;
@@ -0,0 +1,240 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as Primitive from '@radix-ui/react-dropdown-menu';
3
+ import { createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
4
+ import { DropdownMenu } from '../dropdown-menu/index.js';
5
+ import { PencilIcon } from '../PencilIcon.js';
6
+ import { PencilSurface } from '../PencilSurface.js';
7
+ import { PencilText } from '../PencilText.js';
8
+ import { PortalThemeBridge, PortalThemeProvider, usePortalTriggerRef, } from '../overlay/themeBridge.js';
9
+ const DEFAULT_EXCLUDE = '[data-context-menu-native]';
10
+ const DEFAULT_LONG_PRESS_DELAY = 600;
11
+ const DEFAULT_LONG_PRESS_TOLERANCE = 10;
12
+ const submenuPaths = [{ d: 'm9 18 6-6-6-6' }];
13
+ const classes = (...values) => values.filter(Boolean).join(' ');
14
+ const ContextMenuContext = createContext(null);
15
+ const ContextMenuAnchor = forwardRef(function ContextMenuAnchor({ type: _type, ...props }, ref) {
16
+ return _jsx("span", { ...props, ref: ref });
17
+ });
18
+ function useContextMenuContext(component) {
19
+ const context = useContext(ContextMenuContext);
20
+ if (!context)
21
+ throw new Error(`${component} must be used inside ContextMenu.Root.`);
22
+ return context;
23
+ }
24
+ function viewportPoint(point) {
25
+ if (typeof window === 'undefined')
26
+ return point;
27
+ return {
28
+ x: Math.max(0, Math.min(point.x, window.innerWidth)),
29
+ y: Math.max(0, Math.min(point.y, window.innerHeight)),
30
+ };
31
+ }
32
+ export function ContextMenuRoot({ children, defaultOpen = false, dir, modal = true, onOpenChange, open, }) {
33
+ const controlled = open !== undefined;
34
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
35
+ const [point, setPoint] = useState({ x: 0, y: 0 });
36
+ const [positioned, setPositioned] = useState(false);
37
+ const [anchorVersion, setAnchorVersion] = useState(0);
38
+ const targetRef = useRef(null);
39
+ const currentOpen = controlled ? open : uncontrolledOpen;
40
+ const changeOpen = useCallback((nextOpen, details) => {
41
+ if (!controlled)
42
+ setUncontrolledOpen(nextOpen);
43
+ onOpenChange?.(nextOpen, details);
44
+ }, [controlled, onOpenChange]);
45
+ const openAt = useCallback((nextPoint, reason, event) => {
46
+ setPoint(viewportPoint(nextPoint));
47
+ setPositioned(true);
48
+ setAnchorVersion(version => version + 1);
49
+ changeOpen(true, { reason, event });
50
+ }, [changeOpen]);
51
+ const restoreFocus = useCallback(() => {
52
+ const target = targetRef.current;
53
+ if (!target || typeof target.focus !== 'function')
54
+ return;
55
+ target.focus({ preventScroll: true });
56
+ }, []);
57
+ useEffect(() => {
58
+ if (!currentOpen || positioned)
59
+ return;
60
+ const rect = targetRef.current?.getBoundingClientRect();
61
+ if (!rect)
62
+ return;
63
+ setPoint(viewportPoint({ x: rect.left, y: rect.bottom }));
64
+ setPositioned(true);
65
+ }, [currentOpen, positioned]);
66
+ const context = useMemo(() => ({
67
+ openAt,
68
+ restoreFocus,
69
+ setTarget: target => { targetRef.current = target; },
70
+ }), [openAt, restoreFocus]);
71
+ return (_jsx(PortalThemeProvider, { children: _jsx(ContextMenuContext.Provider, { value: context, children: _jsxs(Primitive.Root, { dir: dir, modal: modal, open: currentOpen, onOpenChange: nextOpen => changeOpen(nextOpen, { reason: 'dismiss' }), children: [children, _jsx(Primitive.Trigger, { asChild: true, children: _jsx(ContextMenuAnchor, { "aria-hidden": "true", className: "inkra-context-menu__anchor", style: { left: point.x, top: point.y } }) }, anchorVersion)] }) }) }));
72
+ }
73
+ function hasTextSelectionWithin(element) {
74
+ if (typeof window === 'undefined')
75
+ return false;
76
+ const selection = window.getSelection();
77
+ if (!selection || selection.isCollapsed)
78
+ return false;
79
+ return Boolean((selection.anchorNode && element.contains(selection.anchorNode))
80
+ || (selection.focusNode && element.contains(selection.focusNode)));
81
+ }
82
+ function isEditableTarget(target) {
83
+ return Boolean(target.closest('input, textarea, select, [contenteditable]:not([contenteditable="false"])'));
84
+ }
85
+ function matchesExclusion(exclude, target, currentTarget) {
86
+ if (typeof exclude === 'function')
87
+ return exclude(target, currentTarget);
88
+ if (!exclude)
89
+ return false;
90
+ try {
91
+ return Boolean(target.closest(exclude));
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
97
+ function shouldUseNativeMenu(policy, target, currentTarget, exclude) {
98
+ if (matchesExclusion(exclude, target, currentTarget))
99
+ return true;
100
+ if (policy === 'always')
101
+ return true;
102
+ if (policy === 'never')
103
+ return false;
104
+ return isEditableTarget(target) || hasTextSelectionWithin(currentTarget);
105
+ }
106
+ function composeHandler(supplied, owned) {
107
+ return (event) => {
108
+ supplied?.(event);
109
+ const maybePrevented = event;
110
+ if (!maybePrevented.defaultPrevented)
111
+ owned(event);
112
+ };
113
+ }
114
+ export const ContextMenuTrigger = forwardRef(function ContextMenuTrigger({ as: Component = 'div', children, disabled = false, exclude = DEFAULT_EXCLUDE, longPress = false, nativeContextMenu = 'editable', onContextMenu, onKeyDown, onPointerCancel, onPointerDown, onPointerLeave, onPointerMove, onPointerUp, tabIndex, ...props }, forwardedRef) {
115
+ const context = useContextMenuContext('ContextMenu.Trigger');
116
+ const longPressTimer = useRef(null);
117
+ const startPoint = useRef(null);
118
+ const pointerType = useRef('');
119
+ const suppressContextMenuUntil = useRef(0);
120
+ const { setTarget } = context;
121
+ const captureTarget = useCallback((node) => {
122
+ setTarget(node);
123
+ if (typeof forwardedRef === 'function')
124
+ forwardedRef(node);
125
+ else if (forwardedRef)
126
+ forwardedRef.current = node;
127
+ }, [forwardedRef, setTarget]);
128
+ const targetRef = usePortalTriggerRef(captureTarget);
129
+ const longPressOptions = typeof longPress === 'object' ? longPress : {};
130
+ const longPressEnabled = Boolean(longPress);
131
+ const delay = Math.max(300, longPressOptions.delay ?? DEFAULT_LONG_PRESS_DELAY);
132
+ const tolerance = Math.max(4, longPressOptions.movementTolerance ?? DEFAULT_LONG_PRESS_TOLERANCE);
133
+ useEffect(() => () => {
134
+ if (longPressTimer.current)
135
+ clearTimeout(longPressTimer.current);
136
+ }, []);
137
+ const cancelLongPress = () => {
138
+ if (longPressTimer.current)
139
+ clearTimeout(longPressTimer.current);
140
+ longPressTimer.current = null;
141
+ startPoint.current = null;
142
+ };
143
+ const openFromKeyboard = (event) => {
144
+ if (disabled || (event.key !== 'ContextMenu' && !(event.shiftKey && event.key === 'F10')))
145
+ return;
146
+ const target = event.target instanceof Element ? event.target : event.currentTarget;
147
+ if (shouldUseNativeMenu(nativeContextMenu, target, event.currentTarget, exclude))
148
+ return;
149
+ event.preventDefault();
150
+ context.setTarget(event.currentTarget);
151
+ const rect = event.currentTarget.getBoundingClientRect();
152
+ context.openAt({ x: rect.left, y: rect.bottom }, 'keyboard', event.nativeEvent);
153
+ };
154
+ const openFromPointer = (event) => {
155
+ if (disabled)
156
+ return;
157
+ const target = event.target instanceof Element ? event.target : event.currentTarget;
158
+ if (pointerType.current === 'touch' && !longPressEnabled)
159
+ return;
160
+ if (shouldUseNativeMenu(nativeContextMenu, target, event.currentTarget, exclude))
161
+ return;
162
+ event.preventDefault();
163
+ cancelLongPress();
164
+ if (Date.now() < suppressContextMenuUntil.current)
165
+ return;
166
+ context.setTarget(event.currentTarget);
167
+ context.openAt({ x: event.clientX, y: event.clientY }, 'pointer', event.nativeEvent);
168
+ };
169
+ const startLongPress = (event) => {
170
+ pointerType.current = event.pointerType;
171
+ if (!longPressEnabled || disabled || (event.pointerType !== 'touch' && event.pointerType !== 'pen'))
172
+ return;
173
+ const target = event.target instanceof Element ? event.target : event.currentTarget;
174
+ if (shouldUseNativeMenu(nativeContextMenu, target, event.currentTarget, exclude))
175
+ return;
176
+ cancelLongPress();
177
+ startPoint.current = { x: event.clientX, y: event.clientY };
178
+ const nativeEvent = event.nativeEvent;
179
+ const currentTarget = event.currentTarget;
180
+ const pressPoint = { x: event.clientX, y: event.clientY };
181
+ longPressTimer.current = setTimeout(() => {
182
+ longPressTimer.current = null;
183
+ if (hasTextSelectionWithin(currentTarget))
184
+ return;
185
+ suppressContextMenuUntil.current = Date.now() + 1000;
186
+ context.setTarget(currentTarget);
187
+ context.openAt(pressPoint, 'long-press', nativeEvent);
188
+ }, delay);
189
+ };
190
+ const trackLongPress = (event) => {
191
+ if (!startPoint.current || !longPressTimer.current)
192
+ return;
193
+ const distance = Math.hypot(event.clientX - startPoint.current.x, event.clientY - startPoint.current.y);
194
+ if (distance > tolerance)
195
+ cancelLongPress();
196
+ };
197
+ return (_jsx(Component, { ...props, "aria-disabled": disabled || undefined, "data-context-menu-trigger": "", ref: targetRef, tabIndex: disabled ? -1 : (tabIndex ?? 0), onContextMenu: composeHandler(onContextMenu, openFromPointer), onKeyDown: composeHandler(onKeyDown, openFromKeyboard), onPointerCancel: composeHandler(onPointerCancel, cancelLongPress), onPointerDown: composeHandler(onPointerDown, startLongPress), onPointerLeave: composeHandler(onPointerLeave, cancelLongPress), onPointerMove: composeHandler(onPointerMove, trackLongPress), onPointerUp: composeHandler(onPointerUp, cancelLongPress), children: children }));
198
+ });
199
+ export const ContextMenuPortal = DropdownMenu.Portal;
200
+ export const ContextMenuContent = forwardRef(function ContextMenuContent({ align = 'start', onCloseAutoFocus, side = 'right', sideOffset = 2, ...props }, ref) {
201
+ const context = useContextMenuContext('ContextMenu.Content');
202
+ return (_jsx(DropdownMenu.Content, { ...props, align: align, ref: ref, side: side, sideOffset: sideOffset, onCloseAutoFocus: event => {
203
+ onCloseAutoFocus?.(event);
204
+ if (event.defaultPrevented)
205
+ return;
206
+ event.preventDefault();
207
+ context.restoreFocus();
208
+ } }));
209
+ });
210
+ export const ContextMenuItem = DropdownMenu.Item;
211
+ export const ContextMenuCheckboxItem = DropdownMenu.CheckboxItem;
212
+ export const ContextMenuRadioItem = DropdownMenu.RadioItem;
213
+ export const ContextMenuLabel = DropdownMenu.Label;
214
+ export const ContextMenuSeparator = DropdownMenu.Separator;
215
+ export const ContextMenuGroup = DropdownMenu.Group;
216
+ export const ContextMenuRadioGroup = DropdownMenu.RadioGroup;
217
+ export const ContextMenuSub = Primitive.Sub;
218
+ export const ContextMenuSubTrigger = forwardRef(function ContextMenuSubTrigger({ children, className, icon, ...props }, ref) {
219
+ return (_jsxs(Primitive.SubTrigger, { ...props, className: classes('inkra-menu__item', 'inkra-context-menu__sub-trigger', className), ref: ref, children: [_jsx(PencilSurface, { as: "span", "aria-hidden": "true", variant: "selection", className: "inkra-menu__highlight" }), _jsx("span", { className: "inkra-menu__icon", "aria-hidden": "true", children: icon }), _jsx("span", { className: "inkra-menu__label", children: typeof children === 'string' ? _jsx(PencilText, { children: children }) : children }), _jsx("span", { "aria-hidden": "true", className: "inkra-context-menu__sub-indicator", children: _jsx(PencilIcon, { decorative: true, paths: submenuPaths, size: 16, sourceSize: 24 }) })] }));
220
+ });
221
+ export const ContextMenuSubContent = forwardRef(function ContextMenuSubContent({ children, className, collisionPadding = 8, seed, sideOffset = 4, ...props }, ref) {
222
+ const id = useId();
223
+ return (_jsx(Primitive.Portal, { children: _jsx(PortalThemeBridge, { children: _jsx(Primitive.SubContent, { ...props, className: classes('inkra-menu', 'inkra-context-menu__sub-content', className), collisionPadding: collisionPadding, ref: ref, sideOffset: sideOffset, children: _jsx(PencilSurface, { className: "inkra-menu__paper", radius: 8, seed: seed ?? `context-submenu-${id}`, variant: "paper", children: children }) }) }) }));
224
+ });
225
+ export const ContextMenu = Object.freeze({
226
+ Root: ContextMenuRoot,
227
+ Trigger: ContextMenuTrigger,
228
+ Portal: ContextMenuPortal,
229
+ Content: ContextMenuContent,
230
+ Item: ContextMenuItem,
231
+ CheckboxItem: ContextMenuCheckboxItem,
232
+ RadioItem: ContextMenuRadioItem,
233
+ Label: ContextMenuLabel,
234
+ Separator: ContextMenuSeparator,
235
+ Group: ContextMenuGroup,
236
+ RadioGroup: ContextMenuRadioGroup,
237
+ Sub: ContextMenuSub,
238
+ SubTrigger: ContextMenuSubTrigger,
239
+ SubContent: ContextMenuSubContent,
240
+ });
@@ -0,0 +1,47 @@
1
+ import type { ColumnDef, OnChangeFn, PaginationState, Row, RowSelectionState, SortingState, TableOptions } from '@tanstack/react-table';
2
+ import type { HTMLAttributes, ReactNode } from 'react';
3
+ export type DataTableColumnDef<TData, TValue = unknown> = ColumnDef<TData, TValue>;
4
+ export type DataTableSortingState = SortingState;
5
+ export type DataTablePaginationState = PaginationState;
6
+ export type DataTableRowSelectionState = RowSelectionState;
7
+ export interface DataTableProps<TData> extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
8
+ data: TData[];
9
+ columns: DataTableColumnDef<TData>[];
10
+ getRowId: NonNullable<TableOptions<TData>['getRowId']>;
11
+ caption?: ReactNode;
12
+ density?: 'compact' | 'comfortable';
13
+ layout?: 'auto' | 'fixed';
14
+ striped?: boolean;
15
+ hoverable?: boolean;
16
+ stickyHeader?: boolean;
17
+ captionSide?: 'top' | 'bottom';
18
+ filterable?: boolean;
19
+ filterLabel?: string;
20
+ filterPlaceholder?: string;
21
+ globalFilter?: string;
22
+ defaultGlobalFilter?: string;
23
+ onGlobalFilterChange?: (value: string) => void;
24
+ sorting?: SortingState;
25
+ defaultSorting?: SortingState;
26
+ onSortingChange?: OnChangeFn<SortingState>;
27
+ pagination?: boolean;
28
+ paginationState?: PaginationState;
29
+ defaultPagination?: PaginationState;
30
+ onPaginationChange?: OnChangeFn<PaginationState>;
31
+ selectable?: boolean;
32
+ rowSelection?: RowSelectionState;
33
+ defaultRowSelection?: RowSelectionState;
34
+ onRowSelectionChange?: OnChangeFn<RowSelectionState>;
35
+ getRowCanSelect?: (row: Row<TData>) => boolean;
36
+ loading?: boolean;
37
+ loadingMessage?: ReactNode;
38
+ emptyMessage?: ReactNode;
39
+ selectAllLabel?: string;
40
+ selectRowLabel?: (row: Row<TData>) => string;
41
+ previousPageLabel?: string;
42
+ nextPageLabel?: string;
43
+ paginationLabel?: string;
44
+ pageLabel?: (page: number, pageCount: number) => ReactNode;
45
+ sortStatusLabel?: (columnId: string, direction: false | 'asc' | 'desc') => string;
46
+ }
47
+ export declare function DataTable<TData>({ data, columns, getRowId, caption, density, layout, striped, hoverable, stickyHeader, captionSide, filterable, filterLabel, filterPlaceholder, globalFilter, defaultGlobalFilter, onGlobalFilterChange, sorting, defaultSorting, onSortingChange, pagination, paginationState, defaultPagination, onPaginationChange, selectable, rowSelection, defaultRowSelection, onRowSelectionChange, getRowCanSelect, loading, loadingMessage, emptyMessage, selectAllLabel, selectRowLabel, previousPageLabel, nextPageLabel, paginationLabel, pageLabel, sortStatusLabel, className, ...props }: DataTableProps<TData>): import("react").JSX.Element;
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { flexRender, functionalUpdate, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
3
+ import { useEffect, useState } from 'react';
4
+ import { Button } from '../Button.js';
5
+ import { PencilIcon } from '../PencilIcon.js';
6
+ import { Input } from '../form/Input.js';
7
+ import { Table } from '../content/index.js';
8
+ import { Checkbox } from '../selection/Checkbox.js';
9
+ const sortIcon = [{ d: 'M 8 10 L 12 6 L 16 10 M 8 14 L 12 18 L 16 14' }];
10
+ const previousIcon = [{ d: 'M 14 7 L 9 12 L 14 17' }];
11
+ const nextIcon = [{ d: 'M 10 7 L 15 12 L 10 17' }];
12
+ function classes(...values) {
13
+ return values.filter(Boolean).join(' ');
14
+ }
15
+ function resolve(updater, current) {
16
+ return functionalUpdate(updater, current);
17
+ }
18
+ function SelectionCheckbox(props) {
19
+ return _jsx(Checkbox, { className: "inkra-data-table__selection", checked: props.checked, indeterminate: props.indeterminate, disabled: props.disabled, label: _jsx("span", { className: "inkra-visually-hidden", children: props.label }), onCheckedChange: props.onChange });
20
+ }
21
+ export function DataTable({ data, columns, getRowId, caption, density = 'comfortable', layout = 'auto', striped = false, hoverable = true, stickyHeader = false, captionSide = 'top', filterable = false, filterLabel = 'Filter rows', filterPlaceholder = 'Filter rows', globalFilter, defaultGlobalFilter = '', onGlobalFilterChange, sorting, defaultSorting = [], onSortingChange, pagination = false, paginationState, defaultPagination = { pageIndex: 0, pageSize: 10 }, onPaginationChange, selectable = false, rowSelection, defaultRowSelection = {}, onRowSelectionChange, getRowCanSelect, loading = false, loadingMessage = 'Loading rows', emptyMessage = 'No rows', selectAllLabel = 'Select all rows on this page', selectRowLabel = row => `Select row ${row.id}`, previousPageLabel = 'Previous page', nextPageLabel = 'Next page', paginationLabel = 'Table pagination', pageLabel = (page, pageCount) => `Page ${page} of ${pageCount}`, sortStatusLabel = (columnId, direction) => direction === 'asc' ? `${columnId}: sorted ascending` : direction === 'desc' ? `${columnId}: sorted descending` : `${columnId}: not sorted`, className, ...props }) {
22
+ const [internalFilter, setInternalFilter] = useState(defaultGlobalFilter);
23
+ const [internalSorting, setInternalSorting] = useState(defaultSorting);
24
+ const [internalPagination, setInternalPagination] = useState(defaultPagination);
25
+ const [internalSelection, setInternalSelection] = useState(defaultRowSelection);
26
+ const resolvedFilter = globalFilter ?? internalFilter;
27
+ const resolvedSorting = sorting ?? internalSorting;
28
+ const resolvedPagination = paginationState ?? internalPagination;
29
+ const resolvedSelection = rowSelection ?? internalSelection;
30
+ const handleSorting = updater => {
31
+ if (sorting === undefined)
32
+ setInternalSorting(current => resolve(updater, current));
33
+ onSortingChange?.(updater);
34
+ };
35
+ const handlePagination = updater => {
36
+ if (paginationState === undefined)
37
+ setInternalPagination(current => resolve(updater, current));
38
+ onPaginationChange?.(updater);
39
+ };
40
+ const handleSelection = updater => {
41
+ if (rowSelection === undefined)
42
+ setInternalSelection(current => resolve(updater, current));
43
+ onRowSelectionChange?.(updater);
44
+ };
45
+ const handleFilter = (value) => {
46
+ if (globalFilter === undefined)
47
+ setInternalFilter(value);
48
+ onGlobalFilterChange?.(value);
49
+ if (pagination && resolvedPagination.pageIndex !== 0)
50
+ handlePagination(current => ({ ...current, pageIndex: 0 }));
51
+ };
52
+ const table = useReactTable({
53
+ data,
54
+ columns,
55
+ getRowId,
56
+ state: {
57
+ globalFilter: resolvedFilter,
58
+ sorting: resolvedSorting,
59
+ pagination: resolvedPagination,
60
+ rowSelection: resolvedSelection,
61
+ },
62
+ onGlobalFilterChange: updater => handleFilter(resolve(updater, resolvedFilter)),
63
+ onSortingChange: handleSorting,
64
+ onPaginationChange: handlePagination,
65
+ onRowSelectionChange: handleSelection,
66
+ enableRowSelection: selectable ? getRowCanSelect ?? true : false,
67
+ getCoreRowModel: getCoreRowModel(),
68
+ getFilteredRowModel: getFilteredRowModel(),
69
+ getSortedRowModel: getSortedRowModel(),
70
+ getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
71
+ });
72
+ const rows = table.getRowModel().rows;
73
+ const filteredRowCount = table.getPrePaginationRowModel().rows.length;
74
+ const pageCount = Math.max(1, table.getPageCount());
75
+ const visibleColumnCount = Math.max(1, table.getVisibleLeafColumns().length + (selectable ? 1 : 0));
76
+ const hasSelectableRows = rows.some(row => row.getCanSelect());
77
+ useEffect(() => {
78
+ if (!pagination)
79
+ return;
80
+ const lastPageIndex = Math.max(0, Math.ceil(filteredRowCount / resolvedPagination.pageSize) - 1);
81
+ if (resolvedPagination.pageIndex <= lastPageIndex)
82
+ return;
83
+ const next = { ...resolvedPagination, pageIndex: lastPageIndex };
84
+ if (paginationState === undefined)
85
+ setInternalPagination(next);
86
+ onPaginationChange?.(next);
87
+ }, [filteredRowCount, onPaginationChange, pagination, paginationState, resolvedPagination]);
88
+ return _jsxs("div", { ...props, className: classes('inkra-data-table', className), "data-loading": loading || undefined, children: [filterable && _jsx("div", { className: "inkra-data-table__toolbar", children: _jsxs("label", { className: "inkra-data-table__filter", children: [_jsx("span", { children: filterLabel }), _jsx(Input, { type: "search", value: resolvedFilter, placeholder: filterPlaceholder, onChange: event => handleFilter(event.currentTarget.value) })] }) }), _jsx(Table.Container, { className: "inkra-data-table__container", children: _jsxs(Table.Root, { density: density, layout: layout, striped: striped, hoverable: hoverable, stickyHeader: stickyHeader, captionSide: captionSide, children: [caption != null && _jsx(Table.Caption, { children: caption }), _jsx(Table.Header, { children: table.getHeaderGroups().map(headerGroup => _jsxs(Table.Row, { children: [selectable && _jsx(Table.Head, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: pagination ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected(), indeterminate: pagination ? table.getIsSomePageRowsSelected() : table.getIsSomeRowsSelected(), label: selectAllLabel, disabled: loading || !hasSelectableRows, onChange: checked => pagination ? table.toggleAllPageRowsSelected(checked) : table.toggleAllRowsSelected(checked) }) }), headerGroup.headers.map(header => _jsx(Table.Head, { colSpan: header.colSpan, "aria-sort": header.column.getIsSorted() === 'asc' ? 'ascending' : header.column.getIsSorted() === 'desc' ? 'descending' : header.column.getCanSort() ? 'none' : undefined, children: header.isPlaceholder ? null : header.column.getCanSort() ? _jsxs("button", { type: "button", className: "inkra-data-table__sort", "data-sorted": header.column.getIsSorted() || undefined, onClick: header.column.getToggleSortingHandler(), children: [_jsx("span", { children: flexRender(header.column.columnDef.header, header.getContext()) }), _jsx(PencilIcon, { "aria-hidden": "true", paths: sortIcon, size: 18, sourceSize: 24, active: Boolean(header.column.getIsSorted()) }), _jsx("span", { className: "inkra-visually-hidden", children: sortStatusLabel(header.column.id, header.column.getIsSorted()) })] }) : flexRender(header.column.columnDef.header, header.getContext()) }, header.id))] }, headerGroup.id)) }), _jsx(Table.Body, { children: loading ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", role: "status", children: loadingMessage }) }) })
89
+ : rows.length === 0 ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", children: emptyMessage }) }) })
90
+ : rows.map(row => _jsxs(Table.Row, { selected: row.getIsSelected(), children: [selectable && _jsx(Table.Cell, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: row.getIsSelected(), label: selectRowLabel(row), disabled: !row.getCanSelect(), onChange: checked => row.toggleSelected(checked) }) }), row.getVisibleCells().map(cell => _jsx(Table.Cell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))] }, row.id)) })] }) }), pagination && _jsxs("nav", { className: "inkra-data-table__pagination", "aria-label": paginationLabel, children: [_jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": previousPageLabel, disabled: loading || !table.getCanPreviousPage(), onClick: () => table.previousPage(), children: _jsx(PencilIcon, { paths: previousIcon, size: 18, sourceSize: 24 }) }), _jsx("span", { "aria-live": "polite", children: pageLabel(table.getState().pagination.pageIndex + 1, pageCount) }), _jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": nextPageLabel, disabled: loading || !table.getCanNextPage(), onClick: () => table.nextPage(), children: _jsx(PencilIcon, { paths: nextIcon, size: 18, sourceSize: 24 }) })] })] });
91
+ }