@elementor/editor-ui 3.33.0-117 → 3.33.0-119
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.d.mts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +127 -35
- package/dist/index.mjs +117 -21
- package/package.json +2 -2
- package/src/components/global-dialog/__tests__/global-dialog.test.tsx +272 -0
- package/src/components/global-dialog/__tests__/subscribers.test.ts +90 -0
- package/src/components/global-dialog/components/global-dialog.tsx +30 -0
- package/src/components/global-dialog/index.ts +2 -0
- package/src/components/global-dialog/subscribers.ts +36 -0
- package/src/components/save-changes-dialog.tsx +106 -0
- package/src/index.ts +2 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React$1 from 'react';
|
|
2
|
-
import { ReactNode, PropsWithChildren } from 'react';
|
|
3
|
-
import { MenuItemProps, AlertProps, InfotipProps, MenuList } from '@elementor/ui';
|
|
2
|
+
import { ReactNode, PropsWithChildren, ReactElement } from 'react';
|
|
3
|
+
import { MenuItemProps, AlertProps, InfotipProps, MenuList, DialogProps, DialogContentTextProps } from '@elementor/ui';
|
|
4
4
|
import * as _emotion_styled from '@emotion/styled';
|
|
5
5
|
|
|
6
6
|
type EllipsisWithTooltipProps<T extends React$1.ElementType> = {
|
|
@@ -58,6 +58,14 @@ interface WarningInfotipProps extends PropsWithChildren {
|
|
|
58
58
|
}
|
|
59
59
|
declare const WarningInfotip: React$1.ForwardRefExoticComponent<WarningInfotipProps & React$1.RefAttributes<unknown>>;
|
|
60
60
|
|
|
61
|
+
declare const GlobalDialog: () => React$1.JSX.Element | null;
|
|
62
|
+
|
|
63
|
+
type DialogContent = {
|
|
64
|
+
component: ReactElement;
|
|
65
|
+
};
|
|
66
|
+
declare const openDialog: ({ component }: DialogContent) => void;
|
|
67
|
+
declare const closeDialog: () => void;
|
|
68
|
+
|
|
61
69
|
type PopoverBodyProps = PropsWithChildren<{
|
|
62
70
|
height?: number | 'auto';
|
|
63
71
|
width?: number;
|
|
@@ -108,6 +116,32 @@ type Props = {
|
|
|
108
116
|
};
|
|
109
117
|
declare const PopoverSearch: ({ value, onSearch, placeholder }: Props) => React$1.JSX.Element;
|
|
110
118
|
|
|
119
|
+
declare const SaveChangesDialog: {
|
|
120
|
+
({ children, onClose }: Pick<DialogProps, "children" | "onClose">): React$1.JSX.Element;
|
|
121
|
+
Title: ({ children, onClose }: React$1.PropsWithChildren & {
|
|
122
|
+
onClose?: () => void;
|
|
123
|
+
}) => React$1.JSX.Element;
|
|
124
|
+
Content: ({ children }: React$1.PropsWithChildren) => React$1.JSX.Element;
|
|
125
|
+
ContentText: (props: DialogContentTextProps) => React$1.JSX.Element;
|
|
126
|
+
Actions: ({ actions }: ConfirmationDialogActionsProps) => React$1.JSX.Element;
|
|
127
|
+
};
|
|
128
|
+
type Action = {
|
|
129
|
+
label: string;
|
|
130
|
+
action: () => void | Promise<void>;
|
|
131
|
+
};
|
|
132
|
+
type ConfirmationDialogActionsProps = {
|
|
133
|
+
actions: {
|
|
134
|
+
cancel?: Action;
|
|
135
|
+
confirm: Action;
|
|
136
|
+
discard?: Action;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
declare const useDialog: () => {
|
|
140
|
+
isOpen: boolean;
|
|
141
|
+
open: () => void;
|
|
142
|
+
close: () => void;
|
|
143
|
+
};
|
|
144
|
+
|
|
111
145
|
type UseEditableParams = {
|
|
112
146
|
value: string;
|
|
113
147
|
onSubmit: (value: string) => unknown;
|
|
@@ -134,4 +168,4 @@ declare const useEditable: ({ value, onSubmit, validation, onClick, onError }: U
|
|
|
134
168
|
};
|
|
135
169
|
};
|
|
136
170
|
|
|
137
|
-
export { EditableField, EllipsisWithTooltip, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PopoverSearch, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, useEditable };
|
|
171
|
+
export { EditableField, EllipsisWithTooltip, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PopoverSearch, SaveChangesDialog, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React$1 from 'react';
|
|
2
|
-
import { ReactNode, PropsWithChildren } from 'react';
|
|
3
|
-
import { MenuItemProps, AlertProps, InfotipProps, MenuList } from '@elementor/ui';
|
|
2
|
+
import { ReactNode, PropsWithChildren, ReactElement } from 'react';
|
|
3
|
+
import { MenuItemProps, AlertProps, InfotipProps, MenuList, DialogProps, DialogContentTextProps } from '@elementor/ui';
|
|
4
4
|
import * as _emotion_styled from '@emotion/styled';
|
|
5
5
|
|
|
6
6
|
type EllipsisWithTooltipProps<T extends React$1.ElementType> = {
|
|
@@ -58,6 +58,14 @@ interface WarningInfotipProps extends PropsWithChildren {
|
|
|
58
58
|
}
|
|
59
59
|
declare const WarningInfotip: React$1.ForwardRefExoticComponent<WarningInfotipProps & React$1.RefAttributes<unknown>>;
|
|
60
60
|
|
|
61
|
+
declare const GlobalDialog: () => React$1.JSX.Element | null;
|
|
62
|
+
|
|
63
|
+
type DialogContent = {
|
|
64
|
+
component: ReactElement;
|
|
65
|
+
};
|
|
66
|
+
declare const openDialog: ({ component }: DialogContent) => void;
|
|
67
|
+
declare const closeDialog: () => void;
|
|
68
|
+
|
|
61
69
|
type PopoverBodyProps = PropsWithChildren<{
|
|
62
70
|
height?: number | 'auto';
|
|
63
71
|
width?: number;
|
|
@@ -108,6 +116,32 @@ type Props = {
|
|
|
108
116
|
};
|
|
109
117
|
declare const PopoverSearch: ({ value, onSearch, placeholder }: Props) => React$1.JSX.Element;
|
|
110
118
|
|
|
119
|
+
declare const SaveChangesDialog: {
|
|
120
|
+
({ children, onClose }: Pick<DialogProps, "children" | "onClose">): React$1.JSX.Element;
|
|
121
|
+
Title: ({ children, onClose }: React$1.PropsWithChildren & {
|
|
122
|
+
onClose?: () => void;
|
|
123
|
+
}) => React$1.JSX.Element;
|
|
124
|
+
Content: ({ children }: React$1.PropsWithChildren) => React$1.JSX.Element;
|
|
125
|
+
ContentText: (props: DialogContentTextProps) => React$1.JSX.Element;
|
|
126
|
+
Actions: ({ actions }: ConfirmationDialogActionsProps) => React$1.JSX.Element;
|
|
127
|
+
};
|
|
128
|
+
type Action = {
|
|
129
|
+
label: string;
|
|
130
|
+
action: () => void | Promise<void>;
|
|
131
|
+
};
|
|
132
|
+
type ConfirmationDialogActionsProps = {
|
|
133
|
+
actions: {
|
|
134
|
+
cancel?: Action;
|
|
135
|
+
confirm: Action;
|
|
136
|
+
discard?: Action;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
declare const useDialog: () => {
|
|
140
|
+
isOpen: boolean;
|
|
141
|
+
open: () => void;
|
|
142
|
+
close: () => void;
|
|
143
|
+
};
|
|
144
|
+
|
|
111
145
|
type UseEditableParams = {
|
|
112
146
|
value: string;
|
|
113
147
|
onSubmit: (value: string) => unknown;
|
|
@@ -134,4 +168,4 @@ declare const useEditable: ({ value, onSubmit, validation, onClick, onError }: U
|
|
|
134
168
|
};
|
|
135
169
|
};
|
|
136
170
|
|
|
137
|
-
export { EditableField, EllipsisWithTooltip, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PopoverSearch, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, useEditable };
|
|
171
|
+
export { EditableField, EllipsisWithTooltip, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PopoverSearch, SaveChangesDialog, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable };
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
EditableField: () => EditableField,
|
|
34
34
|
EllipsisWithTooltip: () => EllipsisWithTooltip,
|
|
35
|
+
GlobalDialog: () => GlobalDialog,
|
|
35
36
|
ITEM_HEIGHT: () => ITEM_HEIGHT,
|
|
36
37
|
InfoAlert: () => InfoAlert,
|
|
37
38
|
InfoTipCard: () => InfoTipCard,
|
|
@@ -42,9 +43,13 @@ __export(index_exports, {
|
|
|
42
43
|
PopoverHeader: () => PopoverHeader,
|
|
43
44
|
PopoverMenuList: () => PopoverMenuList,
|
|
44
45
|
PopoverSearch: () => PopoverSearch,
|
|
46
|
+
SaveChangesDialog: () => SaveChangesDialog,
|
|
45
47
|
StyledMenuList: () => StyledMenuList,
|
|
46
48
|
ThemeProvider: () => ThemeProvider,
|
|
47
49
|
WarningInfotip: () => WarningInfotip,
|
|
50
|
+
closeDialog: () => closeDialog,
|
|
51
|
+
openDialog: () => openDialog,
|
|
52
|
+
useDialog: () => useDialog,
|
|
48
53
|
useEditable: () => useEditable
|
|
49
54
|
});
|
|
50
55
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -291,15 +296,55 @@ var WarningInfotip = (0, import_react6.forwardRef)(
|
|
|
291
296
|
}
|
|
292
297
|
);
|
|
293
298
|
|
|
294
|
-
// src/components/
|
|
299
|
+
// src/components/global-dialog/components/global-dialog.tsx
|
|
300
|
+
var import_react7 = require("react");
|
|
295
301
|
var React9 = __toESM(require("react"));
|
|
296
302
|
var import_ui9 = require("@elementor/ui");
|
|
303
|
+
|
|
304
|
+
// src/components/global-dialog/subscribers.ts
|
|
305
|
+
var currentDialogState = null;
|
|
306
|
+
var stateSubscribers = /* @__PURE__ */ new Set();
|
|
307
|
+
var subscribeToDialogState = (callback) => {
|
|
308
|
+
stateSubscribers.add(callback);
|
|
309
|
+
callback(currentDialogState);
|
|
310
|
+
return () => stateSubscribers.delete(callback);
|
|
311
|
+
};
|
|
312
|
+
var notifySubscribers = () => {
|
|
313
|
+
stateSubscribers.forEach((callback) => callback(currentDialogState));
|
|
314
|
+
};
|
|
315
|
+
var openDialog = ({ component }) => {
|
|
316
|
+
currentDialogState = { component };
|
|
317
|
+
notifySubscribers();
|
|
318
|
+
};
|
|
319
|
+
var closeDialog = () => {
|
|
320
|
+
currentDialogState = null;
|
|
321
|
+
notifySubscribers();
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/components/global-dialog/components/global-dialog.tsx
|
|
325
|
+
var GlobalDialog = () => {
|
|
326
|
+
const [content, setContent] = (0, import_react7.useState)(null);
|
|
327
|
+
(0, import_react7.useEffect)(() => {
|
|
328
|
+
const unsubscribe = subscribeToDialogState(setContent);
|
|
329
|
+
return () => {
|
|
330
|
+
unsubscribe();
|
|
331
|
+
};
|
|
332
|
+
}, []);
|
|
333
|
+
if (!content) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
return /* @__PURE__ */ React9.createElement(ThemeProvider, null, /* @__PURE__ */ React9.createElement(import_ui9.Dialog, { role: "dialog", open: true, onClose: closeDialog, maxWidth: "sm", fullWidth: true }, content.component));
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// src/components/popover/body.tsx
|
|
340
|
+
var React10 = __toESM(require("react"));
|
|
341
|
+
var import_ui10 = require("@elementor/ui");
|
|
297
342
|
var SECTION_PADDING_INLINE = 32;
|
|
298
343
|
var DEFAULT_POPOVER_HEIGHT = 348;
|
|
299
344
|
var FALLBACK_POPOVER_WIDTH = 220;
|
|
300
345
|
var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width }) => {
|
|
301
|
-
return /* @__PURE__ */
|
|
302
|
-
|
|
346
|
+
return /* @__PURE__ */ React10.createElement(
|
|
347
|
+
import_ui10.Box,
|
|
303
348
|
{
|
|
304
349
|
display: "flex",
|
|
305
350
|
flexDirection: "column",
|
|
@@ -315,8 +360,8 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width }) => {
|
|
|
315
360
|
};
|
|
316
361
|
|
|
317
362
|
// src/components/popover/header.tsx
|
|
318
|
-
var
|
|
319
|
-
var
|
|
363
|
+
var React11 = __toESM(require("react"));
|
|
364
|
+
var import_ui11 = require("@elementor/ui");
|
|
320
365
|
var SIZE = "tiny";
|
|
321
366
|
var PopoverHeader = ({ title, onClose, icon, actions }) => {
|
|
322
367
|
const paddingAndSizing = {
|
|
@@ -325,23 +370,23 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
|
|
|
325
370
|
py: 1.5,
|
|
326
371
|
maxHeight: 36
|
|
327
372
|
};
|
|
328
|
-
return /* @__PURE__ */
|
|
373
|
+
return /* @__PURE__ */ React11.createElement(import_ui11.Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React11.createElement(import_ui11.Typography, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React11.createElement(import_ui11.Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React11.createElement(import_ui11.CloseButton, { slotProps: { icon: { fontSize: SIZE } }, sx: { ml: "auto" }, onClick: onClose })));
|
|
329
374
|
};
|
|
330
375
|
|
|
331
376
|
// src/components/popover/menu-list.tsx
|
|
332
|
-
var
|
|
333
|
-
var
|
|
334
|
-
var
|
|
377
|
+
var React12 = __toESM(require("react"));
|
|
378
|
+
var import_react10 = require("react");
|
|
379
|
+
var import_ui12 = require("@elementor/ui");
|
|
335
380
|
var import_react_virtual = require("@tanstack/react-virtual");
|
|
336
381
|
|
|
337
382
|
// src/hooks/use-scroll-to-selected.ts
|
|
338
|
-
var
|
|
383
|
+
var import_react8 = require("react");
|
|
339
384
|
var useScrollToSelected = ({
|
|
340
385
|
selectedValue,
|
|
341
386
|
items,
|
|
342
387
|
virtualizer
|
|
343
388
|
}) => {
|
|
344
|
-
(0,
|
|
389
|
+
(0, import_react8.useEffect)(() => {
|
|
345
390
|
if (!selectedValue || items.length === 0) {
|
|
346
391
|
return;
|
|
347
392
|
}
|
|
@@ -353,10 +398,10 @@ var useScrollToSelected = ({
|
|
|
353
398
|
};
|
|
354
399
|
|
|
355
400
|
// src/hooks/use-scroll-top.ts
|
|
356
|
-
var
|
|
401
|
+
var import_react9 = require("react");
|
|
357
402
|
var useScrollTop = ({ containerRef }) => {
|
|
358
|
-
const [scrollTop, setScrollTop] = (0,
|
|
359
|
-
(0,
|
|
403
|
+
const [scrollTop, setScrollTop] = (0, import_react9.useState)(0);
|
|
404
|
+
(0, import_react9.useEffect)(() => {
|
|
360
405
|
const container = containerRef.current;
|
|
361
406
|
if (!container) {
|
|
362
407
|
return;
|
|
@@ -395,10 +440,10 @@ var PopoverMenuList = ({
|
|
|
395
440
|
noResultsComponent,
|
|
396
441
|
menuListTemplate: CustomMenuList
|
|
397
442
|
}) => {
|
|
398
|
-
const containerRef = (0,
|
|
443
|
+
const containerRef = (0, import_react10.useRef)(null);
|
|
399
444
|
const scrollTop = useScrollTop({ containerRef });
|
|
400
445
|
const MenuListComponent = CustomMenuList || StyledMenuList;
|
|
401
|
-
const stickyIndices = (0,
|
|
446
|
+
const stickyIndices = (0, import_react10.useMemo)(
|
|
402
447
|
() => items.reduce((categoryIndices, item, index) => {
|
|
403
448
|
if (item.type === "category") {
|
|
404
449
|
categoryIndices.push(index);
|
|
@@ -430,7 +475,7 @@ var PopoverMenuList = ({
|
|
|
430
475
|
});
|
|
431
476
|
useScrollToSelected({ selectedValue, items, virtualizer });
|
|
432
477
|
const virtualItems = virtualizer.getVirtualItems();
|
|
433
|
-
return /* @__PURE__ */
|
|
478
|
+
return /* @__PURE__ */ React12.createElement(import_ui12.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React12.createElement(
|
|
434
479
|
MenuListComponent,
|
|
435
480
|
{
|
|
436
481
|
role: "listbox",
|
|
@@ -448,8 +493,8 @@ var PopoverMenuList = ({
|
|
|
448
493
|
}
|
|
449
494
|
if (item.type === "category") {
|
|
450
495
|
const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
|
|
451
|
-
return /* @__PURE__ */
|
|
452
|
-
|
|
496
|
+
return /* @__PURE__ */ React12.createElement(
|
|
497
|
+
import_ui12.MenuSubheader,
|
|
453
498
|
{
|
|
454
499
|
key: virtualRow.key,
|
|
455
500
|
style: shouldStick ? {} : menuSubHeaderAbsoluteStyling(virtualRow.start),
|
|
@@ -459,8 +504,8 @@ var PopoverMenuList = ({
|
|
|
459
504
|
);
|
|
460
505
|
}
|
|
461
506
|
const isDisabled = item.disabled;
|
|
462
|
-
return /* @__PURE__ */
|
|
463
|
-
|
|
507
|
+
return /* @__PURE__ */ React12.createElement(
|
|
508
|
+
import_ui12.ListItem,
|
|
464
509
|
{
|
|
465
510
|
key: virtualRow.key,
|
|
466
511
|
role: "option",
|
|
@@ -498,7 +543,7 @@ var PopoverMenuList = ({
|
|
|
498
543
|
})
|
|
499
544
|
));
|
|
500
545
|
};
|
|
501
|
-
var StyledMenuList = (0,
|
|
546
|
+
var StyledMenuList = (0, import_ui12.styled)(import_ui12.MenuList)(({ theme }) => ({
|
|
502
547
|
"& > li": {
|
|
503
548
|
height: ITEM_HEIGHT,
|
|
504
549
|
width: "100%",
|
|
@@ -529,14 +574,14 @@ var StyledMenuList = (0, import_ui11.styled)(import_ui11.MenuList)(({ theme }) =
|
|
|
529
574
|
}));
|
|
530
575
|
|
|
531
576
|
// src/components/popover/search.tsx
|
|
532
|
-
var
|
|
533
|
-
var
|
|
577
|
+
var React13 = __toESM(require("react"));
|
|
578
|
+
var import_react11 = require("react");
|
|
534
579
|
var import_icons2 = require("@elementor/icons");
|
|
535
|
-
var
|
|
580
|
+
var import_ui13 = require("@elementor/ui");
|
|
536
581
|
var import_i18n2 = require("@wordpress/i18n");
|
|
537
582
|
var SIZE2 = "tiny";
|
|
538
583
|
var PopoverSearch = ({ value, onSearch, placeholder }) => {
|
|
539
|
-
const inputRef = (0,
|
|
584
|
+
const inputRef = (0, import_react11.useRef)(null);
|
|
540
585
|
const handleClear = () => {
|
|
541
586
|
onSearch("");
|
|
542
587
|
inputRef.current?.focus();
|
|
@@ -544,8 +589,8 @@ var PopoverSearch = ({ value, onSearch, placeholder }) => {
|
|
|
544
589
|
const handleInputChange = (event) => {
|
|
545
590
|
onSearch(event.target.value);
|
|
546
591
|
};
|
|
547
|
-
return /* @__PURE__ */
|
|
548
|
-
|
|
592
|
+
return /* @__PURE__ */ React13.createElement(import_ui13.Box, { sx: { px: 2, pb: 1.5 } }, /* @__PURE__ */ React13.createElement(
|
|
593
|
+
import_ui13.TextField,
|
|
549
594
|
{
|
|
550
595
|
autoFocus: true,
|
|
551
596
|
fullWidth: true,
|
|
@@ -555,18 +600,60 @@ var PopoverSearch = ({ value, onSearch, placeholder }) => {
|
|
|
555
600
|
onChange: handleInputChange,
|
|
556
601
|
placeholder,
|
|
557
602
|
InputProps: {
|
|
558
|
-
startAdornment: /* @__PURE__ */
|
|
559
|
-
endAdornment: value && /* @__PURE__ */
|
|
603
|
+
startAdornment: /* @__PURE__ */ React13.createElement(import_ui13.InputAdornment, { position: "start" }, /* @__PURE__ */ React13.createElement(import_icons2.SearchIcon, { fontSize: SIZE2 })),
|
|
604
|
+
endAdornment: value && /* @__PURE__ */ React13.createElement(import_ui13.IconButton, { size: SIZE2, onClick: handleClear, "aria-label": (0, import_i18n2.__)("Clear", "elementor") }, /* @__PURE__ */ React13.createElement(import_icons2.XIcon, { color: "action", fontSize: SIZE2 }))
|
|
560
605
|
}
|
|
561
606
|
}
|
|
562
607
|
));
|
|
563
608
|
};
|
|
564
609
|
|
|
610
|
+
// src/components/save-changes-dialog.tsx
|
|
611
|
+
var React14 = __toESM(require("react"));
|
|
612
|
+
var import_react12 = require("react");
|
|
613
|
+
var import_icons3 = require("@elementor/icons");
|
|
614
|
+
var import_ui14 = require("@elementor/ui");
|
|
615
|
+
var TITLE_ID = "save-changes-dialog";
|
|
616
|
+
var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React14.createElement(import_ui14.Dialog, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
|
|
617
|
+
var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React14.createElement(
|
|
618
|
+
import_ui14.DialogTitle,
|
|
619
|
+
{
|
|
620
|
+
id: TITLE_ID,
|
|
621
|
+
display: "flex",
|
|
622
|
+
alignItems: "center",
|
|
623
|
+
gap: 1,
|
|
624
|
+
sx: { lineHeight: 1, justifyContent: "space-between" }
|
|
625
|
+
},
|
|
626
|
+
/* @__PURE__ */ React14.createElement(import_ui14.Stack, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React14.createElement(import_icons3.AlertTriangleFilledIcon, { color: "secondary" }), children),
|
|
627
|
+
onClose && /* @__PURE__ */ React14.createElement(import_ui14.IconButton, { onClick: onClose, size: "small" }, /* @__PURE__ */ React14.createElement(import_icons3.XIcon, null))
|
|
628
|
+
);
|
|
629
|
+
var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React14.createElement(import_ui14.DialogContent, null, children);
|
|
630
|
+
var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React14.createElement(import_ui14.DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
|
|
631
|
+
var SaveChangesDialogActions = ({ actions }) => {
|
|
632
|
+
const [isConfirming, setIsConfirming] = (0, import_react12.useState)(false);
|
|
633
|
+
const { cancel, confirm, discard } = actions;
|
|
634
|
+
const onConfirm = async () => {
|
|
635
|
+
setIsConfirming(true);
|
|
636
|
+
await confirm.action();
|
|
637
|
+
setIsConfirming(false);
|
|
638
|
+
};
|
|
639
|
+
return /* @__PURE__ */ React14.createElement(import_ui14.DialogActions, null, cancel && /* @__PURE__ */ React14.createElement(import_ui14.Button, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React14.createElement(import_ui14.Button, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React14.createElement(import_ui14.Button, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
|
|
640
|
+
};
|
|
641
|
+
SaveChangesDialog.Title = SaveChangesDialogTitle;
|
|
642
|
+
SaveChangesDialog.Content = SaveChangesDialogContent;
|
|
643
|
+
SaveChangesDialog.ContentText = SaveChangesDialogContentText;
|
|
644
|
+
SaveChangesDialog.Actions = SaveChangesDialogActions;
|
|
645
|
+
var useDialog = () => {
|
|
646
|
+
const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
|
|
647
|
+
const open = () => setIsOpen(true);
|
|
648
|
+
const close = () => setIsOpen(false);
|
|
649
|
+
return { isOpen, open, close };
|
|
650
|
+
};
|
|
651
|
+
|
|
565
652
|
// src/hooks/use-editable.ts
|
|
566
|
-
var
|
|
653
|
+
var import_react13 = require("react");
|
|
567
654
|
var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
|
|
568
|
-
const [isEditing, setIsEditing] = (0,
|
|
569
|
-
const [error, setError] = (0,
|
|
655
|
+
const [isEditing, setIsEditing] = (0, import_react13.useState)(false);
|
|
656
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
570
657
|
const ref = useSelection(isEditing);
|
|
571
658
|
const isDirty = (newValue) => newValue !== value;
|
|
572
659
|
const openEditMode = () => {
|
|
@@ -639,8 +726,8 @@ var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
|
|
|
639
726
|
};
|
|
640
727
|
};
|
|
641
728
|
var useSelection = (isEditing) => {
|
|
642
|
-
const ref = (0,
|
|
643
|
-
(0,
|
|
729
|
+
const ref = (0, import_react13.useRef)(null);
|
|
730
|
+
(0, import_react13.useEffect)(() => {
|
|
644
731
|
if (isEditing) {
|
|
645
732
|
selectAll(ref.current);
|
|
646
733
|
}
|
|
@@ -661,6 +748,7 @@ var selectAll = (el) => {
|
|
|
661
748
|
0 && (module.exports = {
|
|
662
749
|
EditableField,
|
|
663
750
|
EllipsisWithTooltip,
|
|
751
|
+
GlobalDialog,
|
|
664
752
|
ITEM_HEIGHT,
|
|
665
753
|
InfoAlert,
|
|
666
754
|
InfoTipCard,
|
|
@@ -671,9 +759,13 @@ var selectAll = (el) => {
|
|
|
671
759
|
PopoverHeader,
|
|
672
760
|
PopoverMenuList,
|
|
673
761
|
PopoverSearch,
|
|
762
|
+
SaveChangesDialog,
|
|
674
763
|
StyledMenuList,
|
|
675
764
|
ThemeProvider,
|
|
676
765
|
WarningInfotip,
|
|
766
|
+
closeDialog,
|
|
767
|
+
openDialog,
|
|
768
|
+
useDialog,
|
|
677
769
|
useEditable
|
|
678
770
|
});
|
|
679
771
|
//# sourceMappingURL=index.js.map
|
package/dist/index.mjs
CHANGED
|
@@ -254,14 +254,54 @@ var WarningInfotip = forwardRef5(
|
|
|
254
254
|
}
|
|
255
255
|
);
|
|
256
256
|
|
|
257
|
-
// src/components/
|
|
257
|
+
// src/components/global-dialog/components/global-dialog.tsx
|
|
258
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
258
259
|
import * as React9 from "react";
|
|
260
|
+
import { Dialog as Dialog2 } from "@elementor/ui";
|
|
261
|
+
|
|
262
|
+
// src/components/global-dialog/subscribers.ts
|
|
263
|
+
var currentDialogState = null;
|
|
264
|
+
var stateSubscribers = /* @__PURE__ */ new Set();
|
|
265
|
+
var subscribeToDialogState = (callback) => {
|
|
266
|
+
stateSubscribers.add(callback);
|
|
267
|
+
callback(currentDialogState);
|
|
268
|
+
return () => stateSubscribers.delete(callback);
|
|
269
|
+
};
|
|
270
|
+
var notifySubscribers = () => {
|
|
271
|
+
stateSubscribers.forEach((callback) => callback(currentDialogState));
|
|
272
|
+
};
|
|
273
|
+
var openDialog = ({ component }) => {
|
|
274
|
+
currentDialogState = { component };
|
|
275
|
+
notifySubscribers();
|
|
276
|
+
};
|
|
277
|
+
var closeDialog = () => {
|
|
278
|
+
currentDialogState = null;
|
|
279
|
+
notifySubscribers();
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/components/global-dialog/components/global-dialog.tsx
|
|
283
|
+
var GlobalDialog = () => {
|
|
284
|
+
const [content, setContent] = useState4(null);
|
|
285
|
+
useEffect3(() => {
|
|
286
|
+
const unsubscribe = subscribeToDialogState(setContent);
|
|
287
|
+
return () => {
|
|
288
|
+
unsubscribe();
|
|
289
|
+
};
|
|
290
|
+
}, []);
|
|
291
|
+
if (!content) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
return /* @__PURE__ */ React9.createElement(ThemeProvider, null, /* @__PURE__ */ React9.createElement(Dialog2, { role: "dialog", open: true, onClose: closeDialog, maxWidth: "sm", fullWidth: true }, content.component));
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/components/popover/body.tsx
|
|
298
|
+
import * as React10 from "react";
|
|
259
299
|
import { Box as Box4 } from "@elementor/ui";
|
|
260
300
|
var SECTION_PADDING_INLINE = 32;
|
|
261
301
|
var DEFAULT_POPOVER_HEIGHT = 348;
|
|
262
302
|
var FALLBACK_POPOVER_WIDTH = 220;
|
|
263
303
|
var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width }) => {
|
|
264
|
-
return /* @__PURE__ */
|
|
304
|
+
return /* @__PURE__ */ React10.createElement(
|
|
265
305
|
Box4,
|
|
266
306
|
{
|
|
267
307
|
display: "flex",
|
|
@@ -278,7 +318,7 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width }) => {
|
|
|
278
318
|
};
|
|
279
319
|
|
|
280
320
|
// src/components/popover/header.tsx
|
|
281
|
-
import * as
|
|
321
|
+
import * as React11 from "react";
|
|
282
322
|
import { CloseButton, Stack, Typography as Typography3 } from "@elementor/ui";
|
|
283
323
|
var SIZE = "tiny";
|
|
284
324
|
var PopoverHeader = ({ title, onClose, icon, actions }) => {
|
|
@@ -288,23 +328,23 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
|
|
|
288
328
|
py: 1.5,
|
|
289
329
|
maxHeight: 36
|
|
290
330
|
};
|
|
291
|
-
return /* @__PURE__ */
|
|
331
|
+
return /* @__PURE__ */ React11.createElement(Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React11.createElement(Typography3, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React11.createElement(Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React11.createElement(CloseButton, { slotProps: { icon: { fontSize: SIZE } }, sx: { ml: "auto" }, onClick: onClose })));
|
|
292
332
|
};
|
|
293
333
|
|
|
294
334
|
// src/components/popover/menu-list.tsx
|
|
295
|
-
import * as
|
|
335
|
+
import * as React12 from "react";
|
|
296
336
|
import { useMemo, useRef } from "react";
|
|
297
337
|
import { Box as Box5, ListItem, MenuList, MenuSubheader, styled as styled2 } from "@elementor/ui";
|
|
298
338
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
299
339
|
|
|
300
340
|
// src/hooks/use-scroll-to-selected.ts
|
|
301
|
-
import { useEffect as
|
|
341
|
+
import { useEffect as useEffect4 } from "react";
|
|
302
342
|
var useScrollToSelected = ({
|
|
303
343
|
selectedValue,
|
|
304
344
|
items,
|
|
305
345
|
virtualizer
|
|
306
346
|
}) => {
|
|
307
|
-
|
|
347
|
+
useEffect4(() => {
|
|
308
348
|
if (!selectedValue || items.length === 0) {
|
|
309
349
|
return;
|
|
310
350
|
}
|
|
@@ -316,10 +356,10 @@ var useScrollToSelected = ({
|
|
|
316
356
|
};
|
|
317
357
|
|
|
318
358
|
// src/hooks/use-scroll-top.ts
|
|
319
|
-
import { useEffect as
|
|
359
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
320
360
|
var useScrollTop = ({ containerRef }) => {
|
|
321
|
-
const [scrollTop, setScrollTop] =
|
|
322
|
-
|
|
361
|
+
const [scrollTop, setScrollTop] = useState5(0);
|
|
362
|
+
useEffect5(() => {
|
|
323
363
|
const container = containerRef.current;
|
|
324
364
|
if (!container) {
|
|
325
365
|
return;
|
|
@@ -393,7 +433,7 @@ var PopoverMenuList = ({
|
|
|
393
433
|
});
|
|
394
434
|
useScrollToSelected({ selectedValue, items, virtualizer });
|
|
395
435
|
const virtualItems = virtualizer.getVirtualItems();
|
|
396
|
-
return /* @__PURE__ */
|
|
436
|
+
return /* @__PURE__ */ React12.createElement(Box5, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React12.createElement(
|
|
397
437
|
MenuListComponent,
|
|
398
438
|
{
|
|
399
439
|
role: "listbox",
|
|
@@ -411,7 +451,7 @@ var PopoverMenuList = ({
|
|
|
411
451
|
}
|
|
412
452
|
if (item.type === "category") {
|
|
413
453
|
const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
|
|
414
|
-
return /* @__PURE__ */
|
|
454
|
+
return /* @__PURE__ */ React12.createElement(
|
|
415
455
|
MenuSubheader,
|
|
416
456
|
{
|
|
417
457
|
key: virtualRow.key,
|
|
@@ -422,7 +462,7 @@ var PopoverMenuList = ({
|
|
|
422
462
|
);
|
|
423
463
|
}
|
|
424
464
|
const isDisabled = item.disabled;
|
|
425
|
-
return /* @__PURE__ */
|
|
465
|
+
return /* @__PURE__ */ React12.createElement(
|
|
426
466
|
ListItem,
|
|
427
467
|
{
|
|
428
468
|
key: virtualRow.key,
|
|
@@ -492,7 +532,7 @@ var StyledMenuList = styled2(MenuList)(({ theme }) => ({
|
|
|
492
532
|
}));
|
|
493
533
|
|
|
494
534
|
// src/components/popover/search.tsx
|
|
495
|
-
import * as
|
|
535
|
+
import * as React13 from "react";
|
|
496
536
|
import { useRef as useRef2 } from "react";
|
|
497
537
|
import { SearchIcon, XIcon } from "@elementor/icons";
|
|
498
538
|
import { Box as Box6, IconButton, InputAdornment, TextField } from "@elementor/ui";
|
|
@@ -507,7 +547,7 @@ var PopoverSearch = ({ value, onSearch, placeholder }) => {
|
|
|
507
547
|
const handleInputChange = (event) => {
|
|
508
548
|
onSearch(event.target.value);
|
|
509
549
|
};
|
|
510
|
-
return /* @__PURE__ */
|
|
550
|
+
return /* @__PURE__ */ React13.createElement(Box6, { sx: { px: 2, pb: 1.5 } }, /* @__PURE__ */ React13.createElement(
|
|
511
551
|
TextField,
|
|
512
552
|
{
|
|
513
553
|
autoFocus: true,
|
|
@@ -518,18 +558,69 @@ var PopoverSearch = ({ value, onSearch, placeholder }) => {
|
|
|
518
558
|
onChange: handleInputChange,
|
|
519
559
|
placeholder,
|
|
520
560
|
InputProps: {
|
|
521
|
-
startAdornment: /* @__PURE__ */
|
|
522
|
-
endAdornment: value && /* @__PURE__ */
|
|
561
|
+
startAdornment: /* @__PURE__ */ React13.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React13.createElement(SearchIcon, { fontSize: SIZE2 })),
|
|
562
|
+
endAdornment: value && /* @__PURE__ */ React13.createElement(IconButton, { size: SIZE2, onClick: handleClear, "aria-label": __2("Clear", "elementor") }, /* @__PURE__ */ React13.createElement(XIcon, { color: "action", fontSize: SIZE2 }))
|
|
523
563
|
}
|
|
524
564
|
}
|
|
525
565
|
));
|
|
526
566
|
};
|
|
527
567
|
|
|
568
|
+
// src/components/save-changes-dialog.tsx
|
|
569
|
+
import * as React14 from "react";
|
|
570
|
+
import { useState as useState6 } from "react";
|
|
571
|
+
import { AlertTriangleFilledIcon, XIcon as XIcon2 } from "@elementor/icons";
|
|
572
|
+
import {
|
|
573
|
+
Button as Button3,
|
|
574
|
+
Dialog as Dialog3,
|
|
575
|
+
DialogActions as DialogActions2,
|
|
576
|
+
DialogContent,
|
|
577
|
+
DialogContentText,
|
|
578
|
+
DialogTitle as DialogTitle2,
|
|
579
|
+
IconButton as IconButton2,
|
|
580
|
+
Stack as Stack2
|
|
581
|
+
} from "@elementor/ui";
|
|
582
|
+
var TITLE_ID = "save-changes-dialog";
|
|
583
|
+
var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React14.createElement(Dialog3, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
|
|
584
|
+
var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React14.createElement(
|
|
585
|
+
DialogTitle2,
|
|
586
|
+
{
|
|
587
|
+
id: TITLE_ID,
|
|
588
|
+
display: "flex",
|
|
589
|
+
alignItems: "center",
|
|
590
|
+
gap: 1,
|
|
591
|
+
sx: { lineHeight: 1, justifyContent: "space-between" }
|
|
592
|
+
},
|
|
593
|
+
/* @__PURE__ */ React14.createElement(Stack2, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React14.createElement(AlertTriangleFilledIcon, { color: "secondary" }), children),
|
|
594
|
+
onClose && /* @__PURE__ */ React14.createElement(IconButton2, { onClick: onClose, size: "small" }, /* @__PURE__ */ React14.createElement(XIcon2, null))
|
|
595
|
+
);
|
|
596
|
+
var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React14.createElement(DialogContent, null, children);
|
|
597
|
+
var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React14.createElement(DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
|
|
598
|
+
var SaveChangesDialogActions = ({ actions }) => {
|
|
599
|
+
const [isConfirming, setIsConfirming] = useState6(false);
|
|
600
|
+
const { cancel, confirm, discard } = actions;
|
|
601
|
+
const onConfirm = async () => {
|
|
602
|
+
setIsConfirming(true);
|
|
603
|
+
await confirm.action();
|
|
604
|
+
setIsConfirming(false);
|
|
605
|
+
};
|
|
606
|
+
return /* @__PURE__ */ React14.createElement(DialogActions2, null, cancel && /* @__PURE__ */ React14.createElement(Button3, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React14.createElement(Button3, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React14.createElement(Button3, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
|
|
607
|
+
};
|
|
608
|
+
SaveChangesDialog.Title = SaveChangesDialogTitle;
|
|
609
|
+
SaveChangesDialog.Content = SaveChangesDialogContent;
|
|
610
|
+
SaveChangesDialog.ContentText = SaveChangesDialogContentText;
|
|
611
|
+
SaveChangesDialog.Actions = SaveChangesDialogActions;
|
|
612
|
+
var useDialog = () => {
|
|
613
|
+
const [isOpen, setIsOpen] = useState6(false);
|
|
614
|
+
const open = () => setIsOpen(true);
|
|
615
|
+
const close = () => setIsOpen(false);
|
|
616
|
+
return { isOpen, open, close };
|
|
617
|
+
};
|
|
618
|
+
|
|
528
619
|
// src/hooks/use-editable.ts
|
|
529
|
-
import { useEffect as
|
|
620
|
+
import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
|
|
530
621
|
var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
|
|
531
|
-
const [isEditing, setIsEditing] =
|
|
532
|
-
const [error, setError] =
|
|
622
|
+
const [isEditing, setIsEditing] = useState7(false);
|
|
623
|
+
const [error, setError] = useState7(null);
|
|
533
624
|
const ref = useSelection(isEditing);
|
|
534
625
|
const isDirty = (newValue) => newValue !== value;
|
|
535
626
|
const openEditMode = () => {
|
|
@@ -603,7 +694,7 @@ var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
|
|
|
603
694
|
};
|
|
604
695
|
var useSelection = (isEditing) => {
|
|
605
696
|
const ref = useRef3(null);
|
|
606
|
-
|
|
697
|
+
useEffect6(() => {
|
|
607
698
|
if (isEditing) {
|
|
608
699
|
selectAll(ref.current);
|
|
609
700
|
}
|
|
@@ -623,6 +714,7 @@ var selectAll = (el) => {
|
|
|
623
714
|
export {
|
|
624
715
|
EditableField,
|
|
625
716
|
EllipsisWithTooltip,
|
|
717
|
+
GlobalDialog,
|
|
626
718
|
ITEM_HEIGHT,
|
|
627
719
|
InfoAlert,
|
|
628
720
|
InfoTipCard,
|
|
@@ -633,9 +725,13 @@ export {
|
|
|
633
725
|
PopoverHeader,
|
|
634
726
|
PopoverMenuList,
|
|
635
727
|
PopoverSearch,
|
|
728
|
+
SaveChangesDialog,
|
|
636
729
|
StyledMenuList,
|
|
637
730
|
ThemeProvider,
|
|
638
731
|
WarningInfotip,
|
|
732
|
+
closeDialog,
|
|
733
|
+
openDialog,
|
|
734
|
+
useDialog,
|
|
639
735
|
useEditable
|
|
640
736
|
};
|
|
641
737
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-ui",
|
|
3
3
|
"description": "Elementor Editor UI",
|
|
4
|
-
"version": "3.33.0-
|
|
4
|
+
"version": "3.33.0-119",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor-v1-adapters": "3.33.0-
|
|
40
|
+
"@elementor/editor-v1-adapters": "3.33.0-119",
|
|
41
41
|
"@elementor/icons": "1.46.0",
|
|
42
42
|
"@elementor/ui": "1.36.12",
|
|
43
43
|
"@tanstack/react-virtual": "^3.13.3",
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { GlobalDialog } from '../components/global-dialog';
|
|
5
|
+
import { closeDialog, type DialogStateCallback, openDialog } from '../subscribers';
|
|
6
|
+
|
|
7
|
+
// Get mock functions for cleanup
|
|
8
|
+
const mockEventBus = jest.requireMock( '../subscribers' );
|
|
9
|
+
|
|
10
|
+
jest.mock( '../subscribers', () => {
|
|
11
|
+
let currentState: { component: React.ReactElement } | null = null;
|
|
12
|
+
const subscribers = new Set< DialogStateCallback >();
|
|
13
|
+
|
|
14
|
+
const notifySubscribers = () => {
|
|
15
|
+
subscribers.forEach( ( callback ) => callback( currentState ) );
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
subscribeToDialogState: jest.fn( ( callback: DialogStateCallback ) => {
|
|
20
|
+
subscribers.add( callback );
|
|
21
|
+
// Call callback immediately with current state (matches real implementation)
|
|
22
|
+
callback( currentState );
|
|
23
|
+
return () => subscribers.delete( callback );
|
|
24
|
+
} ),
|
|
25
|
+
openDialog: jest.fn( ( { component }: { component: React.ReactElement } ) => {
|
|
26
|
+
currentState = { component };
|
|
27
|
+
// Notify synchronously (matches real implementation)
|
|
28
|
+
notifySubscribers();
|
|
29
|
+
} ),
|
|
30
|
+
closeDialog: jest.fn( () => {
|
|
31
|
+
currentState = null;
|
|
32
|
+
// Notify synchronously (matches real implementation)
|
|
33
|
+
notifySubscribers();
|
|
34
|
+
} ),
|
|
35
|
+
// For manual control in tests
|
|
36
|
+
__notifySubscribers: notifySubscribers,
|
|
37
|
+
__getCurrentState: () => currentState,
|
|
38
|
+
__getSubscribers: () => subscribers,
|
|
39
|
+
__reset: () => {
|
|
40
|
+
currentState = null;
|
|
41
|
+
subscribers.clear();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
// Helper function to render GlobalDialog
|
|
47
|
+
const renderGlobalDialog = () => {
|
|
48
|
+
return render( <GlobalDialog /> );
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe( 'GlobalDialog', () => {
|
|
52
|
+
// Reset dialog state before each test to avoid test interference
|
|
53
|
+
beforeEach( () => {
|
|
54
|
+
mockEventBus.__reset();
|
|
55
|
+
} );
|
|
56
|
+
|
|
57
|
+
afterEach( () => {
|
|
58
|
+
// Clean up any open dialogs after each test
|
|
59
|
+
mockEventBus.__reset();
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
it( 'should not render anything when no dialog is open', () => {
|
|
63
|
+
// Act
|
|
64
|
+
renderGlobalDialog();
|
|
65
|
+
|
|
66
|
+
// Assert
|
|
67
|
+
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
it( 'should render dialog when openDialog is called', async () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
const TestDialogContent = () => <div>Test Dialog Content</div>;
|
|
73
|
+
|
|
74
|
+
renderGlobalDialog();
|
|
75
|
+
|
|
76
|
+
// Act
|
|
77
|
+
act( () => {
|
|
78
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
79
|
+
} );
|
|
80
|
+
|
|
81
|
+
// Assert - Wait for the dialog to appear
|
|
82
|
+
await waitFor( () => {
|
|
83
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 ); // MUI creates 2 dialog elements
|
|
84
|
+
} );
|
|
85
|
+
expect( screen.getByText( 'Test Dialog Content' ) ).toBeInTheDocument();
|
|
86
|
+
} );
|
|
87
|
+
|
|
88
|
+
it( 'should close dialog when closeDialog is called', async () => {
|
|
89
|
+
// Arrange
|
|
90
|
+
const TestDialogContent = () => <div>Test Dialog Content</div>;
|
|
91
|
+
|
|
92
|
+
renderGlobalDialog();
|
|
93
|
+
|
|
94
|
+
// Open dialog first
|
|
95
|
+
act( () => {
|
|
96
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
await waitFor( () => {
|
|
100
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 );
|
|
101
|
+
} );
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
act( () => {
|
|
105
|
+
closeDialog();
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
// Assert
|
|
109
|
+
await waitFor( () => {
|
|
110
|
+
expect( screen.queryAllByRole( 'dialog' ) ).toHaveLength( 0 );
|
|
111
|
+
} );
|
|
112
|
+
expect( screen.queryByText( 'Test Dialog Content' ) ).not.toBeInTheDocument();
|
|
113
|
+
} );
|
|
114
|
+
|
|
115
|
+
it( 'should close dialog when Dialog onClose is triggered', async () => {
|
|
116
|
+
// Arrange
|
|
117
|
+
const TestDialogContent = () => <div>Test Dialog Content</div>;
|
|
118
|
+
|
|
119
|
+
renderGlobalDialog();
|
|
120
|
+
|
|
121
|
+
// Open dialog first
|
|
122
|
+
act( () => {
|
|
123
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
124
|
+
} );
|
|
125
|
+
|
|
126
|
+
await waitFor( () => {
|
|
127
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 );
|
|
128
|
+
} );
|
|
129
|
+
expect( screen.getByText( 'Test Dialog Content' ) ).toBeInTheDocument();
|
|
130
|
+
|
|
131
|
+
// Act - Simulate Dialog's onClose being called (like when user clicks close button)
|
|
132
|
+
act( () => {
|
|
133
|
+
closeDialog();
|
|
134
|
+
} );
|
|
135
|
+
|
|
136
|
+
// Assert
|
|
137
|
+
await waitFor( () => {
|
|
138
|
+
expect( screen.queryAllByRole( 'dialog' ) ).toHaveLength( 0 );
|
|
139
|
+
} );
|
|
140
|
+
expect( screen.queryByText( 'Test Dialog Content' ) ).not.toBeInTheDocument();
|
|
141
|
+
} );
|
|
142
|
+
|
|
143
|
+
it( 'should handle null/undefined dialog content gracefully', () => {
|
|
144
|
+
// This test ensures the component doesn't crash with invalid content
|
|
145
|
+
renderGlobalDialog();
|
|
146
|
+
|
|
147
|
+
// No dialog should be rendered when content is null
|
|
148
|
+
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
|
|
149
|
+
} );
|
|
150
|
+
it( 'should replace dialog content when a new dialog is opened', async () => {
|
|
151
|
+
// Arrange
|
|
152
|
+
const FirstDialogContent = () => <div>First Dialog</div>;
|
|
153
|
+
const SecondDialogContent = () => <div>Second Dialog</div>;
|
|
154
|
+
|
|
155
|
+
renderGlobalDialog();
|
|
156
|
+
|
|
157
|
+
// Open first dialog
|
|
158
|
+
act( () => {
|
|
159
|
+
openDialog( { component: <FirstDialogContent /> } );
|
|
160
|
+
} );
|
|
161
|
+
|
|
162
|
+
await waitFor( () => {
|
|
163
|
+
expect( screen.getByText( 'First Dialog' ) ).toBeInTheDocument();
|
|
164
|
+
} );
|
|
165
|
+
|
|
166
|
+
// Act - Open second dialog
|
|
167
|
+
act( () => {
|
|
168
|
+
openDialog( { component: <SecondDialogContent /> } );
|
|
169
|
+
} );
|
|
170
|
+
|
|
171
|
+
// Assert
|
|
172
|
+
await waitFor( () => {
|
|
173
|
+
expect( screen.queryByText( 'First Dialog' ) ).not.toBeInTheDocument();
|
|
174
|
+
} );
|
|
175
|
+
expect( screen.getByText( 'Second Dialog' ) ).toBeInTheDocument();
|
|
176
|
+
} );
|
|
177
|
+
|
|
178
|
+
it( 'should subscribe to dialog state on mount', async () => {
|
|
179
|
+
// Arrange
|
|
180
|
+
const TestDialogContent = () => <div>Test Content</div>;
|
|
181
|
+
|
|
182
|
+
// Act
|
|
183
|
+
renderGlobalDialog();
|
|
184
|
+
|
|
185
|
+
// The component should be subscribed, so opening a dialog should work
|
|
186
|
+
act( () => {
|
|
187
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
188
|
+
} );
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
await waitFor( () => {
|
|
192
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 );
|
|
193
|
+
} );
|
|
194
|
+
} );
|
|
195
|
+
|
|
196
|
+
it( 'should clean up subscription when component unmounts', async () => {
|
|
197
|
+
// Arrange
|
|
198
|
+
const TestDialogContent = () => <div>Test Dialog</div>;
|
|
199
|
+
|
|
200
|
+
const view = render( <GlobalDialog /> );
|
|
201
|
+
const unmount = view.unmount;
|
|
202
|
+
|
|
203
|
+
// Open dialog to verify subscription is working
|
|
204
|
+
act( () => {
|
|
205
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
206
|
+
} );
|
|
207
|
+
|
|
208
|
+
await waitFor( () => {
|
|
209
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 );
|
|
210
|
+
} );
|
|
211
|
+
|
|
212
|
+
// Act - Unmount component
|
|
213
|
+
unmount();
|
|
214
|
+
|
|
215
|
+
// Try to open dialog again - should not affect the unmounted component
|
|
216
|
+
act( () => {
|
|
217
|
+
openDialog( { component: <TestDialogContent /> } );
|
|
218
|
+
} );
|
|
219
|
+
|
|
220
|
+
// Assert - No dialog should be rendered since component is unmounted
|
|
221
|
+
expect( screen.queryAllByRole( 'dialog' ) ).toHaveLength( 0 );
|
|
222
|
+
} );
|
|
223
|
+
it( 'should work with store dispatch patterns like error dialogs', async () => {
|
|
224
|
+
// Arrange - Simulate error dialog pattern from editor-global-classes
|
|
225
|
+
const ErrorDialogContent = ( {
|
|
226
|
+
modifiedLabels,
|
|
227
|
+
}: {
|
|
228
|
+
modifiedLabels: Array< { original: string; modified: string; id: string } >;
|
|
229
|
+
} ) => (
|
|
230
|
+
<div>
|
|
231
|
+
<h3>Duplicate Labels Found</h3>
|
|
232
|
+
<ul>
|
|
233
|
+
{ modifiedLabels.map( ( label ) => (
|
|
234
|
+
<li key={ label.id }>
|
|
235
|
+
{ label.original } → { label.modified }
|
|
236
|
+
</li>
|
|
237
|
+
) ) }
|
|
238
|
+
</ul>
|
|
239
|
+
<button onClick={ () => closeDialog() }>Close</button>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const mockModifiedLabels = [
|
|
244
|
+
{ original: 'MyClass', modified: 'DUP_MyClass', id: 'class-1' },
|
|
245
|
+
{ original: 'Button', modified: 'DUP_Button', id: 'class-2' },
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
renderGlobalDialog();
|
|
249
|
+
|
|
250
|
+
// Act - Simulate error dialog opening (like in show-error-dialog.tsx)
|
|
251
|
+
act( () => {
|
|
252
|
+
openDialog( {
|
|
253
|
+
component: <ErrorDialogContent modifiedLabels={ mockModifiedLabels } />,
|
|
254
|
+
} );
|
|
255
|
+
} );
|
|
256
|
+
|
|
257
|
+
// Assert
|
|
258
|
+
await waitFor( () => {
|
|
259
|
+
expect( screen.getAllByRole( 'dialog' ) ).toHaveLength( 2 );
|
|
260
|
+
} );
|
|
261
|
+
expect( screen.getByText( 'Duplicate Labels Found' ) ).toBeInTheDocument();
|
|
262
|
+
expect( screen.getByText( 'MyClass → DUP_MyClass' ) ).toBeInTheDocument();
|
|
263
|
+
expect( screen.getByText( 'Button → DUP_Button' ) ).toBeInTheDocument();
|
|
264
|
+
|
|
265
|
+
// Test closing
|
|
266
|
+
fireEvent.click( screen.getByRole( 'button', { name: 'Close' } ) );
|
|
267
|
+
|
|
268
|
+
await waitFor( () => {
|
|
269
|
+
expect( screen.queryAllByRole( 'dialog' ) ).toHaveLength( 0 );
|
|
270
|
+
} );
|
|
271
|
+
} );
|
|
272
|
+
} );
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { waitFor } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { closeDialog, type DialogContent, openDialog, subscribeToDialogState } from '../subscribers';
|
|
5
|
+
|
|
6
|
+
describe( 'subscribers', () => {
|
|
7
|
+
// Clean up state after each test
|
|
8
|
+
afterEach( () => {
|
|
9
|
+
closeDialog();
|
|
10
|
+
} );
|
|
11
|
+
|
|
12
|
+
it( 'should update state when openDialog is called', async () => {
|
|
13
|
+
// Arrange
|
|
14
|
+
const callback = jest.fn();
|
|
15
|
+
const testComponent = React.createElement( 'div', { children: 'Test' } );
|
|
16
|
+
const dialogContent: DialogContent = { component: testComponent };
|
|
17
|
+
|
|
18
|
+
subscribeToDialogState( callback );
|
|
19
|
+
callback.mockClear(); // Clear the initial call
|
|
20
|
+
|
|
21
|
+
// Act
|
|
22
|
+
openDialog( dialogContent );
|
|
23
|
+
|
|
24
|
+
// Assert
|
|
25
|
+
await waitFor( () => {
|
|
26
|
+
expect( callback ).toHaveBeenCalledWith( { component: testComponent } );
|
|
27
|
+
} );
|
|
28
|
+
await waitFor( () => {
|
|
29
|
+
expect( callback ).toHaveBeenCalledTimes( 1 );
|
|
30
|
+
} );
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'should reset state to null when closeDialog is called', async () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
const callback = jest.fn();
|
|
36
|
+
const testComponent = React.createElement( 'div', { children: 'Test' } );
|
|
37
|
+
|
|
38
|
+
subscribeToDialogState( callback );
|
|
39
|
+
openDialog( { component: testComponent } );
|
|
40
|
+
|
|
41
|
+
// Wait for the openDialog to complete
|
|
42
|
+
await waitFor( () => {
|
|
43
|
+
expect( callback ).toHaveBeenCalledWith( { component: testComponent } );
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
callback.mockClear(); // Clear previous calls
|
|
47
|
+
|
|
48
|
+
// Act
|
|
49
|
+
closeDialog();
|
|
50
|
+
|
|
51
|
+
// Assert
|
|
52
|
+
await waitFor( () => {
|
|
53
|
+
expect( callback ).toHaveBeenCalledWith( null );
|
|
54
|
+
} );
|
|
55
|
+
await waitFor( () => {
|
|
56
|
+
expect( callback ).toHaveBeenCalledTimes( 1 );
|
|
57
|
+
} );
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
it( 'should call callback immediately with current state on subscription', () => {
|
|
61
|
+
// Arrange
|
|
62
|
+
const testComponent = React.createElement( 'div', { children: 'Test' } );
|
|
63
|
+
openDialog( { component: testComponent } );
|
|
64
|
+
|
|
65
|
+
const callback = jest.fn();
|
|
66
|
+
|
|
67
|
+
// Act
|
|
68
|
+
subscribeToDialogState( callback );
|
|
69
|
+
|
|
70
|
+
// Assert
|
|
71
|
+
expect( callback ).toHaveBeenCalledWith( { component: testComponent } );
|
|
72
|
+
expect( callback ).toHaveBeenCalledTimes( 1 );
|
|
73
|
+
} );
|
|
74
|
+
|
|
75
|
+
it( 'should unsubscribe callback when unsubscribe function is called', () => {
|
|
76
|
+
// Arrange
|
|
77
|
+
const callback = jest.fn();
|
|
78
|
+
const testComponent = React.createElement( 'div', { children: 'Test' } );
|
|
79
|
+
|
|
80
|
+
const unsubscribe = subscribeToDialogState( callback );
|
|
81
|
+
callback.mockClear(); // Clear initial call
|
|
82
|
+
|
|
83
|
+
// Act
|
|
84
|
+
unsubscribe();
|
|
85
|
+
openDialog( { component: testComponent } );
|
|
86
|
+
|
|
87
|
+
// Assert
|
|
88
|
+
expect( callback ).not.toHaveBeenCalled();
|
|
89
|
+
} );
|
|
90
|
+
} );
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Dialog } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import ThemeProvider from '../../theme-provider';
|
|
6
|
+
import { closeDialog, subscribeToDialogState } from '../subscribers';
|
|
7
|
+
import { type DialogContent } from '../subscribers';
|
|
8
|
+
|
|
9
|
+
export const GlobalDialog = () => {
|
|
10
|
+
const [ content, setContent ] = useState< DialogContent | null >( null );
|
|
11
|
+
|
|
12
|
+
useEffect( () => {
|
|
13
|
+
const unsubscribe = subscribeToDialogState( setContent );
|
|
14
|
+
return () => {
|
|
15
|
+
unsubscribe();
|
|
16
|
+
};
|
|
17
|
+
}, [] );
|
|
18
|
+
|
|
19
|
+
if ( ! content ) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<ThemeProvider>
|
|
25
|
+
<Dialog role="dialog" open onClose={ closeDialog } maxWidth="sm" fullWidth>
|
|
26
|
+
{ content.component }
|
|
27
|
+
</Dialog>
|
|
28
|
+
</ThemeProvider>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
type DialogState = {
|
|
4
|
+
component: ReactElement;
|
|
5
|
+
} | null;
|
|
6
|
+
|
|
7
|
+
export type DialogStateCallback = ( state: DialogState ) => void;
|
|
8
|
+
|
|
9
|
+
let currentDialogState: DialogState = null;
|
|
10
|
+
|
|
11
|
+
const stateSubscribers = new Set< DialogStateCallback >();
|
|
12
|
+
|
|
13
|
+
export const subscribeToDialogState = ( callback: DialogStateCallback ) => {
|
|
14
|
+
stateSubscribers.add( callback );
|
|
15
|
+
|
|
16
|
+
callback( currentDialogState );
|
|
17
|
+
return () => stateSubscribers.delete( callback );
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const notifySubscribers = () => {
|
|
21
|
+
stateSubscribers.forEach( ( callback ) => callback( currentDialogState ) );
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type DialogContent = {
|
|
25
|
+
component: ReactElement;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const openDialog = ( { component }: DialogContent ) => {
|
|
29
|
+
currentDialogState = { component };
|
|
30
|
+
notifySubscribers();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const closeDialog = () => {
|
|
34
|
+
currentDialogState = null;
|
|
35
|
+
notifySubscribers();
|
|
36
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { AlertTriangleFilledIcon, XIcon } from '@elementor/icons';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogActions,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogContentText,
|
|
10
|
+
type DialogContentTextProps,
|
|
11
|
+
type DialogProps,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
IconButton,
|
|
14
|
+
Stack,
|
|
15
|
+
} from '@elementor/ui';
|
|
16
|
+
|
|
17
|
+
const TITLE_ID = 'save-changes-dialog';
|
|
18
|
+
|
|
19
|
+
export const SaveChangesDialog = ( { children, onClose }: Pick< DialogProps, 'children' | 'onClose' > ) => (
|
|
20
|
+
<Dialog open onClose={ onClose } aria-labelledby={ TITLE_ID } maxWidth="xs">
|
|
21
|
+
{ children }
|
|
22
|
+
</Dialog>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const SaveChangesDialogTitle = ( { children, onClose }: React.PropsWithChildren & { onClose?: () => void } ) => (
|
|
26
|
+
<DialogTitle
|
|
27
|
+
id={ TITLE_ID }
|
|
28
|
+
display="flex"
|
|
29
|
+
alignItems="center"
|
|
30
|
+
gap={ 1 }
|
|
31
|
+
sx={ { lineHeight: 1, justifyContent: 'space-between' } }
|
|
32
|
+
>
|
|
33
|
+
<Stack direction="row" alignItems="center" gap={ 1 }>
|
|
34
|
+
<AlertTriangleFilledIcon color="secondary" />
|
|
35
|
+
{ children }
|
|
36
|
+
</Stack>
|
|
37
|
+
{ onClose && (
|
|
38
|
+
<IconButton onClick={ onClose } size="small">
|
|
39
|
+
<XIcon />
|
|
40
|
+
</IconButton>
|
|
41
|
+
) }
|
|
42
|
+
</DialogTitle>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const SaveChangesDialogContent = ( { children }: React.PropsWithChildren ) => (
|
|
46
|
+
<DialogContent>{ children }</DialogContent>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const SaveChangesDialogContentText = ( props: DialogContentTextProps ) => (
|
|
50
|
+
<DialogContentText variant="body2" color="textPrimary" display="flex" flexDirection="column" { ...props } />
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
type Action = {
|
|
54
|
+
label: string;
|
|
55
|
+
action: () => void | Promise< void >;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type ConfirmationDialogActionsProps = {
|
|
59
|
+
actions: {
|
|
60
|
+
cancel?: Action;
|
|
61
|
+
confirm: Action;
|
|
62
|
+
discard?: Action;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const SaveChangesDialogActions = ( { actions }: ConfirmationDialogActionsProps ) => {
|
|
67
|
+
const [ isConfirming, setIsConfirming ] = useState( false );
|
|
68
|
+
const { cancel, confirm, discard } = actions;
|
|
69
|
+
|
|
70
|
+
const onConfirm = async () => {
|
|
71
|
+
setIsConfirming( true );
|
|
72
|
+
await confirm.action();
|
|
73
|
+
setIsConfirming( false );
|
|
74
|
+
};
|
|
75
|
+
return (
|
|
76
|
+
<DialogActions>
|
|
77
|
+
{ cancel && (
|
|
78
|
+
<Button variant="text" color="secondary" onClick={ cancel.action }>
|
|
79
|
+
{ cancel.label }
|
|
80
|
+
</Button>
|
|
81
|
+
) }
|
|
82
|
+
{ discard && (
|
|
83
|
+
<Button variant="text" color="secondary" onClick={ discard.action }>
|
|
84
|
+
{ discard.label }
|
|
85
|
+
</Button>
|
|
86
|
+
) }
|
|
87
|
+
<Button variant="contained" color="secondary" onClick={ onConfirm } loading={ isConfirming }>
|
|
88
|
+
{ confirm.label }
|
|
89
|
+
</Button>
|
|
90
|
+
</DialogActions>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
SaveChangesDialog.Title = SaveChangesDialogTitle;
|
|
95
|
+
SaveChangesDialog.Content = SaveChangesDialogContent;
|
|
96
|
+
SaveChangesDialog.ContentText = SaveChangesDialogContentText;
|
|
97
|
+
SaveChangesDialog.Actions = SaveChangesDialogActions;
|
|
98
|
+
|
|
99
|
+
export const useDialog = () => {
|
|
100
|
+
const [ isOpen, setIsOpen ] = useState( false );
|
|
101
|
+
|
|
102
|
+
const open = () => setIsOpen( true );
|
|
103
|
+
const close = () => setIsOpen( false );
|
|
104
|
+
|
|
105
|
+
return { isOpen, open, close };
|
|
106
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,9 @@ export { MenuListItem, MenuItemInfotip } from './components/menu-item';
|
|
|
7
7
|
export { InfoTipCard } from './components/infotip-card';
|
|
8
8
|
export { InfoAlert } from './components/info-alert';
|
|
9
9
|
export { WarningInfotip } from './components/warning-infotip';
|
|
10
|
+
export { GlobalDialog, openDialog, closeDialog } from './components/global-dialog';
|
|
10
11
|
export * from './components/popover';
|
|
12
|
+
export * from './components/save-changes-dialog';
|
|
11
13
|
|
|
12
14
|
// hooks
|
|
13
15
|
export { useEditable } from './hooks/use-editable';
|