@launchpad-ui/inline-edit 0.2.7 → 0.2.9
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/index.es.js +136 -140
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +136 -140
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.es.js
CHANGED
@@ -12,148 +12,144 @@ import { forwardRef, useState, useRef, cloneElement, Children } from "react";
|
|
12
12
|
import { createRuntimeFn } from "@vanilla-extract/recipes/createRuntimeFn";
|
13
13
|
var cancelButton = "_1oig0624";
|
14
14
|
var container = "_1oig0620";
|
15
|
-
var inline = createRuntimeFn({
|
15
|
+
var inline = createRuntimeFn({
|
16
|
+
defaultClassName: "_1oig0621",
|
17
|
+
variantClassNames: {
|
18
|
+
layout: {
|
19
|
+
vertical: "_1oig0622",
|
20
|
+
horizontal: "_1oig0623"
|
21
|
+
}
|
22
|
+
},
|
23
|
+
defaultVariants: {},
|
24
|
+
compoundVariants: []
|
25
|
+
});
|
16
26
|
var readButton = "_1oig0625";
|
17
|
-
const InlineEdit = /* @__PURE__ */ forwardRef(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
27
|
+
const InlineEdit = /* @__PURE__ */ forwardRef(({
|
28
|
+
"data-test-id": testId = "inline-edit",
|
29
|
+
layout = "horizontal",
|
30
|
+
children,
|
31
|
+
defaultValue,
|
32
|
+
onConfirm,
|
33
|
+
hideEdit = false,
|
34
|
+
renderInput = /* @__PURE__ */ jsx(TextField, {}),
|
35
|
+
"aria-label": ariaLabel,
|
36
|
+
isEditing: isEditingProp,
|
37
|
+
onCancel,
|
38
|
+
onEdit,
|
39
|
+
cancelButtonLabel = "cancel",
|
40
|
+
editButtonLabel = "edit",
|
41
|
+
confirmButtonLabel = "confirm",
|
42
|
+
className,
|
43
|
+
...rest
|
44
|
+
}, ref) => {
|
45
|
+
const [isEditing, setEditing] = useState(isEditingProp ?? false);
|
46
|
+
const [isFocusWithin, setFocusWithin] = useState(false);
|
47
|
+
const inputRef = useRef(null);
|
48
|
+
const editRef = useRef(null);
|
49
|
+
const controlled = isEditingProp !== void 0;
|
50
|
+
useUpdateEffect(() => {
|
51
|
+
if (controlled) {
|
52
|
+
setEditing(isEditingProp);
|
53
|
+
}
|
54
|
+
}, [isEditingProp]);
|
55
|
+
useUpdateEffect(() => {
|
56
|
+
if (isFocusWithin) {
|
57
|
+
isEditing ? inputRef.current && focusSafely(inputRef.current) : editRef.current && focusSafely(editRef.current);
|
58
|
+
}
|
59
|
+
}, [isEditing]);
|
60
|
+
const handleEdit = () => {
|
61
|
+
!controlled && setEditing(true);
|
62
|
+
onEdit == null ? void 0 : onEdit();
|
63
|
+
};
|
64
|
+
const handleCancel = () => {
|
65
|
+
!controlled && setEditing(false);
|
66
|
+
onCancel == null ? void 0 : onCancel();
|
67
|
+
};
|
68
|
+
const handleConfirm = () => {
|
69
|
+
var _a;
|
70
|
+
onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
|
71
|
+
!controlled && setEditing(false);
|
72
|
+
};
|
73
|
+
const handleKeyDown = (event) => {
|
74
|
+
if (event.key === "Enter") {
|
75
|
+
event.preventDefault();
|
76
|
+
handleConfirm();
|
77
|
+
} else if (event.key === "Escape") {
|
78
|
+
event.preventDefault();
|
79
|
+
handleCancel();
|
80
|
+
}
|
81
|
+
};
|
82
|
+
const {
|
83
|
+
focusWithinProps
|
84
|
+
} = useFocusWithin({
|
85
|
+
onBlurWithin: () => isEditing && handleCancel(),
|
86
|
+
onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
|
87
|
+
});
|
88
|
+
const {
|
89
|
+
buttonProps
|
90
|
+
} = useButton({
|
91
|
+
"aria-label": editButtonLabel,
|
92
|
+
elementType: "span",
|
93
|
+
onPress: handleEdit
|
94
|
+
}, editRef);
|
95
|
+
const renderReadContent = hideEdit ? /* @__PURE__ */ jsx("span", {
|
96
|
+
ref: editRef,
|
97
|
+
...buttonProps,
|
98
|
+
className: readButton,
|
99
|
+
children
|
100
|
+
}) : /* @__PURE__ */ jsxs(Fragment, {
|
101
|
+
children: [children, /* @__PURE__ */ jsx(IconButton, {
|
102
|
+
ref: editRef,
|
103
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
104
|
+
name: "edit"
|
105
|
+
}),
|
106
|
+
"aria-label": editButtonLabel,
|
107
|
+
size: "small",
|
108
|
+
onClick: handleEdit
|
109
|
+
})]
|
110
|
+
});
|
111
|
+
const inputProps = {
|
112
|
+
ref: mergeRefs(inputRef, ref),
|
22
113
|
defaultValue,
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
...
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
!controlled && setEditing(false);
|
63
|
-
};
|
64
|
-
const handleKeyDown = (event) => {
|
65
|
-
if (event.key === "Enter") {
|
66
|
-
event.preventDefault();
|
67
|
-
handleConfirm();
|
68
|
-
} else if (event.key === "Escape") {
|
69
|
-
event.preventDefault();
|
70
|
-
handleCancel();
|
71
|
-
}
|
72
|
-
};
|
73
|
-
const { focusWithinProps } = useFocusWithin({
|
74
|
-
onBlurWithin: () => isEditing && handleCancel(),
|
75
|
-
onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
|
76
|
-
});
|
77
|
-
const { buttonProps } = useButton(
|
78
|
-
{
|
79
|
-
"aria-label": editButtonLabel,
|
80
|
-
elementType: "span",
|
81
|
-
onPress: handleEdit
|
82
|
-
},
|
83
|
-
editRef
|
84
|
-
);
|
85
|
-
const renderReadContent = hideEdit ? /* @__PURE__ */ jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
86
|
-
children,
|
87
|
-
/* @__PURE__ */ jsx(
|
88
|
-
IconButton,
|
89
|
-
{
|
90
|
-
ref: editRef,
|
91
|
-
icon: /* @__PURE__ */ jsx(Icon, { name: "edit" }),
|
92
|
-
"aria-label": editButtonLabel,
|
93
|
-
size: "small",
|
94
|
-
onClick: handleEdit
|
95
|
-
}
|
96
|
-
)
|
97
|
-
] });
|
98
|
-
const inputProps = {
|
99
|
-
ref: mergeRefs(inputRef, ref),
|
100
|
-
defaultValue,
|
101
|
-
onKeyDown: handleKeyDown,
|
102
|
-
"aria-label": ariaLabel
|
103
|
-
};
|
104
|
-
const inputChildren = renderInput.props.children;
|
105
|
-
const input = /* @__PURE__ */ cloneElement(
|
106
|
-
renderInput,
|
107
|
-
mergeProps(renderInput.props, inputChildren ? {} : inputProps),
|
108
|
-
inputChildren && Children.map(
|
109
|
-
inputChildren,
|
110
|
-
(child) => child.type === TextField || child.type === TextArea ? /* @__PURE__ */ cloneElement(child, mergeProps(child.props, inputProps)) : child
|
111
|
-
)
|
112
|
-
);
|
113
|
-
return isEditing ? /* @__PURE__ */ jsxs(
|
114
|
-
"div",
|
115
|
-
{
|
116
|
-
...rest,
|
117
|
-
className: cx(container, inline({ layout }), className),
|
118
|
-
"data-test-id": testId,
|
119
|
-
...focusWithinProps,
|
120
|
-
children: [
|
121
|
-
input,
|
122
|
-
/* @__PURE__ */ jsxs(ButtonGroup, { spacing: "compact", children: [
|
123
|
-
/* @__PURE__ */ jsx(
|
124
|
-
IconButton,
|
125
|
-
{
|
126
|
-
kind: "primary",
|
127
|
-
icon: /* @__PURE__ */ jsx(Icon, { name: "check" }),
|
128
|
-
"aria-label": confirmButtonLabel,
|
129
|
-
onClick: handleConfirm
|
130
|
-
}
|
131
|
-
),
|
132
|
-
/* @__PURE__ */ jsx(
|
133
|
-
IconButton,
|
134
|
-
{
|
135
|
-
kind: "default",
|
136
|
-
icon: /* @__PURE__ */ jsx(Icon, { name: "cancel" }),
|
137
|
-
"aria-label": cancelButtonLabel,
|
138
|
-
className: cancelButton,
|
139
|
-
onClick: handleCancel
|
140
|
-
}
|
141
|
-
)
|
142
|
-
] })
|
143
|
-
]
|
144
|
-
}
|
145
|
-
) : /* @__PURE__ */ jsx(
|
146
|
-
"div",
|
147
|
-
{
|
148
|
-
...rest,
|
149
|
-
className: cx(!hideEdit && container, className),
|
150
|
-
"data-test-id": testId,
|
151
|
-
...focusWithinProps,
|
152
|
-
children: renderReadContent
|
153
|
-
}
|
154
|
-
);
|
155
|
-
}
|
156
|
-
);
|
114
|
+
onKeyDown: handleKeyDown,
|
115
|
+
"aria-label": ariaLabel
|
116
|
+
};
|
117
|
+
const inputChildren = renderInput.props.children;
|
118
|
+
const input = /* @__PURE__ */ cloneElement(renderInput, mergeProps(renderInput.props, inputChildren ? {} : inputProps), inputChildren && Children.map(inputChildren, (child) => child.type === TextField || child.type === TextArea ? /* @__PURE__ */ cloneElement(child, mergeProps(child.props, inputProps)) : child));
|
119
|
+
return isEditing ? /* @__PURE__ */ jsxs("div", {
|
120
|
+
...rest,
|
121
|
+
className: cx(container, inline({
|
122
|
+
layout
|
123
|
+
}), className),
|
124
|
+
"data-test-id": testId,
|
125
|
+
...focusWithinProps,
|
126
|
+
children: [input, /* @__PURE__ */ jsxs(ButtonGroup, {
|
127
|
+
spacing: "compact",
|
128
|
+
children: [/* @__PURE__ */ jsx(IconButton, {
|
129
|
+
kind: "primary",
|
130
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
131
|
+
name: "check"
|
132
|
+
}),
|
133
|
+
"aria-label": confirmButtonLabel,
|
134
|
+
onClick: handleConfirm
|
135
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
136
|
+
kind: "default",
|
137
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
138
|
+
name: "cancel"
|
139
|
+
}),
|
140
|
+
"aria-label": cancelButtonLabel,
|
141
|
+
className: cancelButton,
|
142
|
+
onClick: handleCancel
|
143
|
+
})]
|
144
|
+
})]
|
145
|
+
}) : /* @__PURE__ */ jsx("div", {
|
146
|
+
...rest,
|
147
|
+
className: cx(!hideEdit && container, className),
|
148
|
+
"data-test-id": testId,
|
149
|
+
...focusWithinProps,
|
150
|
+
children: renderReadContent
|
151
|
+
});
|
152
|
+
});
|
157
153
|
InlineEdit.displayName = "InlineEdit";
|
158
154
|
export {
|
159
155
|
InlineEdit
|
package/dist/index.es.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { IconFieldProps, TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField, TextArea } from '@launchpad-ui/form';\nimport { Icon } from '@launchpad-ui/icons';\nimport { useButton } from '@react-aria/button';\nimport { focusSafely } from '@react-aria/focus';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { mergeProps, mergeRefs, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { Children, cloneElement, forwardRef, useRef, useState } from 'react';\n\nimport { container, cancelButton, inline, readButton } from './styles/InlineEdit.css';\n\ntype InlineEditProps = ComponentProps<'div'> &\n InlineVariants &\n Pick<ComponentProps<'input'>, 'defaultValue'> & {\n 'data-test-id'?: string;\n onConfirm: (value: string) => void;\n hideEdit?: boolean;\n renderInput?: ReactElement<IconFieldProps | TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n 'data-test-id': testId = 'inline-edit',\n layout = 'horizontal',\n children,\n defaultValue,\n onConfirm,\n hideEdit = false,\n renderInput = <TextField />,\n 'aria-label': ariaLabel,\n isEditing: isEditingProp,\n onCancel,\n onEdit,\n cancelButtonLabel = 'cancel',\n editButtonLabel = 'edit',\n confirmButtonLabel = 'confirm',\n className,\n ...rest\n },\n ref\n ) => {\n const [isEditing, setEditing] = useState(isEditingProp ?? false);\n const [isFocusWithin, setFocusWithin] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const editRef = useRef<HTMLButtonElement>(null);\n const controlled = isEditingProp !== undefined;\n\n useUpdateEffect(() => {\n if (controlled) {\n setEditing(isEditingProp);\n }\n }, [isEditingProp]);\n\n useUpdateEffect(() => {\n if (isFocusWithin) {\n isEditing\n ? inputRef.current && focusSafely(inputRef.current)\n : editRef.current && focusSafely(editRef.current);\n }\n }, [isEditing]);\n\n const handleEdit = () => {\n !controlled && setEditing(true);\n onEdit?.();\n };\n\n const handleCancel = () => {\n !controlled && setEditing(false);\n onCancel?.();\n };\n\n const handleConfirm = () => {\n onConfirm(inputRef.current?.value || '');\n !controlled && setEditing(false);\n };\n\n const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n handleConfirm();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n handleCancel();\n }\n };\n\n const { focusWithinProps } = useFocusWithin({\n onBlurWithin: () => isEditing && handleCancel(),\n onFocusWithinChange: (isFocusWithin) => setFocusWithin(isFocusWithin),\n });\n\n const { buttonProps } = useButton(\n {\n 'aria-label': editButtonLabel,\n elementType: 'span',\n onPress: handleEdit,\n },\n editRef\n );\n\n const renderReadContent = hideEdit ? (\n <span ref={editRef} {...buttonProps} className={readButton}>\n {children}\n </span>\n ) : (\n <>\n {children}\n <IconButton\n ref={editRef}\n icon={<Icon name=\"edit\" />}\n aria-label={editButtonLabel}\n size=\"small\"\n onClick={handleEdit}\n />\n </>\n );\n\n const inputProps = {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n };\n\n const inputChildren = renderInput.props.children;\n\n const input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, inputChildren ? {} : inputProps),\n inputChildren &&\n Children.map(inputChildren, (child) =>\n child.type === TextField || child.type === TextArea\n ? cloneElement(child, mergeProps(child.props, inputProps))\n : child\n )\n );\n\n return isEditing ? (\n <div\n {...rest}\n className={cx(container, inline({ layout }), className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {input}\n <ButtonGroup spacing=\"compact\">\n <IconButton\n kind=\"primary\"\n icon={<Icon name=\"check\" />}\n aria-label={confirmButtonLabel}\n onClick={handleConfirm}\n />\n <IconButton\n kind=\"default\"\n icon={<Icon name=\"cancel\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div\n {...rest}\n className={cx(!hideEdit && container, className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {renderReadContent}\n </div>\n );\n }\n);\n\nInlineEdit.displayName = 'InlineEdit';\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":[
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { IconFieldProps, TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField, TextArea } from '@launchpad-ui/form';\nimport { Icon } from '@launchpad-ui/icons';\nimport { useButton } from '@react-aria/button';\nimport { focusSafely } from '@react-aria/focus';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { mergeProps, mergeRefs, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { Children, cloneElement, forwardRef, useRef, useState } from 'react';\n\nimport { container, cancelButton, inline, readButton } from './styles/InlineEdit.css';\n\ntype InlineEditProps = ComponentProps<'div'> &\n InlineVariants &\n Pick<ComponentProps<'input'>, 'defaultValue'> & {\n 'data-test-id'?: string;\n onConfirm: (value: string) => void;\n hideEdit?: boolean;\n renderInput?: ReactElement<IconFieldProps | TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n 'data-test-id': testId = 'inline-edit',\n layout = 'horizontal',\n children,\n defaultValue,\n onConfirm,\n hideEdit = false,\n renderInput = <TextField />,\n 'aria-label': ariaLabel,\n isEditing: isEditingProp,\n onCancel,\n onEdit,\n cancelButtonLabel = 'cancel',\n editButtonLabel = 'edit',\n confirmButtonLabel = 'confirm',\n className,\n ...rest\n },\n ref\n ) => {\n const [isEditing, setEditing] = useState(isEditingProp ?? false);\n const [isFocusWithin, setFocusWithin] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const editRef = useRef<HTMLButtonElement>(null);\n const controlled = isEditingProp !== undefined;\n\n useUpdateEffect(() => {\n if (controlled) {\n setEditing(isEditingProp);\n }\n }, [isEditingProp]);\n\n useUpdateEffect(() => {\n if (isFocusWithin) {\n isEditing\n ? inputRef.current && focusSafely(inputRef.current)\n : editRef.current && focusSafely(editRef.current);\n }\n }, [isEditing]);\n\n const handleEdit = () => {\n !controlled && setEditing(true);\n onEdit?.();\n };\n\n const handleCancel = () => {\n !controlled && setEditing(false);\n onCancel?.();\n };\n\n const handleConfirm = () => {\n onConfirm(inputRef.current?.value || '');\n !controlled && setEditing(false);\n };\n\n const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n handleConfirm();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n handleCancel();\n }\n };\n\n const { focusWithinProps } = useFocusWithin({\n onBlurWithin: () => isEditing && handleCancel(),\n onFocusWithinChange: (isFocusWithin) => setFocusWithin(isFocusWithin),\n });\n\n const { buttonProps } = useButton(\n {\n 'aria-label': editButtonLabel,\n elementType: 'span',\n onPress: handleEdit,\n },\n editRef\n );\n\n const renderReadContent = hideEdit ? (\n <span ref={editRef} {...buttonProps} className={readButton}>\n {children}\n </span>\n ) : (\n <>\n {children}\n <IconButton\n ref={editRef}\n icon={<Icon name=\"edit\" />}\n aria-label={editButtonLabel}\n size=\"small\"\n onClick={handleEdit}\n />\n </>\n );\n\n const inputProps = {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n };\n\n const inputChildren = renderInput.props.children;\n\n const input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, inputChildren ? {} : inputProps),\n inputChildren &&\n Children.map(inputChildren, (child) =>\n child.type === TextField || child.type === TextArea\n ? cloneElement(child, mergeProps(child.props, inputProps))\n : child\n )\n );\n\n return isEditing ? (\n <div\n {...rest}\n className={cx(container, inline({ layout }), className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {input}\n <ButtonGroup spacing=\"compact\">\n <IconButton\n kind=\"primary\"\n icon={<Icon name=\"check\" />}\n aria-label={confirmButtonLabel}\n onClick={handleConfirm}\n />\n <IconButton\n kind=\"default\"\n icon={<Icon name=\"cancel\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div\n {...rest}\n className={cx(!hideEdit && container, className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {renderReadContent}\n </div>\n );\n }\n);\n\nInlineEdit.displayName = 'InlineEdit';\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
@@ -14,148 +14,144 @@ const react = require("react");
|
|
14
14
|
const createRuntimeFn = require("@vanilla-extract/recipes/createRuntimeFn");
|
15
15
|
var cancelButton = "_1oig0624";
|
16
16
|
var container = "_1oig0620";
|
17
|
-
var inline = createRuntimeFn.createRuntimeFn({
|
17
|
+
var inline = createRuntimeFn.createRuntimeFn({
|
18
|
+
defaultClassName: "_1oig0621",
|
19
|
+
variantClassNames: {
|
20
|
+
layout: {
|
21
|
+
vertical: "_1oig0622",
|
22
|
+
horizontal: "_1oig0623"
|
23
|
+
}
|
24
|
+
},
|
25
|
+
defaultVariants: {},
|
26
|
+
compoundVariants: []
|
27
|
+
});
|
18
28
|
var readButton = "_1oig0625";
|
19
|
-
const InlineEdit = /* @__PURE__ */ react.forwardRef(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
const InlineEdit = /* @__PURE__ */ react.forwardRef(({
|
30
|
+
"data-test-id": testId = "inline-edit",
|
31
|
+
layout = "horizontal",
|
32
|
+
children,
|
33
|
+
defaultValue,
|
34
|
+
onConfirm,
|
35
|
+
hideEdit = false,
|
36
|
+
renderInput = /* @__PURE__ */ jsxRuntime.jsx(form.TextField, {}),
|
37
|
+
"aria-label": ariaLabel,
|
38
|
+
isEditing: isEditingProp,
|
39
|
+
onCancel,
|
40
|
+
onEdit,
|
41
|
+
cancelButtonLabel = "cancel",
|
42
|
+
editButtonLabel = "edit",
|
43
|
+
confirmButtonLabel = "confirm",
|
44
|
+
className,
|
45
|
+
...rest
|
46
|
+
}, ref) => {
|
47
|
+
const [isEditing, setEditing] = react.useState(isEditingProp ?? false);
|
48
|
+
const [isFocusWithin, setFocusWithin] = react.useState(false);
|
49
|
+
const inputRef = react.useRef(null);
|
50
|
+
const editRef = react.useRef(null);
|
51
|
+
const controlled = isEditingProp !== void 0;
|
52
|
+
utils.useUpdateEffect(() => {
|
53
|
+
if (controlled) {
|
54
|
+
setEditing(isEditingProp);
|
55
|
+
}
|
56
|
+
}, [isEditingProp]);
|
57
|
+
utils.useUpdateEffect(() => {
|
58
|
+
if (isFocusWithin) {
|
59
|
+
isEditing ? inputRef.current && focus.focusSafely(inputRef.current) : editRef.current && focus.focusSafely(editRef.current);
|
60
|
+
}
|
61
|
+
}, [isEditing]);
|
62
|
+
const handleEdit = () => {
|
63
|
+
!controlled && setEditing(true);
|
64
|
+
onEdit == null ? void 0 : onEdit();
|
65
|
+
};
|
66
|
+
const handleCancel = () => {
|
67
|
+
!controlled && setEditing(false);
|
68
|
+
onCancel == null ? void 0 : onCancel();
|
69
|
+
};
|
70
|
+
const handleConfirm = () => {
|
71
|
+
var _a;
|
72
|
+
onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
|
73
|
+
!controlled && setEditing(false);
|
74
|
+
};
|
75
|
+
const handleKeyDown = (event) => {
|
76
|
+
if (event.key === "Enter") {
|
77
|
+
event.preventDefault();
|
78
|
+
handleConfirm();
|
79
|
+
} else if (event.key === "Escape") {
|
80
|
+
event.preventDefault();
|
81
|
+
handleCancel();
|
82
|
+
}
|
83
|
+
};
|
84
|
+
const {
|
85
|
+
focusWithinProps
|
86
|
+
} = interactions.useFocusWithin({
|
87
|
+
onBlurWithin: () => isEditing && handleCancel(),
|
88
|
+
onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
|
89
|
+
});
|
90
|
+
const {
|
91
|
+
buttonProps
|
92
|
+
} = button.useButton({
|
93
|
+
"aria-label": editButtonLabel,
|
94
|
+
elementType: "span",
|
95
|
+
onPress: handleEdit
|
96
|
+
}, editRef);
|
97
|
+
const renderReadContent = hideEdit ? /* @__PURE__ */ jsxRuntime.jsx("span", {
|
98
|
+
ref: editRef,
|
99
|
+
...buttonProps,
|
100
|
+
className: readButton,
|
101
|
+
children
|
102
|
+
}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
103
|
+
children: [children, /* @__PURE__ */ jsxRuntime.jsx(button$1.IconButton, {
|
104
|
+
ref: editRef,
|
105
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, {
|
106
|
+
name: "edit"
|
107
|
+
}),
|
108
|
+
"aria-label": editButtonLabel,
|
109
|
+
size: "small",
|
110
|
+
onClick: handleEdit
|
111
|
+
})]
|
112
|
+
});
|
113
|
+
const inputProps = {
|
114
|
+
ref: utils.mergeRefs(inputRef, ref),
|
24
115
|
defaultValue,
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
...
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
}
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
!controlled && setEditing(false);
|
65
|
-
};
|
66
|
-
const handleKeyDown = (event) => {
|
67
|
-
if (event.key === "Enter") {
|
68
|
-
event.preventDefault();
|
69
|
-
handleConfirm();
|
70
|
-
} else if (event.key === "Escape") {
|
71
|
-
event.preventDefault();
|
72
|
-
handleCancel();
|
73
|
-
}
|
74
|
-
};
|
75
|
-
const { focusWithinProps } = interactions.useFocusWithin({
|
76
|
-
onBlurWithin: () => isEditing && handleCancel(),
|
77
|
-
onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
|
78
|
-
});
|
79
|
-
const { buttonProps } = button.useButton(
|
80
|
-
{
|
81
|
-
"aria-label": editButtonLabel,
|
82
|
-
elementType: "span",
|
83
|
-
onPress: handleEdit
|
84
|
-
},
|
85
|
-
editRef
|
86
|
-
);
|
87
|
-
const renderReadContent = hideEdit ? /* @__PURE__ */ jsxRuntime.jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
88
|
-
children,
|
89
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
90
|
-
button$1.IconButton,
|
91
|
-
{
|
92
|
-
ref: editRef,
|
93
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "edit" }),
|
94
|
-
"aria-label": editButtonLabel,
|
95
|
-
size: "small",
|
96
|
-
onClick: handleEdit
|
97
|
-
}
|
98
|
-
)
|
99
|
-
] });
|
100
|
-
const inputProps = {
|
101
|
-
ref: utils.mergeRefs(inputRef, ref),
|
102
|
-
defaultValue,
|
103
|
-
onKeyDown: handleKeyDown,
|
104
|
-
"aria-label": ariaLabel
|
105
|
-
};
|
106
|
-
const inputChildren = renderInput.props.children;
|
107
|
-
const input = /* @__PURE__ */ react.cloneElement(
|
108
|
-
renderInput,
|
109
|
-
utils.mergeProps(renderInput.props, inputChildren ? {} : inputProps),
|
110
|
-
inputChildren && react.Children.map(
|
111
|
-
inputChildren,
|
112
|
-
(child) => child.type === form.TextField || child.type === form.TextArea ? /* @__PURE__ */ react.cloneElement(child, utils.mergeProps(child.props, inputProps)) : child
|
113
|
-
)
|
114
|
-
);
|
115
|
-
return isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(
|
116
|
-
"div",
|
117
|
-
{
|
118
|
-
...rest,
|
119
|
-
className: classix.cx(container, inline({ layout }), className),
|
120
|
-
"data-test-id": testId,
|
121
|
-
...focusWithinProps,
|
122
|
-
children: [
|
123
|
-
input,
|
124
|
-
/* @__PURE__ */ jsxRuntime.jsxs(button$1.ButtonGroup, { spacing: "compact", children: [
|
125
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
126
|
-
button$1.IconButton,
|
127
|
-
{
|
128
|
-
kind: "primary",
|
129
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "check" }),
|
130
|
-
"aria-label": confirmButtonLabel,
|
131
|
-
onClick: handleConfirm
|
132
|
-
}
|
133
|
-
),
|
134
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
135
|
-
button$1.IconButton,
|
136
|
-
{
|
137
|
-
kind: "default",
|
138
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "cancel" }),
|
139
|
-
"aria-label": cancelButtonLabel,
|
140
|
-
className: cancelButton,
|
141
|
-
onClick: handleCancel
|
142
|
-
}
|
143
|
-
)
|
144
|
-
] })
|
145
|
-
]
|
146
|
-
}
|
147
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
148
|
-
"div",
|
149
|
-
{
|
150
|
-
...rest,
|
151
|
-
className: classix.cx(!hideEdit && container, className),
|
152
|
-
"data-test-id": testId,
|
153
|
-
...focusWithinProps,
|
154
|
-
children: renderReadContent
|
155
|
-
}
|
156
|
-
);
|
157
|
-
}
|
158
|
-
);
|
116
|
+
onKeyDown: handleKeyDown,
|
117
|
+
"aria-label": ariaLabel
|
118
|
+
};
|
119
|
+
const inputChildren = renderInput.props.children;
|
120
|
+
const input = /* @__PURE__ */ react.cloneElement(renderInput, utils.mergeProps(renderInput.props, inputChildren ? {} : inputProps), inputChildren && react.Children.map(inputChildren, (child) => child.type === form.TextField || child.type === form.TextArea ? /* @__PURE__ */ react.cloneElement(child, utils.mergeProps(child.props, inputProps)) : child));
|
121
|
+
return isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
122
|
+
...rest,
|
123
|
+
className: classix.cx(container, inline({
|
124
|
+
layout
|
125
|
+
}), className),
|
126
|
+
"data-test-id": testId,
|
127
|
+
...focusWithinProps,
|
128
|
+
children: [input, /* @__PURE__ */ jsxRuntime.jsxs(button$1.ButtonGroup, {
|
129
|
+
spacing: "compact",
|
130
|
+
children: [/* @__PURE__ */ jsxRuntime.jsx(button$1.IconButton, {
|
131
|
+
kind: "primary",
|
132
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, {
|
133
|
+
name: "check"
|
134
|
+
}),
|
135
|
+
"aria-label": confirmButtonLabel,
|
136
|
+
onClick: handleConfirm
|
137
|
+
}), /* @__PURE__ */ jsxRuntime.jsx(button$1.IconButton, {
|
138
|
+
kind: "default",
|
139
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, {
|
140
|
+
name: "cancel"
|
141
|
+
}),
|
142
|
+
"aria-label": cancelButtonLabel,
|
143
|
+
className: cancelButton,
|
144
|
+
onClick: handleCancel
|
145
|
+
})]
|
146
|
+
})]
|
147
|
+
}) : /* @__PURE__ */ jsxRuntime.jsx("div", {
|
148
|
+
...rest,
|
149
|
+
className: classix.cx(!hideEdit && container, className),
|
150
|
+
"data-test-id": testId,
|
151
|
+
...focusWithinProps,
|
152
|
+
children: renderReadContent
|
153
|
+
});
|
154
|
+
});
|
159
155
|
InlineEdit.displayName = "InlineEdit";
|
160
156
|
exports.InlineEdit = InlineEdit;
|
161
157
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { IconFieldProps, TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField, TextArea } from '@launchpad-ui/form';\nimport { Icon } from '@launchpad-ui/icons';\nimport { useButton } from '@react-aria/button';\nimport { focusSafely } from '@react-aria/focus';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { mergeProps, mergeRefs, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { Children, cloneElement, forwardRef, useRef, useState } from 'react';\n\nimport { container, cancelButton, inline, readButton } from './styles/InlineEdit.css';\n\ntype InlineEditProps = ComponentProps<'div'> &\n InlineVariants &\n Pick<ComponentProps<'input'>, 'defaultValue'> & {\n 'data-test-id'?: string;\n onConfirm: (value: string) => void;\n hideEdit?: boolean;\n renderInput?: ReactElement<IconFieldProps | TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n 'data-test-id': testId = 'inline-edit',\n layout = 'horizontal',\n children,\n defaultValue,\n onConfirm,\n hideEdit = false,\n renderInput = <TextField />,\n 'aria-label': ariaLabel,\n isEditing: isEditingProp,\n onCancel,\n onEdit,\n cancelButtonLabel = 'cancel',\n editButtonLabel = 'edit',\n confirmButtonLabel = 'confirm',\n className,\n ...rest\n },\n ref\n ) => {\n const [isEditing, setEditing] = useState(isEditingProp ?? false);\n const [isFocusWithin, setFocusWithin] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const editRef = useRef<HTMLButtonElement>(null);\n const controlled = isEditingProp !== undefined;\n\n useUpdateEffect(() => {\n if (controlled) {\n setEditing(isEditingProp);\n }\n }, [isEditingProp]);\n\n useUpdateEffect(() => {\n if (isFocusWithin) {\n isEditing\n ? inputRef.current && focusSafely(inputRef.current)\n : editRef.current && focusSafely(editRef.current);\n }\n }, [isEditing]);\n\n const handleEdit = () => {\n !controlled && setEditing(true);\n onEdit?.();\n };\n\n const handleCancel = () => {\n !controlled && setEditing(false);\n onCancel?.();\n };\n\n const handleConfirm = () => {\n onConfirm(inputRef.current?.value || '');\n !controlled && setEditing(false);\n };\n\n const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n handleConfirm();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n handleCancel();\n }\n };\n\n const { focusWithinProps } = useFocusWithin({\n onBlurWithin: () => isEditing && handleCancel(),\n onFocusWithinChange: (isFocusWithin) => setFocusWithin(isFocusWithin),\n });\n\n const { buttonProps } = useButton(\n {\n 'aria-label': editButtonLabel,\n elementType: 'span',\n onPress: handleEdit,\n },\n editRef\n );\n\n const renderReadContent = hideEdit ? (\n <span ref={editRef} {...buttonProps} className={readButton}>\n {children}\n </span>\n ) : (\n <>\n {children}\n <IconButton\n ref={editRef}\n icon={<Icon name=\"edit\" />}\n aria-label={editButtonLabel}\n size=\"small\"\n onClick={handleEdit}\n />\n </>\n );\n\n const inputProps = {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n };\n\n const inputChildren = renderInput.props.children;\n\n const input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, inputChildren ? {} : inputProps),\n inputChildren &&\n Children.map(inputChildren, (child) =>\n child.type === TextField || child.type === TextArea\n ? cloneElement(child, mergeProps(child.props, inputProps))\n : child\n )\n );\n\n return isEditing ? (\n <div\n {...rest}\n className={cx(container, inline({ layout }), className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {input}\n <ButtonGroup spacing=\"compact\">\n <IconButton\n kind=\"primary\"\n icon={<Icon name=\"check\" />}\n aria-label={confirmButtonLabel}\n onClick={handleConfirm}\n />\n <IconButton\n kind=\"default\"\n icon={<Icon name=\"cancel\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div\n {...rest}\n className={cx(!hideEdit && container, className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {renderReadContent}\n </div>\n );\n }\n);\n\nInlineEdit.displayName = 'InlineEdit';\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":["forwardRef","TextField","useState","useRef","useUpdateEffect","focusSafely","useFocusWithin","
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { IconFieldProps, TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField, TextArea } from '@launchpad-ui/form';\nimport { Icon } from '@launchpad-ui/icons';\nimport { useButton } from '@react-aria/button';\nimport { focusSafely } from '@react-aria/focus';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { mergeProps, mergeRefs, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { Children, cloneElement, forwardRef, useRef, useState } from 'react';\n\nimport { container, cancelButton, inline, readButton } from './styles/InlineEdit.css';\n\ntype InlineEditProps = ComponentProps<'div'> &\n InlineVariants &\n Pick<ComponentProps<'input'>, 'defaultValue'> & {\n 'data-test-id'?: string;\n onConfirm: (value: string) => void;\n hideEdit?: boolean;\n renderInput?: ReactElement<IconFieldProps | TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n 'data-test-id': testId = 'inline-edit',\n layout = 'horizontal',\n children,\n defaultValue,\n onConfirm,\n hideEdit = false,\n renderInput = <TextField />,\n 'aria-label': ariaLabel,\n isEditing: isEditingProp,\n onCancel,\n onEdit,\n cancelButtonLabel = 'cancel',\n editButtonLabel = 'edit',\n confirmButtonLabel = 'confirm',\n className,\n ...rest\n },\n ref\n ) => {\n const [isEditing, setEditing] = useState(isEditingProp ?? false);\n const [isFocusWithin, setFocusWithin] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const editRef = useRef<HTMLButtonElement>(null);\n const controlled = isEditingProp !== undefined;\n\n useUpdateEffect(() => {\n if (controlled) {\n setEditing(isEditingProp);\n }\n }, [isEditingProp]);\n\n useUpdateEffect(() => {\n if (isFocusWithin) {\n isEditing\n ? inputRef.current && focusSafely(inputRef.current)\n : editRef.current && focusSafely(editRef.current);\n }\n }, [isEditing]);\n\n const handleEdit = () => {\n !controlled && setEditing(true);\n onEdit?.();\n };\n\n const handleCancel = () => {\n !controlled && setEditing(false);\n onCancel?.();\n };\n\n const handleConfirm = () => {\n onConfirm(inputRef.current?.value || '');\n !controlled && setEditing(false);\n };\n\n const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n handleConfirm();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n handleCancel();\n }\n };\n\n const { focusWithinProps } = useFocusWithin({\n onBlurWithin: () => isEditing && handleCancel(),\n onFocusWithinChange: (isFocusWithin) => setFocusWithin(isFocusWithin),\n });\n\n const { buttonProps } = useButton(\n {\n 'aria-label': editButtonLabel,\n elementType: 'span',\n onPress: handleEdit,\n },\n editRef\n );\n\n const renderReadContent = hideEdit ? (\n <span ref={editRef} {...buttonProps} className={readButton}>\n {children}\n </span>\n ) : (\n <>\n {children}\n <IconButton\n ref={editRef}\n icon={<Icon name=\"edit\" />}\n aria-label={editButtonLabel}\n size=\"small\"\n onClick={handleEdit}\n />\n </>\n );\n\n const inputProps = {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n };\n\n const inputChildren = renderInput.props.children;\n\n const input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, inputChildren ? {} : inputProps),\n inputChildren &&\n Children.map(inputChildren, (child) =>\n child.type === TextField || child.type === TextArea\n ? cloneElement(child, mergeProps(child.props, inputProps))\n : child\n )\n );\n\n return isEditing ? (\n <div\n {...rest}\n className={cx(container, inline({ layout }), className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {input}\n <ButtonGroup spacing=\"compact\">\n <IconButton\n kind=\"primary\"\n icon={<Icon name=\"check\" />}\n aria-label={confirmButtonLabel}\n onClick={handleConfirm}\n />\n <IconButton\n kind=\"default\"\n icon={<Icon name=\"cancel\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div\n {...rest}\n className={cx(!hideEdit && container, className)}\n data-test-id={testId}\n {...focusWithinProps}\n >\n {renderReadContent}\n </div>\n );\n }\n);\n\nInlineEdit.displayName = 'InlineEdit';\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":["forwardRef","jsx","TextField","useState","useRef","useUpdateEffect","focusSafely","useFocusWithin","useButton","jsxs","Fragment","IconButton","Icon","mergeRefs","mergeProps","Children","TextArea","cloneElement","cx","ButtonGroup"],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@launchpad-ui/inline-edit",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.9",
|
4
4
|
"status": "alpha",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
@@ -32,11 +32,11 @@
|
|
32
32
|
"@react-aria/utils": "3.22.0",
|
33
33
|
"@vanilla-extract/recipes": "^0.5.0",
|
34
34
|
"classix": "2.1.17",
|
35
|
-
"@launchpad-ui/button": "~0.11.
|
36
|
-
"@launchpad-ui/form": "~0.10.
|
37
|
-
"@launchpad-ui/icons": "~0.14.
|
38
|
-
"@launchpad-ui/tokens": "~0.9.
|
39
|
-
"@launchpad-ui/vars": "~0.2.
|
35
|
+
"@launchpad-ui/button": "~0.11.8",
|
36
|
+
"@launchpad-ui/form": "~0.10.9",
|
37
|
+
"@launchpad-ui/icons": "~0.14.8",
|
38
|
+
"@launchpad-ui/tokens": "~0.9.2",
|
39
|
+
"@launchpad-ui/vars": "~0.2.9"
|
40
40
|
},
|
41
41
|
"peerDependencies": {
|
42
42
|
"@vanilla-extract/css": "^1.14.0",
|