@launchpad-ui/inline-edit 0.1.0 → 0.1.1

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.
@@ -1,9 +1,9 @@
1
1
  import type { InlineVariants } from './styles/InlineEdit.css';
2
2
  import type { TextAreaProps, TextFieldProps } from '@launchpad-ui/form';
3
- import type { ComponentProps, Dispatch, ReactElement, SetStateAction } from 'react';
3
+ import type { ComponentProps, ReactElement } from 'react';
4
4
  type InlineEditProps = ComponentProps<'div'> & InlineVariants & Pick<ComponentProps<'input'>, 'defaultValue'> & {
5
5
  'data-test-id'?: string;
6
- onConfirm: Dispatch<SetStateAction<string>>;
6
+ onConfirm: (value: string) => void;
7
7
  hideEdit?: boolean;
8
8
  renderInput?: ReactElement<TextFieldProps | TextAreaProps>;
9
9
  isEditing?: boolean;
@@ -13,7 +13,20 @@ type InlineEditProps = ComponentProps<'div'> & InlineVariants & Pick<ComponentPr
13
13
  editButtonLabel?: string;
14
14
  confirmButtonLabel?: string;
15
15
  };
16
- declare const InlineEdit: ({ "data-test-id": testId, layout, children, defaultValue, onConfirm, hideEdit, renderInput, "aria-label": ariaLabel, isEditing: isEditingProp, onCancel, onEdit, cancelButtonLabel, editButtonLabel, confirmButtonLabel, }: InlineEditProps) => import("react/jsx-runtime").JSX.Element;
16
+ declare const InlineEdit: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & {
17
+ layout?: "horizontal" | "vertical" | undefined;
18
+ } & Pick<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "defaultValue"> & {
19
+ 'data-test-id'?: string | undefined;
20
+ onConfirm: (value: string) => void;
21
+ hideEdit?: boolean | undefined;
22
+ renderInput?: ReactElement<TextFieldProps | TextAreaProps, string | import("react").JSXElementConstructor<any>> | undefined;
23
+ isEditing?: boolean | undefined;
24
+ onCancel?: (() => void) | undefined;
25
+ onEdit?: (() => void) | undefined;
26
+ cancelButtonLabel?: string | undefined;
27
+ editButtonLabel?: string | undefined;
28
+ confirmButtonLabel?: string | undefined;
29
+ }, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
17
30
  export { InlineEdit };
18
31
  export type { InlineEditProps };
19
32
  //# sourceMappingURL=InlineEdit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InlineEdit.d.ts","sourceRoot":"","sources":["../src/InlineEdit.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EACV,cAAc,EACd,QAAQ,EAER,YAAY,EACZ,cAAc,EACf,MAAM,OAAO,CAAC;AAcf,KAAK,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,GAC1C,cAAc,GACd,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,GAAG;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEJ,QAAA,MAAM,UAAU,+NAeb,eAAe,4CA+GjB,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"InlineEdit.d.ts","sourceRoot":"","sources":["../src/InlineEdit.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAwB,YAAY,EAAE,MAAM,OAAO,CAAC;AAchF,KAAK,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,GAC1C,cAAc,GACd,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,GAAG;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEJ,QAAA,MAAM,UAAU;;;;uBAXO,MAAM,KAAK,IAAI;;;;sBAIjB,IAAI;oBACN,IAAI;;;;4DAqJtB,CAAC;AAIF,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC"}
package/dist/index.es.js CHANGED
@@ -6,127 +6,150 @@ import { Icon } from "@launchpad-ui/icons";
6
6
  import { useButton } from "@react-aria/button";
7
7
  import { focusSafely } from "@react-aria/focus";
8
8
  import { useFocusWithin } from "@react-aria/interactions";
9
- import { useUpdateEffect, mergeProps } from "@react-aria/utils";
9
+ import { useUpdateEffect, mergeProps, mergeRefs } from "@react-aria/utils";
10
10
  import { cx } from "classix";
11
- import { useState, useRef, cloneElement } from "react";
11
+ import { forwardRef, useState, useRef, cloneElement } from "react";
12
12
  import { createRuntimeFn } from "@vanilla-extract/recipes/createRuntimeFn";
13
13
  const InlineEdit_css_ts_vanilla = "";
14
14
  var cancelButton = "_1oig0624";
15
15
  var container = "_1oig0620";
16
16
  var inline = createRuntimeFn({ defaultClassName: "_1oig0621", variantClassNames: { layout: { vertical: "_1oig0622", horizontal: "_1oig0623" } }, defaultVariants: {}, compoundVariants: [] });
17
17
  var readButton = "_1oig0625";
18
- const InlineEdit = ({
19
- "data-test-id": testId = "inline-edit",
20
- layout = "horizontal",
21
- children,
22
- defaultValue,
23
- onConfirm,
24
- hideEdit = false,
25
- renderInput = /* @__PURE__ */ jsx(TextField, {}),
26
- "aria-label": ariaLabel,
27
- isEditing: isEditingProp,
28
- onCancel,
29
- onEdit,
30
- cancelButtonLabel = "cancel",
31
- editButtonLabel = "edit",
32
- confirmButtonLabel = "confirm"
33
- }) => {
34
- const [isEditing, setEditing] = useState(isEditingProp ?? false);
35
- const [isFocusWithin, setFocusWithin] = useState(false);
36
- const inputRef = useRef(null);
37
- const editRef = useRef(null);
38
- const controlled = isEditingProp !== void 0;
39
- useUpdateEffect(() => {
40
- if (controlled) {
41
- setEditing(isEditingProp);
42
- }
43
- }, [isEditingProp]);
44
- useUpdateEffect(() => {
45
- if (isFocusWithin) {
46
- isEditing ? inputRef.current && focusSafely(inputRef.current) : editRef.current && focusSafely(editRef.current);
47
- }
48
- }, [isEditing]);
49
- const handleEdit = () => {
50
- !controlled && setEditing(true);
51
- onEdit == null ? void 0 : onEdit();
52
- };
53
- const handleCancel = () => {
54
- !controlled && setEditing(false);
55
- onCancel == null ? void 0 : onCancel();
56
- };
57
- const handleConfirm = () => {
58
- var _a;
59
- onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
60
- !controlled && setEditing(false);
61
- };
62
- const handleKeyDown = (event) => {
63
- if (event.key === "Enter") {
64
- event.preventDefault();
65
- handleConfirm();
66
- } else if (event.key === "Escape") {
67
- event.preventDefault();
68
- handleCancel();
69
- }
70
- };
71
- const { focusWithinProps } = useFocusWithin({
72
- onBlurWithin: () => isEditing && handleCancel(),
73
- onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
74
- });
75
- const { buttonProps } = useButton(
76
- {
77
- "aria-label": editButtonLabel,
78
- elementType: "span",
79
- onPress: handleEdit
80
- },
81
- editRef
82
- );
83
- const renderReadContent = hideEdit ? /* @__PURE__ */ jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxs(Fragment, { children: [
18
+ const InlineEdit = forwardRef(
19
+ ({
20
+ "data-test-id": testId = "inline-edit",
21
+ layout = "horizontal",
84
22
  children,
85
- /* @__PURE__ */ jsx(
86
- IconButton,
23
+ defaultValue,
24
+ onConfirm,
25
+ hideEdit = false,
26
+ renderInput = /* @__PURE__ */ jsx(TextField, {}),
27
+ "aria-label": ariaLabel,
28
+ isEditing: isEditingProp,
29
+ onCancel,
30
+ onEdit,
31
+ cancelButtonLabel = "cancel",
32
+ editButtonLabel = "edit",
33
+ confirmButtonLabel = "confirm",
34
+ className,
35
+ ...rest
36
+ }, ref) => {
37
+ const [isEditing, setEditing] = useState(isEditingProp ?? false);
38
+ const [isFocusWithin, setFocusWithin] = useState(false);
39
+ const inputRef = useRef(null);
40
+ const editRef = useRef(null);
41
+ const controlled = isEditingProp !== void 0;
42
+ useUpdateEffect(() => {
43
+ if (controlled) {
44
+ setEditing(isEditingProp);
45
+ }
46
+ }, [isEditingProp]);
47
+ useUpdateEffect(() => {
48
+ if (isFocusWithin) {
49
+ isEditing ? inputRef.current && focusSafely(inputRef.current) : editRef.current && focusSafely(editRef.current);
50
+ }
51
+ }, [isEditing]);
52
+ const handleEdit = () => {
53
+ !controlled && setEditing(true);
54
+ onEdit == null ? void 0 : onEdit();
55
+ };
56
+ const handleCancel = () => {
57
+ !controlled && setEditing(false);
58
+ onCancel == null ? void 0 : onCancel();
59
+ };
60
+ const handleConfirm = () => {
61
+ var _a;
62
+ onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
63
+ !controlled && setEditing(false);
64
+ };
65
+ const handleKeyDown = (event) => {
66
+ if (event.key === "Enter") {
67
+ event.preventDefault();
68
+ handleConfirm();
69
+ } else if (event.key === "Escape") {
70
+ event.preventDefault();
71
+ handleCancel();
72
+ }
73
+ };
74
+ const { focusWithinProps } = useFocusWithin({
75
+ onBlurWithin: () => isEditing && handleCancel(),
76
+ onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
77
+ });
78
+ const { buttonProps } = useButton(
87
79
  {
88
- ref: editRef,
89
- icon: /* @__PURE__ */ jsx(Icon, { name: "edit" }),
90
80
  "aria-label": editButtonLabel,
91
- size: "small",
92
- onClick: handleEdit
93
- }
94
- )
95
- ] });
96
- const input = cloneElement(
97
- renderInput,
98
- mergeProps(renderInput.props, {
99
- ref: inputRef,
100
- defaultValue,
101
- onKeyDown: handleKeyDown,
102
- "aria-label": ariaLabel
103
- })
104
- );
105
- return isEditing ? /* @__PURE__ */ jsxs("div", { className: cx(container, inline({ layout })), "data-test-id": testId, ...focusWithinProps, children: [
106
- input,
107
- /* @__PURE__ */ jsxs(ButtonGroup, { spacing: "compact", children: [
81
+ elementType: "span",
82
+ onPress: handleEdit
83
+ },
84
+ editRef
85
+ );
86
+ const renderReadContent = hideEdit ? /* @__PURE__ */ jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxs(Fragment, { children: [
87
+ children,
108
88
  /* @__PURE__ */ jsx(
109
89
  IconButton,
110
90
  {
111
- kind: "primary",
112
- icon: /* @__PURE__ */ jsx(Icon, { name: "check" }),
113
- "aria-label": confirmButtonLabel,
114
- onClick: handleConfirm
115
- }
116
- ),
117
- /* @__PURE__ */ jsx(
118
- IconButton,
119
- {
120
- kind: "default",
121
- icon: /* @__PURE__ */ jsx(Icon, { name: "close" }),
122
- "aria-label": cancelButtonLabel,
123
- className: cancelButton,
124
- onClick: handleCancel
91
+ ref: editRef,
92
+ icon: /* @__PURE__ */ jsx(Icon, { name: "edit" }),
93
+ "aria-label": editButtonLabel,
94
+ size: "small",
95
+ onClick: handleEdit
125
96
  }
126
97
  )
127
- ] })
128
- ] }) : /* @__PURE__ */ jsx("div", { className: cx(!hideEdit && container), "data-test-id": testId, ...focusWithinProps, children: renderReadContent });
129
- };
98
+ ] });
99
+ const input = cloneElement(
100
+ renderInput,
101
+ mergeProps(renderInput.props, {
102
+ ref: mergeRefs(inputRef, ref),
103
+ defaultValue,
104
+ onKeyDown: handleKeyDown,
105
+ "aria-label": ariaLabel
106
+ })
107
+ );
108
+ return isEditing ? /* @__PURE__ */ jsxs(
109
+ "div",
110
+ {
111
+ ...rest,
112
+ className: cx(container, inline({ layout }), className),
113
+ "data-test-id": testId,
114
+ ...focusWithinProps,
115
+ children: [
116
+ input,
117
+ /* @__PURE__ */ jsxs(ButtonGroup, { spacing: "compact", children: [
118
+ /* @__PURE__ */ jsx(
119
+ IconButton,
120
+ {
121
+ kind: "primary",
122
+ icon: /* @__PURE__ */ jsx(Icon, { name: "check" }),
123
+ "aria-label": confirmButtonLabel,
124
+ onClick: handleConfirm
125
+ }
126
+ ),
127
+ /* @__PURE__ */ jsx(
128
+ IconButton,
129
+ {
130
+ kind: "default",
131
+ icon: /* @__PURE__ */ jsx(Icon, { name: "close" }),
132
+ "aria-label": cancelButtonLabel,
133
+ className: cancelButton,
134
+ onClick: handleCancel
135
+ }
136
+ )
137
+ ] })
138
+ ]
139
+ }
140
+ ) : /* @__PURE__ */ jsx(
141
+ "div",
142
+ {
143
+ ...rest,
144
+ className: cx(!hideEdit && container, className),
145
+ "data-test-id": testId,
146
+ ...focusWithinProps,
147
+ children: renderReadContent
148
+ }
149
+ );
150
+ }
151
+ );
152
+ InlineEdit.displayName = "InlineEdit";
130
153
  export {
131
154
  InlineEdit
132
155
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type {\n ComponentProps,\n Dispatch,\n KeyboardEventHandler,\n ReactElement,\n SetStateAction,\n} from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField } 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, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { cloneElement, 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: Dispatch<SetStateAction<string>>;\n hideEdit?: boolean;\n renderInput?: ReactElement<TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = ({\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}: InlineEditProps) => {\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 input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, {\n ref: inputRef,\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n })\n );\n\n return isEditing ? (\n <div className={cx(container, inline({ layout }))} data-test-id={testId} {...focusWithinProps}>\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=\"close\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div className={cx(!hideEdit && container)} data-test-id={testId} {...focusWithinProps}>\n {renderReadContent}\n </div>\n );\n};\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":["isFocusWithin"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField } 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 { 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<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 input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\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=\"close\" />}\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":["isFocusWithin"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -17,117 +17,140 @@ var cancelButton = "_1oig0624";
17
17
  var container = "_1oig0620";
18
18
  var inline = createRuntimeFn.createRuntimeFn({ defaultClassName: "_1oig0621", variantClassNames: { layout: { vertical: "_1oig0622", horizontal: "_1oig0623" } }, defaultVariants: {}, compoundVariants: [] });
19
19
  var readButton = "_1oig0625";
20
- const InlineEdit = ({
21
- "data-test-id": testId = "inline-edit",
22
- layout = "horizontal",
23
- children,
24
- defaultValue,
25
- onConfirm,
26
- hideEdit = false,
27
- renderInput = /* @__PURE__ */ jsxRuntime.jsx(form.TextField, {}),
28
- "aria-label": ariaLabel,
29
- isEditing: isEditingProp,
30
- onCancel,
31
- onEdit,
32
- cancelButtonLabel = "cancel",
33
- editButtonLabel = "edit",
34
- confirmButtonLabel = "confirm"
35
- }) => {
36
- const [isEditing, setEditing] = react.useState(isEditingProp ?? false);
37
- const [isFocusWithin, setFocusWithin] = react.useState(false);
38
- const inputRef = react.useRef(null);
39
- const editRef = react.useRef(null);
40
- const controlled = isEditingProp !== void 0;
41
- utils.useUpdateEffect(() => {
42
- if (controlled) {
43
- setEditing(isEditingProp);
44
- }
45
- }, [isEditingProp]);
46
- utils.useUpdateEffect(() => {
47
- if (isFocusWithin) {
48
- isEditing ? inputRef.current && focus.focusSafely(inputRef.current) : editRef.current && focus.focusSafely(editRef.current);
49
- }
50
- }, [isEditing]);
51
- const handleEdit = () => {
52
- !controlled && setEditing(true);
53
- onEdit == null ? void 0 : onEdit();
54
- };
55
- const handleCancel = () => {
56
- !controlled && setEditing(false);
57
- onCancel == null ? void 0 : onCancel();
58
- };
59
- const handleConfirm = () => {
60
- var _a;
61
- onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
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 } = interactions.useFocusWithin({
74
- onBlurWithin: () => isEditing && handleCancel(),
75
- onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
76
- });
77
- const { buttonProps } = button.useButton(
78
- {
79
- "aria-label": editButtonLabel,
80
- elementType: "span",
81
- onPress: handleEdit
82
- },
83
- editRef
84
- );
85
- const renderReadContent = hideEdit ? /* @__PURE__ */ jsxRuntime.jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20
+ const InlineEdit = react.forwardRef(
21
+ ({
22
+ "data-test-id": testId = "inline-edit",
23
+ layout = "horizontal",
86
24
  children,
87
- /* @__PURE__ */ jsxRuntime.jsx(
88
- button$1.IconButton,
25
+ defaultValue,
26
+ onConfirm,
27
+ hideEdit = false,
28
+ renderInput = /* @__PURE__ */ jsxRuntime.jsx(form.TextField, {}),
29
+ "aria-label": ariaLabel,
30
+ isEditing: isEditingProp,
31
+ onCancel,
32
+ onEdit,
33
+ cancelButtonLabel = "cancel",
34
+ editButtonLabel = "edit",
35
+ confirmButtonLabel = "confirm",
36
+ className,
37
+ ...rest
38
+ }, ref) => {
39
+ const [isEditing, setEditing] = react.useState(isEditingProp ?? false);
40
+ const [isFocusWithin, setFocusWithin] = react.useState(false);
41
+ const inputRef = react.useRef(null);
42
+ const editRef = react.useRef(null);
43
+ const controlled = isEditingProp !== void 0;
44
+ utils.useUpdateEffect(() => {
45
+ if (controlled) {
46
+ setEditing(isEditingProp);
47
+ }
48
+ }, [isEditingProp]);
49
+ utils.useUpdateEffect(() => {
50
+ if (isFocusWithin) {
51
+ isEditing ? inputRef.current && focus.focusSafely(inputRef.current) : editRef.current && focus.focusSafely(editRef.current);
52
+ }
53
+ }, [isEditing]);
54
+ const handleEdit = () => {
55
+ !controlled && setEditing(true);
56
+ onEdit == null ? void 0 : onEdit();
57
+ };
58
+ const handleCancel = () => {
59
+ !controlled && setEditing(false);
60
+ onCancel == null ? void 0 : onCancel();
61
+ };
62
+ const handleConfirm = () => {
63
+ var _a;
64
+ onConfirm(((_a = inputRef.current) == null ? void 0 : _a.value) || "");
65
+ !controlled && setEditing(false);
66
+ };
67
+ const handleKeyDown = (event) => {
68
+ if (event.key === "Enter") {
69
+ event.preventDefault();
70
+ handleConfirm();
71
+ } else if (event.key === "Escape") {
72
+ event.preventDefault();
73
+ handleCancel();
74
+ }
75
+ };
76
+ const { focusWithinProps } = interactions.useFocusWithin({
77
+ onBlurWithin: () => isEditing && handleCancel(),
78
+ onFocusWithinChange: (isFocusWithin2) => setFocusWithin(isFocusWithin2)
79
+ });
80
+ const { buttonProps } = button.useButton(
89
81
  {
90
- ref: editRef,
91
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "edit" }),
92
82
  "aria-label": editButtonLabel,
93
- size: "small",
94
- onClick: handleEdit
95
- }
96
- )
97
- ] });
98
- const input = react.cloneElement(
99
- renderInput,
100
- utils.mergeProps(renderInput.props, {
101
- ref: inputRef,
102
- defaultValue,
103
- onKeyDown: handleKeyDown,
104
- "aria-label": ariaLabel
105
- })
106
- );
107
- return isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classix.cx(container, inline({ layout })), "data-test-id": testId, ...focusWithinProps, children: [
108
- input,
109
- /* @__PURE__ */ jsxRuntime.jsxs(button$1.ButtonGroup, { spacing: "compact", children: [
110
- /* @__PURE__ */ jsxRuntime.jsx(
111
- button$1.IconButton,
112
- {
113
- kind: "primary",
114
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "check" }),
115
- "aria-label": confirmButtonLabel,
116
- onClick: handleConfirm
117
- }
118
- ),
83
+ elementType: "span",
84
+ onPress: handleEdit
85
+ },
86
+ editRef
87
+ );
88
+ const renderReadContent = hideEdit ? /* @__PURE__ */ jsxRuntime.jsx("span", { ref: editRef, ...buttonProps, className: readButton, children }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
89
+ children,
119
90
  /* @__PURE__ */ jsxRuntime.jsx(
120
91
  button$1.IconButton,
121
92
  {
122
- kind: "default",
123
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "close" }),
124
- "aria-label": cancelButtonLabel,
125
- className: cancelButton,
126
- onClick: handleCancel
93
+ ref: editRef,
94
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "edit" }),
95
+ "aria-label": editButtonLabel,
96
+ size: "small",
97
+ onClick: handleEdit
127
98
  }
128
99
  )
129
- ] })
130
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: classix.cx(!hideEdit && container), "data-test-id": testId, ...focusWithinProps, children: renderReadContent });
131
- };
100
+ ] });
101
+ const input = react.cloneElement(
102
+ renderInput,
103
+ utils.mergeProps(renderInput.props, {
104
+ ref: utils.mergeRefs(inputRef, ref),
105
+ defaultValue,
106
+ onKeyDown: handleKeyDown,
107
+ "aria-label": ariaLabel
108
+ })
109
+ );
110
+ return isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(
111
+ "div",
112
+ {
113
+ ...rest,
114
+ className: classix.cx(container, inline({ layout }), className),
115
+ "data-test-id": testId,
116
+ ...focusWithinProps,
117
+ children: [
118
+ input,
119
+ /* @__PURE__ */ jsxRuntime.jsxs(button$1.ButtonGroup, { spacing: "compact", children: [
120
+ /* @__PURE__ */ jsxRuntime.jsx(
121
+ button$1.IconButton,
122
+ {
123
+ kind: "primary",
124
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "check" }),
125
+ "aria-label": confirmButtonLabel,
126
+ onClick: handleConfirm
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsxRuntime.jsx(
130
+ button$1.IconButton,
131
+ {
132
+ kind: "default",
133
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Icon, { name: "close" }),
134
+ "aria-label": cancelButtonLabel,
135
+ className: cancelButton,
136
+ onClick: handleCancel
137
+ }
138
+ )
139
+ ] })
140
+ ]
141
+ }
142
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
143
+ "div",
144
+ {
145
+ ...rest,
146
+ className: classix.cx(!hideEdit && container, className),
147
+ "data-test-id": testId,
148
+ ...focusWithinProps,
149
+ children: renderReadContent
150
+ }
151
+ );
152
+ }
153
+ );
154
+ InlineEdit.displayName = "InlineEdit";
132
155
  exports.InlineEdit = InlineEdit;
133
156
  //# 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 { TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type {\n ComponentProps,\n Dispatch,\n KeyboardEventHandler,\n ReactElement,\n SetStateAction,\n} from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField } 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, useUpdateEffect } from '@react-aria/utils';\nimport { cx } from 'classix';\nimport { cloneElement, 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: Dispatch<SetStateAction<string>>;\n hideEdit?: boolean;\n renderInput?: ReactElement<TextFieldProps | TextAreaProps>;\n isEditing?: boolean;\n onCancel?: () => void;\n onEdit?: () => void;\n cancelButtonLabel?: string;\n editButtonLabel?: string;\n confirmButtonLabel?: string;\n };\n\nconst InlineEdit = ({\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}: InlineEditProps) => {\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 input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, {\n ref: inputRef,\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\n })\n );\n\n return isEditing ? (\n <div className={cx(container, inline({ layout }))} data-test-id={testId} {...focusWithinProps}>\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=\"close\" />}\n aria-label={cancelButtonLabel}\n className={cancelButton}\n onClick={handleCancel}\n />\n </ButtonGroup>\n </div>\n ) : (\n <div className={cx(!hideEdit && container)} data-test-id={testId} {...focusWithinProps}>\n {renderReadContent}\n </div>\n );\n};\n\nexport { InlineEdit };\nexport type { InlineEditProps };\n"],"names":["TextField","useState","useRef","useUpdateEffect","focusSafely","useFocusWithin","isFocusWithin","useButton","jsx","jsxs","Fragment","IconButton","Icon","cloneElement","mergeProps","cx","ButtonGroup"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/InlineEdit.tsx"],"sourcesContent":["import type { InlineVariants } from './styles/InlineEdit.css';\nimport type { TextAreaProps, TextFieldProps } from '@launchpad-ui/form';\nimport type { ComponentProps, KeyboardEventHandler, ReactElement } from 'react';\n\nimport { ButtonGroup, IconButton } from '@launchpad-ui/button';\nimport { TextField } 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 { 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<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 input = cloneElement(\n renderInput,\n mergeProps(renderInput.props, {\n ref: mergeRefs(inputRef, ref),\n defaultValue,\n onKeyDown: handleKeyDown,\n 'aria-label': ariaLabel,\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=\"close\" />}\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","isFocusWithin","useButton","jsx","jsxs","Fragment","IconButton","Icon","cloneElement","mergeProps","mergeRefs","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.1.0",
3
+ "version": "0.1.1",
4
4
  "status": "alpha",
5
5
  "publishConfig": {
6
6
  "access": "public"