@uipath/apollo-wind 2.79.2 → 2.80.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 (36) hide show
  1. package/dist/components/ui/editable-text.cjs +251 -0
  2. package/dist/components/ui/editable-text.d.ts +48 -0
  3. package/dist/components/ui/editable-text.js +217 -0
  4. package/dist/components/ui/field-addons/field-menu.cjs +2 -1
  5. package/dist/components/ui/field-addons/field-menu.d.ts +6 -1
  6. package/dist/components/ui/field-addons/field-menu.js +2 -1
  7. package/dist/components/ui/field-addons/value-mode-switch-dialog.cjs +2 -1
  8. package/dist/components/ui/field-addons/value-mode-switch-dialog.d.ts +6 -1
  9. package/dist/components/ui/field-addons/value-mode-switch-dialog.js +2 -1
  10. package/dist/components/ui/index.cjs +214 -201
  11. package/dist/components/ui/index.d.ts +1 -0
  12. package/dist/components/ui/index.js +1 -0
  13. package/dist/components/ui/input-group.cjs +7 -3
  14. package/dist/components/ui/input-group.d.ts +1 -1
  15. package/dist/components/ui/input-group.js +7 -3
  16. package/dist/components/ui/quick-form-field/components/field-header.cjs +41 -38
  17. package/dist/components/ui/quick-form-field/components/field-header.d.ts +13 -4
  18. package/dist/components/ui/quick-form-field/components/field-header.js +38 -38
  19. package/dist/components/ui/quick-form-field/index.d.ts +1 -1
  20. package/dist/components/ui/quick-form-field/quick-form-field.cjs +134 -17
  21. package/dist/components/ui/quick-form-field/quick-form-field.d.ts +6 -3
  22. package/dist/components/ui/quick-form-field/quick-form-field.js +136 -19
  23. package/dist/components/ui/quick-form-field/types.cjs +8 -0
  24. package/dist/components/ui/quick-form-field/types.d.ts +81 -14
  25. package/dist/components/ui/quick-form-field/types.js +8 -0
  26. package/dist/components/ui/quick-form-field/utils.cjs +30 -1
  27. package/dist/components/ui/quick-form-field/utils.d.ts +8 -0
  28. package/dist/components/ui/quick-form-field/utils.js +27 -1
  29. package/dist/components/ui/variable-value-control.cjs +7 -4
  30. package/dist/components/ui/variable-value-control.d.ts +5 -1
  31. package/dist/components/ui/variable-value-control.js +7 -4
  32. package/dist/index.cjs +4 -0
  33. package/dist/index.d.ts +3 -1
  34. package/dist/index.js +2 -1
  35. package/dist/styles.css +69 -9
  36. package/package.json +1 -1
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ EditableText: ()=>EditableText
28
+ });
29
+ const jsx_runtime_namespaceObject = require("react/jsx-runtime");
30
+ const external_react_namespaceObject = require("react");
31
+ const external_form_field_cjs_namespaceObject = require("./form-field.cjs");
32
+ const external_tooltip_cjs_namespaceObject = require("./tooltip.cjs");
33
+ const index_cjs_namespaceObject = require("../../lib/index.cjs");
34
+ const useIsomorphicLayoutEffect = "u" < typeof window ? external_react_namespaceObject.useEffect : external_react_namespaceObject.useLayoutEffect;
35
+ const stripNewlines = (text)=>text.replace(/\n+/g, ' ');
36
+ const READ_CLASS = {
37
+ label: 'text-xs font-medium leading-4 text-foreground',
38
+ lg: 'text-base font-semibold leading-5 tracking-[-0.3px] text-foreground',
39
+ sm: 'text-xs leading-4 text-foreground-muted'
40
+ };
41
+ const EDIT_CLASS = {
42
+ label: 'text-xs font-medium leading-4',
43
+ lg: 'text-base font-semibold leading-5 tracking-[-0.3px]',
44
+ sm: 'text-xs leading-4'
45
+ };
46
+ const LINE_HEIGHT = {
47
+ label: 16,
48
+ lg: 20,
49
+ sm: 16
50
+ };
51
+ const VERTICAL_PADDING = 4;
52
+ const CLAMP_CLASS = 'line-clamp-[var(--editable-text-lines)] whitespace-pre-line wrap-break-word';
53
+ const INTERACTIVE_CLASS = 'rounded px-1.5 py-0.5 -mx-1.5';
54
+ const EDITOR_SPACING_CLASS = 'rounded px-1.5 py-0.5 -ml-1.5';
55
+ const READ_MAX_WIDTH_CLASS = 'max-w-[calc(100%+--spacing(3))]';
56
+ const ERROR_RING_CLASS = 'ring-1 ring-error';
57
+ function TruncationTooltip({ content, multiline, children }) {
58
+ const triggerRef = external_react_namespaceObject.useRef(null);
59
+ const setTrigger = external_react_namespaceObject.useCallback((node)=>{
60
+ triggerRef.current = node;
61
+ }, []);
62
+ const [open, setOpen] = external_react_namespaceObject.useState(false);
63
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_tooltip_cjs_namespaceObject.TooltipProvider, {
64
+ delayDuration: 700,
65
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_tooltip_cjs_namespaceObject.Tooltip, {
66
+ open: open,
67
+ onOpenChange: (next)=>{
68
+ const node = triggerRef.current;
69
+ setOpen(next && !!node && (node.scrollWidth > node.clientWidth || node.scrollHeight > node.clientHeight));
70
+ },
71
+ children: [
72
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_tooltip_cjs_namespaceObject.TooltipTrigger, {
73
+ asChild: true,
74
+ ref: setTrigger,
75
+ children: children
76
+ }),
77
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_tooltip_cjs_namespaceObject.TooltipPortal, {
78
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_tooltip_cjs_namespaceObject.TooltipContent, {
79
+ className: (0, index_cjs_namespaceObject.cn)('max-w-80 wrap-break-word', multiline && 'whitespace-pre-line'),
80
+ children: content
81
+ })
82
+ })
83
+ ]
84
+ })
85
+ });
86
+ }
87
+ function EditableText({ value, placeholder, size = 'lg', multiline, maxLines = 3, onChange, onSubmit, editing: editingProp, onEditingChange, disabled, truncationTooltip = true, error, 'aria-label': ariaLabel, className, 'data-testid': dataTestId }) {
88
+ const allowNewlines = true === multiline;
89
+ const editable = !!onChange && !!onSubmit;
90
+ const [editingState, setEditingState] = external_react_namespaceObject.useState(false);
91
+ const controlled = void 0 !== editingProp;
92
+ const requested = controlled ? editingProp : editingState;
93
+ const isEditing = requested && editable && !disabled;
94
+ const setEditing = external_react_namespaceObject.useCallback((next)=>{
95
+ if (!controlled) setEditingState(next);
96
+ onEditingChange?.(next);
97
+ }, [
98
+ controlled,
99
+ onEditingChange
100
+ ]);
101
+ const inputRef = external_react_namespaceObject.useRef(null);
102
+ const openedWith = external_react_namespaceObject.useRef(value);
103
+ const latestValue = external_react_namespaceObject.useRef(value);
104
+ latestValue.current = value;
105
+ const errorId = `editable-text-${external_react_namespaceObject.useId().replace(/:/g, '')}-error`;
106
+ const hasError = !!error;
107
+ const describedBy = hasError ? errorId : void 0;
108
+ useIsomorphicLayoutEffect(()=>{
109
+ if (disabled && requested) setEditing(false);
110
+ }, [
111
+ disabled,
112
+ requested,
113
+ setEditing
114
+ ]);
115
+ useIsomorphicLayoutEffect(()=>{
116
+ if (!isEditing) return;
117
+ openedWith.current = latestValue.current;
118
+ inputRef.current?.focus();
119
+ inputRef.current?.select();
120
+ }, [
121
+ isEditing
122
+ ]);
123
+ const cancel = external_react_namespaceObject.useCallback(()=>{
124
+ setEditing(false);
125
+ if (openedWith.current !== value) onChange?.(openedWith.current);
126
+ }, [
127
+ value,
128
+ onChange,
129
+ setEditing
130
+ ]);
131
+ const commit = external_react_namespaceObject.useCallback(()=>{
132
+ const next = value.trim();
133
+ setEditing(false);
134
+ if (next !== value) onChange?.(next);
135
+ if (next !== openedWith.current) onSubmit?.(next);
136
+ }, [
137
+ value,
138
+ onChange,
139
+ onSubmit,
140
+ setEditing
141
+ ]);
142
+ const handleKeyDown = external_react_namespaceObject.useCallback((e)=>{
143
+ if ('Enter' !== e.key || allowNewlines && e.shiftKey) {
144
+ if ('Escape' === e.key) {
145
+ e.preventDefault();
146
+ cancel();
147
+ }
148
+ } else {
149
+ e.preventDefault();
150
+ commit();
151
+ }
152
+ e.stopPropagation();
153
+ }, [
154
+ commit,
155
+ cancel,
156
+ allowNewlines
157
+ ]);
158
+ const editorMaxHeight = multiline ? {
159
+ maxHeight: maxLines * LINE_HEIGHT[size] + VERTICAL_PADDING
160
+ } : void 0;
161
+ const readClampStyle = multiline ? {
162
+ '--editable-text-lines': maxLines
163
+ } : void 0;
164
+ const readClampClass = multiline ? CLAMP_CLASS : 'block truncate';
165
+ const withTooltip = (trigger)=>truncationTooltip && (value || placeholder) ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(TruncationTooltip, {
166
+ content: value || placeholder,
167
+ multiline: !!multiline,
168
+ children: trigger
169
+ }) : trigger;
170
+ const message = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_form_field_cjs_namespaceObject.FormFieldError, {
171
+ id: errorId,
172
+ "data-slot": "editable-text-error",
173
+ className: "-ml-1 mt-1",
174
+ children: error
175
+ });
176
+ if (!editable) return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(jsx_runtime_namespaceObject.Fragment, {
177
+ children: [
178
+ withTooltip(/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
179
+ "data-slot": "editable-text",
180
+ "data-testid": dataTestId,
181
+ "aria-describedby": describedBy,
182
+ style: readClampStyle,
183
+ className: (0, index_cjs_namespaceObject.cn)(readClampClass, READ_CLASS[size], !value && 'text-foreground-subtle', hasError && (0, index_cjs_namespaceObject.cn)(INTERACTIVE_CLASS, ERROR_RING_CLASS), className),
184
+ children: value || placeholder
185
+ })),
186
+ message
187
+ ]
188
+ });
189
+ if (isEditing) {
190
+ const sharedProps = {
191
+ 'data-slot': 'editable-text-input',
192
+ 'data-testid': dataTestId ? `${dataTestId}-input` : void 0,
193
+ value,
194
+ placeholder,
195
+ 'aria-label': ariaLabel,
196
+ 'aria-describedby': describedBy,
197
+ 'aria-errormessage': hasError ? errorId : void 0,
198
+ 'aria-invalid': hasError || void 0,
199
+ onKeyDown: handleKeyDown,
200
+ onBlur: commit,
201
+ className: (0, index_cjs_namespaceObject.cn)('w-full min-w-0 border-none bg-surface-overlay text-foreground outline-none ring-1', hasError ? 'ring-error' : 'ring-brand', EDITOR_SPACING_CLASS, EDIT_CLASS[size], className)
202
+ };
203
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(jsx_runtime_namespaceObject.Fragment, {
204
+ children: [
205
+ multiline ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("textarea", {
206
+ ...sharedProps,
207
+ ref: inputRef,
208
+ rows: 1,
209
+ style: editorMaxHeight,
210
+ className: (0, index_cjs_namespaceObject.cn)(sharedProps.className, 'field-sizing-content max-w-full resize-none overflow-y-auto wrap-break-word'),
211
+ onChange: (e)=>onChange?.(allowNewlines ? e.target.value : stripNewlines(e.target.value))
212
+ }) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("input", {
213
+ ...sharedProps,
214
+ ref: inputRef,
215
+ autoComplete: "off",
216
+ onChange: (e)=>onChange?.(e.target.value)
217
+ }),
218
+ message
219
+ ]
220
+ });
221
+ }
222
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(jsx_runtime_namespaceObject.Fragment, {
223
+ children: [
224
+ withTooltip(/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("button", {
225
+ type: "button",
226
+ "data-slot": "editable-text",
227
+ "data-testid": dataTestId,
228
+ "aria-label": ariaLabel && `${ariaLabel}: ${value || placeholder || ''}`,
229
+ "aria-describedby": describedBy,
230
+ "aria-errormessage": hasError ? errorId : void 0,
231
+ "aria-invalid": hasError || void 0,
232
+ "aria-disabled": disabled || void 0,
233
+ onClick: ()=>{
234
+ if (disabled) return;
235
+ setEditing(true);
236
+ },
237
+ style: readClampStyle,
238
+ className: (0, index_cjs_namespaceObject.cn)('cursor-text border-none bg-transparent text-left transition hover:bg-surface-overlay', READ_MAX_WIDTH_CLASS, 'aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:hover:bg-transparent', readClampClass, INTERACTIVE_CLASS, READ_CLASS[size], !value && 'text-foreground-subtle', hasError && ERROR_RING_CLASS, className),
239
+ children: value || placeholder
240
+ })),
241
+ message
242
+ ]
243
+ });
244
+ }
245
+ exports.EditableText = __webpack_exports__.EditableText;
246
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
247
+ "EditableText"
248
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
249
+ Object.defineProperty(exports, '__esModule', {
250
+ value: true
251
+ });
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ /** `label` matches a field label; `sm` and `lg` a secondary line and a title. */
3
+ export type EditableTextSize = 'label' | 'sm' | 'lg';
4
+ export interface EditableTextProps {
5
+ /** The text on screen, in both modes: this is a controlled field, so an edit shows up only once `onChange` has been applied here. */
6
+ value: string;
7
+ placeholder?: string;
8
+ /** Defaults to `lg`. */
9
+ size?: EditableTextSize;
10
+ /**
11
+ * Text layout. `true` accepts newlines (Shift+Enter); `'wrap'` wraps across lines but keeps the
12
+ * value single-line, so every Enter commits and pasted newlines collapse. Enter always commits.
13
+ */
14
+ multiline?: boolean | 'wrap';
15
+ /** Visible-line ceiling when `multiline`. The editor scrolls past it. Defaults to 3. */
16
+ maxLines?: number;
17
+ /** Fires on every keystroke with the text as typed, like any controlled input. Pair with `onSubmit`: both are required to make the text editable, and `value` has to follow this callback or the field won't accept input. Escape reports the text held when the editor opened, so the owner reverts through this same path. */
18
+ onChange?: (next: string) => void;
19
+ /** Fires on Enter or blur with the trimmed `value`, and only when it differs from the text the editor opened with, so a click in and straight back out is silent. The owner decides whether to persist it. */
20
+ onSubmit?: (next: string) => void;
21
+ /**
22
+ * Whether the editor is open, for an owner that holds that state: it opens focused with the text
23
+ * selected, and asks to close through `onEditingChange`. Omit to let the component hold it.
24
+ */
25
+ editing?: boolean;
26
+ /**
27
+ * Called when the editor asks to open (a click) or close (commit, Escape, or going disabled).
28
+ * Required alongside `editing` for the editor to open or close at all.
29
+ */
30
+ onEditingChange?: (editing: boolean) => void;
31
+ /** Locks the text as-is: no edit mode, no commit. Ignored for static text. */
32
+ disabled?: boolean;
33
+ /** Field-specific feedback rendered immediately below the text. */
34
+ error?: React.ReactNode;
35
+ /** Show the full text in a tooltip on hover when it is cut off. On by default. */
36
+ truncationTooltip?: boolean;
37
+ /** Names the editor and its click target. Unused for static text. */
38
+ 'aria-label'?: string;
39
+ className?: string;
40
+ 'data-testid'?: string;
41
+ }
42
+ /**
43
+ * Click-to-edit text, such as a name or title: enters on click, submits on Enter or blur, reverts
44
+ * on Escape. Controlled: the owner holds the text and feeds it back through `value`, which lets it
45
+ * validate what is being typed and render the message through `error`. Renders static text unless
46
+ * both `onChange` and `onSubmit` are given. Text that is cut off shows in full in a tooltip.
47
+ */
48
+ export declare function EditableText({ value, placeholder, size, multiline, maxLines, onChange, onSubmit, editing: editingProp, onEditingChange, disabled, truncationTooltip, error, 'aria-label': ariaLabel, className, 'data-testid': dataTestId, }: EditableTextProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,217 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
3
+ import { FormFieldError } from "./form-field.js";
4
+ import { Tooltip, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger } from "./tooltip.js";
5
+ import { cn } from "../../lib/index.js";
6
+ const useIsomorphicLayoutEffect = "u" < typeof window ? useEffect : useLayoutEffect;
7
+ const stripNewlines = (text)=>text.replace(/\n+/g, ' ');
8
+ const READ_CLASS = {
9
+ label: 'text-xs font-medium leading-4 text-foreground',
10
+ lg: 'text-base font-semibold leading-5 tracking-[-0.3px] text-foreground',
11
+ sm: 'text-xs leading-4 text-foreground-muted'
12
+ };
13
+ const EDIT_CLASS = {
14
+ label: 'text-xs font-medium leading-4',
15
+ lg: 'text-base font-semibold leading-5 tracking-[-0.3px]',
16
+ sm: 'text-xs leading-4'
17
+ };
18
+ const LINE_HEIGHT = {
19
+ label: 16,
20
+ lg: 20,
21
+ sm: 16
22
+ };
23
+ const VERTICAL_PADDING = 4;
24
+ const CLAMP_CLASS = 'line-clamp-[var(--editable-text-lines)] whitespace-pre-line wrap-break-word';
25
+ const INTERACTIVE_CLASS = 'rounded px-1.5 py-0.5 -mx-1.5';
26
+ const EDITOR_SPACING_CLASS = 'rounded px-1.5 py-0.5 -ml-1.5';
27
+ const READ_MAX_WIDTH_CLASS = 'max-w-[calc(100%+--spacing(3))]';
28
+ const ERROR_RING_CLASS = 'ring-1 ring-error';
29
+ function TruncationTooltip({ content, multiline, children }) {
30
+ const triggerRef = useRef(null);
31
+ const setTrigger = useCallback((node)=>{
32
+ triggerRef.current = node;
33
+ }, []);
34
+ const [open, setOpen] = useState(false);
35
+ return /*#__PURE__*/ jsx(TooltipProvider, {
36
+ delayDuration: 700,
37
+ children: /*#__PURE__*/ jsxs(Tooltip, {
38
+ open: open,
39
+ onOpenChange: (next)=>{
40
+ const node = triggerRef.current;
41
+ setOpen(next && !!node && (node.scrollWidth > node.clientWidth || node.scrollHeight > node.clientHeight));
42
+ },
43
+ children: [
44
+ /*#__PURE__*/ jsx(TooltipTrigger, {
45
+ asChild: true,
46
+ ref: setTrigger,
47
+ children: children
48
+ }),
49
+ /*#__PURE__*/ jsx(TooltipPortal, {
50
+ children: /*#__PURE__*/ jsx(TooltipContent, {
51
+ className: cn('max-w-80 wrap-break-word', multiline && 'whitespace-pre-line'),
52
+ children: content
53
+ })
54
+ })
55
+ ]
56
+ })
57
+ });
58
+ }
59
+ function EditableText({ value, placeholder, size = 'lg', multiline, maxLines = 3, onChange, onSubmit, editing: editingProp, onEditingChange, disabled, truncationTooltip = true, error, 'aria-label': ariaLabel, className, 'data-testid': dataTestId }) {
60
+ const allowNewlines = true === multiline;
61
+ const editable = !!onChange && !!onSubmit;
62
+ const [editingState, setEditingState] = useState(false);
63
+ const controlled = void 0 !== editingProp;
64
+ const requested = controlled ? editingProp : editingState;
65
+ const isEditing = requested && editable && !disabled;
66
+ const setEditing = useCallback((next)=>{
67
+ if (!controlled) setEditingState(next);
68
+ onEditingChange?.(next);
69
+ }, [
70
+ controlled,
71
+ onEditingChange
72
+ ]);
73
+ const inputRef = useRef(null);
74
+ const openedWith = useRef(value);
75
+ const latestValue = useRef(value);
76
+ latestValue.current = value;
77
+ const errorId = `editable-text-${useId().replace(/:/g, '')}-error`;
78
+ const hasError = !!error;
79
+ const describedBy = hasError ? errorId : void 0;
80
+ useIsomorphicLayoutEffect(()=>{
81
+ if (disabled && requested) setEditing(false);
82
+ }, [
83
+ disabled,
84
+ requested,
85
+ setEditing
86
+ ]);
87
+ useIsomorphicLayoutEffect(()=>{
88
+ if (!isEditing) return;
89
+ openedWith.current = latestValue.current;
90
+ inputRef.current?.focus();
91
+ inputRef.current?.select();
92
+ }, [
93
+ isEditing
94
+ ]);
95
+ const cancel = useCallback(()=>{
96
+ setEditing(false);
97
+ if (openedWith.current !== value) onChange?.(openedWith.current);
98
+ }, [
99
+ value,
100
+ onChange,
101
+ setEditing
102
+ ]);
103
+ const commit = useCallback(()=>{
104
+ const next = value.trim();
105
+ setEditing(false);
106
+ if (next !== value) onChange?.(next);
107
+ if (next !== openedWith.current) onSubmit?.(next);
108
+ }, [
109
+ value,
110
+ onChange,
111
+ onSubmit,
112
+ setEditing
113
+ ]);
114
+ const handleKeyDown = useCallback((e)=>{
115
+ if ('Enter' !== e.key || allowNewlines && e.shiftKey) {
116
+ if ('Escape' === e.key) {
117
+ e.preventDefault();
118
+ cancel();
119
+ }
120
+ } else {
121
+ e.preventDefault();
122
+ commit();
123
+ }
124
+ e.stopPropagation();
125
+ }, [
126
+ commit,
127
+ cancel,
128
+ allowNewlines
129
+ ]);
130
+ const editorMaxHeight = multiline ? {
131
+ maxHeight: maxLines * LINE_HEIGHT[size] + VERTICAL_PADDING
132
+ } : void 0;
133
+ const readClampStyle = multiline ? {
134
+ '--editable-text-lines': maxLines
135
+ } : void 0;
136
+ const readClampClass = multiline ? CLAMP_CLASS : 'block truncate';
137
+ const withTooltip = (trigger)=>truncationTooltip && (value || placeholder) ? /*#__PURE__*/ jsx(TruncationTooltip, {
138
+ content: value || placeholder,
139
+ multiline: !!multiline,
140
+ children: trigger
141
+ }) : trigger;
142
+ const message = /*#__PURE__*/ jsx(FormFieldError, {
143
+ id: errorId,
144
+ "data-slot": "editable-text-error",
145
+ className: "-ml-1 mt-1",
146
+ children: error
147
+ });
148
+ if (!editable) return /*#__PURE__*/ jsxs(Fragment, {
149
+ children: [
150
+ withTooltip(/*#__PURE__*/ jsx("span", {
151
+ "data-slot": "editable-text",
152
+ "data-testid": dataTestId,
153
+ "aria-describedby": describedBy,
154
+ style: readClampStyle,
155
+ className: cn(readClampClass, READ_CLASS[size], !value && 'text-foreground-subtle', hasError && cn(INTERACTIVE_CLASS, ERROR_RING_CLASS), className),
156
+ children: value || placeholder
157
+ })),
158
+ message
159
+ ]
160
+ });
161
+ if (isEditing) {
162
+ const sharedProps = {
163
+ 'data-slot': 'editable-text-input',
164
+ 'data-testid': dataTestId ? `${dataTestId}-input` : void 0,
165
+ value,
166
+ placeholder,
167
+ 'aria-label': ariaLabel,
168
+ 'aria-describedby': describedBy,
169
+ 'aria-errormessage': hasError ? errorId : void 0,
170
+ 'aria-invalid': hasError || void 0,
171
+ onKeyDown: handleKeyDown,
172
+ onBlur: commit,
173
+ className: cn('w-full min-w-0 border-none bg-surface-overlay text-foreground outline-none ring-1', hasError ? 'ring-error' : 'ring-brand', EDITOR_SPACING_CLASS, EDIT_CLASS[size], className)
174
+ };
175
+ return /*#__PURE__*/ jsxs(Fragment, {
176
+ children: [
177
+ multiline ? /*#__PURE__*/ jsx("textarea", {
178
+ ...sharedProps,
179
+ ref: inputRef,
180
+ rows: 1,
181
+ style: editorMaxHeight,
182
+ className: cn(sharedProps.className, 'field-sizing-content max-w-full resize-none overflow-y-auto wrap-break-word'),
183
+ onChange: (e)=>onChange?.(allowNewlines ? e.target.value : stripNewlines(e.target.value))
184
+ }) : /*#__PURE__*/ jsx("input", {
185
+ ...sharedProps,
186
+ ref: inputRef,
187
+ autoComplete: "off",
188
+ onChange: (e)=>onChange?.(e.target.value)
189
+ }),
190
+ message
191
+ ]
192
+ });
193
+ }
194
+ return /*#__PURE__*/ jsxs(Fragment, {
195
+ children: [
196
+ withTooltip(/*#__PURE__*/ jsx("button", {
197
+ type: "button",
198
+ "data-slot": "editable-text",
199
+ "data-testid": dataTestId,
200
+ "aria-label": ariaLabel && `${ariaLabel}: ${value || placeholder || ''}`,
201
+ "aria-describedby": describedBy,
202
+ "aria-errormessage": hasError ? errorId : void 0,
203
+ "aria-invalid": hasError || void 0,
204
+ "aria-disabled": disabled || void 0,
205
+ onClick: ()=>{
206
+ if (disabled) return;
207
+ setEditing(true);
208
+ },
209
+ style: readClampStyle,
210
+ className: cn('cursor-text border-none bg-transparent text-left transition hover:bg-surface-overlay', READ_MAX_WIDTH_CLASS, 'aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:hover:bg-transparent', readClampClass, INTERACTIVE_CLASS, READ_CLASS[size], !value && 'text-foreground-subtle', hasError && ERROR_RING_CLASS, className),
211
+ children: value || placeholder
212
+ })),
213
+ message
214
+ ]
215
+ });
216
+ }
217
+ export { EditableText };
@@ -67,7 +67,7 @@ function ValueModeMenuItem({ icon: Icon, label, description, active, disabled, o
67
67
  ]
68
68
  });
69
69
  }
70
- function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings }) {
70
+ function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings, onCloseAutoFocus }) {
71
71
  const text = {
72
72
  ...external_value_mode_strings_cjs_namespaceObject.DEFAULT_VALUE_MODE_STRINGS,
73
73
  ...strings
@@ -103,6 +103,7 @@ function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesD
103
103
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_dropdown_menu_cjs_namespaceObject.DropdownMenuContent, {
104
104
  align: "end",
105
105
  className: "w-56",
106
+ onCloseAutoFocus: onCloseAutoFocus,
106
107
  children: [
107
108
  !modesDisabled && resolvedModes.map((item)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(ValueModeMenuItem, {
108
109
  icon: item.icon,
@@ -35,6 +35,11 @@ interface FieldMenuBaseProps<Id extends string> {
35
35
  triggerLabel?: string;
36
36
  /** Overrides for any subset of the English strings. */
37
37
  strings?: Partial<ValueModeStrings>;
38
+ /**
39
+ * Called as the menu closes, before it returns focus to its trigger. Call `preventDefault` to keep
40
+ * focus elsewhere, such as on the field's control after a mode is chosen.
41
+ */
42
+ onCloseAutoFocus?: (event: Event) => void;
38
43
  }
39
44
  /**
40
45
  * `modes` may be omitted when `Id` is the built-in modes, or one of them as `mode="literal"` infers,
@@ -68,5 +73,5 @@ export declare function ValueModeMenuItem({ icon: Icon, label, description, acti
68
73
  * shows the current mode's icon and is named by its title; with the modes hidden it is the field's
69
74
  * overflow menu.
70
75
  */
71
- export declare function FieldMenu<Id extends string = ValueMode>({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings, }: FieldMenuProps<Id>): import("react/jsx-runtime").JSX.Element;
76
+ export declare function FieldMenu<Id extends string = ValueMode>({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings, onCloseAutoFocus, }: FieldMenuProps<Id>): import("react/jsx-runtime").JSX.Element;
72
77
  export {};
@@ -38,7 +38,7 @@ function ValueModeMenuItem({ icon: Icon, label, description, active, disabled, o
38
38
  ]
39
39
  });
40
40
  }
41
- function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings }) {
41
+ function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesDisabled, disabled, expectedType, literalDescription, triggerLabel: triggerLabelOverride, strings, onCloseAutoFocus }) {
42
42
  const text = {
43
43
  ...DEFAULT_VALUE_MODE_STRINGS,
44
44
  ...strings
@@ -74,6 +74,7 @@ function FieldMenu({ mode, onSelect, modes, extraModes, actions, checked, modesD
74
74
  /*#__PURE__*/ jsxs(DropdownMenuContent, {
75
75
  align: "end",
76
76
  className: "w-56",
77
+ onCloseAutoFocus: onCloseAutoFocus,
77
78
  children: [
78
79
  !modesDisabled && resolvedModes.map((item)=>/*#__PURE__*/ jsx(ValueModeMenuItem, {
79
80
  icon: item.icon,
@@ -29,7 +29,7 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
30
30
  const external_alert_dialog_cjs_namespaceObject = require("../alert-dialog.cjs");
31
31
  const external_value_mode_strings_cjs_namespaceObject = require("./value-mode-strings.cjs");
32
- function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings }) {
32
+ function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings, onCloseAutoFocus }) {
33
33
  const text = {
34
34
  ...external_value_mode_strings_cjs_namespaceObject.DEFAULT_VALUE_MODE_STRINGS,
35
35
  ...strings
@@ -38,6 +38,7 @@ function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description,
38
38
  open: open,
39
39
  onOpenChange: (next)=>!next && onCancel(),
40
40
  children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_alert_dialog_cjs_namespaceObject.AlertDialogContent, {
41
+ onCloseAutoFocus: onCloseAutoFocus,
41
42
  children: [
42
43
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_alert_dialog_cjs_namespaceObject.AlertDialogHeader, {
43
44
  children: [
@@ -11,6 +11,11 @@ export interface ValueModeSwitchDialogProps {
11
11
  confirmLabel?: string;
12
12
  /** Overrides for any subset of the English strings. */
13
13
  strings?: Partial<ValueModeStrings>;
14
+ /**
15
+ * Called as the dialog closes, before it returns focus to where it opened from. Call
16
+ * `preventDefault` to keep focus elsewhere, such as on the control of the mode just switched to.
17
+ */
18
+ onCloseAutoFocus?: (event: Event) => void;
14
19
  }
15
20
  /** Confirms a mode switch that would lose the field's current value. */
16
- export declare function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings, }: ValueModeSwitchDialogProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings, onCloseAutoFocus, }: ValueModeSwitchDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "../alert-dialog.js";
3
3
  import { DEFAULT_VALUE_MODE_STRINGS } from "./value-mode-strings.js";
4
- function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings }) {
4
+ function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description, confirmLabel, strings, onCloseAutoFocus }) {
5
5
  const text = {
6
6
  ...DEFAULT_VALUE_MODE_STRINGS,
7
7
  ...strings
@@ -10,6 +10,7 @@ function ValueModeSwitchDialog({ open, onConfirm, onCancel, title, description,
10
10
  open: open,
11
11
  onOpenChange: (next)=>!next && onCancel(),
12
12
  children: /*#__PURE__*/ jsxs(AlertDialogContent, {
13
+ onCloseAutoFocus: onCloseAutoFocus,
13
14
  children: [
14
15
  /*#__PURE__*/ jsxs(AlertDialogHeader, {
15
16
  children: [