@alpic-ai/ui 1.170.0 → 1.172.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.
- package/dist/components/area-chart.d.mts +3 -3
- package/dist/components/avatar.d.mts +1 -1
- package/dist/components/badge.d.mts +1 -1
- package/dist/components/bar-chart.d.mts +2 -2
- package/dist/components/bar-chart.mjs +1 -1
- package/dist/components/bar-list.d.mts +2 -2
- package/dist/components/button.d.mts +1 -1
- package/dist/components/chart-card.d.mts +2 -2
- package/dist/components/chart-legend.d.mts +3 -3
- package/dist/components/chart-primitives.d.mts +9 -10
- package/dist/components/chart-tooltip.d.mts +3 -3
- package/dist/components/donut-chart.d.mts +2 -2
- package/dist/components/editable.d.mts +101 -0
- package/dist/components/editable.mjs +509 -0
- package/dist/components/form.d.mts +10 -8
- package/dist/components/form.mjs +18 -8
- package/dist/components/heatmap-chart.d.mts +2 -2
- package/dist/components/input.d.mts +3 -1
- package/dist/components/input.mjs +5 -3
- package/dist/components/line-chart.d.mts +2 -2
- package/dist/components/select-trigger-variants.d.mts +2 -3
- package/dist/components/shimmer-text.d.mts +2 -3
- package/dist/components/skeleton.d.mts +1 -1
- package/dist/components/spinner.d.mts +2 -2
- package/dist/components/stat.d.mts +3 -3
- package/dist/components/typography.d.mts +5 -6
- package/dist/components/visually-hidden-input.d.mts +12 -0
- package/dist/components/visually-hidden-input.mjs +109 -0
- package/dist/hooks/use-as-ref.d.mts +5 -0
- package/dist/hooks/use-as-ref.mjs +12 -0
- package/dist/hooks/use-chart-theme.d.mts +3 -4
- package/dist/hooks/use-copy-to-clipboard.d.mts +2 -3
- package/dist/hooks/use-isomorphic-layout-effect.d.mts +5 -0
- package/dist/hooks/use-isomorphic-layout-effect.mjs +5 -0
- package/dist/hooks/use-lazy-ref.d.mts +6 -0
- package/dist/hooks/use-lazy-ref.mjs +9 -0
- package/dist/hooks/use-mobile.d.mts +2 -3
- package/dist/lib/chart-palette.d.mts +13 -14
- package/dist/lib/chart.d.mts +3 -4
- package/dist/lib/cn.d.mts +2 -3
- package/dist/lib/compose-refs.mjs +23 -0
- package/package.json +9 -13
- package/src/components/bar-chart.tsx +1 -1
- package/src/components/editable.tsx +871 -0
- package/src/components/form.tsx +16 -6
- package/src/components/input.tsx +8 -0
- package/src/components/visually-hidden-input.tsx +148 -0
- package/src/hooks/use-as-ref.ts +15 -0
- package/src/hooks/use-isomorphic-layout-effect.ts +5 -0
- package/src/hooks/use-lazy-ref.ts +13 -0
- package/src/lib/compose-refs.ts +38 -0
- package/src/stories/input.stories.tsx +3 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.mjs";
|
|
3
|
+
import { useIsomorphicLayoutEffect } from "../hooks/use-isomorphic-layout-effect.mjs";
|
|
4
|
+
import { useAsRef } from "../hooks/use-as-ref.mjs";
|
|
5
|
+
import { useLazyRef } from "../hooks/use-lazy-ref.mjs";
|
|
6
|
+
import { useComposedRefs } from "../lib/compose-refs.mjs";
|
|
7
|
+
import { VisuallyHiddenInput } from "./visually-hidden-input.mjs";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import * as React$1 from "react";
|
|
10
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
11
|
+
//#region src/components/editable.tsx
|
|
12
|
+
const ROOT_NAME = "Editable";
|
|
13
|
+
const LABEL_NAME = "EditableLabel";
|
|
14
|
+
const AREA_NAME = "EditableArea";
|
|
15
|
+
const PREVIEW_NAME = "EditablePreview";
|
|
16
|
+
const INPUT_NAME = "EditableInput";
|
|
17
|
+
const TRIGGER_NAME = "EditableTrigger";
|
|
18
|
+
const TOOLBAR_NAME = "EditableToolbar";
|
|
19
|
+
const CANCEL_NAME = "EditableCancel";
|
|
20
|
+
const SUBMIT_NAME = "EditableSubmit";
|
|
21
|
+
const StoreContext = React$1.createContext(null);
|
|
22
|
+
function useStoreContext(consumerName) {
|
|
23
|
+
const context = React$1.useContext(StoreContext);
|
|
24
|
+
if (!context) throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
27
|
+
function useStore(selector, ogStore) {
|
|
28
|
+
const contextStore = React$1.useContext(StoreContext);
|
|
29
|
+
const store = ogStore ?? contextStore;
|
|
30
|
+
if (!store) throw new Error(`\`useStore\` must be used within \`${ROOT_NAME}\``);
|
|
31
|
+
const getSnapshot = React$1.useCallback(() => selector(store.getState()), [store, selector]);
|
|
32
|
+
return React$1.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
33
|
+
}
|
|
34
|
+
const EditableContext = React$1.createContext(null);
|
|
35
|
+
function useEditableContext(consumerName) {
|
|
36
|
+
const context = React$1.useContext(EditableContext);
|
|
37
|
+
if (!context) throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
|
|
38
|
+
return context;
|
|
39
|
+
}
|
|
40
|
+
function Editable(props) {
|
|
41
|
+
const { value: valueProp, defaultValue = "", defaultEditing, editing: editingProp, onValueChange, onEditingChange, onCancel: onCancelProp, onEdit: onEditProp, onSubmit: onSubmitProp, onEscapeKeyDown, onEnterKeyDown, dir: dirProp, maxLength, name, placeholder, triggerMode = "click", asChild, autosize = false, multiline = false, disabled, required, readOnly, invalid, className, id, ref, ...rootProps } = props;
|
|
42
|
+
const instanceId = React$1.useId();
|
|
43
|
+
const rootId = id ?? instanceId;
|
|
44
|
+
const inputId = React$1.useId();
|
|
45
|
+
const labelId = React$1.useId();
|
|
46
|
+
const dir = dirProp ?? "ltr";
|
|
47
|
+
const previousValueRef = React$1.useRef(defaultValue);
|
|
48
|
+
const [formTrigger, setFormTrigger] = React$1.useState(null);
|
|
49
|
+
const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));
|
|
50
|
+
const isFormControl = formTrigger ? !!formTrigger.closest("form") : true;
|
|
51
|
+
const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set());
|
|
52
|
+
const stateRef = useLazyRef(() => ({
|
|
53
|
+
value: valueProp ?? defaultValue,
|
|
54
|
+
editing: editingProp ?? defaultEditing ?? false
|
|
55
|
+
}));
|
|
56
|
+
const propsRef = useAsRef({
|
|
57
|
+
onValueChange,
|
|
58
|
+
onEditingChange,
|
|
59
|
+
onCancel: onCancelProp,
|
|
60
|
+
onEdit: onEditProp,
|
|
61
|
+
onSubmit: onSubmitProp,
|
|
62
|
+
onEscapeKeyDown,
|
|
63
|
+
onEnterKeyDown
|
|
64
|
+
});
|
|
65
|
+
const store = React$1.useMemo(() => {
|
|
66
|
+
return {
|
|
67
|
+
subscribe: (cb) => {
|
|
68
|
+
listenersRef.current.add(cb);
|
|
69
|
+
return () => listenersRef.current.delete(cb);
|
|
70
|
+
},
|
|
71
|
+
getState: () => stateRef.current,
|
|
72
|
+
setState: (key, value) => {
|
|
73
|
+
if (Object.is(stateRef.current[key], value)) return;
|
|
74
|
+
if (key === "value" && typeof value === "string") {
|
|
75
|
+
stateRef.current.value = value;
|
|
76
|
+
propsRef.current.onValueChange?.(value);
|
|
77
|
+
} else if (key === "editing" && typeof value === "boolean") {
|
|
78
|
+
stateRef.current.editing = value;
|
|
79
|
+
propsRef.current.onEditingChange?.(value);
|
|
80
|
+
} else stateRef.current[key] = value;
|
|
81
|
+
store.notify();
|
|
82
|
+
},
|
|
83
|
+
notify: () => {
|
|
84
|
+
for (const cb of listenersRef.current) cb();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}, [
|
|
88
|
+
listenersRef,
|
|
89
|
+
stateRef,
|
|
90
|
+
propsRef
|
|
91
|
+
]);
|
|
92
|
+
const value = useStore((state) => state.value, store);
|
|
93
|
+
useIsomorphicLayoutEffect(() => {
|
|
94
|
+
if (valueProp !== void 0) store.setState("value", valueProp);
|
|
95
|
+
}, [valueProp]);
|
|
96
|
+
useIsomorphicLayoutEffect(() => {
|
|
97
|
+
if (editingProp !== void 0) store.setState("editing", editingProp);
|
|
98
|
+
}, [editingProp]);
|
|
99
|
+
const onCancel = React$1.useCallback(() => {
|
|
100
|
+
const prevValue = previousValueRef.current;
|
|
101
|
+
store.setState("value", prevValue);
|
|
102
|
+
store.setState("editing", false);
|
|
103
|
+
propsRef.current.onCancel?.();
|
|
104
|
+
}, [store, propsRef]);
|
|
105
|
+
const onEdit = React$1.useCallback(() => {
|
|
106
|
+
const currentValue = store.getState().value;
|
|
107
|
+
previousValueRef.current = currentValue;
|
|
108
|
+
store.setState("editing", true);
|
|
109
|
+
propsRef.current.onEdit?.();
|
|
110
|
+
}, [store, propsRef]);
|
|
111
|
+
const onSubmit = React$1.useCallback((newValue) => {
|
|
112
|
+
store.setState("value", newValue);
|
|
113
|
+
store.setState("editing", false);
|
|
114
|
+
propsRef.current.onSubmit?.(newValue);
|
|
115
|
+
}, [store, propsRef]);
|
|
116
|
+
const contextValue = React$1.useMemo(() => ({
|
|
117
|
+
rootId,
|
|
118
|
+
inputId,
|
|
119
|
+
labelId,
|
|
120
|
+
defaultValue,
|
|
121
|
+
onSubmit,
|
|
122
|
+
onEdit,
|
|
123
|
+
onCancel,
|
|
124
|
+
onEscapeKeyDown,
|
|
125
|
+
onEnterKeyDown,
|
|
126
|
+
dir,
|
|
127
|
+
maxLength,
|
|
128
|
+
placeholder,
|
|
129
|
+
triggerMode,
|
|
130
|
+
autosize,
|
|
131
|
+
multiline,
|
|
132
|
+
disabled,
|
|
133
|
+
readOnly,
|
|
134
|
+
required,
|
|
135
|
+
invalid
|
|
136
|
+
}), [
|
|
137
|
+
rootId,
|
|
138
|
+
inputId,
|
|
139
|
+
labelId,
|
|
140
|
+
defaultValue,
|
|
141
|
+
onSubmit,
|
|
142
|
+
onCancel,
|
|
143
|
+
onEdit,
|
|
144
|
+
onEscapeKeyDown,
|
|
145
|
+
onEnterKeyDown,
|
|
146
|
+
dir,
|
|
147
|
+
maxLength,
|
|
148
|
+
placeholder,
|
|
149
|
+
triggerMode,
|
|
150
|
+
autosize,
|
|
151
|
+
multiline,
|
|
152
|
+
disabled,
|
|
153
|
+
required,
|
|
154
|
+
readOnly,
|
|
155
|
+
invalid
|
|
156
|
+
]);
|
|
157
|
+
const RootPrimitive = asChild ? Slot : "div";
|
|
158
|
+
return /* @__PURE__ */ jsx(StoreContext.Provider, {
|
|
159
|
+
value: store,
|
|
160
|
+
children: /* @__PURE__ */ jsxs(EditableContext.Provider, {
|
|
161
|
+
value: contextValue,
|
|
162
|
+
children: [/* @__PURE__ */ jsx(RootPrimitive, {
|
|
163
|
+
"data-slot": "editable",
|
|
164
|
+
...rootProps,
|
|
165
|
+
id,
|
|
166
|
+
ref: composedRef,
|
|
167
|
+
className: cn("flex min-w-0 flex-col gap-2", className)
|
|
168
|
+
}), isFormControl && /* @__PURE__ */ jsx(VisuallyHiddenInput, {
|
|
169
|
+
type: "hidden",
|
|
170
|
+
control: formTrigger,
|
|
171
|
+
name,
|
|
172
|
+
value,
|
|
173
|
+
disabled,
|
|
174
|
+
readOnly,
|
|
175
|
+
required
|
|
176
|
+
})]
|
|
177
|
+
})
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function EditableLabel(props) {
|
|
181
|
+
const { asChild, className, children, ref, ...labelProps } = props;
|
|
182
|
+
const context = useEditableContext(LABEL_NAME);
|
|
183
|
+
return /* @__PURE__ */ jsx(asChild ? Slot : "label", {
|
|
184
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
185
|
+
"data-invalid": context.invalid ? "" : void 0,
|
|
186
|
+
"data-required": context.required ? "" : void 0,
|
|
187
|
+
"data-slot": "editable-label",
|
|
188
|
+
...labelProps,
|
|
189
|
+
ref,
|
|
190
|
+
id: context.labelId,
|
|
191
|
+
htmlFor: context.inputId,
|
|
192
|
+
className: cn("text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70 data-required:after:ml-0.5 data-required:after:text-destructive data-required:after:content-['*']", className),
|
|
193
|
+
children
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function EditableArea(props) {
|
|
197
|
+
const { asChild, className, ref, ...areaProps } = props;
|
|
198
|
+
const context = useEditableContext(AREA_NAME);
|
|
199
|
+
const editing = useStore((state) => state.editing);
|
|
200
|
+
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
201
|
+
role: "group",
|
|
202
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
203
|
+
"data-editing": editing ? "" : void 0,
|
|
204
|
+
"data-slot": "editable-area",
|
|
205
|
+
dir: context.dir,
|
|
206
|
+
...areaProps,
|
|
207
|
+
ref,
|
|
208
|
+
className: cn("relative inline-block min-w-0 data-disabled:cursor-not-allowed data-disabled:opacity-50", className)
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function EditablePreview(props) {
|
|
212
|
+
const { onClick: onClickProp, onDoubleClick: onDoubleClickProp, onFocus: onFocusProp, onKeyDown: onKeyDownProp, asChild, className, ref, ...previewProps } = props;
|
|
213
|
+
const context = useEditableContext(PREVIEW_NAME);
|
|
214
|
+
const value = useStore((state) => state.value);
|
|
215
|
+
const editing = useStore((state) => state.editing);
|
|
216
|
+
const propsRef = useAsRef({
|
|
217
|
+
onClick: onClickProp,
|
|
218
|
+
onDoubleClick: onDoubleClickProp,
|
|
219
|
+
onFocus: onFocusProp,
|
|
220
|
+
onKeyDown: onKeyDownProp
|
|
221
|
+
});
|
|
222
|
+
const onTrigger = React$1.useCallback(() => {
|
|
223
|
+
if (context.disabled || context.readOnly) return;
|
|
224
|
+
context.onEdit();
|
|
225
|
+
}, [
|
|
226
|
+
context.onEdit,
|
|
227
|
+
context.disabled,
|
|
228
|
+
context.readOnly
|
|
229
|
+
]);
|
|
230
|
+
const onClick = React$1.useCallback((event) => {
|
|
231
|
+
propsRef.current.onClick?.(event);
|
|
232
|
+
if (event.defaultPrevented || context.triggerMode !== "click") return;
|
|
233
|
+
onTrigger();
|
|
234
|
+
}, [
|
|
235
|
+
propsRef,
|
|
236
|
+
onTrigger,
|
|
237
|
+
context.triggerMode
|
|
238
|
+
]);
|
|
239
|
+
const onDoubleClick = React$1.useCallback((event) => {
|
|
240
|
+
propsRef.current.onDoubleClick?.(event);
|
|
241
|
+
if (event.defaultPrevented || context.triggerMode !== "dblclick") return;
|
|
242
|
+
onTrigger();
|
|
243
|
+
}, [
|
|
244
|
+
propsRef,
|
|
245
|
+
onTrigger,
|
|
246
|
+
context.triggerMode
|
|
247
|
+
]);
|
|
248
|
+
const onFocus = React$1.useCallback((event) => {
|
|
249
|
+
propsRef.current.onFocus?.(event);
|
|
250
|
+
if (event.defaultPrevented || context.triggerMode !== "focus") return;
|
|
251
|
+
onTrigger();
|
|
252
|
+
}, [
|
|
253
|
+
propsRef,
|
|
254
|
+
onTrigger,
|
|
255
|
+
context.triggerMode
|
|
256
|
+
]);
|
|
257
|
+
const onKeyDown = React$1.useCallback((event) => {
|
|
258
|
+
propsRef.current.onKeyDown?.(event);
|
|
259
|
+
if (event.defaultPrevented) return;
|
|
260
|
+
if (event.key === "Enter") {
|
|
261
|
+
const nativeEvent = event.nativeEvent;
|
|
262
|
+
if (context.onEnterKeyDown) {
|
|
263
|
+
context.onEnterKeyDown(nativeEvent);
|
|
264
|
+
if (nativeEvent.defaultPrevented) return;
|
|
265
|
+
}
|
|
266
|
+
onTrigger();
|
|
267
|
+
}
|
|
268
|
+
}, [
|
|
269
|
+
propsRef,
|
|
270
|
+
onTrigger,
|
|
271
|
+
context.onEnterKeyDown
|
|
272
|
+
]);
|
|
273
|
+
const PreviewPrimitive = asChild ? Slot : "div";
|
|
274
|
+
if (editing || context.readOnly) return null;
|
|
275
|
+
return /* @__PURE__ */ jsx(PreviewPrimitive, {
|
|
276
|
+
role: "button",
|
|
277
|
+
"aria-disabled": context.disabled || context.readOnly,
|
|
278
|
+
"data-empty": !value ? "" : void 0,
|
|
279
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
280
|
+
"data-readonly": context.readOnly ? "" : void 0,
|
|
281
|
+
"data-slot": "editable-preview",
|
|
282
|
+
tabIndex: context.disabled || context.readOnly ? void 0 : 0,
|
|
283
|
+
...previewProps,
|
|
284
|
+
ref,
|
|
285
|
+
onClick,
|
|
286
|
+
onDoubleClick,
|
|
287
|
+
onFocus,
|
|
288
|
+
onKeyDown,
|
|
289
|
+
className: cn("cursor-text whitespace-pre-wrap rounded-sm border border-transparent px-1.5 py-1 transition-colors hover:bg-muted focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden data-disabled:cursor-not-allowed data-disabled:opacity-50 data-empty:text-muted-foreground/60 data-empty:italic data-readonly:cursor-default data-readonly:hover:bg-transparent", className),
|
|
290
|
+
children: value || context.placeholder
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function EditableInput(props) {
|
|
294
|
+
const { onBlur: onBlurProp, onChange: onChangeProp, onKeyDown: onKeyDownProp, asChild, className, disabled, readOnly, required, maxLength, ref, ...inputProps } = props;
|
|
295
|
+
const context = useEditableContext(INPUT_NAME);
|
|
296
|
+
const store = useStoreContext(INPUT_NAME);
|
|
297
|
+
const value = useStore((state) => state.value);
|
|
298
|
+
const editing = useStore((state) => state.editing);
|
|
299
|
+
const inputRef = React$1.useRef(null);
|
|
300
|
+
const composedRef = useComposedRefs(ref, inputRef);
|
|
301
|
+
const propsRef = useAsRef({
|
|
302
|
+
onBlur: onBlurProp,
|
|
303
|
+
onChange: onChangeProp,
|
|
304
|
+
onKeyDown: onKeyDownProp
|
|
305
|
+
});
|
|
306
|
+
const isDisabled = disabled || context.disabled;
|
|
307
|
+
const isReadOnly = readOnly || context.readOnly;
|
|
308
|
+
const isRequired = required || context.required;
|
|
309
|
+
const onAutosize = React$1.useCallback((target) => {
|
|
310
|
+
if (!context.autosize) return;
|
|
311
|
+
const { borderTopWidth, borderBottomWidth, borderLeftWidth, borderRightWidth } = getComputedStyle(target);
|
|
312
|
+
if (target instanceof HTMLTextAreaElement) {
|
|
313
|
+
target.style.height = "0";
|
|
314
|
+
target.style.height = `${target.scrollHeight + Number.parseFloat(borderTopWidth) + Number.parseFloat(borderBottomWidth)}px`;
|
|
315
|
+
} else {
|
|
316
|
+
target.style.width = "0";
|
|
317
|
+
target.style.width = `${target.scrollWidth + Number.parseFloat(borderLeftWidth) + Number.parseFloat(borderRightWidth)}px`;
|
|
318
|
+
}
|
|
319
|
+
}, [context.autosize]);
|
|
320
|
+
const onBlur = React$1.useCallback((event) => {
|
|
321
|
+
if (isDisabled || isReadOnly) return;
|
|
322
|
+
propsRef.current.onBlur?.(event);
|
|
323
|
+
if (event.defaultPrevented) return;
|
|
324
|
+
const relatedTarget = event.relatedTarget;
|
|
325
|
+
if (!(relatedTarget instanceof HTMLElement && (relatedTarget.closest(`[data-slot="editable-trigger"]`) || relatedTarget.closest(`[data-slot="editable-cancel"]`)))) context.onSubmit(value);
|
|
326
|
+
}, [
|
|
327
|
+
value,
|
|
328
|
+
context.onSubmit,
|
|
329
|
+
propsRef,
|
|
330
|
+
isDisabled,
|
|
331
|
+
isReadOnly
|
|
332
|
+
]);
|
|
333
|
+
const onChange = React$1.useCallback((event) => {
|
|
334
|
+
if (isDisabled || isReadOnly) return;
|
|
335
|
+
propsRef.current.onChange?.(event);
|
|
336
|
+
if (event.defaultPrevented) return;
|
|
337
|
+
store.setState("value", event.target.value);
|
|
338
|
+
onAutosize(event.target);
|
|
339
|
+
}, [
|
|
340
|
+
store,
|
|
341
|
+
propsRef,
|
|
342
|
+
onAutosize,
|
|
343
|
+
isDisabled,
|
|
344
|
+
isReadOnly
|
|
345
|
+
]);
|
|
346
|
+
const onKeyDown = React$1.useCallback((event) => {
|
|
347
|
+
if (isDisabled || isReadOnly) return;
|
|
348
|
+
propsRef.current.onKeyDown?.(event);
|
|
349
|
+
if (event.defaultPrevented) return;
|
|
350
|
+
if (event.key === "Escape") {
|
|
351
|
+
const nativeEvent = event.nativeEvent;
|
|
352
|
+
if (context.onEscapeKeyDown) {
|
|
353
|
+
context.onEscapeKeyDown(nativeEvent);
|
|
354
|
+
if (nativeEvent.defaultPrevented) return;
|
|
355
|
+
}
|
|
356
|
+
context.onCancel();
|
|
357
|
+
} else if (event.key === "Enter" && !context.multiline) {
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
context.onSubmit(value);
|
|
360
|
+
}
|
|
361
|
+
}, [
|
|
362
|
+
value,
|
|
363
|
+
context.onSubmit,
|
|
364
|
+
context.onCancel,
|
|
365
|
+
context.onEscapeKeyDown,
|
|
366
|
+
context.multiline,
|
|
367
|
+
propsRef,
|
|
368
|
+
isDisabled,
|
|
369
|
+
isReadOnly
|
|
370
|
+
]);
|
|
371
|
+
useIsomorphicLayoutEffect(() => {
|
|
372
|
+
if (!editing || isDisabled || isReadOnly || !inputRef.current) return;
|
|
373
|
+
const frameId = window.requestAnimationFrame(() => {
|
|
374
|
+
if (!inputRef.current) return;
|
|
375
|
+
inputRef.current.focus();
|
|
376
|
+
inputRef.current.select();
|
|
377
|
+
onAutosize(inputRef.current);
|
|
378
|
+
});
|
|
379
|
+
return () => {
|
|
380
|
+
window.cancelAnimationFrame(frameId);
|
|
381
|
+
};
|
|
382
|
+
}, [
|
|
383
|
+
editing,
|
|
384
|
+
onAutosize,
|
|
385
|
+
isDisabled,
|
|
386
|
+
isReadOnly
|
|
387
|
+
]);
|
|
388
|
+
const InputPrimitive = asChild ? Slot : "input";
|
|
389
|
+
if (!editing && !isReadOnly) return null;
|
|
390
|
+
return /* @__PURE__ */ jsx(InputPrimitive, {
|
|
391
|
+
"aria-required": isRequired,
|
|
392
|
+
"aria-invalid": context.invalid,
|
|
393
|
+
"data-slot": "editable-input",
|
|
394
|
+
dir: context.dir,
|
|
395
|
+
disabled: isDisabled,
|
|
396
|
+
readOnly: isReadOnly,
|
|
397
|
+
required: isRequired,
|
|
398
|
+
...inputProps,
|
|
399
|
+
id: context.inputId,
|
|
400
|
+
"aria-labelledby": context.labelId,
|
|
401
|
+
ref: composedRef,
|
|
402
|
+
maxLength: maxLength ?? context.maxLength,
|
|
403
|
+
placeholder: context.placeholder,
|
|
404
|
+
value,
|
|
405
|
+
onBlur,
|
|
406
|
+
onChange,
|
|
407
|
+
onKeyDown,
|
|
408
|
+
className: cn("flex rounded-sm border border-input bg-transparent px-1.5 py-1 shadow-xs transition-colors placeholder:text-muted-foreground/60 placeholder:italic focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50", context.autosize && !context.multiline ? "w-auto" : "w-full", className)
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
function EditableTrigger(props) {
|
|
412
|
+
const { asChild, forceMount = false, ref, ...triggerProps } = props;
|
|
413
|
+
const context = useEditableContext(TRIGGER_NAME);
|
|
414
|
+
const editing = useStore((state) => state.editing);
|
|
415
|
+
const onTrigger = React$1.useCallback(() => {
|
|
416
|
+
if (context.disabled || context.readOnly) return;
|
|
417
|
+
context.onEdit();
|
|
418
|
+
}, [
|
|
419
|
+
context.disabled,
|
|
420
|
+
context.readOnly,
|
|
421
|
+
context.onEdit
|
|
422
|
+
]);
|
|
423
|
+
const TriggerPrimitive = asChild ? Slot : "button";
|
|
424
|
+
if (!forceMount && (editing || context.readOnly)) return null;
|
|
425
|
+
return /* @__PURE__ */ jsx(TriggerPrimitive, {
|
|
426
|
+
type: "button",
|
|
427
|
+
"aria-controls": context.rootId,
|
|
428
|
+
"aria-disabled": context.disabled || context.readOnly,
|
|
429
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
430
|
+
"data-readonly": context.readOnly ? "" : void 0,
|
|
431
|
+
"data-slot": "editable-trigger",
|
|
432
|
+
...triggerProps,
|
|
433
|
+
ref,
|
|
434
|
+
onClick: context.triggerMode === "click" ? onTrigger : void 0,
|
|
435
|
+
onDoubleClick: context.triggerMode === "dblclick" ? onTrigger : void 0
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function EditableToolbar(props) {
|
|
439
|
+
const { asChild, className, orientation = "horizontal", ref, ...toolbarProps } = props;
|
|
440
|
+
const context = useEditableContext(TOOLBAR_NAME);
|
|
441
|
+
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
442
|
+
role: "toolbar",
|
|
443
|
+
"aria-controls": context.rootId,
|
|
444
|
+
"aria-orientation": orientation,
|
|
445
|
+
"data-slot": "editable-toolbar",
|
|
446
|
+
dir: context.dir,
|
|
447
|
+
...toolbarProps,
|
|
448
|
+
ref,
|
|
449
|
+
className: cn("flex items-center gap-2", orientation === "vertical" && "flex-col", className)
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function EditableCancel(props) {
|
|
453
|
+
const { onClick: onClickProp, asChild, ref, ...cancelProps } = props;
|
|
454
|
+
const context = useEditableContext(CANCEL_NAME);
|
|
455
|
+
const editing = useStore((state) => state.editing);
|
|
456
|
+
const propsRef = useAsRef({ onClick: onClickProp });
|
|
457
|
+
const onClick = React$1.useCallback((event) => {
|
|
458
|
+
if (context.disabled || context.readOnly) return;
|
|
459
|
+
propsRef.current.onClick?.(event);
|
|
460
|
+
if (event.defaultPrevented) return;
|
|
461
|
+
context.onCancel();
|
|
462
|
+
}, [
|
|
463
|
+
propsRef,
|
|
464
|
+
context.onCancel,
|
|
465
|
+
context.disabled,
|
|
466
|
+
context.readOnly
|
|
467
|
+
]);
|
|
468
|
+
const CancelPrimitive = asChild ? Slot : "button";
|
|
469
|
+
if (!editing && !context.readOnly) return null;
|
|
470
|
+
return /* @__PURE__ */ jsx(CancelPrimitive, {
|
|
471
|
+
type: "button",
|
|
472
|
+
"aria-controls": context.rootId,
|
|
473
|
+
"data-slot": "editable-cancel",
|
|
474
|
+
...cancelProps,
|
|
475
|
+
onClick,
|
|
476
|
+
ref
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
function EditableSubmit(props) {
|
|
480
|
+
const { onClick: onClickProp, asChild, ref, ...submitProps } = props;
|
|
481
|
+
const context = useEditableContext(SUBMIT_NAME);
|
|
482
|
+
const value = useStore((state) => state.value);
|
|
483
|
+
const editing = useStore((state) => state.editing);
|
|
484
|
+
const propsRef = useAsRef({ onClick: onClickProp });
|
|
485
|
+
const onClick = React$1.useCallback((event) => {
|
|
486
|
+
if (context.disabled || context.readOnly) return;
|
|
487
|
+
propsRef.current.onClick?.(event);
|
|
488
|
+
if (event.defaultPrevented) return;
|
|
489
|
+
context.onSubmit(value);
|
|
490
|
+
}, [
|
|
491
|
+
propsRef,
|
|
492
|
+
context.onSubmit,
|
|
493
|
+
value,
|
|
494
|
+
context.disabled,
|
|
495
|
+
context.readOnly
|
|
496
|
+
]);
|
|
497
|
+
const SubmitPrimitive = asChild ? Slot : "button";
|
|
498
|
+
if (!editing && !context.readOnly) return null;
|
|
499
|
+
return /* @__PURE__ */ jsx(SubmitPrimitive, {
|
|
500
|
+
type: "button",
|
|
501
|
+
"aria-controls": context.rootId,
|
|
502
|
+
"data-slot": "editable-submit",
|
|
503
|
+
...submitProps,
|
|
504
|
+
ref,
|
|
505
|
+
onClick
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
export { Editable, EditableArea, EditableCancel, EditableInput, EditableLabel, EditablePreview, EditableSubmit, EditableToolbar, EditableTrigger, useStore as useEditable };
|
|
@@ -5,7 +5,7 @@ import * as React$1 from "react";
|
|
|
5
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
6
6
|
import { ControllerProps, FieldPath, FieldValues } from "react-hook-form";
|
|
7
7
|
//#region src/components/form.d.ts
|
|
8
|
-
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe }: import("react-hook-form").FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React$1.JSX.Element;
|
|
8
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe }: import("react-hook-form").FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React$1.JSX.Element;
|
|
9
9
|
declare const FormField: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ ...props }: ControllerProps<TFieldValues, TName>) => React$1.JSX.Element;
|
|
10
10
|
declare const useFormField: () => {
|
|
11
11
|
invalid: boolean;
|
|
@@ -23,8 +23,9 @@ declare function FormItem({ className, ...props }: React$1.ComponentProps<"div">
|
|
|
23
23
|
interface FormLabelProps extends React$1.ComponentProps<typeof Label> {
|
|
24
24
|
required?: boolean;
|
|
25
25
|
tooltip?: string;
|
|
26
|
+
marker?: React$1.ReactNode;
|
|
26
27
|
}
|
|
27
|
-
declare function FormLabel({ className, required, tooltip, children, ...props }: FormLabelProps): React$1.JSX.Element;
|
|
28
|
+
declare function FormLabel({ className, required, tooltip, marker, children, ...props }: FormLabelProps): React$1.JSX.Element;
|
|
28
29
|
declare function FormControl({ ...props }: React$1.ComponentProps<typeof Slot>): React$1.JSX.Element;
|
|
29
30
|
declare function FormDescription({ className, ...props }: React$1.ComponentProps<"p">): React$1.JSX.Element;
|
|
30
31
|
declare function FormMessage({ className, ...props }: React$1.ComponentProps<"p">): React$1.JSX.Element | null;
|
|
@@ -42,11 +43,12 @@ interface FormFieldBaseProps<TFieldValues extends FieldValues, TName extends Fie
|
|
|
42
43
|
label?: string;
|
|
43
44
|
description?: string;
|
|
44
45
|
tooltip?: string;
|
|
46
|
+
marker?: React$1.ReactNode;
|
|
45
47
|
}
|
|
46
48
|
interface InputFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> extends FormFieldBaseProps<TFieldValues, TName>, Omit<InputProps, "name" | "label"> {}
|
|
47
|
-
declare function InputField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, ...inputProps }: InputFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
49
|
+
declare function InputField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, marker, ...inputProps }: InputFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
48
50
|
interface TextareaFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> extends FormFieldBaseProps<TFieldValues, TName>, Omit<TextareaProps, "name" | "label"> {}
|
|
49
|
-
declare function TextareaField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, ...textareaProps }: TextareaFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
51
|
+
declare function TextareaField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, marker, ...textareaProps }: TextareaFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
50
52
|
interface SelectFieldOption {
|
|
51
53
|
value: string;
|
|
52
54
|
label: string;
|
|
@@ -56,16 +58,16 @@ interface SelectFieldProps<TFieldValues extends FieldValues, TName extends Field
|
|
|
56
58
|
options: SelectFieldOption[];
|
|
57
59
|
placeholder?: string;
|
|
58
60
|
}
|
|
59
|
-
declare function SelectField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, options, placeholder }: SelectFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
61
|
+
declare function SelectField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, marker, options, placeholder }: SelectFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
60
62
|
interface RadioFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> extends FormFieldBaseProps<TFieldValues, TName> {
|
|
61
63
|
options: SelectFieldOption[];
|
|
62
64
|
}
|
|
63
|
-
declare function RadioField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, options }: RadioFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
65
|
+
declare function RadioField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, marker, options }: RadioFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
64
66
|
interface ChecklistFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> extends FormFieldBaseProps<TFieldValues, TName> {
|
|
65
67
|
options: SelectFieldOption[];
|
|
66
68
|
}
|
|
67
|
-
declare function ChecklistField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, options }: ChecklistFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
69
|
+
declare function ChecklistField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, required, label, description, tooltip, marker, options }: ChecklistFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
68
70
|
interface CheckboxFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> extends Omit<FormFieldBaseProps<TFieldValues, TName>, "required"> {}
|
|
69
|
-
declare function CheckboxField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, label, description, tooltip }: CheckboxFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
71
|
+
declare function CheckboxField<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>({ control, name, rules, label, description, tooltip, marker }: CheckboxFieldProps<TFieldValues, TName>): React$1.JSX.Element;
|
|
70
72
|
//#endregion
|
|
71
73
|
export { CheckboxField, ChecklistField, Form, FormControl, FormDescription, FormField, FormFields, FormHeader, FormItem, FormLabel, FormMessage, InputField, RadioField, SelectField, type SelectFieldOption, TextareaField, useFormField };
|
package/dist/components/form.mjs
CHANGED
|
@@ -52,7 +52,7 @@ function FormItem({ className, ...props }) {
|
|
|
52
52
|
})
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
function FormLabel({ className, required, tooltip, children, ...props }) {
|
|
55
|
+
function FormLabel({ className, required, tooltip, marker, children, ...props }) {
|
|
56
56
|
const { error, formItemId } = useFormField();
|
|
57
57
|
return /* @__PURE__ */ jsxs("div", {
|
|
58
58
|
className: "flex items-center gap-0.5",
|
|
@@ -73,7 +73,11 @@ function FormLabel({ className, required, tooltip, children, ...props }) {
|
|
|
73
73
|
tooltip && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
74
74
|
asChild: true,
|
|
75
75
|
children: /* @__PURE__ */ jsx(Info, { className: "size-4 text-quaternary-foreground" })
|
|
76
|
-
}), /* @__PURE__ */ jsx(TooltipContent, { children: tooltip })] })
|
|
76
|
+
}), /* @__PURE__ */ jsx(TooltipContent, { children: tooltip })] }),
|
|
77
|
+
marker && /* @__PURE__ */ jsx("span", {
|
|
78
|
+
className: "ml-1 flex items-center",
|
|
79
|
+
children: marker
|
|
80
|
+
})
|
|
77
81
|
]
|
|
78
82
|
});
|
|
79
83
|
}
|
|
@@ -127,7 +131,7 @@ function FormFields({ className, ...props }) {
|
|
|
127
131
|
...props
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
|
-
function InputField({ control, name, rules, required, label, description, tooltip, ...inputProps }) {
|
|
134
|
+
function InputField({ control, name, rules, required, label, description, tooltip, marker, ...inputProps }) {
|
|
131
135
|
return /* @__PURE__ */ jsx(FormField, {
|
|
132
136
|
control,
|
|
133
137
|
name,
|
|
@@ -136,6 +140,7 @@ function InputField({ control, name, rules, required, label, description, toolti
|
|
|
136
140
|
label && /* @__PURE__ */ jsx(FormLabel, {
|
|
137
141
|
required,
|
|
138
142
|
tooltip,
|
|
143
|
+
marker,
|
|
139
144
|
children: label
|
|
140
145
|
}),
|
|
141
146
|
description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
@@ -147,7 +152,7 @@ function InputField({ control, name, rules, required, label, description, toolti
|
|
|
147
152
|
] })
|
|
148
153
|
});
|
|
149
154
|
}
|
|
150
|
-
function TextareaField({ control, name, rules, required, label, description, tooltip, ...textareaProps }) {
|
|
155
|
+
function TextareaField({ control, name, rules, required, label, description, tooltip, marker, ...textareaProps }) {
|
|
151
156
|
return /* @__PURE__ */ jsx(FormField, {
|
|
152
157
|
control,
|
|
153
158
|
name,
|
|
@@ -156,6 +161,7 @@ function TextareaField({ control, name, rules, required, label, description, too
|
|
|
156
161
|
label && /* @__PURE__ */ jsx(FormLabel, {
|
|
157
162
|
required,
|
|
158
163
|
tooltip,
|
|
164
|
+
marker,
|
|
159
165
|
children: label
|
|
160
166
|
}),
|
|
161
167
|
description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
@@ -167,7 +173,7 @@ function TextareaField({ control, name, rules, required, label, description, too
|
|
|
167
173
|
] })
|
|
168
174
|
});
|
|
169
175
|
}
|
|
170
|
-
function SelectField({ control, name, rules, required, label, description, tooltip, options, placeholder }) {
|
|
176
|
+
function SelectField({ control, name, rules, required, label, description, tooltip, marker, options, placeholder }) {
|
|
171
177
|
return /* @__PURE__ */ jsx(FormField, {
|
|
172
178
|
control,
|
|
173
179
|
name,
|
|
@@ -176,6 +182,7 @@ function SelectField({ control, name, rules, required, label, description, toolt
|
|
|
176
182
|
label && /* @__PURE__ */ jsx(FormLabel, {
|
|
177
183
|
required,
|
|
178
184
|
tooltip,
|
|
185
|
+
marker,
|
|
179
186
|
children: label
|
|
180
187
|
}),
|
|
181
188
|
description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
@@ -192,7 +199,7 @@ function SelectField({ control, name, rules, required, label, description, toolt
|
|
|
192
199
|
] })
|
|
193
200
|
});
|
|
194
201
|
}
|
|
195
|
-
function RadioField({ control, name, rules, required, label, description, tooltip, options }) {
|
|
202
|
+
function RadioField({ control, name, rules, required, label, description, tooltip, marker, options }) {
|
|
196
203
|
return /* @__PURE__ */ jsx(FormField, {
|
|
197
204
|
control,
|
|
198
205
|
name,
|
|
@@ -201,6 +208,7 @@ function RadioField({ control, name, rules, required, label, description, toolti
|
|
|
201
208
|
label && /* @__PURE__ */ jsx(FormLabel, {
|
|
202
209
|
required,
|
|
203
210
|
tooltip,
|
|
211
|
+
marker,
|
|
204
212
|
children: label
|
|
205
213
|
}),
|
|
206
214
|
description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
@@ -228,7 +236,7 @@ function RadioField({ control, name, rules, required, label, description, toolti
|
|
|
228
236
|
] })
|
|
229
237
|
});
|
|
230
238
|
}
|
|
231
|
-
function ChecklistField({ control, name, rules, required, label, description, tooltip, options }) {
|
|
239
|
+
function ChecklistField({ control, name, rules, required, label, description, tooltip, marker, options }) {
|
|
232
240
|
return /* @__PURE__ */ jsx(FormField, {
|
|
233
241
|
control,
|
|
234
242
|
name,
|
|
@@ -243,6 +251,7 @@ function ChecklistField({ control, name, rules, required, label, description, to
|
|
|
243
251
|
label && /* @__PURE__ */ jsx(FormLabel, {
|
|
244
252
|
required,
|
|
245
253
|
tooltip,
|
|
254
|
+
marker,
|
|
246
255
|
children: label
|
|
247
256
|
}),
|
|
248
257
|
description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
@@ -272,7 +281,7 @@ function ChecklistField({ control, name, rules, required, label, description, to
|
|
|
272
281
|
}
|
|
273
282
|
});
|
|
274
283
|
}
|
|
275
|
-
function CheckboxField({ control, name, rules, label, description, tooltip }) {
|
|
284
|
+
function CheckboxField({ control, name, rules, label, description, tooltip, marker }) {
|
|
276
285
|
return /* @__PURE__ */ jsx(FormField, {
|
|
277
286
|
control,
|
|
278
287
|
name,
|
|
@@ -288,6 +297,7 @@ function CheckboxField({ control, name, rules, label, description, tooltip }) {
|
|
|
288
297
|
onBlur: field.onBlur
|
|
289
298
|
}) }), label && /* @__PURE__ */ jsx(FormLabel, {
|
|
290
299
|
tooltip,
|
|
300
|
+
marker,
|
|
291
301
|
className: "font-normal max-md:-ml-2 max-md:min-h-11 max-md:flex-1 max-md:items-start max-md:pl-2",
|
|
292
302
|
children: label
|
|
293
303
|
})]
|