@elementor/editor-ui 0.3.0 → 0.4.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +33 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/introduction-modal.tsx +11 -1
- package/src/hooks/__tests__/use-editable.test.ts +51 -0
- package/src/hooks/use-editable.ts +9 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @elementor/editor-ui@0.
|
|
2
|
+
> @elementor/editor-ui@0.4.1 build
|
|
3
3
|
> tsup --config=../../tsup.build.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m7.34 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m11.09 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 117ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m5.45 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m10.80 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 129ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 24603ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.75 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.75 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @elementor/editor-ui
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- de85397: Add entrance animation to introduction modal
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- b001371: Prevent editable field validation for initial value.
|
|
14
|
+
|
|
3
15
|
## 0.3.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -20,7 +20,7 @@ declare const IntroductionModal: ({ open, handleClose, title, content }: Introdu
|
|
|
20
20
|
type UseEditableParams = {
|
|
21
21
|
value: string;
|
|
22
22
|
onSubmit: (value: string) => unknown;
|
|
23
|
-
validation?: (value: string) => string |
|
|
23
|
+
validation?: (value: string) => string | null;
|
|
24
24
|
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
25
25
|
};
|
|
26
26
|
declare const useEditable: ({ value, onSubmit, validation, onClick }: UseEditableParams) => {
|
|
@@ -29,7 +29,7 @@ declare const useEditable: ({ value, onSubmit, validation, onClick }: UseEditabl
|
|
|
29
29
|
readonly openEditMode: () => void;
|
|
30
30
|
readonly closeEditMode: () => void;
|
|
31
31
|
readonly value: string;
|
|
32
|
-
readonly error: string | null
|
|
32
|
+
readonly error: string | null;
|
|
33
33
|
readonly getProps: () => {
|
|
34
34
|
suppressContentEditableWarning?: boolean | undefined;
|
|
35
35
|
value: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ declare const IntroductionModal: ({ open, handleClose, title, content }: Introdu
|
|
|
20
20
|
type UseEditableParams = {
|
|
21
21
|
value: string;
|
|
22
22
|
onSubmit: (value: string) => unknown;
|
|
23
|
-
validation?: (value: string) => string |
|
|
23
|
+
validation?: (value: string) => string | null;
|
|
24
24
|
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
25
25
|
};
|
|
26
26
|
declare const useEditable: ({ value, onSubmit, validation, onClick }: UseEditableParams) => {
|
|
@@ -29,7 +29,7 @@ declare const useEditable: ({ value, onSubmit, validation, onClick }: UseEditabl
|
|
|
29
29
|
readonly openEditMode: () => void;
|
|
30
30
|
readonly closeEditMode: () => void;
|
|
31
31
|
readonly value: string;
|
|
32
|
-
readonly error: string | null
|
|
32
|
+
readonly error: string | null;
|
|
33
33
|
readonly getProps: () => {
|
|
34
34
|
suppressContentEditableWarning?: boolean | undefined;
|
|
35
35
|
value: string;
|
package/dist/index.js
CHANGED
|
@@ -96,22 +96,37 @@ var React3 = __toESM(require("react"));
|
|
|
96
96
|
var import_react3 = require("react");
|
|
97
97
|
var import_ui3 = require("@elementor/ui");
|
|
98
98
|
var import_i18n = require("@wordpress/i18n");
|
|
99
|
+
var OPEN_DELAY = 1e3;
|
|
99
100
|
var IntroductionModal = ({ open, handleClose, title, content }) => {
|
|
100
101
|
const [shouldShowAgain, setShouldShowAgain] = (0, import_react3.useState)(true);
|
|
101
|
-
return /* @__PURE__ */ React3.createElement(
|
|
102
|
-
import_ui3.
|
|
102
|
+
return /* @__PURE__ */ React3.createElement(
|
|
103
|
+
import_ui3.Dialog,
|
|
103
104
|
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
105
|
+
open,
|
|
106
|
+
onClose: handleClose,
|
|
107
|
+
maxWidth: "sm",
|
|
108
|
+
TransitionProps: {
|
|
109
|
+
timeout: 700,
|
|
110
|
+
style: { transitionDelay: open ? `${OPEN_DELAY}ms` : "0ms" }
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
/* @__PURE__ */ React3.createElement(import_ui3.DialogHeader, { logo: false }, /* @__PURE__ */ React3.createElement(import_ui3.DialogTitle, null, title)),
|
|
114
|
+
content,
|
|
115
|
+
/* @__PURE__ */ React3.createElement(import_ui3.DialogActions, null, /* @__PURE__ */ React3.createElement(
|
|
116
|
+
import_ui3.FormControlLabel,
|
|
117
|
+
{
|
|
118
|
+
sx: { marginRight: "auto" },
|
|
119
|
+
control: /* @__PURE__ */ React3.createElement(
|
|
120
|
+
import_ui3.Checkbox,
|
|
121
|
+
{
|
|
122
|
+
checked: !shouldShowAgain,
|
|
123
|
+
onChange: () => setShouldShowAgain(!shouldShowAgain)
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
label: (0, import_i18n.__)("Don't show this again")
|
|
127
|
+
}
|
|
128
|
+
), /* @__PURE__ */ React3.createElement(import_ui3.Button, { size: "medium", onClick: () => handleClose(shouldShowAgain), variant: "contained" }, (0, import_i18n.__)("Got it")))
|
|
129
|
+
);
|
|
115
130
|
};
|
|
116
131
|
|
|
117
132
|
// src/hooks/use-editable.ts
|
|
@@ -120,6 +135,7 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
120
135
|
const [isEditing, setIsEditing] = (0, import_react4.useState)(false);
|
|
121
136
|
const [error, setError] = (0, import_react4.useState)(null);
|
|
122
137
|
const ref = useSelection(isEditing);
|
|
138
|
+
const isDirty = (newValue) => newValue !== value;
|
|
123
139
|
const openEditMode = () => {
|
|
124
140
|
setIsEditing(true);
|
|
125
141
|
};
|
|
@@ -129,6 +145,9 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
129
145
|
setIsEditing(false);
|
|
130
146
|
};
|
|
131
147
|
const submit = (newValue) => {
|
|
148
|
+
if (!isDirty(newValue)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
132
151
|
if (!error) {
|
|
133
152
|
try {
|
|
134
153
|
onSubmit(newValue);
|
|
@@ -140,7 +159,7 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
140
159
|
const onChange = (event) => {
|
|
141
160
|
const { innerText: newValue } = event.target;
|
|
142
161
|
if (validation) {
|
|
143
|
-
setError(validation(newValue));
|
|
162
|
+
setError(isDirty(newValue) ? validation(newValue) : null);
|
|
144
163
|
}
|
|
145
164
|
};
|
|
146
165
|
const handleKeyDown = (event) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/ellipsis-with-tooltip.tsx","../src/components/editable-field.tsx","../src/components/introduction-modal.tsx","../src/hooks/use-editable.ts"],"sourcesContent":["// components\nexport { EllipsisWithTooltip } from './components/ellipsis-with-tooltip';\nexport { EditableField } from './components/editable-field';\nexport { IntroductionModal } from './components/introduction-modal';\n\n// hooks\nexport { useEditable } from './hooks/use-editable';\n","import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Box, Tooltip } from '@elementor/ui';\n\ntype EllipsisWithTooltipProps< T extends React.ElementType > = {\n\tmaxWidth?: React.CSSProperties[ 'maxWidth' ];\n\ttitle: string;\n\tas?: T;\n} & React.ComponentProps< T >;\n\nexport const EllipsisWithTooltip = < T extends React.ElementType >( {\n\tmaxWidth,\n\ttitle,\n\tas,\n\t...props\n}: EllipsisWithTooltipProps< T > ) => {\n\tconst [ setRef, isOverflowing ] = useIsOverflowing();\n\n\tif ( isOverflowing ) {\n\t\treturn (\n\t\t\t<Tooltip title={ title } placement=\"top\">\n\t\t\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t\t\t{ title }\n\t\t\t\t</Content>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t{ title }\n\t\t</Content>\n\t);\n};\n\ntype ContentProps< T extends React.ElementType > = React.PropsWithChildren<\n\tOmit< EllipsisWithTooltipProps< T >, 'title' >\n>;\n\nconst Content = React.forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ maxWidth, as: Component = Box, ...props }: ContentProps< T >,\n\t\t// forwardRef loses the typing when using generic components.\n\t\tref: unknown\n\t) => (\n\t\t<Component\n\t\t\tref={ ref }\n\t\t\tposition=\"relative\"\n\t\t\t{ ...props }\n\t\t\tstyle={ { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth } }\n\t\t/>\n\t)\n);\n\nconst useIsOverflowing = () => {\n\tconst [ el, setEl ] = useState< HTMLElement | null >( null );\n\tconst [ isOverflowing, setIsOverflown ] = useState( false );\n\n\tuseEffect( () => {\n\t\tconst observer = new ResizeObserver( ( [ { target } ] ) => {\n\t\t\tsetIsOverflown( target.scrollWidth > target.clientWidth );\n\t\t} );\n\n\t\tif ( el ) {\n\t\t\tobserver.observe( el );\n\t\t}\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [ el ] );\n\n\treturn [ setEl, isOverflowing ] as const;\n};\n","import * as React from 'react';\nimport { forwardRef } from 'react';\nimport { Tooltip } from '@elementor/ui';\n\ntype EditableFieldProps< T extends React.ElementType > = {\n\tvalue: string;\n\terror?: string;\n\tas?: T;\n} & React.ComponentPropsWithRef< T >;\n\nexport const EditableField = forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ value, error, as: Component = 'span', sx, ...props }: EditableFieldProps< T >,\n\t\tref: unknown\n\t) => {\n\t\treturn (\n\t\t\t<Tooltip title={ error } open={ !! error } placement=\"top\">\n\t\t\t\t<Component ref={ ref } sx={ { width: '100%', ...sx } } { ...props }>\n\t\t\t\t\t{ value }\n\t\t\t\t</Component>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n);\n","import * as React from 'react';\nimport { useState } from 'react';\nimport { Button, Checkbox, Dialog, DialogActions, DialogHeader, DialogTitle, FormControlLabel } from '@elementor/ui';\nimport { __ } from '@wordpress/i18n';\n\ntype IntroductionModalProps = {\n\topen: boolean;\n\thandleClose: ( shouldShowAgain: boolean ) => void;\n\ttitle: string;\n\tcontent: React.ReactNode;\n};\n\nexport const IntroductionModal = ( { open, handleClose, title, content }: IntroductionModalProps ) => {\n\tconst [ shouldShowAgain, setShouldShowAgain ] = useState( true );\n\n\treturn (\n\t\t<Dialog open={ open } onClose={ handleClose } maxWidth={ 'sm' }>\n\t\t\t<DialogHeader logo={ false }>\n\t\t\t\t<DialogTitle>{ title }</DialogTitle>\n\t\t\t</DialogHeader>\n\t\t\t{ content }\n\t\t\t<DialogActions>\n\t\t\t\t<FormControlLabel\n\t\t\t\t\tsx={ { marginRight: 'auto' } }\n\t\t\t\t\tcontrol={\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={ ! shouldShowAgain }\n\t\t\t\t\t\t\tonChange={ () => setShouldShowAgain( ! shouldShowAgain ) }\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t\tlabel={ __( \"Don't show this again\" ) }\n\t\t\t\t/>\n\t\t\t\t<Button size={ 'medium' } onClick={ () => handleClose( shouldShowAgain ) } variant=\"contained\">\n\t\t\t\t\t{ __( 'Got it' ) }\n\t\t\t\t</Button>\n\t\t\t</DialogActions>\n\t\t</Dialog>\n\t);\n};\n","import { useEffect, useRef, useState } from 'react';\n\ntype UseEditableParams = {\n\tvalue: string;\n\tonSubmit: ( value: string ) => unknown;\n\tvalidation?: ( value: string ) => string | undefined | null;\n\tonClick?: ( event: React.MouseEvent< HTMLDivElement > ) => void;\n};\n\nexport const useEditable = ( { value, onSubmit, validation, onClick }: UseEditableParams ) => {\n\tconst [ isEditing, setIsEditing ] = useState( false );\n\tconst [ error, setError ] = useState< string | null | undefined >( null );\n\n\tconst ref = useSelection( isEditing );\n\n\tconst openEditMode = () => {\n\t\tsetIsEditing( true );\n\t};\n\n\tconst closeEditMode = () => {\n\t\tref.current?.blur();\n\n\t\tsetError( null );\n\t\tsetIsEditing( false );\n\t};\n\n\tconst submit = ( newValue: string ) => {\n\t\tif ( ! error ) {\n\t\t\ttry {\n\t\t\t\tonSubmit( newValue );\n\t\t\t} finally {\n\t\t\t\tcloseEditMode();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst onChange = ( event: React.ChangeEvent< HTMLSpanElement > ) => {\n\t\tconst { innerText: newValue } = event.target;\n\n\t\tif ( validation ) {\n\t\t\tsetError( validation( newValue ) );\n\t\t}\n\t};\n\n\tconst handleKeyDown = ( event: React.KeyboardEvent ) => {\n\t\tevent.stopPropagation();\n\n\t\tif ( [ 'Escape' ].includes( event.key ) ) {\n\t\t\treturn closeEditMode();\n\t\t}\n\n\t\tif ( [ 'Enter' ].includes( event.key ) ) {\n\t\t\tevent.preventDefault();\n\t\t\treturn submit( ( event.target as HTMLElement ).innerText );\n\t\t}\n\t};\n\n\tconst handleClick = ( event: React.MouseEvent< HTMLDivElement > ) => {\n\t\tif ( isEditing ) {\n\t\t\tevent.stopPropagation();\n\t\t}\n\n\t\tonClick?.( event );\n\t};\n\n\tconst listeners = {\n\t\tonClick: handleClick,\n\t\tonKeyDown: handleKeyDown,\n\t\tonInput: onChange,\n\t\tonBlur: closeEditMode,\n\t} as const;\n\n\tconst attributes = {\n\t\tvalue,\n\t\trole: 'textbox',\n\t\tcontentEditable: isEditing,\n\t\t...( isEditing && {\n\t\t\tsuppressContentEditableWarning: true,\n\t\t} ),\n\t} as const;\n\n\treturn {\n\t\tref,\n\t\tisEditing,\n\t\topenEditMode,\n\t\tcloseEditMode,\n\t\tvalue,\n\t\terror,\n\t\tgetProps: () => ( { ...listeners, ...attributes } ),\n\t} as const;\n};\n\nconst useSelection = ( isEditing: boolean ) => {\n\tconst ref = useRef< HTMLElement | null >( null );\n\n\tuseEffect( () => {\n\t\tif ( isEditing ) {\n\t\t\tselectAll( ref.current );\n\t\t}\n\t}, [ isEditing ] );\n\n\treturn ref;\n};\n\nconst selectAll = ( el: HTMLElement | null ) => {\n\tconst selection = getSelection();\n\n\tif ( ! selection || ! el ) {\n\t\treturn;\n\t}\n\n\tconst range = document.createRange();\n\trange.selectNodeContents( el );\n\n\tselection.removeAllRanges();\n\tselection.addRange( range );\n};\n"],"mappings":"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","names":["React","import_react","import_ui","React","import_react","import_ui","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/ellipsis-with-tooltip.tsx","../src/components/editable-field.tsx","../src/components/introduction-modal.tsx","../src/hooks/use-editable.ts"],"sourcesContent":["// components\nexport { EllipsisWithTooltip } from './components/ellipsis-with-tooltip';\nexport { EditableField } from './components/editable-field';\nexport { IntroductionModal } from './components/introduction-modal';\n\n// hooks\nexport { useEditable } from './hooks/use-editable';\n","import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Box, Tooltip } from '@elementor/ui';\n\ntype EllipsisWithTooltipProps< T extends React.ElementType > = {\n\tmaxWidth?: React.CSSProperties[ 'maxWidth' ];\n\ttitle: string;\n\tas?: T;\n} & React.ComponentProps< T >;\n\nexport const EllipsisWithTooltip = < T extends React.ElementType >( {\n\tmaxWidth,\n\ttitle,\n\tas,\n\t...props\n}: EllipsisWithTooltipProps< T > ) => {\n\tconst [ setRef, isOverflowing ] = useIsOverflowing();\n\n\tif ( isOverflowing ) {\n\t\treturn (\n\t\t\t<Tooltip title={ title } placement=\"top\">\n\t\t\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t\t\t{ title }\n\t\t\t\t</Content>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t{ title }\n\t\t</Content>\n\t);\n};\n\ntype ContentProps< T extends React.ElementType > = React.PropsWithChildren<\n\tOmit< EllipsisWithTooltipProps< T >, 'title' >\n>;\n\nconst Content = React.forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ maxWidth, as: Component = Box, ...props }: ContentProps< T >,\n\t\t// forwardRef loses the typing when using generic components.\n\t\tref: unknown\n\t) => (\n\t\t<Component\n\t\t\tref={ ref }\n\t\t\tposition=\"relative\"\n\t\t\t{ ...props }\n\t\t\tstyle={ { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth } }\n\t\t/>\n\t)\n);\n\nconst useIsOverflowing = () => {\n\tconst [ el, setEl ] = useState< HTMLElement | null >( null );\n\tconst [ isOverflowing, setIsOverflown ] = useState( false );\n\n\tuseEffect( () => {\n\t\tconst observer = new ResizeObserver( ( [ { target } ] ) => {\n\t\t\tsetIsOverflown( target.scrollWidth > target.clientWidth );\n\t\t} );\n\n\t\tif ( el ) {\n\t\t\tobserver.observe( el );\n\t\t}\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [ el ] );\n\n\treturn [ setEl, isOverflowing ] as const;\n};\n","import * as React from 'react';\nimport { forwardRef } from 'react';\nimport { Tooltip } from '@elementor/ui';\n\ntype EditableFieldProps< T extends React.ElementType > = {\n\tvalue: string;\n\terror?: string;\n\tas?: T;\n} & React.ComponentPropsWithRef< T >;\n\nexport const EditableField = forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ value, error, as: Component = 'span', sx, ...props }: EditableFieldProps< T >,\n\t\tref: unknown\n\t) => {\n\t\treturn (\n\t\t\t<Tooltip title={ error } open={ !! error } placement=\"top\">\n\t\t\t\t<Component ref={ ref } sx={ { width: '100%', ...sx } } { ...props }>\n\t\t\t\t\t{ value }\n\t\t\t\t</Component>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n);\n","import * as React from 'react';\nimport { useState } from 'react';\nimport { Button, Checkbox, Dialog, DialogActions, DialogHeader, DialogTitle, FormControlLabel } from '@elementor/ui';\nimport { __ } from '@wordpress/i18n';\n\ntype IntroductionModalProps = {\n\topen: boolean;\n\thandleClose: ( shouldShowAgain: boolean ) => void;\n\ttitle: string;\n\tcontent: React.ReactNode;\n};\n\nconst OPEN_DELAY = 1000;\n\nexport const IntroductionModal = ( { open, handleClose, title, content }: IntroductionModalProps ) => {\n\tconst [ shouldShowAgain, setShouldShowAgain ] = useState( true );\n\n\treturn (\n\t\t<Dialog\n\t\t\topen={ open }\n\t\t\tonClose={ handleClose }\n\t\t\tmaxWidth={ 'sm' }\n\t\t\tTransitionProps={ {\n\t\t\t\ttimeout: 700,\n\t\t\t\tstyle: { transitionDelay: open ? `${ OPEN_DELAY }ms` : '0ms' },\n\t\t\t} }\n\t\t>\n\t\t\t<DialogHeader logo={ false }>\n\t\t\t\t<DialogTitle>{ title }</DialogTitle>\n\t\t\t</DialogHeader>\n\t\t\t{ content }\n\t\t\t<DialogActions>\n\t\t\t\t<FormControlLabel\n\t\t\t\t\tsx={ { marginRight: 'auto' } }\n\t\t\t\t\tcontrol={\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={ ! shouldShowAgain }\n\t\t\t\t\t\t\tonChange={ () => setShouldShowAgain( ! shouldShowAgain ) }\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t\tlabel={ __( \"Don't show this again\" ) }\n\t\t\t\t/>\n\t\t\t\t<Button size={ 'medium' } onClick={ () => handleClose( shouldShowAgain ) } variant=\"contained\">\n\t\t\t\t\t{ __( 'Got it' ) }\n\t\t\t\t</Button>\n\t\t\t</DialogActions>\n\t\t</Dialog>\n\t);\n};\n","import { useEffect, useRef, useState } from 'react';\n\ntype UseEditableParams = {\n\tvalue: string;\n\tonSubmit: ( value: string ) => unknown;\n\tvalidation?: ( value: string ) => string | null;\n\tonClick?: ( event: React.MouseEvent< HTMLDivElement > ) => void;\n};\n\nexport const useEditable = ( { value, onSubmit, validation, onClick }: UseEditableParams ) => {\n\tconst [ isEditing, setIsEditing ] = useState( false );\n\tconst [ error, setError ] = useState< string | null >( null );\n\n\tconst ref = useSelection( isEditing );\n\n\tconst isDirty = ( newValue: string ) => newValue !== value;\n\n\tconst openEditMode = () => {\n\t\tsetIsEditing( true );\n\t};\n\n\tconst closeEditMode = () => {\n\t\tref.current?.blur();\n\n\t\tsetError( null );\n\t\tsetIsEditing( false );\n\t};\n\n\tconst submit = ( newValue: string ) => {\n\t\tif ( ! isDirty( newValue ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! error ) {\n\t\t\ttry {\n\t\t\t\tonSubmit( newValue );\n\t\t\t} finally {\n\t\t\t\tcloseEditMode();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst onChange = ( event: React.ChangeEvent< HTMLSpanElement > ) => {\n\t\tconst { innerText: newValue } = event.target;\n\n\t\tif ( validation ) {\n\t\t\tsetError( isDirty( newValue ) ? validation( newValue ) : null );\n\t\t}\n\t};\n\n\tconst handleKeyDown = ( event: React.KeyboardEvent ) => {\n\t\tevent.stopPropagation();\n\n\t\tif ( [ 'Escape' ].includes( event.key ) ) {\n\t\t\treturn closeEditMode();\n\t\t}\n\n\t\tif ( [ 'Enter' ].includes( event.key ) ) {\n\t\t\tevent.preventDefault();\n\t\t\treturn submit( ( event.target as HTMLElement ).innerText );\n\t\t}\n\t};\n\n\tconst handleClick = ( event: React.MouseEvent< HTMLDivElement > ) => {\n\t\tif ( isEditing ) {\n\t\t\tevent.stopPropagation();\n\t\t}\n\n\t\tonClick?.( event );\n\t};\n\n\tconst listeners = {\n\t\tonClick: handleClick,\n\t\tonKeyDown: handleKeyDown,\n\t\tonInput: onChange,\n\t\tonBlur: closeEditMode,\n\t} as const;\n\n\tconst attributes = {\n\t\tvalue,\n\t\trole: 'textbox',\n\t\tcontentEditable: isEditing,\n\t\t...( isEditing && {\n\t\t\tsuppressContentEditableWarning: true,\n\t\t} ),\n\t} as const;\n\n\treturn {\n\t\tref,\n\t\tisEditing,\n\t\topenEditMode,\n\t\tcloseEditMode,\n\t\tvalue,\n\t\terror,\n\t\tgetProps: () => ( { ...listeners, ...attributes } ),\n\t} as const;\n};\n\nconst useSelection = ( isEditing: boolean ) => {\n\tconst ref = useRef< HTMLElement | null >( null );\n\n\tuseEffect( () => {\n\t\tif ( isEditing ) {\n\t\t\tselectAll( ref.current );\n\t\t}\n\t}, [ isEditing ] );\n\n\treturn ref;\n};\n\nconst selectAll = ( el: HTMLElement | null ) => {\n\tconst selection = getSelection();\n\n\tif ( ! selection || ! el ) {\n\t\treturn;\n\t}\n\n\tconst range = document.createRange();\n\trange.selectNodeContents( el );\n\n\tselection.removeAllRanges();\n\tselection.addRange( range );\n};\n"],"mappings":"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","names":["React","import_react","import_ui","React","import_react","import_ui","import_react"]}
|
package/dist/index.mjs
CHANGED
|
@@ -57,22 +57,37 @@ import * as React3 from "react";
|
|
|
57
57
|
import { useState as useState2 } from "react";
|
|
58
58
|
import { Button, Checkbox, Dialog, DialogActions, DialogHeader, DialogTitle, FormControlLabel } from "@elementor/ui";
|
|
59
59
|
import { __ } from "@wordpress/i18n";
|
|
60
|
+
var OPEN_DELAY = 1e3;
|
|
60
61
|
var IntroductionModal = ({ open, handleClose, title, content }) => {
|
|
61
62
|
const [shouldShowAgain, setShouldShowAgain] = useState2(true);
|
|
62
|
-
return /* @__PURE__ */ React3.createElement(
|
|
63
|
-
|
|
63
|
+
return /* @__PURE__ */ React3.createElement(
|
|
64
|
+
Dialog,
|
|
64
65
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
open,
|
|
67
|
+
onClose: handleClose,
|
|
68
|
+
maxWidth: "sm",
|
|
69
|
+
TransitionProps: {
|
|
70
|
+
timeout: 700,
|
|
71
|
+
style: { transitionDelay: open ? `${OPEN_DELAY}ms` : "0ms" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
/* @__PURE__ */ React3.createElement(DialogHeader, { logo: false }, /* @__PURE__ */ React3.createElement(DialogTitle, null, title)),
|
|
75
|
+
content,
|
|
76
|
+
/* @__PURE__ */ React3.createElement(DialogActions, null, /* @__PURE__ */ React3.createElement(
|
|
77
|
+
FormControlLabel,
|
|
78
|
+
{
|
|
79
|
+
sx: { marginRight: "auto" },
|
|
80
|
+
control: /* @__PURE__ */ React3.createElement(
|
|
81
|
+
Checkbox,
|
|
82
|
+
{
|
|
83
|
+
checked: !shouldShowAgain,
|
|
84
|
+
onChange: () => setShouldShowAgain(!shouldShowAgain)
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
label: __("Don't show this again")
|
|
88
|
+
}
|
|
89
|
+
), /* @__PURE__ */ React3.createElement(Button, { size: "medium", onClick: () => handleClose(shouldShowAgain), variant: "contained" }, __("Got it")))
|
|
90
|
+
);
|
|
76
91
|
};
|
|
77
92
|
|
|
78
93
|
// src/hooks/use-editable.ts
|
|
@@ -81,6 +96,7 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
81
96
|
const [isEditing, setIsEditing] = useState3(false);
|
|
82
97
|
const [error, setError] = useState3(null);
|
|
83
98
|
const ref = useSelection(isEditing);
|
|
99
|
+
const isDirty = (newValue) => newValue !== value;
|
|
84
100
|
const openEditMode = () => {
|
|
85
101
|
setIsEditing(true);
|
|
86
102
|
};
|
|
@@ -90,6 +106,9 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
90
106
|
setIsEditing(false);
|
|
91
107
|
};
|
|
92
108
|
const submit = (newValue) => {
|
|
109
|
+
if (!isDirty(newValue)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
93
112
|
if (!error) {
|
|
94
113
|
try {
|
|
95
114
|
onSubmit(newValue);
|
|
@@ -101,7 +120,7 @@ var useEditable = ({ value, onSubmit, validation, onClick }) => {
|
|
|
101
120
|
const onChange = (event) => {
|
|
102
121
|
const { innerText: newValue } = event.target;
|
|
103
122
|
if (validation) {
|
|
104
|
-
setError(validation(newValue));
|
|
123
|
+
setError(isDirty(newValue) ? validation(newValue) : null);
|
|
105
124
|
}
|
|
106
125
|
};
|
|
107
126
|
const handleKeyDown = (event) => {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ellipsis-with-tooltip.tsx","../src/components/editable-field.tsx","../src/components/introduction-modal.tsx","../src/hooks/use-editable.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Box, Tooltip } from '@elementor/ui';\n\ntype EllipsisWithTooltipProps< T extends React.ElementType > = {\n\tmaxWidth?: React.CSSProperties[ 'maxWidth' ];\n\ttitle: string;\n\tas?: T;\n} & React.ComponentProps< T >;\n\nexport const EllipsisWithTooltip = < T extends React.ElementType >( {\n\tmaxWidth,\n\ttitle,\n\tas,\n\t...props\n}: EllipsisWithTooltipProps< T > ) => {\n\tconst [ setRef, isOverflowing ] = useIsOverflowing();\n\n\tif ( isOverflowing ) {\n\t\treturn (\n\t\t\t<Tooltip title={ title } placement=\"top\">\n\t\t\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t\t\t{ title }\n\t\t\t\t</Content>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t{ title }\n\t\t</Content>\n\t);\n};\n\ntype ContentProps< T extends React.ElementType > = React.PropsWithChildren<\n\tOmit< EllipsisWithTooltipProps< T >, 'title' >\n>;\n\nconst Content = React.forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ maxWidth, as: Component = Box, ...props }: ContentProps< T >,\n\t\t// forwardRef loses the typing when using generic components.\n\t\tref: unknown\n\t) => (\n\t\t<Component\n\t\t\tref={ ref }\n\t\t\tposition=\"relative\"\n\t\t\t{ ...props }\n\t\t\tstyle={ { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth } }\n\t\t/>\n\t)\n);\n\nconst useIsOverflowing = () => {\n\tconst [ el, setEl ] = useState< HTMLElement | null >( null );\n\tconst [ isOverflowing, setIsOverflown ] = useState( false );\n\n\tuseEffect( () => {\n\t\tconst observer = new ResizeObserver( ( [ { target } ] ) => {\n\t\t\tsetIsOverflown( target.scrollWidth > target.clientWidth );\n\t\t} );\n\n\t\tif ( el ) {\n\t\t\tobserver.observe( el );\n\t\t}\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [ el ] );\n\n\treturn [ setEl, isOverflowing ] as const;\n};\n","import * as React from 'react';\nimport { forwardRef } from 'react';\nimport { Tooltip } from '@elementor/ui';\n\ntype EditableFieldProps< T extends React.ElementType > = {\n\tvalue: string;\n\terror?: string;\n\tas?: T;\n} & React.ComponentPropsWithRef< T >;\n\nexport const EditableField = forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ value, error, as: Component = 'span', sx, ...props }: EditableFieldProps< T >,\n\t\tref: unknown\n\t) => {\n\t\treturn (\n\t\t\t<Tooltip title={ error } open={ !! error } placement=\"top\">\n\t\t\t\t<Component ref={ ref } sx={ { width: '100%', ...sx } } { ...props }>\n\t\t\t\t\t{ value }\n\t\t\t\t</Component>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n);\n","import * as React from 'react';\nimport { useState } from 'react';\nimport { Button, Checkbox, Dialog, DialogActions, DialogHeader, DialogTitle, FormControlLabel } from '@elementor/ui';\nimport { __ } from '@wordpress/i18n';\n\ntype IntroductionModalProps = {\n\topen: boolean;\n\thandleClose: ( shouldShowAgain: boolean ) => void;\n\ttitle: string;\n\tcontent: React.ReactNode;\n};\n\nexport const IntroductionModal = ( { open, handleClose, title, content }: IntroductionModalProps ) => {\n\tconst [ shouldShowAgain, setShouldShowAgain ] = useState( true );\n\n\treturn (\n\t\t<Dialog open={ open } onClose={ handleClose } maxWidth={ 'sm' }>\n\t\t\t<DialogHeader logo={ false }>\n\t\t\t\t<DialogTitle>{ title }</DialogTitle>\n\t\t\t</DialogHeader>\n\t\t\t{ content }\n\t\t\t<DialogActions>\n\t\t\t\t<FormControlLabel\n\t\t\t\t\tsx={ { marginRight: 'auto' } }\n\t\t\t\t\tcontrol={\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={ ! shouldShowAgain }\n\t\t\t\t\t\t\tonChange={ () => setShouldShowAgain( ! shouldShowAgain ) }\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t\tlabel={ __( \"Don't show this again\" ) }\n\t\t\t\t/>\n\t\t\t\t<Button size={ 'medium' } onClick={ () => handleClose( shouldShowAgain ) } variant=\"contained\">\n\t\t\t\t\t{ __( 'Got it' ) }\n\t\t\t\t</Button>\n\t\t\t</DialogActions>\n\t\t</Dialog>\n\t);\n};\n","import { useEffect, useRef, useState } from 'react';\n\ntype UseEditableParams = {\n\tvalue: string;\n\tonSubmit: ( value: string ) => unknown;\n\tvalidation?: ( value: string ) => string | undefined | null;\n\tonClick?: ( event: React.MouseEvent< HTMLDivElement > ) => void;\n};\n\nexport const useEditable = ( { value, onSubmit, validation, onClick }: UseEditableParams ) => {\n\tconst [ isEditing, setIsEditing ] = useState( false );\n\tconst [ error, setError ] = useState< string | null | undefined >( null );\n\n\tconst ref = useSelection( isEditing );\n\n\tconst openEditMode = () => {\n\t\tsetIsEditing( true );\n\t};\n\n\tconst closeEditMode = () => {\n\t\tref.current?.blur();\n\n\t\tsetError( null );\n\t\tsetIsEditing( false );\n\t};\n\n\tconst submit = ( newValue: string ) => {\n\t\tif ( ! error ) {\n\t\t\ttry {\n\t\t\t\tonSubmit( newValue );\n\t\t\t} finally {\n\t\t\t\tcloseEditMode();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst onChange = ( event: React.ChangeEvent< HTMLSpanElement > ) => {\n\t\tconst { innerText: newValue } = event.target;\n\n\t\tif ( validation ) {\n\t\t\tsetError( validation( newValue ) );\n\t\t}\n\t};\n\n\tconst handleKeyDown = ( event: React.KeyboardEvent ) => {\n\t\tevent.stopPropagation();\n\n\t\tif ( [ 'Escape' ].includes( event.key ) ) {\n\t\t\treturn closeEditMode();\n\t\t}\n\n\t\tif ( [ 'Enter' ].includes( event.key ) ) {\n\t\t\tevent.preventDefault();\n\t\t\treturn submit( ( event.target as HTMLElement ).innerText );\n\t\t}\n\t};\n\n\tconst handleClick = ( event: React.MouseEvent< HTMLDivElement > ) => {\n\t\tif ( isEditing ) {\n\t\t\tevent.stopPropagation();\n\t\t}\n\n\t\tonClick?.( event );\n\t};\n\n\tconst listeners = {\n\t\tonClick: handleClick,\n\t\tonKeyDown: handleKeyDown,\n\t\tonInput: onChange,\n\t\tonBlur: closeEditMode,\n\t} as const;\n\n\tconst attributes = {\n\t\tvalue,\n\t\trole: 'textbox',\n\t\tcontentEditable: isEditing,\n\t\t...( isEditing && {\n\t\t\tsuppressContentEditableWarning: true,\n\t\t} ),\n\t} as const;\n\n\treturn {\n\t\tref,\n\t\tisEditing,\n\t\topenEditMode,\n\t\tcloseEditMode,\n\t\tvalue,\n\t\terror,\n\t\tgetProps: () => ( { ...listeners, ...attributes } ),\n\t} as const;\n};\n\nconst useSelection = ( isEditing: boolean ) => {\n\tconst ref = useRef< HTMLElement | null >( null );\n\n\tuseEffect( () => {\n\t\tif ( isEditing ) {\n\t\t\tselectAll( ref.current );\n\t\t}\n\t}, [ isEditing ] );\n\n\treturn ref;\n};\n\nconst selectAll = ( el: HTMLElement | null ) => {\n\tconst selection = getSelection();\n\n\tif ( ! selection || ! el ) {\n\t\treturn;\n\t}\n\n\tconst range = document.createRange();\n\trange.selectNodeContents( el );\n\n\tselection.removeAllRanges();\n\tselection.addRange( range );\n};\n"],"mappings":"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","names":["React","forwardRef","Tooltip","React","useState","useEffect","useState"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ellipsis-with-tooltip.tsx","../src/components/editable-field.tsx","../src/components/introduction-modal.tsx","../src/hooks/use-editable.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Box, Tooltip } from '@elementor/ui';\n\ntype EllipsisWithTooltipProps< T extends React.ElementType > = {\n\tmaxWidth?: React.CSSProperties[ 'maxWidth' ];\n\ttitle: string;\n\tas?: T;\n} & React.ComponentProps< T >;\n\nexport const EllipsisWithTooltip = < T extends React.ElementType >( {\n\tmaxWidth,\n\ttitle,\n\tas,\n\t...props\n}: EllipsisWithTooltipProps< T > ) => {\n\tconst [ setRef, isOverflowing ] = useIsOverflowing();\n\n\tif ( isOverflowing ) {\n\t\treturn (\n\t\t\t<Tooltip title={ title } placement=\"top\">\n\t\t\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t\t\t{ title }\n\t\t\t\t</Content>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Content maxWidth={ maxWidth } ref={ setRef } as={ as } { ...props }>\n\t\t\t{ title }\n\t\t</Content>\n\t);\n};\n\ntype ContentProps< T extends React.ElementType > = React.PropsWithChildren<\n\tOmit< EllipsisWithTooltipProps< T >, 'title' >\n>;\n\nconst Content = React.forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ maxWidth, as: Component = Box, ...props }: ContentProps< T >,\n\t\t// forwardRef loses the typing when using generic components.\n\t\tref: unknown\n\t) => (\n\t\t<Component\n\t\t\tref={ ref }\n\t\t\tposition=\"relative\"\n\t\t\t{ ...props }\n\t\t\tstyle={ { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth } }\n\t\t/>\n\t)\n);\n\nconst useIsOverflowing = () => {\n\tconst [ el, setEl ] = useState< HTMLElement | null >( null );\n\tconst [ isOverflowing, setIsOverflown ] = useState( false );\n\n\tuseEffect( () => {\n\t\tconst observer = new ResizeObserver( ( [ { target } ] ) => {\n\t\t\tsetIsOverflown( target.scrollWidth > target.clientWidth );\n\t\t} );\n\n\t\tif ( el ) {\n\t\t\tobserver.observe( el );\n\t\t}\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [ el ] );\n\n\treturn [ setEl, isOverflowing ] as const;\n};\n","import * as React from 'react';\nimport { forwardRef } from 'react';\nimport { Tooltip } from '@elementor/ui';\n\ntype EditableFieldProps< T extends React.ElementType > = {\n\tvalue: string;\n\terror?: string;\n\tas?: T;\n} & React.ComponentPropsWithRef< T >;\n\nexport const EditableField = forwardRef(\n\t< T extends React.ElementType >(\n\t\t{ value, error, as: Component = 'span', sx, ...props }: EditableFieldProps< T >,\n\t\tref: unknown\n\t) => {\n\t\treturn (\n\t\t\t<Tooltip title={ error } open={ !! error } placement=\"top\">\n\t\t\t\t<Component ref={ ref } sx={ { width: '100%', ...sx } } { ...props }>\n\t\t\t\t\t{ value }\n\t\t\t\t</Component>\n\t\t\t</Tooltip>\n\t\t);\n\t}\n);\n","import * as React from 'react';\nimport { useState } from 'react';\nimport { Button, Checkbox, Dialog, DialogActions, DialogHeader, DialogTitle, FormControlLabel } from '@elementor/ui';\nimport { __ } from '@wordpress/i18n';\n\ntype IntroductionModalProps = {\n\topen: boolean;\n\thandleClose: ( shouldShowAgain: boolean ) => void;\n\ttitle: string;\n\tcontent: React.ReactNode;\n};\n\nconst OPEN_DELAY = 1000;\n\nexport const IntroductionModal = ( { open, handleClose, title, content }: IntroductionModalProps ) => {\n\tconst [ shouldShowAgain, setShouldShowAgain ] = useState( true );\n\n\treturn (\n\t\t<Dialog\n\t\t\topen={ open }\n\t\t\tonClose={ handleClose }\n\t\t\tmaxWidth={ 'sm' }\n\t\t\tTransitionProps={ {\n\t\t\t\ttimeout: 700,\n\t\t\t\tstyle: { transitionDelay: open ? `${ OPEN_DELAY }ms` : '0ms' },\n\t\t\t} }\n\t\t>\n\t\t\t<DialogHeader logo={ false }>\n\t\t\t\t<DialogTitle>{ title }</DialogTitle>\n\t\t\t</DialogHeader>\n\t\t\t{ content }\n\t\t\t<DialogActions>\n\t\t\t\t<FormControlLabel\n\t\t\t\t\tsx={ { marginRight: 'auto' } }\n\t\t\t\t\tcontrol={\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={ ! shouldShowAgain }\n\t\t\t\t\t\t\tonChange={ () => setShouldShowAgain( ! shouldShowAgain ) }\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t\tlabel={ __( \"Don't show this again\" ) }\n\t\t\t\t/>\n\t\t\t\t<Button size={ 'medium' } onClick={ () => handleClose( shouldShowAgain ) } variant=\"contained\">\n\t\t\t\t\t{ __( 'Got it' ) }\n\t\t\t\t</Button>\n\t\t\t</DialogActions>\n\t\t</Dialog>\n\t);\n};\n","import { useEffect, useRef, useState } from 'react';\n\ntype UseEditableParams = {\n\tvalue: string;\n\tonSubmit: ( value: string ) => unknown;\n\tvalidation?: ( value: string ) => string | null;\n\tonClick?: ( event: React.MouseEvent< HTMLDivElement > ) => void;\n};\n\nexport const useEditable = ( { value, onSubmit, validation, onClick }: UseEditableParams ) => {\n\tconst [ isEditing, setIsEditing ] = useState( false );\n\tconst [ error, setError ] = useState< string | null >( null );\n\n\tconst ref = useSelection( isEditing );\n\n\tconst isDirty = ( newValue: string ) => newValue !== value;\n\n\tconst openEditMode = () => {\n\t\tsetIsEditing( true );\n\t};\n\n\tconst closeEditMode = () => {\n\t\tref.current?.blur();\n\n\t\tsetError( null );\n\t\tsetIsEditing( false );\n\t};\n\n\tconst submit = ( newValue: string ) => {\n\t\tif ( ! isDirty( newValue ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! error ) {\n\t\t\ttry {\n\t\t\t\tonSubmit( newValue );\n\t\t\t} finally {\n\t\t\t\tcloseEditMode();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst onChange = ( event: React.ChangeEvent< HTMLSpanElement > ) => {\n\t\tconst { innerText: newValue } = event.target;\n\n\t\tif ( validation ) {\n\t\t\tsetError( isDirty( newValue ) ? validation( newValue ) : null );\n\t\t}\n\t};\n\n\tconst handleKeyDown = ( event: React.KeyboardEvent ) => {\n\t\tevent.stopPropagation();\n\n\t\tif ( [ 'Escape' ].includes( event.key ) ) {\n\t\t\treturn closeEditMode();\n\t\t}\n\n\t\tif ( [ 'Enter' ].includes( event.key ) ) {\n\t\t\tevent.preventDefault();\n\t\t\treturn submit( ( event.target as HTMLElement ).innerText );\n\t\t}\n\t};\n\n\tconst handleClick = ( event: React.MouseEvent< HTMLDivElement > ) => {\n\t\tif ( isEditing ) {\n\t\t\tevent.stopPropagation();\n\t\t}\n\n\t\tonClick?.( event );\n\t};\n\n\tconst listeners = {\n\t\tonClick: handleClick,\n\t\tonKeyDown: handleKeyDown,\n\t\tonInput: onChange,\n\t\tonBlur: closeEditMode,\n\t} as const;\n\n\tconst attributes = {\n\t\tvalue,\n\t\trole: 'textbox',\n\t\tcontentEditable: isEditing,\n\t\t...( isEditing && {\n\t\t\tsuppressContentEditableWarning: true,\n\t\t} ),\n\t} as const;\n\n\treturn {\n\t\tref,\n\t\tisEditing,\n\t\topenEditMode,\n\t\tcloseEditMode,\n\t\tvalue,\n\t\terror,\n\t\tgetProps: () => ( { ...listeners, ...attributes } ),\n\t} as const;\n};\n\nconst useSelection = ( isEditing: boolean ) => {\n\tconst ref = useRef< HTMLElement | null >( null );\n\n\tuseEffect( () => {\n\t\tif ( isEditing ) {\n\t\t\tselectAll( ref.current );\n\t\t}\n\t}, [ isEditing ] );\n\n\treturn ref;\n};\n\nconst selectAll = ( el: HTMLElement | null ) => {\n\tconst selection = getSelection();\n\n\tif ( ! selection || ! el ) {\n\t\treturn;\n\t}\n\n\tconst range = document.createRange();\n\trange.selectNodeContents( el );\n\n\tselection.removeAllRanges();\n\tselection.addRange( range );\n};\n"],"mappings":"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","names":["React","forwardRef","Tooltip","React","useState","useEffect","useState"]}
|
package/package.json
CHANGED
|
@@ -10,11 +10,21 @@ type IntroductionModalProps = {
|
|
|
10
10
|
content: React.ReactNode;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const OPEN_DELAY = 1000;
|
|
14
|
+
|
|
13
15
|
export const IntroductionModal = ( { open, handleClose, title, content }: IntroductionModalProps ) => {
|
|
14
16
|
const [ shouldShowAgain, setShouldShowAgain ] = useState( true );
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
|
-
<Dialog
|
|
19
|
+
<Dialog
|
|
20
|
+
open={ open }
|
|
21
|
+
onClose={ handleClose }
|
|
22
|
+
maxWidth={ 'sm' }
|
|
23
|
+
TransitionProps={ {
|
|
24
|
+
timeout: 700,
|
|
25
|
+
style: { transitionDelay: open ? `${ OPEN_DELAY }ms` : '0ms' },
|
|
26
|
+
} }
|
|
27
|
+
>
|
|
18
28
|
<DialogHeader logo={ false }>
|
|
19
29
|
<DialogTitle>{ title }</DialogTitle>
|
|
20
30
|
</DialogHeader>
|
|
@@ -155,4 +155,55 @@ describe( 'useEditable', () => {
|
|
|
155
155
|
// Assert.
|
|
156
156
|
expect( onSubmit ).not.toHaveBeenCalled();
|
|
157
157
|
} );
|
|
158
|
+
|
|
159
|
+
it( 'should not run validation & submit if the value has not changed', () => {
|
|
160
|
+
// Arrange.
|
|
161
|
+
const value = 'initial value';
|
|
162
|
+
const onSubmit = jest.fn();
|
|
163
|
+
|
|
164
|
+
const validation = () => {
|
|
165
|
+
return 'test-error';
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const { result } = renderHook( () =>
|
|
169
|
+
useEditable( {
|
|
170
|
+
value,
|
|
171
|
+
onSubmit,
|
|
172
|
+
validation,
|
|
173
|
+
} )
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Act.
|
|
177
|
+
act( result.current.openEditMode );
|
|
178
|
+
|
|
179
|
+
// Assert.
|
|
180
|
+
expect( result.current.error ).toBeNull();
|
|
181
|
+
|
|
182
|
+
// Act.
|
|
183
|
+
act( () => {
|
|
184
|
+
result.current.getProps().onInput( { target: { innerText: 'new value' } } as never );
|
|
185
|
+
} );
|
|
186
|
+
|
|
187
|
+
// Assert.
|
|
188
|
+
expect( result.current.error ).toBe( 'test-error' );
|
|
189
|
+
|
|
190
|
+
act( () => {
|
|
191
|
+
result.current.getProps().onInput( { target: { innerText: value } } as never );
|
|
192
|
+
} );
|
|
193
|
+
|
|
194
|
+
expect( result.current.error ).toBe( null );
|
|
195
|
+
|
|
196
|
+
// Act.
|
|
197
|
+
act( () => {
|
|
198
|
+
result.current.getProps().onKeyDown( {
|
|
199
|
+
key: 'Enter',
|
|
200
|
+
stopPropagation: jest.fn(),
|
|
201
|
+
preventDefault: jest.fn(),
|
|
202
|
+
target: { innerText: value },
|
|
203
|
+
} as never );
|
|
204
|
+
} );
|
|
205
|
+
|
|
206
|
+
// Assert.
|
|
207
|
+
expect( onSubmit ).not.toHaveBeenCalled();
|
|
208
|
+
} );
|
|
158
209
|
} );
|
|
@@ -3,16 +3,18 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
3
3
|
type UseEditableParams = {
|
|
4
4
|
value: string;
|
|
5
5
|
onSubmit: ( value: string ) => unknown;
|
|
6
|
-
validation?: ( value: string ) => string |
|
|
6
|
+
validation?: ( value: string ) => string | null;
|
|
7
7
|
onClick?: ( event: React.MouseEvent< HTMLDivElement > ) => void;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export const useEditable = ( { value, onSubmit, validation, onClick }: UseEditableParams ) => {
|
|
11
11
|
const [ isEditing, setIsEditing ] = useState( false );
|
|
12
|
-
const [ error, setError ] = useState< string | null
|
|
12
|
+
const [ error, setError ] = useState< string | null >( null );
|
|
13
13
|
|
|
14
14
|
const ref = useSelection( isEditing );
|
|
15
15
|
|
|
16
|
+
const isDirty = ( newValue: string ) => newValue !== value;
|
|
17
|
+
|
|
16
18
|
const openEditMode = () => {
|
|
17
19
|
setIsEditing( true );
|
|
18
20
|
};
|
|
@@ -25,6 +27,10 @@ export const useEditable = ( { value, onSubmit, validation, onClick }: UseEditab
|
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const submit = ( newValue: string ) => {
|
|
30
|
+
if ( ! isDirty( newValue ) ) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
if ( ! error ) {
|
|
29
35
|
try {
|
|
30
36
|
onSubmit( newValue );
|
|
@@ -38,7 +44,7 @@ export const useEditable = ( { value, onSubmit, validation, onClick }: UseEditab
|
|
|
38
44
|
const { innerText: newValue } = event.target;
|
|
39
45
|
|
|
40
46
|
if ( validation ) {
|
|
41
|
-
setError( validation( newValue ) );
|
|
47
|
+
setError( isDirty( newValue ) ? validation( newValue ) : null );
|
|
42
48
|
}
|
|
43
49
|
};
|
|
44
50
|
|