@wallarm-org/design-system 0.71.4 → 0.72.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 (91) hide show
  1. package/README.md +1 -3
  2. package/dist/components/Attribute/AttributeValue.js +1 -1
  3. package/dist/components/Calendar/Calendar.js +14 -4
  4. package/dist/components/Calendar/CalendarContext.d.ts +19 -2
  5. package/dist/components/Calendar/CalendarHeader.js +2 -1
  6. package/dist/components/Calendar/CalendarInputHeader.js +27 -27
  7. package/dist/components/Calendar/dateValue.d.ts +18 -0
  8. package/dist/components/Calendar/dateValue.js +7 -0
  9. package/dist/components/Calendar/useCalendarTime.d.ts +25 -0
  10. package/dist/components/Calendar/useCalendarTime.js +38 -0
  11. package/dist/components/DropdownMenu/DropdownMenuInput.d.ts +1 -1
  12. package/dist/components/InlineEdit/InlineEdit.d.ts +52 -0
  13. package/dist/components/InlineEdit/InlineEdit.js +223 -0
  14. package/dist/components/InlineEdit/InlineEditContext.d.ts +43 -0
  15. package/dist/components/InlineEdit/InlineEditContext.js +16 -0
  16. package/dist/components/InlineEdit/InlineEditControl.d.ts +14 -0
  17. package/dist/components/InlineEdit/InlineEditControl.js +72 -0
  18. package/dist/components/InlineEdit/InlineEditDate.d.ts +19 -0
  19. package/dist/components/InlineEdit/InlineEditDate.js +22 -0
  20. package/dist/components/InlineEdit/InlineEditDateTime.d.ts +21 -0
  21. package/dist/components/InlineEdit/InlineEditDateTime.js +23 -0
  22. package/dist/components/InlineEdit/InlineEditError.d.ts +6 -0
  23. package/dist/components/InlineEdit/InlineEditError.js +32 -0
  24. package/dist/components/InlineEdit/InlineEditInput.d.ts +4 -0
  25. package/dist/components/InlineEdit/InlineEditInput.js +18 -0
  26. package/dist/components/InlineEdit/InlineEditNumber.d.ts +11 -0
  27. package/dist/components/InlineEdit/InlineEditNumber.js +18 -0
  28. package/dist/components/InlineEdit/InlineEditPreview.d.ts +23 -0
  29. package/dist/components/InlineEdit/InlineEditPreview.js +105 -0
  30. package/dist/components/InlineEdit/InlineEditPreviewIcon.d.ts +12 -0
  31. package/dist/components/InlineEdit/InlineEditPreviewIcon.js +43 -0
  32. package/dist/components/InlineEdit/InlineEditPreviewValue.d.ts +11 -0
  33. package/dist/components/InlineEdit/InlineEditPreviewValue.js +25 -0
  34. package/dist/components/InlineEdit/InlineEditSelect.d.ts +39 -0
  35. package/dist/components/InlineEdit/InlineEditSelect.js +42 -0
  36. package/dist/components/InlineEdit/InlineEditTextarea.d.ts +4 -0
  37. package/dist/components/InlineEdit/InlineEditTextarea.js +18 -0
  38. package/dist/components/InlineEdit/InlineEditTime.d.ts +12 -0
  39. package/dist/components/InlineEdit/InlineEditTime.js +21 -0
  40. package/dist/components/InlineEdit/classes.d.ts +5 -0
  41. package/dist/components/InlineEdit/classes.js +35 -0
  42. package/dist/components/InlineEdit/dateValueCast.d.ts +25 -0
  43. package/dist/components/InlineEdit/dateValueCast.js +9 -0
  44. package/dist/components/InlineEdit/index.d.ts +15 -0
  45. package/dist/components/InlineEdit/index.js +16 -0
  46. package/dist/components/Input/Input.d.ts +1 -1
  47. package/dist/components/Input/Input.js +4 -3
  48. package/dist/components/Input/classes.d.ts +1 -0
  49. package/dist/components/Input/classes.js +8 -0
  50. package/dist/components/NumberInput/NumberInput.d.ts +4 -1
  51. package/dist/components/NumberInput/NumberInput.js +17 -7
  52. package/dist/components/NumberInput/classes.d.ts +12 -0
  53. package/dist/components/NumberInput/classes.js +51 -0
  54. package/dist/components/Select/SelectButton.d.ts +2 -1
  55. package/dist/components/Select/SelectButton.js +7 -2
  56. package/dist/components/Select/SelectInput/SelectInput.d.ts +1 -0
  57. package/dist/components/Select/SelectInput/SelectInput.js +21 -7
  58. package/dist/components/Select/SelectInput/SelectInputItemRenderer.d.ts +4 -0
  59. package/dist/components/Select/SelectInput/SelectInputItemRenderer.js +8 -3
  60. package/dist/components/Select/SelectInput/SelectInputOverflowRenderer.d.ts +1 -1
  61. package/dist/components/Select/SelectInput/SelectInputOverflowRenderer.js +28 -27
  62. package/dist/components/Slider/SliderInput.d.ts +1 -1
  63. package/dist/components/Textarea/classes.js +2 -2
  64. package/dist/components/Toast/Toast.js +6 -6
  65. package/dist/components/Toast/Toaster.js +1 -1
  66. package/dist/components/Tree/TreeContext.d.ts +1 -9
  67. package/dist/components/Tree/TreeContext.js +2 -18
  68. package/dist/components/Tree/TreeItemContent.js +5 -8
  69. package/dist/components/TreeView/TreeView.d.ts +19 -0
  70. package/dist/components/TreeView/TreeView.js +82 -0
  71. package/dist/components/TreeView/TreeViewContext.d.ts +12 -0
  72. package/dist/components/TreeView/TreeViewContext.js +9 -0
  73. package/dist/components/TreeView/TreeViewItem.d.ts +24 -0
  74. package/dist/components/TreeView/TreeViewItem.js +168 -0
  75. package/dist/components/TreeView/classes.d.ts +5 -0
  76. package/dist/components/TreeView/classes.js +30 -0
  77. package/dist/components/TreeView/index.d.ts +9 -0
  78. package/dist/components/TreeView/index.js +5 -0
  79. package/dist/index.d.ts +7 -5
  80. package/dist/index.js +4 -2
  81. package/dist/metadata/components.json +6122 -2828
  82. package/dist/theme/fonts/Geist-Variable.woff2 +0 -0
  83. package/dist/theme/fonts/GeistMono-Variable.woff2 +0 -0
  84. package/dist/theme/fonts/geist.css +21 -0
  85. package/dist/theme/index.css +1 -0
  86. package/dist/theme/typography.css +5 -3
  87. package/dist/utils/Collapse.d.ts +18 -0
  88. package/dist/utils/Collapse.js +15 -0
  89. package/dist/utils/treeDepth.d.ts +11 -0
  90. package/dist/utils/treeDepth.js +18 -0
  91. package/package.json +1 -3
@@ -0,0 +1,14 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ import { type InlineEditContextValue, type InlineEditSubmitMode } from './InlineEditContext';
3
+ export interface InlineEditControlProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
4
+ ref?: Ref<HTMLDivElement>;
5
+ /**
6
+ * Explicit render-time submit mode for this editor session. Highest
7
+ * precedence (beats editor registrations and the root prop) — the escape
8
+ * hatch for custom editors that cannot call useInlineEditSubmitMode.
9
+ */
10
+ submitMode?: InlineEditSubmitMode;
11
+ /** Function children receive the inline-edit context (render-prop). */
12
+ children?: ReactNode | ((ctx: InlineEditContextValue) => ReactNode);
13
+ }
14
+ export declare const InlineEditControl: FC<InlineEditControlProps>;
@@ -0,0 +1,72 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { useTestId } from "../../utils/testId.js";
5
+ import { useInlineEdit } from "./InlineEditContext.js";
6
+ const FOCUSABLE_SELECTOR = 'input, textarea, select, button, [tabindex]:not([tabindex="-1"])';
7
+ const InlineEditControl = ({ ref, children, className, onKeyDown, onBlur, submitMode: submitModeProp, ...props })=>{
8
+ const testId = useTestId('control');
9
+ const ctx = useInlineEdit();
10
+ const { editing, selectOnFocus, submit, cancel, isCommitPending } = ctx;
11
+ const submitMode = submitModeProp ?? ctx.submitMode;
12
+ const divRef = useRef(null);
13
+ const selectOnFocusRef = useRef(selectOnFocus);
14
+ selectOnFocusRef.current = selectOnFocus;
15
+ useEffect(()=>{
16
+ if (!editing) return;
17
+ const node = divRef.current;
18
+ if (!node) return;
19
+ const focusable = node.querySelector(FOCUSABLE_SELECTOR);
20
+ if (!focusable) return;
21
+ focusable.focus();
22
+ if (selectOnFocusRef.current && (focusable instanceof HTMLInputElement || focusable instanceof HTMLTextAreaElement)) focusable.select();
23
+ }, [
24
+ editing
25
+ ]);
26
+ const combinedRef = useCallback((node)=>{
27
+ divRef.current = node;
28
+ if ('function' == typeof ref) ref(node);
29
+ else if (ref) ref.current = node;
30
+ }, [
31
+ ref
32
+ ]);
33
+ if (!editing) return null;
34
+ const submitsOnEnter = 'enter' === submitMode || 'both' === submitMode;
35
+ const submitsOnBlur = 'blur' === submitMode || 'both' === submitMode;
36
+ const handleKeyDown = (event)=>{
37
+ onKeyDown?.(event);
38
+ if (event.defaultPrevented) return;
39
+ if ('Escape' === event.key) {
40
+ event.preventDefault();
41
+ cancel();
42
+ return;
43
+ }
44
+ if ('Enter' === event.key && submitsOnEnter) {
45
+ const target = event.target;
46
+ const isTextarea = 'TEXTAREA' === target.tagName;
47
+ if (isTextarea && !event.metaKey && !event.ctrlKey) return;
48
+ event.preventDefault();
49
+ submit();
50
+ }
51
+ };
52
+ const handleBlur = (event)=>{
53
+ onBlur?.(event);
54
+ if (event.defaultPrevented) return;
55
+ if (event.currentTarget.contains(event.relatedTarget)) return;
56
+ if (isCommitPending()) return;
57
+ if (submitsOnBlur) submit();
58
+ else if ('none' !== submitMode) cancel();
59
+ };
60
+ return /*#__PURE__*/ jsx("div", {
61
+ ...props,
62
+ ref: combinedRef,
63
+ "data-testid": testId,
64
+ "data-slot": "inline-edit-control",
65
+ onKeyDown: handleKeyDown,
66
+ onBlur: handleBlur,
67
+ className: cn('w-full min-w-0', '-ml-7', '[&_[data-slot=input]]:px-6', '[&_textarea]:px-6', '[&_[data-scope=number-input][data-part=input]]:px-6', '[&_[data-scope=select][data-part=trigger]]:px-6', '[&_div[data-scope=select][data-part=trigger]:has([data-slot=overflow-list])]:pl-0', '[&_[data-slot=input-group-addon]]:px-6', '[&_[data-slot=input-group]>div:not([data-slot])]:pl-0', className),
68
+ children: 'function' == typeof children ? children(ctx) : children
69
+ });
70
+ };
71
+ InlineEditControl.displayName = 'InlineEditControl';
72
+ export { InlineEditControl };
@@ -0,0 +1,19 @@
1
+ import type { FC, ReactNode } from 'react';
2
+ export interface InlineEditDateProps {
3
+ /**
4
+ * `InlineEditDate` IS the prewired `Calendar` root (day-only:
5
+ * `closeOnSelect`) — the `DateValue[]` adapter and commit-on-close live
6
+ * here. `children` are ordinary `Calendar` compound parts
7
+ * (`CalendarTrigger > DateInput`, `CalendarContent > CalendarBody >
8
+ * CalendarGrids`) — exactly as you'd compose a standalone `Calendar`. Use
9
+ * `InlineEditDateTime` for date+time. Wire your own `DateInput`'s
10
+ * `value`/`onChange` via `useInlineEdit()` plus the `toReactAriaDateValue`/
11
+ * `toCalendarDateValue` casts from `./dateValueCast` (the two
12
+ * `@internationalized/date` package copies aren't `instanceof`-compatible).
13
+ * `Calendar` does not re-provide its own testid cascade, so a `DateInput`
14
+ * you place inside already resolves `useTestId(...)` from the ambient
15
+ * `InlineEdit` root — no extra wiring needed.
16
+ */
17
+ children: ReactNode;
18
+ }
19
+ export declare const InlineEditDate: FC<InlineEditDateProps>;
@@ -0,0 +1,22 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Calendar } from "../Calendar/index.js";
3
+ import { useInlineEdit, useInlineEditSubmitMode } from "./InlineEditContext.js";
4
+ const InlineEditDate = ({ children })=>{
5
+ const { value, setValue, submit } = useInlineEdit();
6
+ useInlineEditSubmitMode('none');
7
+ return /*#__PURE__*/ jsx(Calendar, {
8
+ type: "single",
9
+ defaultOpen: true,
10
+ closeOnSelect: true,
11
+ value: value ? [
12
+ value
13
+ ] : [],
14
+ onChange: (next)=>setValue(next[0] ?? null),
15
+ onOpenChange: (open)=>{
16
+ if (!open) submit();
17
+ },
18
+ children: children
19
+ });
20
+ };
21
+ InlineEditDate.displayName = 'InlineEditDate';
22
+ export { InlineEditDate };
@@ -0,0 +1,21 @@
1
+ import type { FC, ReactNode } from 'react';
2
+ export interface InlineEditDateTimeProps {
3
+ /**
4
+ * `InlineEditDateTime` IS the prewired `Calendar` root (date+time:
5
+ * `showTime`, `closeOnSelect={false}`) — the `DateValue[]` adapter and
6
+ * commit-on-close live here. `children` are ordinary `Calendar` compound
7
+ * parts (`CalendarTrigger > DateInput`, `CalendarContent > CalendarBody >
8
+ * CalendarInputHeader > CalendarGrids`) — exactly as you'd compose a
9
+ * standalone `Calendar`. Use `InlineEditDate` for day-only. Wire your own
10
+ * `DateInput`'s `value`/`onChange` via `useInlineEdit()` plus the
11
+ * `toReactAriaDateValue`/`toCalendarDateValue`/`withMinuteGranularity`
12
+ * helpers from `./dateValueCast` (minute-granularity `DateInput` needs a
13
+ * promoted day-only value, and the two `@internationalized/date` package
14
+ * copies aren't `instanceof`-compatible). `Calendar` does not re-provide
15
+ * its own testid cascade, so a `DateInput` you place inside already
16
+ * resolves `useTestId(...)` from the ambient `InlineEdit` root — no extra
17
+ * wiring needed.
18
+ */
19
+ children: ReactNode;
20
+ }
21
+ export declare const InlineEditDateTime: FC<InlineEditDateTimeProps>;
@@ -0,0 +1,23 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Calendar } from "../Calendar/index.js";
3
+ import { useInlineEdit, useInlineEditSubmitMode } from "./InlineEditContext.js";
4
+ const InlineEditDateTime = ({ children })=>{
5
+ const { value, setValue, submit } = useInlineEdit();
6
+ useInlineEditSubmitMode('none');
7
+ return /*#__PURE__*/ jsx(Calendar, {
8
+ type: "single",
9
+ defaultOpen: true,
10
+ showTime: true,
11
+ closeOnSelect: false,
12
+ value: value ? [
13
+ value
14
+ ] : [],
15
+ onChange: (next)=>setValue(next[0] ?? null),
16
+ onOpenChange: (open)=>{
17
+ if (!open) submit();
18
+ },
19
+ children: children
20
+ });
21
+ };
22
+ InlineEditDateTime.displayName = 'InlineEditDateTime';
23
+ export { InlineEditDateTime };
@@ -0,0 +1,6 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface InlineEditErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ children?: ReactNode;
5
+ }
6
+ export declare const InlineEditError: FC<InlineEditErrorProps>;
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { OctagonAlert } from "../../icons/index.js";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { useTestId } from "../../utils/testId.js";
5
+ import { Text } from "../Text/index.js";
6
+ import { useInlineEdit } from "./InlineEditContext.js";
7
+ const InlineEditError = ({ ref, children, className, ...props })=>{
8
+ const testId = useTestId('error');
9
+ const { invalid, error } = useInlineEdit();
10
+ const message = children ?? error;
11
+ if (!invalid || !message) return null;
12
+ return /*#__PURE__*/ jsxs("div", {
13
+ ...props,
14
+ ref: ref,
15
+ "data-testid": testId,
16
+ "data-slot": "inline-edit-error",
17
+ className: cn('flex items-center gap-4 pt-4', className),
18
+ children: [
19
+ /*#__PURE__*/ jsx(OctagonAlert, {
20
+ size: "md",
21
+ className: "shrink-0 text-icon-danger"
22
+ }),
23
+ /*#__PURE__*/ jsx(Text, {
24
+ size: "sm",
25
+ color: "danger",
26
+ children: message
27
+ })
28
+ ]
29
+ });
30
+ };
31
+ InlineEditError.displayName = 'InlineEditError';
32
+ export { InlineEditError };
@@ -0,0 +1,4 @@
1
+ import type { FC } from 'react';
2
+ import { type InputProps } from '../Input';
3
+ export type InlineEditInputProps = Omit<InputProps, 'value' | 'onChange' | 'error'>;
4
+ export declare const InlineEditInput: FC<InlineEditInputProps>;
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTestId } from "../../utils/testId.js";
3
+ import { Input } from "../Input/index.js";
4
+ import { useInlineEdit } from "./InlineEditContext.js";
5
+ const InlineEditInput = ({ size = 'small', 'data-testid': testIdProp, ...props })=>{
6
+ const testId = useTestId('input', testIdProp);
7
+ const { value, setValue, invalid } = useInlineEdit();
8
+ return /*#__PURE__*/ jsx(Input, {
9
+ ...props,
10
+ "data-testid": testId,
11
+ value: value ?? '',
12
+ onChange: (event)=>setValue(event.target.value),
13
+ error: invalid,
14
+ size: size
15
+ });
16
+ };
17
+ InlineEditInput.displayName = 'InlineEditInput';
18
+ export { InlineEditInput };
@@ -0,0 +1,11 @@
1
+ import type { FC } from 'react';
2
+ import { type NumberInputProps } from '../NumberInput';
3
+ export type InlineEditNumberProps = Omit<NumberInputProps, 'value' | 'onValueChange' | 'error'>;
4
+ /**
5
+ * Analytics closed-target gap: `NumberInput` spreads consumer props onto its
6
+ * Ark `Root` wrapper rather than the inner `<input>`, so `data-analytics-*`
7
+ * land on the wrapper (click analytics still resolve via `closest()`, but the
8
+ * attribute is not on the real focusable node). The durable fix is to forward
9
+ * those attributes to the input inside `NumberInput`. See ./ANALYTICS_GAPS.md.
10
+ */
11
+ export declare const InlineEditNumber: FC<InlineEditNumberProps>;
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTestId } from "../../utils/testId.js";
3
+ import { NumberInput } from "../NumberInput/index.js";
4
+ import { useInlineEdit } from "./InlineEditContext.js";
5
+ const InlineEditNumber = ({ size = 'small', 'data-testid': testIdProp, ...props })=>{
6
+ const testId = useTestId('input', testIdProp);
7
+ const { value, setValue, invalid } = useInlineEdit();
8
+ return /*#__PURE__*/ jsx(NumberInput, {
9
+ ...props,
10
+ "data-testid": testId,
11
+ value: value ?? '',
12
+ onValueChange: (details)=>setValue(details.value),
13
+ error: invalid,
14
+ size: size
15
+ });
16
+ };
17
+ InlineEditNumber.displayName = 'InlineEditNumber';
18
+ export { InlineEditNumber };
@@ -0,0 +1,23 @@
1
+ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface InlineEditPreviewProps extends HTMLAttributes<HTMLDivElement> {
3
+ ref?: Ref<HTMLDivElement>;
4
+ /**
5
+ * Tooltip shown on hover/focus while the value is editable. Defaults to
6
+ * `'Edit'`. Pass `null` to disable the tooltip.
7
+ */
8
+ tooltip?: ReactNode;
9
+ /**
10
+ * Clamp the read-mode value to this many lines (with an ellipsis) instead of
11
+ * the default single-line truncation. Use for multi-line values (textarea).
12
+ * Applies to the default (no-parts) composition; when composing
13
+ * `InlineEditPreviewValue` explicitly, set `lineClamp` on it directly.
14
+ */
15
+ lineClamp?: 1 | 2 | 3 | 4 | 5 | 6;
16
+ /**
17
+ * Plain content auto-wraps into `InlineEditPreviewValue` + a default
18
+ * `InlineEditPreviewIcon` (pencil). Compose `InlineEditPreviewValue`/
19
+ * `InlineEditPreviewIcon` explicitly for a custom trailing icon.
20
+ */
21
+ children?: ReactNode;
22
+ }
23
+ export declare const InlineEditPreview: FC<InlineEditPreviewProps>;
@@ -0,0 +1,105 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement, useRef } from "react";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { useTestId } from "../../utils/testId.js";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
6
+ import { inlineEditPreviewVariants } from "./classes.js";
7
+ import { useInlineEdit } from "./InlineEditContext.js";
8
+ import { InlineEditPreviewIcon } from "./InlineEditPreviewIcon.js";
9
+ import { InlineEditPreviewValue } from "./InlineEditPreviewValue.js";
10
+ const isInlineEditPreviewValue = (child)=>/*#__PURE__*/ isValidElement(child) && child.type?.displayName === InlineEditPreviewValue.displayName;
11
+ const isInlineEditPreviewIcon = (child)=>/*#__PURE__*/ isValidElement(child) && child.type?.displayName === InlineEditPreviewIcon.displayName;
12
+ const InlineEditPreview = ({ ref, tooltip = 'Edit', lineClamp, children, className, onClick, onFocus, onKeyDown, onPointerMove, ...props })=>{
13
+ const testId = useTestId('preview');
14
+ const { editing, status, invalid, disabled, readOnly, activationMode, edit } = useInlineEdit();
15
+ const pointerRef = useRef({
16
+ x: 0,
17
+ y: 0
18
+ });
19
+ if (editing) return null;
20
+ const isLoading = 'loading' === status;
21
+ const activatable = !disabled && !readOnly && !isLoading && 'none' !== activationMode;
22
+ const childArray = Children.toArray(children);
23
+ const valueChild = childArray.find(isInlineEditPreviewValue);
24
+ const hasExplicitParts = childArray.some((child)=>isInlineEditPreviewValue(child) || isInlineEditPreviewIcon(child));
25
+ const effectiveLineClamp = hasExplicitParts ? valueChild?.props.lineClamp : lineClamp;
26
+ const handleClick = (event)=>{
27
+ onClick?.(event);
28
+ if (event.defaultPrevented) return;
29
+ if (activatable && 'click' === activationMode) edit();
30
+ };
31
+ const handleFocus = (event)=>{
32
+ onFocus?.(event);
33
+ if (event.defaultPrevented) return;
34
+ if (activatable && 'focus' === activationMode) edit();
35
+ };
36
+ const handleKeyDown = (event)=>{
37
+ onKeyDown?.(event);
38
+ if (event.defaultPrevented) return;
39
+ if (activatable && ('Enter' === event.key || ' ' === event.key)) {
40
+ event.preventDefault();
41
+ edit();
42
+ }
43
+ };
44
+ const handlePointerMove = (event)=>{
45
+ onPointerMove?.(event);
46
+ pointerRef.current = {
47
+ x: event.clientX,
48
+ y: event.clientY
49
+ };
50
+ };
51
+ const tooltipPositioning = {
52
+ placement: 'bottom-start',
53
+ offset: {
54
+ mainAxis: 24,
55
+ crossAxis: 0
56
+ },
57
+ getAnchorRect: ()=>({
58
+ x: pointerRef.current.x,
59
+ y: pointerRef.current.y,
60
+ width: 0,
61
+ height: 0
62
+ })
63
+ };
64
+ const target = /*#__PURE__*/ jsx("div", {
65
+ ...props,
66
+ ref: ref,
67
+ "data-testid": testId,
68
+ "data-slot": "inline-edit-preview",
69
+ role: activatable ? 'button' : void 0,
70
+ tabIndex: activatable ? 0 : void 0,
71
+ onClick: handleClick,
72
+ onFocus: handleFocus,
73
+ onKeyDown: handleKeyDown,
74
+ onPointerMove: handlePointerMove,
75
+ className: cn(inlineEditPreviewVariants({
76
+ multiline: Boolean(effectiveLineClamp),
77
+ activatable,
78
+ invalid: activatable && invalid
79
+ }), className),
80
+ children: hasExplicitParts ? children : /*#__PURE__*/ jsxs(Fragment, {
81
+ children: [
82
+ /*#__PURE__*/ jsx(InlineEditPreviewValue, {
83
+ lineClamp: lineClamp,
84
+ children: children
85
+ }),
86
+ /*#__PURE__*/ jsx(InlineEditPreviewIcon, {})
87
+ ]
88
+ })
89
+ });
90
+ if (activatable && null != tooltip) return /*#__PURE__*/ jsxs(Tooltip, {
91
+ positioning: tooltipPositioning,
92
+ children: [
93
+ /*#__PURE__*/ jsx(TooltipTrigger, {
94
+ asChild: true,
95
+ children: target
96
+ }),
97
+ /*#__PURE__*/ jsx(TooltipContent, {
98
+ children: tooltip
99
+ })
100
+ ]
101
+ });
102
+ return target;
103
+ };
104
+ InlineEditPreview.displayName = 'InlineEditPreview';
105
+ export { InlineEditPreview };
@@ -0,0 +1,12 @@
1
+ import type { FC, ReactNode, Ref } from 'react';
2
+ export interface InlineEditPreviewIconProps {
3
+ ref?: Ref<HTMLSpanElement>;
4
+ /** Idle-state icon shown on hover/focus while the value is editable. Defaults to a pencil. */
5
+ children?: ReactNode;
6
+ }
7
+ /**
8
+ * Trailing icon area of `InlineEditPreview`: while a commit is in flight it
9
+ * shows a spinner, right after a successful commit a success check, and
10
+ * otherwise the idle icon (only while the field is actually editable).
11
+ */
12
+ export declare const InlineEditPreviewIcon: FC<InlineEditPreviewIconProps>;
@@ -0,0 +1,43 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Check, Pencil } from "../../icons/index.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ import { Loader } from "../Loader/index.js";
5
+ import { useInlineEdit } from "./InlineEditContext.js";
6
+ const InlineEditPreviewIcon = ({ ref, children = /*#__PURE__*/ jsx(Pencil, {
7
+ size: "md"
8
+ }) })=>{
9
+ const testId = useTestId('preview-icon');
10
+ const { status, disabled, readOnly, activationMode } = useInlineEdit();
11
+ const isLoading = 'loading' === status;
12
+ const activatable = !disabled && !readOnly && !isLoading && 'none' !== activationMode;
13
+ if (isLoading) return /*#__PURE__*/ jsx("span", {
14
+ ref: ref,
15
+ "data-testid": testId,
16
+ "data-slot": "inline-edit-preview-icon",
17
+ className: "inline-flex items-center",
18
+ children: /*#__PURE__*/ jsx(Loader, {
19
+ type: "circle",
20
+ size: "md"
21
+ })
22
+ });
23
+ if ('saved' === status) return /*#__PURE__*/ jsx("span", {
24
+ ref: ref,
25
+ "data-testid": testId,
26
+ "data-slot": "inline-edit-preview-icon",
27
+ className: "inline-flex items-center",
28
+ children: /*#__PURE__*/ jsx(Check, {
29
+ size: "md",
30
+ className: "text-icon-success"
31
+ })
32
+ });
33
+ if (!activatable) return null;
34
+ return /*#__PURE__*/ jsx("span", {
35
+ ref: ref,
36
+ "data-testid": testId,
37
+ "data-slot": "inline-edit-preview-icon",
38
+ className: "inline-flex items-center text-icon-secondary opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100",
39
+ children: children
40
+ });
41
+ };
42
+ InlineEditPreviewIcon.displayName = 'InlineEditPreviewIcon';
43
+ export { InlineEditPreviewIcon };
@@ -0,0 +1,11 @@
1
+ import type { FC, ReactNode, Ref } from 'react';
2
+ export interface InlineEditPreviewValueProps {
3
+ ref?: Ref<HTMLSpanElement>;
4
+ /**
5
+ * Clamp the read-mode value to this many lines (with an ellipsis) instead of
6
+ * the default single-line truncation. Use for multi-line values (textarea).
7
+ */
8
+ lineClamp?: 1 | 2 | 3 | 4 | 5 | 6;
9
+ children?: ReactNode;
10
+ }
11
+ export declare const InlineEditPreviewValue: FC<InlineEditPreviewValueProps>;
@@ -0,0 +1,25 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ import { useInlineEdit } from "./InlineEditContext.js";
5
+ const LINE_CLAMP_CLASS = {
6
+ 1: 'line-clamp-1',
7
+ 2: 'line-clamp-2',
8
+ 3: 'line-clamp-3',
9
+ 4: 'line-clamp-4',
10
+ 5: 'line-clamp-5',
11
+ 6: 'line-clamp-6'
12
+ };
13
+ const InlineEditPreviewValue = ({ ref, lineClamp, children })=>{
14
+ const testId = useTestId('preview-value');
15
+ const { status } = useInlineEdit();
16
+ return /*#__PURE__*/ jsx("span", {
17
+ ref: ref,
18
+ "data-testid": testId,
19
+ "data-slot": "inline-edit-preview-value",
20
+ className: cn('min-w-0 flex-1', lineClamp ? LINE_CLAMP_CLASS[lineClamp] : 'truncate', 'loading' === status && 'opacity-50'),
21
+ children: children
22
+ });
23
+ };
24
+ InlineEditPreviewValue.displayName = 'InlineEditPreviewValue';
25
+ export { InlineEditPreviewValue };
@@ -0,0 +1,39 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ListCollection } from '@ark-ui/react/collection';
3
+ import { type SelectDataItem } from '../Select';
4
+ export interface InlineEditSelectProps {
5
+ /**
6
+ * Options; the collection is built internally (PaginationPageSize
7
+ * precedent). Provide exactly one of `items` / `collection`.
8
+ */
9
+ items?: SelectDataItem[];
10
+ /**
11
+ * A prebuilt collection (e.g. built once upstream, or with custom
12
+ * filtering/sorting). Provide exactly one of `items` / `collection` — a
13
+ * dev-only warning fires otherwise.
14
+ */
15
+ collection?: ListCollection<SelectDataItem>;
16
+ multiple?: boolean;
17
+ /**
18
+ * `InlineEditSelect` IS the prewired `Select` root — the draft binding,
19
+ * `defaultOpen`, and commit-on-close all live here. `children` are
20
+ * ordinary `Select` compound parts (`SelectButton`/`SelectInput`,
21
+ * `SelectPositioner > SelectContent > SelectOption…`) — exactly as you'd
22
+ * compose a standalone `Select`. `SelectButton` already cascades its own
23
+ * testid slot from this root with zero extra wiring (`{base}--button`).
24
+ * `SelectOption` cascades too, but nested one level further: `SelectContent`
25
+ * re-provides the cascade scoped to its own `content` slot (a `ScrollArea`
26
+ * implementation detail — see its own testid comment), so bare options
27
+ * resolve under `{base}--content--option` rather than `{base}--option`.
28
+ * That cascaded id also has no per-item disambiguation (every option in a
29
+ * collection resolves to the same testid) — pass a per-item `data-testid`
30
+ * override if a test needs to address one option by id rather than by text.
31
+ * `SelectInput` (the multiple-select trigger) does not cascade on its own —
32
+ * pass it `data-testid` explicitly if you need one.
33
+ */
34
+ children: ReactNode;
35
+ }
36
+ export declare const InlineEditSelect: {
37
+ ({ items, collection, multiple, children, }: InlineEditSelectProps): import("react/jsx-runtime").JSX.Element;
38
+ displayName: string;
39
+ };
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from "react";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ import { Select, createListCollection } from "../Select/index.js";
5
+ import { useInlineEdit, useInlineEditSubmitMode } from "./InlineEditContext.js";
6
+ const InlineEditSelect = ({ items, collection, multiple = false, children })=>{
7
+ const testId = useTestId();
8
+ const { value, setValue, submit } = useInlineEdit();
9
+ useInlineEditSubmitMode('none');
10
+ const collectionFromItems = useMemo(()=>createListCollection({
11
+ items: items ?? []
12
+ }), [
13
+ items
14
+ ]);
15
+ useEffect(()=>{
16
+ if ('production' === process.env.NODE_ENV) return;
17
+ const hasItems = void 0 !== items;
18
+ const hasCollection = void 0 !== collection;
19
+ if (hasItems === hasCollection) console.warn(`InlineEditSelect: provide exactly one of \`items\` or \`collection\` (got ${hasItems && hasCollection ? 'both' : 'neither'}).`);
20
+ }, [
21
+ items,
22
+ collection
23
+ ]);
24
+ const resolvedCollection = collection ?? collectionFromItems;
25
+ const selected = Array.isArray(value) ? value : 'string' == typeof value && '' !== value ? [
26
+ value
27
+ ] : [];
28
+ return /*#__PURE__*/ jsx(Select, {
29
+ "data-testid": testId,
30
+ defaultOpen: true,
31
+ collection: resolvedCollection,
32
+ multiple: multiple,
33
+ value: selected,
34
+ onValueChange: (details)=>setValue(details.value),
35
+ onOpenChange: (details)=>{
36
+ if (!details.open) submit();
37
+ },
38
+ children: children
39
+ });
40
+ };
41
+ InlineEditSelect.displayName = 'InlineEditSelect';
42
+ export { InlineEditSelect };
@@ -0,0 +1,4 @@
1
+ import type { FC } from 'react';
2
+ import { type TextareaProps } from '../Textarea';
3
+ export type InlineEditTextareaProps = Omit<TextareaProps, 'value' | 'onChange' | 'error'>;
4
+ export declare const InlineEditTextarea: FC<InlineEditTextareaProps>;
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTestId } from "../../utils/testId.js";
3
+ import { Textarea } from "../Textarea/index.js";
4
+ import { useInlineEdit } from "./InlineEditContext.js";
5
+ const InlineEditTextarea = ({ size = 'small', 'data-testid': testIdProp, ...props })=>{
6
+ const testId = useTestId('input', testIdProp);
7
+ const { value, setValue, invalid } = useInlineEdit();
8
+ return /*#__PURE__*/ jsx(Textarea, {
9
+ ...props,
10
+ "data-testid": testId,
11
+ value: value ?? '',
12
+ onChange: (event)=>setValue(event.target.value),
13
+ error: invalid,
14
+ size: size
15
+ });
16
+ };
17
+ InlineEditTextarea.displayName = 'InlineEditTextarea';
18
+ export { InlineEditTextarea };
@@ -0,0 +1,12 @@
1
+ import type { FC } from 'react';
2
+ import { type TimeInputProps } from '../TimeInput';
3
+ export type InlineEditTimeProps = Omit<TimeInputProps, 'value' | 'onChange'>;
4
+ /**
5
+ * Commits on blur — time pickers have no discrete "selection complete" event.
6
+ * Safe because the time dropdown is rendered inline (absolute, non-portaled)
7
+ * and its rows preventDefault() on mousedown, so focus never leaves the
8
+ * control subtree; a future portaled TimeDropdown would need re-evaluation.
9
+ * Clipping ancestors need `overflow-visible` (automatic inside
10
+ * AttributeValue; standalone hosts own it).
11
+ */
12
+ export declare const InlineEditTime: FC<InlineEditTimeProps>;
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTestId } from "../../utils/testId.js";
3
+ import { TimeInput } from "../TimeInput/index.js";
4
+ import { useInlineEdit, useInlineEditSubmitMode } from "./InlineEditContext.js";
5
+ const InlineEditTime = ({ 'data-testid': testIdProp, granularity = 'minute', showTimeDropdown = true, showIcon = false, size = 'small', ...props })=>{
6
+ const testId = useTestId('input', testIdProp);
7
+ const { value, setValue } = useInlineEdit();
8
+ useInlineEditSubmitMode('blur');
9
+ return /*#__PURE__*/ jsx(TimeInput, {
10
+ ...props,
11
+ "data-testid": testId,
12
+ granularity: granularity,
13
+ showTimeDropdown: showTimeDropdown,
14
+ showIcon: showIcon,
15
+ size: size,
16
+ value: value ?? null,
17
+ onChange: (v)=>setValue(v)
18
+ });
19
+ };
20
+ InlineEditTime.displayName = 'InlineEditTime';
21
+ export { InlineEditTime };
@@ -0,0 +1,5 @@
1
+ export declare const inlineEditPreviewVariants: (props?: ({
2
+ multiline?: boolean | null | undefined;
3
+ activatable?: boolean | null | undefined;
4
+ invalid?: boolean | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;