@duskmoon-dev/components 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -2
- package/dist/classes/chat.d.ts +9 -1
- package/dist/classes/console-page.d.ts +16 -0
- package/dist/classes/dropdown.d.ts +5 -5
- package/dist/classes/fab.d.ts +11 -0
- package/dist/classes/index.d.ts +5 -0
- package/dist/classes/index.js +180 -34
- package/dist/classes/megamenu.d.ts +10 -0
- package/dist/classes/otp-input.d.ts +12 -0
- package/dist/classes/popover.d.ts +1 -3
- package/dist/classes/swap.d.ts +12 -0
- package/dist/classes/tree-select.d.ts +6 -6
- package/dist/components/app/index.js +128 -68
- package/dist/components/avatar/Avatar.types.d.ts +1 -1
- package/dist/components/avatar/index.js +2 -1
- package/dist/components/badge/index.js +1 -1
- package/dist/components/button/Button.d.ts +1 -1
- package/dist/components/button/Button.types.d.ts +8 -0
- package/dist/components/button/index.js +253 -24
- package/dist/components/calendar/index.js +44 -19
- package/dist/components/chat/Chat.d.ts +9 -1
- package/dist/components/chat/Chat.types.d.ts +26 -0
- package/dist/components/chat/index.js +92 -3
- package/dist/components/color-picker/ColorPicker.types.d.ts +2 -1
- package/dist/components/color-picker/index.js +206 -9
- package/dist/components/console-page/ConsolePage.d.ts +2 -0
- package/dist/components/console-page/ConsolePage.types.d.ts +29 -0
- package/dist/components/console-page/index.d.ts +2 -0
- package/dist/components/console-page/index.js +205 -0
- package/dist/components/date-picker/DatePicker.types.d.ts +1 -0
- package/dist/components/date-picker/index.js +537 -35
- package/dist/components/date-picker/usePickerDropdown.d.ts +17 -0
- package/dist/components/dm-date-picker/index.js +545 -43
- package/dist/components/dm-drawer/index.js +253 -24
- package/dist/components/dm-layout/index.js +391 -24
- package/dist/components/dm-menu/index.js +380 -13
- package/dist/components/dm-page-header/index.js +404 -114
- package/dist/components/dm-pro-table/index.js +1183 -368
- package/dist/components/dm-query/DmQuery.types.d.ts +1 -1
- package/dist/components/dm-query/index.js +931 -188
- package/dist/components/dm-search/DmSearch.types.d.ts +4 -0
- package/dist/components/dm-search/index.js +917 -163
- package/dist/components/dm-table/index.js +1001 -247
- package/dist/components/dm-toolbar/index.js +400 -110
- package/dist/components/dm-truncate/index.js +115 -35
- package/dist/components/dropdown/Dropdown.types.d.ts +1 -1
- package/dist/components/dropdown/index.js +400 -110
- package/dist/components/fab/Fab.d.ts +4 -0
- package/dist/components/fab/Fab.types.d.ts +25 -0
- package/dist/components/fab/index.d.ts +2 -0
- package/dist/components/fab/index.js +480 -0
- package/dist/components/form/Form.types.d.ts +1 -0
- package/dist/components/form/index.js +14 -3
- package/dist/components/megamenu/Megamenu.d.ts +2 -0
- package/dist/components/megamenu/Megamenu.types.d.ts +32 -0
- package/dist/components/megamenu/index.d.ts +2 -0
- package/dist/components/megamenu/index.js +214 -0
- package/dist/components/modal/index.js +128 -68
- package/dist/components/otp-input/OtpInput.d.ts +2 -0
- package/dist/components/otp-input/OtpInput.types.d.ts +16 -0
- package/dist/components/otp-input/index.d.ts +2 -0
- package/dist/components/otp-input/index.js +112 -0
- package/dist/components/popconfirm/index.js +255 -26
- package/dist/components/popover/index.js +24 -4
- package/dist/components/swap/Swap.d.ts +3 -0
- package/dist/components/swap/Swap.types.d.ts +22 -0
- package/dist/components/swap/index.d.ts +2 -0
- package/dist/components/swap/index.js +109 -0
- package/dist/components/time-picker/TimePicker.types.d.ts +1 -0
- package/dist/components/time-picker/index.js +167 -29
- package/dist/components/tooltip/Tooltip.types.d.ts +4 -0
- package/dist/components/tooltip/index.js +107 -27
- package/dist/components/tour/index.js +253 -24
- package/dist/components/tree-select/index.js +8 -8
- package/dist/components/upload/Upload.types.d.ts +1 -0
- package/dist/components/upload/index.js +2 -0
- package/dist/generated/version.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3330 -1632
- package/dist/infrastructure.js +1 -1
- package/dist/styles.css +1190 -906
- package/package.json +23 -3
|
@@ -3,23 +3,23 @@ export declare const treeSelectBaseClass = "tree-select";
|
|
|
3
3
|
export declare const treeSelectOpenClass = "tree-select-open";
|
|
4
4
|
export declare const treeSelectDisabledClass = "tree-select-disabled";
|
|
5
5
|
export declare const treeSelectMultipleClass = "tree-select-multiple";
|
|
6
|
-
export declare const treeSelectSelectionClass = "tree-select-selection";
|
|
6
|
+
export declare const treeSelectSelectionClass = "tree-select-selection tree-select-value";
|
|
7
7
|
export declare const treeSelectPlaceholderClass = "tree-select-placeholder";
|
|
8
8
|
export declare const treeSelectTagClass = "tree-select-tag";
|
|
9
9
|
export declare const treeSelectClearClass = "tree-select-clear";
|
|
10
|
-
export declare const treeSelectIconClass = "tree-select-icon";
|
|
10
|
+
export declare const treeSelectIconClass = "tree-select-icon tree-select-arrow";
|
|
11
11
|
export declare const treeSelectDropdownClass = "tree-select-dropdown";
|
|
12
|
-
export declare const treeSelectSearchClass = "tree-select-search";
|
|
13
|
-
export declare const treeSelectListClass = "tree-select-list";
|
|
12
|
+
export declare const treeSelectSearchClass = "tree-select-search-input";
|
|
13
|
+
export declare const treeSelectListClass = "tree-select-list tree-select-options";
|
|
14
14
|
export declare const treeSelectEmptyClass = "tree-select-empty";
|
|
15
15
|
export declare const treeSelectNodeClass = "tree-select-node";
|
|
16
16
|
export declare const treeSelectNodeSelectedClass = "tree-select-node-selected";
|
|
17
17
|
export declare const treeSelectNodeCheckedClass = "tree-select-node-checked";
|
|
18
18
|
export declare const treeSelectNodeExpandedClass = "tree-select-node-expanded";
|
|
19
19
|
export declare const treeSelectNodeDisabledClass = "tree-select-node-disabled";
|
|
20
|
-
export declare const treeSelectSwitcherClass = "tree-select-switcher";
|
|
20
|
+
export declare const treeSelectSwitcherClass = "tree-select-switcher tree-select-node-toggle";
|
|
21
21
|
export declare const treeSelectCheckboxClass = "tree-select-checkbox";
|
|
22
|
-
export declare const treeSelectTitleClass = "tree-select-title";
|
|
22
|
+
export declare const treeSelectTitleClass = "tree-select-title tree-select-node-label";
|
|
23
23
|
export declare function getTreeSelectContainerClasses({ open, disabled, multiple, className, }: {
|
|
24
24
|
open?: boolean;
|
|
25
25
|
disabled?: boolean;
|
|
@@ -224,27 +224,18 @@ import {
|
|
|
224
224
|
forwardRef,
|
|
225
225
|
useCallback,
|
|
226
226
|
useEffect,
|
|
227
|
+
useId,
|
|
227
228
|
useRef,
|
|
228
229
|
useState
|
|
229
230
|
} from "react";
|
|
230
231
|
|
|
231
232
|
// src/classes/modal.ts
|
|
232
|
-
var modalBackdropClass = "modal-backdrop";
|
|
233
|
-
var modalOpenClass = "modal-open";
|
|
234
|
-
var modalBackdropCenterClass = "modal-backdrop-center";
|
|
235
233
|
var modalClass = "modal";
|
|
236
234
|
var modalHeaderClass = "modal-header";
|
|
237
235
|
var modalTitleClass = "modal-title";
|
|
238
236
|
var modalCloseClass = "modal-close";
|
|
239
237
|
var modalBodyClass = "modal-body";
|
|
240
238
|
var modalFooterClass = "modal-footer";
|
|
241
|
-
function getModalBackdropClasses({
|
|
242
|
-
open,
|
|
243
|
-
centered,
|
|
244
|
-
className
|
|
245
|
-
}) {
|
|
246
|
-
return cn(modalBackdropClass, open && modalOpenClass, centered && modalBackdropCenterClass, className);
|
|
247
|
-
}
|
|
248
239
|
function getModalClasses({ className }) {
|
|
249
240
|
return cn(modalClass, className);
|
|
250
241
|
}
|
|
@@ -275,6 +266,13 @@ function getModalStyle({
|
|
|
275
266
|
...width !== undefined ? { width } : undefined
|
|
276
267
|
};
|
|
277
268
|
}
|
|
269
|
+
function showNativeModal(dialog) {
|
|
270
|
+
if (!dialog.isConnected || dialog.matches(":modal"))
|
|
271
|
+
return;
|
|
272
|
+
if (dialog.open)
|
|
273
|
+
dialog.close();
|
|
274
|
+
dialog.showModal();
|
|
275
|
+
}
|
|
278
276
|
var ModalBase = forwardRef(({
|
|
279
277
|
open,
|
|
280
278
|
defaultOpen = false,
|
|
@@ -297,15 +295,41 @@ var ModalBase = forwardRef(({
|
|
|
297
295
|
maskClassName,
|
|
298
296
|
closeIcon = defaultCloseIcon,
|
|
299
297
|
style,
|
|
300
|
-
role
|
|
298
|
+
role,
|
|
301
299
|
...props
|
|
302
300
|
}, ref) => {
|
|
303
301
|
const controlled = open !== undefined;
|
|
304
302
|
const [innerOpen, setInnerOpen] = useState(defaultOpen);
|
|
305
303
|
const mergedOpen = controlled ? Boolean(open) : innerOpen;
|
|
306
304
|
const previousOpen = useRef(mergedOpen);
|
|
305
|
+
const desiredOpen = useRef(mergedOpen);
|
|
306
|
+
desiredOpen.current = mergedOpen;
|
|
307
|
+
const dialogRef = useRef(null);
|
|
308
|
+
const cancelRequestRef = useRef(null);
|
|
309
|
+
const programmaticClose = useRef(false);
|
|
310
|
+
const titleId = useId();
|
|
311
|
+
const surfaceWidth = width ?? style?.width;
|
|
312
|
+
const callerNamed = props["aria-label"] != null || props["aria-labelledby"] != null;
|
|
307
313
|
const showClose = closable && closeIcon !== null && closeIcon !== false;
|
|
308
314
|
const body = children ?? content;
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
const dialog = dialogRef.current;
|
|
317
|
+
if (!dialog)
|
|
318
|
+
return;
|
|
319
|
+
if (mergedOpen) {
|
|
320
|
+
programmaticClose.current = false;
|
|
321
|
+
showNativeModal(dialog);
|
|
322
|
+
} else if (dialog.open) {
|
|
323
|
+
programmaticClose.current = true;
|
|
324
|
+
dialog.close();
|
|
325
|
+
}
|
|
326
|
+
return () => {
|
|
327
|
+
if (dialog.open) {
|
|
328
|
+
programmaticClose.current = true;
|
|
329
|
+
dialog.close();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}, [mergedOpen]);
|
|
309
333
|
useEffect(() => {
|
|
310
334
|
if (previousOpen.current === mergedOpen) {
|
|
311
335
|
return;
|
|
@@ -322,73 +346,109 @@ var ModalBase = forwardRef(({
|
|
|
322
346
|
const handleOk = useCallback((event) => {
|
|
323
347
|
onOk?.(event);
|
|
324
348
|
}, [onOk]);
|
|
325
|
-
const
|
|
326
|
-
|
|
349
|
+
const requestCancel = useCallback(() => {
|
|
350
|
+
cancelRequestRef.current?.click();
|
|
351
|
+
}, []);
|
|
352
|
+
const handleNativeClose = useCallback(() => {
|
|
353
|
+
const dialog = dialogRef.current;
|
|
354
|
+
if (!dialog || dialog.open || programmaticClose.current) {
|
|
355
|
+
programmaticClose.current = false;
|
|
327
356
|
return;
|
|
328
357
|
}
|
|
329
|
-
|
|
330
|
-
|
|
358
|
+
if (desiredOpen.current) {
|
|
359
|
+
requestCancel();
|
|
360
|
+
if (controlled) {
|
|
361
|
+
queueMicrotask(() => {
|
|
362
|
+
if (desiredOpen.current) {
|
|
363
|
+
showNativeModal(dialog);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}, [controlled, requestCancel]);
|
|
331
369
|
if (!mergedOpen && destroyOnClose) {
|
|
332
370
|
return null;
|
|
333
371
|
}
|
|
334
|
-
return /* @__PURE__ */
|
|
335
|
-
|
|
336
|
-
role: "
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
children: footer !== undefined ? footer : /* @__PURE__ */ jsxs2(Fragment, {
|
|
372
|
+
return /* @__PURE__ */ jsxs2("dialog", {
|
|
373
|
+
ref: dialogRef,
|
|
374
|
+
role: role === "alertdialog" ? role : undefined,
|
|
375
|
+
"aria-label": props["aria-label"] ?? (!callerNamed && title == null ? "Modal" : undefined),
|
|
376
|
+
"aria-labelledby": props["aria-labelledby"] ?? (!callerNamed && title != null ? titleId : undefined),
|
|
377
|
+
"aria-describedby": props["aria-describedby"],
|
|
378
|
+
className: getModalClasses({ className: cn(maskClassName, centered ? "modal-middle" : "modal-top") }),
|
|
379
|
+
style: surfaceWidth !== undefined ? { width: surfaceWidth, maxWidth: surfaceWidth } : undefined,
|
|
380
|
+
onCancel: (event) => {
|
|
381
|
+
event.preventDefault();
|
|
382
|
+
requestCancel();
|
|
383
|
+
},
|
|
384
|
+
onClose: handleNativeClose,
|
|
385
|
+
onMouseDown: (event) => {
|
|
386
|
+
if (!maskClosable || event.target !== event.currentTarget)
|
|
387
|
+
return;
|
|
388
|
+
const { left, right, top, bottom } = event.currentTarget.getBoundingClientRect();
|
|
389
|
+
if (event.clientX < left || event.clientX > right || event.clientY < top || event.clientY > bottom) {
|
|
390
|
+
requestCancel();
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
children: [
|
|
394
|
+
/* @__PURE__ */ jsx2("button", {
|
|
395
|
+
ref: cancelRequestRef,
|
|
396
|
+
type: "button",
|
|
397
|
+
hidden: true,
|
|
398
|
+
tabIndex: -1,
|
|
399
|
+
"aria-hidden": "true",
|
|
400
|
+
onClick: close
|
|
401
|
+
}),
|
|
402
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
403
|
+
...props,
|
|
404
|
+
ref,
|
|
405
|
+
role,
|
|
406
|
+
className: cn("modal-box", "react-modal-box", className),
|
|
407
|
+
style: getModalStyle({ width, style }),
|
|
408
|
+
children: [
|
|
409
|
+
title !== undefined || showClose ? /* @__PURE__ */ jsxs2("div", {
|
|
410
|
+
className: modalHeaderClass,
|
|
374
411
|
children: [
|
|
375
|
-
/* @__PURE__ */ jsx2("
|
|
412
|
+
title !== undefined ? /* @__PURE__ */ jsx2("h2", {
|
|
413
|
+
id: titleId,
|
|
414
|
+
className: modalTitleClass,
|
|
415
|
+
children: title
|
|
416
|
+
}) : null,
|
|
417
|
+
showClose ? /* @__PURE__ */ jsx2("button", {
|
|
376
418
|
type: "button",
|
|
419
|
+
className: modalCloseClass,
|
|
420
|
+
"aria-label": "Close",
|
|
377
421
|
onClick: close,
|
|
378
|
-
children:
|
|
379
|
-
})
|
|
380
|
-
/* @__PURE__ */ jsx2("button", {
|
|
381
|
-
type: "button",
|
|
382
|
-
"aria-busy": confirmLoading,
|
|
383
|
-
disabled: confirmLoading,
|
|
384
|
-
onClick: handleOk,
|
|
385
|
-
children: okText
|
|
386
|
-
})
|
|
422
|
+
children: closeIcon
|
|
423
|
+
}) : null
|
|
387
424
|
]
|
|
425
|
+
}) : null,
|
|
426
|
+
/* @__PURE__ */ jsx2("div", {
|
|
427
|
+
className: modalBodyClass,
|
|
428
|
+
children: body
|
|
429
|
+
}),
|
|
430
|
+
footer === null ? null : /* @__PURE__ */ jsx2("div", {
|
|
431
|
+
className: modalFooterClass,
|
|
432
|
+
children: footer !== undefined ? footer : /* @__PURE__ */ jsxs2(Fragment, {
|
|
433
|
+
children: [
|
|
434
|
+
/* @__PURE__ */ jsx2("button", {
|
|
435
|
+
type: "button",
|
|
436
|
+
onClick: close,
|
|
437
|
+
children: cancelText
|
|
438
|
+
}),
|
|
439
|
+
/* @__PURE__ */ jsx2("button", {
|
|
440
|
+
type: "button",
|
|
441
|
+
"aria-busy": confirmLoading,
|
|
442
|
+
disabled: confirmLoading,
|
|
443
|
+
onClick: handleOk,
|
|
444
|
+
children: okText
|
|
445
|
+
})
|
|
446
|
+
]
|
|
447
|
+
})
|
|
388
448
|
})
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
449
|
+
]
|
|
450
|
+
})
|
|
451
|
+
]
|
|
392
452
|
});
|
|
393
453
|
});
|
|
394
454
|
ModalBase.displayName = "Modal";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentProps } from "react";
|
|
2
|
-
export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
2
|
+
export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
3
3
|
export type AvatarShape = "circle" | "square";
|
|
4
4
|
export interface AvatarProps extends ComponentProps<"span"> {
|
|
5
5
|
size?: AvatarSize;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ButtonProps } from "./Button.types";
|
|
3
|
-
export declare function Button({ color, appearance, shape, size, block, isLoading, leftIcon, rightIcon, className, children, disabled, ref, ...props }: ButtonProps): React.JSX.Element;
|
|
3
|
+
export declare function Button({ color, appearance, shape, size, block, isLoading, leftIcon, rightIcon, className, children, disabled, ref, confirm, onClick, ...props }: ButtonProps): React.JSX.Element;
|
|
@@ -12,4 +12,12 @@ export interface ButtonProps extends ComponentProps<"button"> {
|
|
|
12
12
|
isLoading?: boolean;
|
|
13
13
|
leftIcon?: React.ReactNode;
|
|
14
14
|
rightIcon?: React.ReactNode;
|
|
15
|
+
confirm?: boolean | {
|
|
16
|
+
title?: React.ReactNode;
|
|
17
|
+
component?: React.ReactElement;
|
|
18
|
+
message?: React.ReactNode;
|
|
19
|
+
description?: React.ReactNode;
|
|
20
|
+
confirmText?: React.ReactNode;
|
|
21
|
+
cancelText?: React.ReactNode;
|
|
22
|
+
};
|
|
15
23
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
// src/components/button/Button.tsx
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
6
|
+
|
|
3
7
|
// ../../node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
4
8
|
function r(e) {
|
|
5
9
|
var t, f, n = "";
|
|
@@ -48,7 +52,7 @@ var buttonAppearanceClasses = {
|
|
|
48
52
|
};
|
|
49
53
|
var buttonShapeClasses = {
|
|
50
54
|
rect: "",
|
|
51
|
-
circle: "btn-circle",
|
|
55
|
+
circle: "btn-circle btn-icon",
|
|
52
56
|
square: "btn-square"
|
|
53
57
|
};
|
|
54
58
|
var buttonSizeClasses = {
|
|
@@ -68,11 +72,14 @@ function getButtonClasses({
|
|
|
68
72
|
isLoading,
|
|
69
73
|
className
|
|
70
74
|
}) {
|
|
71
|
-
return cn(buttonBaseClass, buttonColorClasses[color], buttonAppearanceClasses[appearance], buttonShapeClasses[shape], buttonSizeClasses[size], block && buttonBlockClass, isLoading && buttonIsLoadingClass, className);
|
|
75
|
+
return cn(buttonBaseClass, buttonColorClasses[color], buttonAppearanceClasses[appearance], buttonShapeClasses[shape], buttonSizeClasses[size], shape === "circle" && size === "xs" && "btn-icon-sm", shape === "circle" && size === "sm" && "btn-icon-sm", shape === "circle" && size === "lg" && "btn-icon-lg", block && buttonBlockClass, isLoading && buttonIsLoadingClass, className);
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
// src/components/button/Button.tsx
|
|
75
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
79
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
80
|
+
function isPromiseLike(value) {
|
|
81
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
82
|
+
}
|
|
76
83
|
function Button({
|
|
77
84
|
color = "primary",
|
|
78
85
|
appearance = "filled",
|
|
@@ -86,8 +93,155 @@ function Button({
|
|
|
86
93
|
children,
|
|
87
94
|
disabled,
|
|
88
95
|
ref,
|
|
96
|
+
confirm,
|
|
97
|
+
onClick,
|
|
89
98
|
...props
|
|
90
99
|
}) {
|
|
100
|
+
const buttonRef = React.useRef(null);
|
|
101
|
+
const popoverRef = React.useRef(null);
|
|
102
|
+
const cancelRef = React.useRef(null);
|
|
103
|
+
const approvalRef = React.useRef("idle");
|
|
104
|
+
const pendingRef = React.useRef(false);
|
|
105
|
+
const actionRef = React.useRef(0);
|
|
106
|
+
const operationRef = React.useRef(0);
|
|
107
|
+
const mountedRef = React.useRef(false);
|
|
108
|
+
const [portalTarget, setPortalTarget] = React.useState(null);
|
|
109
|
+
const [confirmLoading, setConfirmLoading] = React.useState(false);
|
|
110
|
+
const [confirmError, setConfirmError] = React.useState(false);
|
|
111
|
+
const generatedId = React.useId().replace(/:/g, "");
|
|
112
|
+
const popoverId = `button-confirm-${generatedId}`;
|
|
113
|
+
const titleId = `${popoverId}-title`;
|
|
114
|
+
const descriptionId = `${popoverId}-description`;
|
|
115
|
+
const anchor = `--${popoverId}-anchor`;
|
|
116
|
+
const hasConfirm = Boolean(confirm);
|
|
117
|
+
const confirmation = confirm && typeof confirm === "object" ? confirm : undefined;
|
|
118
|
+
const title = confirmation ? confirmation.title : "Confirm action";
|
|
119
|
+
const hasTitle = title !== undefined && title !== null && title !== false && title !== "";
|
|
120
|
+
const content = confirmation?.component ?? confirmation?.message ?? confirmation?.description;
|
|
121
|
+
const hasContent = content !== undefined && content !== null && content !== false && content !== "";
|
|
122
|
+
const mergedRef = React.useCallback((node) => {
|
|
123
|
+
buttonRef.current = node;
|
|
124
|
+
if (!node) {
|
|
125
|
+
if (typeof ref === "function")
|
|
126
|
+
ref(null);
|
|
127
|
+
else if (ref)
|
|
128
|
+
ref.current = null;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const cleanup = typeof ref === "function" ? ref(node) : undefined;
|
|
132
|
+
if (ref && typeof ref !== "function")
|
|
133
|
+
ref.current = node;
|
|
134
|
+
return () => {
|
|
135
|
+
buttonRef.current = null;
|
|
136
|
+
if (typeof cleanup === "function")
|
|
137
|
+
cleanup();
|
|
138
|
+
else if (typeof ref === "function")
|
|
139
|
+
ref(null);
|
|
140
|
+
else if (ref)
|
|
141
|
+
ref.current = null;
|
|
142
|
+
};
|
|
143
|
+
}, [ref]);
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
mountedRef.current = true;
|
|
146
|
+
return () => {
|
|
147
|
+
mountedRef.current = false;
|
|
148
|
+
operationRef.current += 1;
|
|
149
|
+
};
|
|
150
|
+
}, []);
|
|
151
|
+
React.useEffect(() => {
|
|
152
|
+
operationRef.current += 1;
|
|
153
|
+
approvalRef.current = "idle";
|
|
154
|
+
setConfirmError(false);
|
|
155
|
+
setPortalTarget(hasConfirm ? document.body : null);
|
|
156
|
+
}, [hasConfirm]);
|
|
157
|
+
function closePopover() {
|
|
158
|
+
if (popoverRef.current?.matches(":popover-open")) {
|
|
159
|
+
popoverRef.current.hidePopover();
|
|
160
|
+
}
|
|
161
|
+
buttonRef.current?.focus();
|
|
162
|
+
}
|
|
163
|
+
function handleClick(event) {
|
|
164
|
+
if (!confirm) {
|
|
165
|
+
onClick?.(event);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (approvalRef.current === "native") {
|
|
169
|
+
approvalRef.current = "idle";
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (approvalRef.current === "action") {
|
|
173
|
+
approvalRef.current = "idle";
|
|
174
|
+
let result;
|
|
175
|
+
try {
|
|
176
|
+
result = onClick?.(event);
|
|
177
|
+
} catch {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
pendingRef.current = false;
|
|
180
|
+
setConfirmError(true);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!isPromiseLike(result)) {
|
|
184
|
+
pendingRef.current = false;
|
|
185
|
+
closePopover();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const userPreventedDefault = event.defaultPrevented;
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
setConfirmLoading(true);
|
|
191
|
+
const action = actionRef.current;
|
|
192
|
+
const operation = operationRef.current;
|
|
193
|
+
Promise.resolve(result).then(() => {
|
|
194
|
+
if (!mountedRef.current || action !== actionRef.current)
|
|
195
|
+
return;
|
|
196
|
+
pendingRef.current = false;
|
|
197
|
+
setConfirmLoading(false);
|
|
198
|
+
if (operation !== operationRef.current)
|
|
199
|
+
return;
|
|
200
|
+
closePopover();
|
|
201
|
+
if (!userPreventedDefault && !buttonRef.current?.disabled) {
|
|
202
|
+
approvalRef.current = "native";
|
|
203
|
+
buttonRef.current?.click();
|
|
204
|
+
approvalRef.current = "idle";
|
|
205
|
+
}
|
|
206
|
+
}, () => {
|
|
207
|
+
if (!mountedRef.current || action !== actionRef.current)
|
|
208
|
+
return;
|
|
209
|
+
pendingRef.current = false;
|
|
210
|
+
setConfirmLoading(false);
|
|
211
|
+
if (operation !== operationRef.current)
|
|
212
|
+
return;
|
|
213
|
+
setConfirmError(true);
|
|
214
|
+
cancelRef.current?.focus();
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
if (disabled || isLoading || pendingRef.current)
|
|
220
|
+
return;
|
|
221
|
+
if (popoverRef.current?.matches(":popover-open")) {
|
|
222
|
+
closePopover();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
setConfirmError(false);
|
|
226
|
+
popoverRef.current?.showPopover();
|
|
227
|
+
cancelRef.current?.focus();
|
|
228
|
+
}
|
|
229
|
+
function handleConfirm(event) {
|
|
230
|
+
event.preventDefault();
|
|
231
|
+
event.stopPropagation();
|
|
232
|
+
const trigger = buttonRef.current;
|
|
233
|
+
if (disabled || isLoading || pendingRef.current || !trigger || trigger.disabled || !event.currentTarget.matches(":popover-open"))
|
|
234
|
+
return;
|
|
235
|
+
setConfirmError(false);
|
|
236
|
+
pendingRef.current = true;
|
|
237
|
+
actionRef.current += 1;
|
|
238
|
+
approvalRef.current = "action";
|
|
239
|
+
trigger.click();
|
|
240
|
+
if (approvalRef.current === "action") {
|
|
241
|
+
approvalRef.current = "idle";
|
|
242
|
+
pendingRef.current = false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
91
245
|
const classes = getButtonClasses({
|
|
92
246
|
color,
|
|
93
247
|
appearance,
|
|
@@ -97,29 +251,104 @@ function Button({
|
|
|
97
251
|
isLoading,
|
|
98
252
|
className
|
|
99
253
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
...props,
|
|
254
|
+
const confirmClasses = getButtonClasses({
|
|
255
|
+
color,
|
|
256
|
+
size: "sm",
|
|
257
|
+
isLoading: confirmLoading
|
|
258
|
+
});
|
|
259
|
+
return /* @__PURE__ */ jsxs(Fragment, {
|
|
107
260
|
children: [
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
261
|
+
/* @__PURE__ */ jsxs("button", {
|
|
262
|
+
ref: confirm ? mergedRef : ref,
|
|
263
|
+
className: classes,
|
|
264
|
+
disabled: disabled || isLoading,
|
|
265
|
+
"aria-busy": isLoading,
|
|
266
|
+
"aria-disabled": disabled || isLoading,
|
|
267
|
+
...props,
|
|
268
|
+
style: confirm ? { ...props.style, anchorName: anchor } : props.style,
|
|
269
|
+
"aria-haspopup": confirm ? "dialog" : props["aria-haspopup"],
|
|
270
|
+
"aria-controls": confirm ? popoverId : props["aria-controls"],
|
|
271
|
+
onClick: confirm ? handleClick : onClick,
|
|
272
|
+
children: [
|
|
273
|
+
isLoading && /* @__PURE__ */ jsx("span", {
|
|
274
|
+
className: "btn-spinner"
|
|
275
|
+
}),
|
|
276
|
+
!isLoading && leftIcon && /* @__PURE__ */ jsx("span", {
|
|
277
|
+
className: "btn-icon-left",
|
|
278
|
+
children: leftIcon
|
|
279
|
+
}),
|
|
280
|
+
/* @__PURE__ */ jsx("span", {
|
|
281
|
+
className: "btn-content",
|
|
282
|
+
children
|
|
283
|
+
}),
|
|
284
|
+
!isLoading && rightIcon && /* @__PURE__ */ jsx("span", {
|
|
285
|
+
className: "btn-icon-right",
|
|
286
|
+
children: rightIcon
|
|
287
|
+
})
|
|
288
|
+
]
|
|
118
289
|
}),
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
290
|
+
confirm && portalTarget && createPortal(/* @__PURE__ */ jsxs("form", {
|
|
291
|
+
id: popoverId,
|
|
292
|
+
ref: popoverRef,
|
|
293
|
+
popover: "auto",
|
|
294
|
+
role: "dialog",
|
|
295
|
+
"aria-labelledby": hasTitle ? titleId : undefined,
|
|
296
|
+
"aria-label": hasTitle ? undefined : "Confirm action",
|
|
297
|
+
"aria-describedby": hasContent ? descriptionId : undefined,
|
|
298
|
+
className: "popover popover-confirm popover-bottom",
|
|
299
|
+
style: { positionAnchor: anchor },
|
|
300
|
+
onSubmit: handleConfirm,
|
|
301
|
+
onClick: (event) => event.stopPropagation(),
|
|
302
|
+
onToggle: (event) => {
|
|
303
|
+
if (!event.currentTarget.matches(":popover-open")) {
|
|
304
|
+
operationRef.current += 1;
|
|
305
|
+
if (event.currentTarget.contains(document.activeElement) || document.activeElement === document.body) {
|
|
306
|
+
buttonRef.current?.focus();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
children: [
|
|
311
|
+
hasTitle && /* @__PURE__ */ jsx("div", {
|
|
312
|
+
className: "popover-header",
|
|
313
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
314
|
+
id: titleId,
|
|
315
|
+
className: "popover-title",
|
|
316
|
+
children: title
|
|
317
|
+
})
|
|
318
|
+
}),
|
|
319
|
+
hasContent && /* @__PURE__ */ jsx("div", {
|
|
320
|
+
id: descriptionId,
|
|
321
|
+
className: "popover-body",
|
|
322
|
+
children: content
|
|
323
|
+
}),
|
|
324
|
+
confirmError && /* @__PURE__ */ jsx("div", {
|
|
325
|
+
role: "alert",
|
|
326
|
+
className: "popover-body",
|
|
327
|
+
children: "The action failed. Please try again."
|
|
328
|
+
}),
|
|
329
|
+
/* @__PURE__ */ jsxs("div", {
|
|
330
|
+
className: "popover-footer",
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ jsx("button", {
|
|
333
|
+
ref: cancelRef,
|
|
334
|
+
type: "button",
|
|
335
|
+
className: "btn btn-text btn-sm",
|
|
336
|
+
popoverTarget: popoverId,
|
|
337
|
+
popoverTargetAction: "hide",
|
|
338
|
+
disabled: confirmLoading,
|
|
339
|
+
children: confirmation?.cancelText ?? "Cancel"
|
|
340
|
+
}),
|
|
341
|
+
/* @__PURE__ */ jsx("button", {
|
|
342
|
+
type: "submit",
|
|
343
|
+
className: confirmClasses,
|
|
344
|
+
disabled: disabled || isLoading || confirmLoading,
|
|
345
|
+
"aria-busy": confirmLoading,
|
|
346
|
+
children: confirmation?.confirmText ?? "Confirm"
|
|
347
|
+
})
|
|
348
|
+
]
|
|
349
|
+
})
|
|
350
|
+
]
|
|
351
|
+
}), portalTarget)
|
|
123
352
|
]
|
|
124
353
|
});
|
|
125
354
|
}
|