@windrun-huaiin/third-ui 29.1.0 → 29.2.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/dist/fuma/base/custom-header.js +6 -3
- package/dist/fuma/base/custom-header.mjs +6 -3
- package/dist/main/alert-dialog/confirm-dialog.d.ts +6 -3
- package/dist/main/alert-dialog/confirm-dialog.js +7 -7
- package/dist/main/alert-dialog/confirm-dialog.mjs +8 -8
- package/dist/main/alert-dialog/dialog-loading-action.d.ts +13 -0
- package/dist/main/alert-dialog/dialog-loading-action.js +42 -0
- package/dist/main/alert-dialog/dialog-loading-action.mjs +40 -0
- package/dist/main/alert-dialog/high-priority-confirm-dialog.d.ts +6 -3
- package/dist/main/alert-dialog/high-priority-confirm-dialog.js +10 -4
- package/dist/main/alert-dialog/high-priority-confirm-dialog.mjs +11 -5
- package/dist/main/alert-dialog/index.d.ts +1 -0
- package/dist/main/alert-dialog/info-dialog.d.ts +5 -2
- package/dist/main/alert-dialog/info-dialog.js +6 -5
- package/dist/main/alert-dialog/info-dialog.mjs +7 -6
- package/dist/main/alert-dialog/undoable-confirm-dialog.d.ts +7 -4
- package/dist/main/alert-dialog/undoable-confirm-dialog.js +18 -17
- package/dist/main/alert-dialog/undoable-confirm-dialog.mjs +19 -18
- package/dist/main/buttons/gradient-button.d.ts +3 -1
- package/dist/main/buttons/gradient-button.js +29 -3
- package/dist/main/buttons/gradient-button.mjs +29 -3
- package/dist/main/buttons/index.d.ts +1 -0
- package/dist/main/buttons/index.js +3 -0
- package/dist/main/buttons/index.mjs +1 -0
- package/dist/main/buttons/use-press-feedback.d.ts +18 -0
- package/dist/main/buttons/use-press-feedback.js +42 -0
- package/dist/main/buttons/use-press-feedback.mjs +39 -0
- package/dist/main/buttons/x-button.d.ts +3 -0
- package/dist/main/buttons/x-button.js +36 -6
- package/dist/main/buttons/x-button.mjs +36 -6
- package/dist/main/calendar/calendar-date-range-input.d.ts +17 -0
- package/dist/main/calendar/calendar-date-range-input.js +81 -0
- package/dist/main/calendar/calendar-date-range-input.mjs +79 -0
- package/dist/main/calendar/calendar-status-view.d.ts +23 -0
- package/dist/main/calendar/calendar-status-view.js +155 -0
- package/dist/main/calendar/calendar-status-view.mjs +153 -0
- package/dist/main/calendar/index.d.ts +3 -0
- package/dist/main/calendar/index.js +12 -0
- package/dist/main/calendar/index.mjs +4 -0
- package/dist/main/calendar/random-date-range-dialog.d.ts +18 -0
- package/dist/main/calendar/random-date-range-dialog.js +451 -0
- package/dist/main/calendar/random-date-range-dialog.mjs +449 -0
- package/package.json +6 -1
- package/src/fuma/base/custom-header.tsx +6 -3
- package/src/main/alert-dialog/confirm-dialog.tsx +54 -47
- package/src/main/alert-dialog/dialog-loading-action.tsx +78 -0
- package/src/main/alert-dialog/high-priority-confirm-dialog.tsx +63 -48
- package/src/main/alert-dialog/index.ts +1 -0
- package/src/main/alert-dialog/info-dialog.tsx +52 -44
- package/src/main/alert-dialog/undoable-confirm-dialog.tsx +90 -82
- package/src/main/buttons/gradient-button.tsx +36 -3
- package/src/main/buttons/index.ts +1 -0
- package/src/main/buttons/use-press-feedback.ts +58 -0
- package/src/main/buttons/x-button.tsx +53 -11
- package/src/main/calendar/calendar-date-range-input.tsx +173 -0
- package/src/main/calendar/calendar-status-view.tsx +365 -0
- package/src/main/calendar/index.ts +5 -0
- package/src/main/calendar/random-date-range-dialog.tsx +753 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { __awaiter } from 'tslib';
|
|
3
|
-
import {
|
|
3
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
4
4
|
import React__default from 'react';
|
|
5
5
|
import { Trash2Icon, CircleAlertIcon, XIcon, Undo2Icon } from '@windrun-huaiin/base-ui/icons';
|
|
6
6
|
import { themeIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
7
7
|
import { AlertDialog, AlertDialogContent, AlertDialogTitle, AlertDialogDescription } from '@windrun-huaiin/base-ui/ui';
|
|
8
8
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
9
9
|
import { dialogThemedOverlayClass, dialogHeaderClass, dialogTitleClass, closeButtonClass, dialogDescriptionClass, secondaryButtonClass, dangerButtonClass, dialogFooterClass, dialogContentClass } from './dialog-styles.mjs';
|
|
10
|
+
import { useDialogLoadingAction } from './dialog-loading-action.mjs';
|
|
10
11
|
|
|
11
|
-
function UndoableConfirmDialog({ open, onOpenChange, title, description, pendingTitle, pendingDescription, cancelText = 'Cancel', confirmText = 'Delete', undoText = 'Undo', emphasis = 'confirm', countdownSeconds = 5, onCancel, onConfirm, onUndo, }) {
|
|
12
|
+
function UndoableConfirmDialog({ open, onOpenChange, title, description, pendingTitle, pendingDescription, cancelText = 'Cancel', confirmText = 'Delete', undoText = 'Undo', emphasis = 'confirm', countdownSeconds = 5, loadingActions, loadingFullPage, onCancel, onConfirm, onUndo, }) {
|
|
12
13
|
const safeCountdownSeconds = Math.max(1, Math.floor(countdownSeconds));
|
|
13
14
|
const [pending, setPending] = React__default.useState(false);
|
|
14
15
|
const [remainingSeconds, setRemainingSeconds] = React__default.useState(safeCountdownSeconds);
|
|
@@ -17,6 +18,7 @@ function UndoableConfirmDialog({ open, onOpenChange, title, description, pending
|
|
|
17
18
|
const intervalRef = React__default.useRef(null);
|
|
18
19
|
const cancelButtonClass = emphasis === 'cancel' ? dangerButtonClass : secondaryButtonClass;
|
|
19
20
|
const confirmButtonClass = emphasis === 'cancel' ? secondaryButtonClass : dangerButtonClass;
|
|
21
|
+
const { dialogLoading, runDialogAction } = useDialogLoadingAction({ loadingActions, loadingFullPage, onOpenChange });
|
|
20
22
|
const clearTimers = React__default.useCallback(() => {
|
|
21
23
|
if (timeoutRef.current) {
|
|
22
24
|
window.clearTimeout(timeoutRef.current);
|
|
@@ -45,13 +47,12 @@ function UndoableConfirmDialog({ open, onOpenChange, title, description, pending
|
|
|
45
47
|
clearTimers();
|
|
46
48
|
setConfirming(true);
|
|
47
49
|
try {
|
|
48
|
-
yield onConfirm
|
|
49
|
-
onOpenChange(false);
|
|
50
|
+
yield runDialogAction('confirm', onConfirm);
|
|
50
51
|
}
|
|
51
52
|
finally {
|
|
52
53
|
setConfirming(false);
|
|
53
54
|
}
|
|
54
|
-
}), [clearTimers, onConfirm,
|
|
55
|
+
}), [clearTimers, onConfirm, runDialogAction]);
|
|
55
56
|
const startCountdown = () => {
|
|
56
57
|
clearTimers();
|
|
57
58
|
setPending(true);
|
|
@@ -65,27 +66,27 @@ function UndoableConfirmDialog({ open, onOpenChange, title, description, pending
|
|
|
65
66
|
};
|
|
66
67
|
const handleCancel = () => {
|
|
67
68
|
resetState();
|
|
68
|
-
|
|
69
|
-
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
69
|
+
void runDialogAction('cancel', onCancel);
|
|
70
70
|
};
|
|
71
71
|
const handleClose = React__default.useCallback(() => {
|
|
72
72
|
resetState();
|
|
73
73
|
onOpenChange(false);
|
|
74
74
|
}, [onOpenChange, resetState]);
|
|
75
|
-
const handleUndo = () => {
|
|
75
|
+
const handleUndo = () => __awaiter(this, void 0, void 0, function* () {
|
|
76
76
|
resetState();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
77
|
+
yield runDialogAction('undo', onUndo);
|
|
78
|
+
});
|
|
80
79
|
const displayTitle = pending ? pendingTitle !== null && pendingTitle !== void 0 ? pendingTitle : title : title;
|
|
81
80
|
const displayDescription = pending ? pendingDescription !== null && pendingDescription !== void 0 ? pendingDescription : description : description;
|
|
82
|
-
return (jsx(AlertDialog, { open: open, onOpenChange: (nextOpen) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
return (jsxs(Fragment, { children: [jsx(AlertDialog, { open: open, onOpenChange: (nextOpen) => {
|
|
82
|
+
if (!nextOpen) {
|
|
83
|
+
handleClose();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
onOpenChange(nextOpen);
|
|
87
|
+
}, children: jsxs(AlertDialogContent, { className: cn(dialogContentClass, 'border-red-300 dark:border-red-700'), overlayClassName: dialogThemedOverlayClass, onOverlayClick: pending ? undefined : handleClose, children: [jsxs("div", { className: dialogHeaderClass, children: [jsx(AlertDialogTitle, { asChild: true, children: jsxs("div", { className: dialogTitleClass, children: [jsx("span", { className: "inline-flex size-9 shrink-0 items-center justify-center rounded-full bg-red-100 text-red-600 ring-1 ring-red-200 dark:bg-red-950 dark:text-red-300 dark:ring-red-900", children: pending ? jsx(Trash2Icon, { className: "size-5" }) : jsx(CircleAlertIcon, { className: "size-5" }) }), jsx("span", { className: "min-w-0 truncate", children: displayTitle })] }) }), jsx("button", { type: "button", className: closeButtonClass, onClick: handleClose, "aria-label": "Close", disabled: confirming, children: jsx(XIcon, { className: "size-4" }) })] }), jsx(AlertDialogDescription, { className: cn(dialogDescriptionClass, 'min-h-[44px]'), children: jsx("span", { children: displayDescription }) }), jsx("div", { className: "flex h-12 items-center justify-center py-1", children: jsxs("div", { className: "flex items-baseline justify-center gap-2", children: [jsx("span", { className: cn('text-4xl font-black leading-none tabular-nums', pending && 'animate-bounce', themeIconColor), children: pending ? remainingSeconds : safeCountdownSeconds }), jsx("span", { className: cn('text-sm font-bold', themeIconColor), children: "s" })] }) }), jsx("div", { className: cn(dialogFooterClass, 'min-h-[88px] sm:min-h-10 sm:items-center'), children: pending ? (jsxs("button", { type: "button", onClick: () => {
|
|
88
|
+
void handleUndo();
|
|
89
|
+
}, className: secondaryButtonClass, disabled: confirming, children: [jsx(Undo2Icon, { className: "mr-1.5 size-4" }), undoText] })) : (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: handleCancel, className: cancelButtonClass, children: cancelText }), jsx("button", { type: "button", onClick: startCountdown, className: confirmButtonClass, children: confirmText })] })) })] }) }), dialogLoading] }));
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
export { UndoableConfirmDialog };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { PressFeedback } from './use-press-feedback';
|
|
2
3
|
type GradientButtonVariant = 'default' | 'soft' | 'subtle';
|
|
3
4
|
export interface GradientButtonProps {
|
|
4
5
|
title: React.ReactNode;
|
|
@@ -15,6 +16,7 @@ export interface GradientButtonProps {
|
|
|
15
16
|
loadingText?: React.ReactNode;
|
|
16
17
|
preventDoubleClick?: boolean;
|
|
17
18
|
variant?: GradientButtonVariant;
|
|
19
|
+
pressFeedback?: PressFeedback;
|
|
18
20
|
}
|
|
19
|
-
export declare function GradientButton({ title, icon, iconForcePosition, align, disabled, className, href, openInNewTab, preserveReferrer, onClick, loadingText, preventDoubleClick, iconClassName, variant, }: GradientButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare function GradientButton({ title, icon, iconForcePosition, align, disabled, className, href, openInNewTab, preserveReferrer, onClick, loadingText, preventDoubleClick, iconClassName, variant, pressFeedback, }: GradientButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
20
22
|
export {};
|
|
@@ -8,9 +8,15 @@ var icons = require('@windrun-huaiin/base-ui/icons');
|
|
|
8
8
|
var lib = require('@windrun-huaiin/base-ui/lib');
|
|
9
9
|
var Link = require('next/link');
|
|
10
10
|
var React = require('react');
|
|
11
|
+
var usePressFeedback = require('./use-press-feedback.js');
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
const PRESS_FEEDBACK_MS = 180;
|
|
14
|
+
const gradientPressSubtleClass = 'translate-y-px scale-[0.98] shadow-inner brightness-95';
|
|
15
|
+
const gradientPressSolidClass = 'translate-y-[2px] scale-[0.96] shadow-[inset_0_2px_4px_rgba(15,23,42,0.22)] brightness-90';
|
|
16
|
+
function GradientButton({ title, icon, iconForcePosition, align = 'left', disabled = false, className = "", href, openInNewTab = true, preserveReferrer = false, onClick, loadingText, preventDoubleClick = true, iconClassName, variant = 'default', pressFeedback, }) {
|
|
13
17
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
18
|
+
const pressMode = usePressFeedback.resolvePressFeedbackMode(pressFeedback);
|
|
19
|
+
const { pressedKey, flash, getPressProps } = usePressFeedback.usePressFeedback(PRESS_FEEDBACK_MS);
|
|
14
20
|
const actualLoadingText = loadingText || (title === null || title === void 0 ? void 0 : title.toString().trim()) || 'Loading...';
|
|
15
21
|
const defaultIconClass = "h-4 w-4";
|
|
16
22
|
const finalIconClass = utils.cn(variant === 'default' ? 'text-white' : lib.themeIconColor, iconClassName || defaultIconClass);
|
|
@@ -48,6 +54,12 @@ function GradientButton({ title, icon, iconForcePosition, align = 'left', disabl
|
|
|
48
54
|
}
|
|
49
55
|
});
|
|
50
56
|
const isDisabled = disabled || isLoading;
|
|
57
|
+
const isPressed = pressMode !== 'none' && pressedKey === 'root' && !disabled;
|
|
58
|
+
const pressClassName = isPressed
|
|
59
|
+
? pressMode === 'solid'
|
|
60
|
+
? gradientPressSolidClass
|
|
61
|
+
: gradientPressSubtleClass
|
|
62
|
+
: null;
|
|
51
63
|
const displayTitle = isLoading ? actualLoadingText : title;
|
|
52
64
|
const iconProvided = icon !== undefined;
|
|
53
65
|
const iconNode = (() => {
|
|
@@ -81,8 +93,22 @@ function GradientButton({ title, icon, iconForcePosition, align = 'left', disabl
|
|
|
81
93
|
: variant === 'subtle'
|
|
82
94
|
? utils.cn(lib.themeMainBgColor, lib.themeIconColor, 'border border-neutral-200 shadow-sm hover:shadow-md hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-800')
|
|
83
95
|
: utils.cn(lib.themeButtonGradientClass, lib.themeButtonGradientHoverClass, 'text-white shadow-lg hover:shadow-xl');
|
|
84
|
-
const buttonClassName = utils.cn(baseButtonStyles, variantClassName, 'text-base font-bold transition-
|
|
85
|
-
|
|
96
|
+
const buttonClassName = utils.cn(baseButtonStyles, variantClassName, 'text-base font-bold transition-[transform,background-color,filter,box-shadow,border-color,color] duration-300 rounded-full', alignmentClass, pressClassName, isDisabled && 'opacity-50 cursor-not-allowed', className);
|
|
97
|
+
const pressProps = pressMode !== 'none' && !isDisabled ? getPressProps('root') : {};
|
|
98
|
+
return (jsxRuntime.jsx("div", { className: `flex flex-row gap-3 ${getAlignmentClass()}`, children: onClick ? (jsxRuntime.jsx("button", Object.assign({ type: "button", className: buttonClassName, onClick: (event) => {
|
|
99
|
+
if (!isDisabled && pressMode !== 'none') {
|
|
100
|
+
flash('root');
|
|
101
|
+
}
|
|
102
|
+
handleClick(event);
|
|
103
|
+
}, disabled: isDisabled }, pressProps, { children: buttonContent }))) : (jsxRuntime.jsx(Link, Object.assign({ href: href || "#", className: utils.cn(buttonClassName, "no-underline hover:no-underline") }, (openInNewTab ? { target: "_blank", rel: preserveReferrer ? 'noopener' : 'noopener noreferrer' } : {}), { onClick: (event) => {
|
|
104
|
+
if (isDisabled) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (pressMode !== 'none') {
|
|
109
|
+
flash('root');
|
|
110
|
+
}
|
|
111
|
+
}, "aria-disabled": isDisabled }, pressProps, { children: buttonContent }))) }));
|
|
86
112
|
}
|
|
87
113
|
|
|
88
114
|
exports.GradientButton = GradientButton;
|
|
@@ -6,9 +6,15 @@ import { Loader2Icon, ArrowRightIcon } from '@windrun-huaiin/base-ui/icons';
|
|
|
6
6
|
import { themeIconColor, themeBgColor, themeBorderColor, themeMainBgColor, themeButtonGradientClass, themeButtonGradientHoverClass } from '@windrun-huaiin/base-ui/lib';
|
|
7
7
|
import Link from 'next/link';
|
|
8
8
|
import React__default, { useState } from 'react';
|
|
9
|
+
import { usePressFeedback, resolvePressFeedbackMode } from './use-press-feedback.mjs';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const PRESS_FEEDBACK_MS = 180;
|
|
12
|
+
const gradientPressSubtleClass = 'translate-y-px scale-[0.98] shadow-inner brightness-95';
|
|
13
|
+
const gradientPressSolidClass = 'translate-y-[2px] scale-[0.96] shadow-[inset_0_2px_4px_rgba(15,23,42,0.22)] brightness-90';
|
|
14
|
+
function GradientButton({ title, icon, iconForcePosition, align = 'left', disabled = false, className = "", href, openInNewTab = true, preserveReferrer = false, onClick, loadingText, preventDoubleClick = true, iconClassName, variant = 'default', pressFeedback, }) {
|
|
11
15
|
const [isLoading, setIsLoading] = useState(false);
|
|
16
|
+
const pressMode = resolvePressFeedbackMode(pressFeedback);
|
|
17
|
+
const { pressedKey, flash, getPressProps } = usePressFeedback(PRESS_FEEDBACK_MS);
|
|
12
18
|
const actualLoadingText = loadingText || (title === null || title === void 0 ? void 0 : title.toString().trim()) || 'Loading...';
|
|
13
19
|
const defaultIconClass = "h-4 w-4";
|
|
14
20
|
const finalIconClass = cn(variant === 'default' ? 'text-white' : themeIconColor, iconClassName || defaultIconClass);
|
|
@@ -46,6 +52,12 @@ function GradientButton({ title, icon, iconForcePosition, align = 'left', disabl
|
|
|
46
52
|
}
|
|
47
53
|
});
|
|
48
54
|
const isDisabled = disabled || isLoading;
|
|
55
|
+
const isPressed = pressMode !== 'none' && pressedKey === 'root' && !disabled;
|
|
56
|
+
const pressClassName = isPressed
|
|
57
|
+
? pressMode === 'solid'
|
|
58
|
+
? gradientPressSolidClass
|
|
59
|
+
: gradientPressSubtleClass
|
|
60
|
+
: null;
|
|
49
61
|
const displayTitle = isLoading ? actualLoadingText : title;
|
|
50
62
|
const iconProvided = icon !== undefined;
|
|
51
63
|
const iconNode = (() => {
|
|
@@ -79,8 +91,22 @@ function GradientButton({ title, icon, iconForcePosition, align = 'left', disabl
|
|
|
79
91
|
: variant === 'subtle'
|
|
80
92
|
? cn(themeMainBgColor, themeIconColor, 'border border-neutral-200 shadow-sm hover:shadow-md hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-800')
|
|
81
93
|
: cn(themeButtonGradientClass, themeButtonGradientHoverClass, 'text-white shadow-lg hover:shadow-xl');
|
|
82
|
-
const buttonClassName = cn(baseButtonStyles, variantClassName, 'text-base font-bold transition-
|
|
83
|
-
|
|
94
|
+
const buttonClassName = cn(baseButtonStyles, variantClassName, 'text-base font-bold transition-[transform,background-color,filter,box-shadow,border-color,color] duration-300 rounded-full', alignmentClass, pressClassName, isDisabled && 'opacity-50 cursor-not-allowed', className);
|
|
95
|
+
const pressProps = pressMode !== 'none' && !isDisabled ? getPressProps('root') : {};
|
|
96
|
+
return (jsx("div", { className: `flex flex-row gap-3 ${getAlignmentClass()}`, children: onClick ? (jsx("button", Object.assign({ type: "button", className: buttonClassName, onClick: (event) => {
|
|
97
|
+
if (!isDisabled && pressMode !== 'none') {
|
|
98
|
+
flash('root');
|
|
99
|
+
}
|
|
100
|
+
handleClick(event);
|
|
101
|
+
}, disabled: isDisabled }, pressProps, { children: buttonContent }))) : (jsx(Link, Object.assign({ href: href || "#", className: cn(buttonClassName, "no-underline hover:no-underline") }, (openInNewTab ? { target: "_blank", rel: preserveReferrer ? 'noopener' : 'noopener noreferrer' } : {}), { onClick: (event) => {
|
|
102
|
+
if (isDisabled) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (pressMode !== 'none') {
|
|
107
|
+
flash('root');
|
|
108
|
+
}
|
|
109
|
+
}, "aria-disabled": isDisabled }, pressProps, { children: buttonContent }))) }));
|
|
84
110
|
}
|
|
85
111
|
|
|
86
112
|
export { GradientButton };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var gradientButton = require('./gradient-button.js');
|
|
5
|
+
var usePressFeedback = require('./use-press-feedback.js');
|
|
5
6
|
var xButton = require('./x-button.js');
|
|
6
7
|
var xSwitchButton = require('./x-switch-button.js');
|
|
7
8
|
var xToggleButton = require('./x-toggle-button.js');
|
|
@@ -9,6 +10,8 @@ var xToggleButton = require('./x-toggle-button.js');
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
exports.GradientButton = gradientButton.GradientButton;
|
|
13
|
+
exports.resolvePressFeedbackMode = usePressFeedback.resolvePressFeedbackMode;
|
|
14
|
+
exports.usePressFeedback = usePressFeedback.usePressFeedback;
|
|
12
15
|
exports.XButton = xButton.XButton;
|
|
13
16
|
exports.XSwitchButton = xSwitchButton.XSwitchButton;
|
|
14
17
|
exports.XToggleButton = xToggleButton.XToggleButton;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
export { GradientButton } from './gradient-button.mjs';
|
|
3
|
+
export { resolvePressFeedbackMode, usePressFeedback } from './use-press-feedback.mjs';
|
|
3
4
|
export { XButton } from './x-button.mjs';
|
|
4
5
|
export { XSwitchButton } from './x-switch-button.mjs';
|
|
5
6
|
export { XToggleButton } from './x-toggle-button.mjs';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type PressFeedbackKey = string;
|
|
2
|
+
export type PressFeedbackMode = 'none' | 'subtle' | 'solid';
|
|
3
|
+
export type PressFeedback = boolean | PressFeedbackMode;
|
|
4
|
+
export interface PressFeedbackProps<T extends PressFeedbackKey> {
|
|
5
|
+
onPointerDown: () => void;
|
|
6
|
+
onPointerUp: () => void;
|
|
7
|
+
onPointerLeave: () => void;
|
|
8
|
+
onPointerCancel: () => void;
|
|
9
|
+
onBlur: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolvePressFeedbackMode(pressFeedback?: PressFeedback): PressFeedbackMode;
|
|
12
|
+
export declare function usePressFeedback<T extends PressFeedbackKey>(durationMs?: number): {
|
|
13
|
+
pressedKey: T | null;
|
|
14
|
+
trigger: (key: T) => void;
|
|
15
|
+
release: (key: T) => void;
|
|
16
|
+
flash: (key: T) => void;
|
|
17
|
+
getPressProps: (key: T) => PressFeedbackProps<T>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
|
|
6
|
+
function resolvePressFeedbackMode(pressFeedback) {
|
|
7
|
+
if (pressFeedback === false || pressFeedback === 'none') {
|
|
8
|
+
return 'none';
|
|
9
|
+
}
|
|
10
|
+
if (pressFeedback === 'solid') {
|
|
11
|
+
return 'solid';
|
|
12
|
+
}
|
|
13
|
+
return 'subtle';
|
|
14
|
+
}
|
|
15
|
+
function usePressFeedback(durationMs = 180) {
|
|
16
|
+
const [pressedKey, setPressedKey] = React.useState(null);
|
|
17
|
+
function release(key) {
|
|
18
|
+
setPressedKey((current) => (current === key ? null : current));
|
|
19
|
+
}
|
|
20
|
+
function trigger(key) {
|
|
21
|
+
setPressedKey(key);
|
|
22
|
+
}
|
|
23
|
+
function flash(key) {
|
|
24
|
+
setPressedKey(key);
|
|
25
|
+
window.setTimeout(() => {
|
|
26
|
+
setPressedKey((current) => (current === key ? null : current));
|
|
27
|
+
}, durationMs);
|
|
28
|
+
}
|
|
29
|
+
function getPressProps(key) {
|
|
30
|
+
return {
|
|
31
|
+
onPointerDown: () => trigger(key),
|
|
32
|
+
onPointerUp: () => release(key),
|
|
33
|
+
onPointerLeave: () => release(key),
|
|
34
|
+
onPointerCancel: () => release(key),
|
|
35
|
+
onBlur: () => release(key),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { pressedKey, trigger, release, flash, getPressProps };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exports.resolvePressFeedbackMode = resolvePressFeedbackMode;
|
|
42
|
+
exports.usePressFeedback = usePressFeedback;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
function resolvePressFeedbackMode(pressFeedback) {
|
|
5
|
+
if (pressFeedback === false || pressFeedback === 'none') {
|
|
6
|
+
return 'none';
|
|
7
|
+
}
|
|
8
|
+
if (pressFeedback === 'solid') {
|
|
9
|
+
return 'solid';
|
|
10
|
+
}
|
|
11
|
+
return 'subtle';
|
|
12
|
+
}
|
|
13
|
+
function usePressFeedback(durationMs = 180) {
|
|
14
|
+
const [pressedKey, setPressedKey] = useState(null);
|
|
15
|
+
function release(key) {
|
|
16
|
+
setPressedKey((current) => (current === key ? null : current));
|
|
17
|
+
}
|
|
18
|
+
function trigger(key) {
|
|
19
|
+
setPressedKey(key);
|
|
20
|
+
}
|
|
21
|
+
function flash(key) {
|
|
22
|
+
setPressedKey(key);
|
|
23
|
+
window.setTimeout(() => {
|
|
24
|
+
setPressedKey((current) => (current === key ? null : current));
|
|
25
|
+
}, durationMs);
|
|
26
|
+
}
|
|
27
|
+
function getPressProps(key) {
|
|
28
|
+
return {
|
|
29
|
+
onPointerDown: () => trigger(key),
|
|
30
|
+
onPointerUp: () => release(key),
|
|
31
|
+
onPointerLeave: () => release(key),
|
|
32
|
+
onPointerCancel: () => release(key),
|
|
33
|
+
onBlur: () => release(key),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { pressedKey, trigger, release, flash, getPressProps };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { resolvePressFeedbackMode, usePressFeedback };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { PressFeedback } from './use-press-feedback';
|
|
2
3
|
type XButtonVariant = 'default' | 'soft' | 'subtle';
|
|
3
4
|
interface BaseButtonConfig {
|
|
4
5
|
icon: ReactNode;
|
|
@@ -21,6 +22,7 @@ interface SingleButtonProps {
|
|
|
21
22
|
className?: string;
|
|
22
23
|
iconClassName?: string;
|
|
23
24
|
variant?: XButtonVariant;
|
|
25
|
+
pressFeedback?: PressFeedback;
|
|
24
26
|
}
|
|
25
27
|
interface SplitButtonProps {
|
|
26
28
|
type: 'split';
|
|
@@ -33,6 +35,7 @@ interface SplitButtonProps {
|
|
|
33
35
|
dropdownButtonClassName?: string;
|
|
34
36
|
iconClassName?: string;
|
|
35
37
|
variant?: XButtonVariant;
|
|
38
|
+
pressFeedback?: PressFeedback;
|
|
36
39
|
}
|
|
37
40
|
type xButtonProps = SingleButtonProps | SplitButtonProps;
|
|
38
41
|
export declare function XButton(props: xButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,12 +7,18 @@ var React = require('react');
|
|
|
7
7
|
var icons = require('@windrun-huaiin/base-ui/icons');
|
|
8
8
|
var lib = require('@windrun-huaiin/base-ui/lib');
|
|
9
9
|
var utils = require('@windrun-huaiin/lib/utils');
|
|
10
|
+
var usePressFeedback = require('./use-press-feedback.js');
|
|
10
11
|
|
|
12
|
+
const PRESS_FEEDBACK_MS = 180;
|
|
13
|
+
const xButtonPressSubtleClass = 'translate-y-px scale-[0.98] shadow-inner brightness-95';
|
|
14
|
+
const xButtonPressSolidClass = 'translate-y-[2px] scale-[0.95] shadow-[inset_0_2px_4px_rgba(15,23,42,0.18)] brightness-95';
|
|
11
15
|
function XButton(props) {
|
|
12
16
|
var _a, _b, _c;
|
|
13
17
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
14
18
|
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
15
|
-
const
|
|
19
|
+
const splitRef = React.useRef(null);
|
|
20
|
+
const pressMode = usePressFeedback.resolvePressFeedbackMode(props.pressFeedback);
|
|
21
|
+
const { pressedKey, flash, getPressProps } = usePressFeedback.usePressFeedback(PRESS_FEEDBACK_MS);
|
|
16
22
|
const { iconClassName } = props;
|
|
17
23
|
const defaultIconClass = "w-5 h-5";
|
|
18
24
|
const variant = (_a = props.variant) !== null && _a !== void 0 ? _a : 'default';
|
|
@@ -30,7 +36,7 @@ function XButton(props) {
|
|
|
30
36
|
React.useEffect(() => {
|
|
31
37
|
if (props.type === 'split') {
|
|
32
38
|
const handleClickOutside = (event) => {
|
|
33
|
-
if (
|
|
39
|
+
if (splitRef.current && !splitRef.current.contains(event.target)) {
|
|
34
40
|
setMenuOpen(false);
|
|
35
41
|
}
|
|
36
42
|
};
|
|
@@ -56,7 +62,8 @@ function XButton(props) {
|
|
|
56
62
|
setIsLoading(false);
|
|
57
63
|
}
|
|
58
64
|
});
|
|
59
|
-
const
|
|
65
|
+
const getButtonPressClass = () => pressMode === 'solid' ? xButtonPressSolidClass : xButtonPressSubtleClass;
|
|
66
|
+
const baseButtonClass = "flex items-center justify-center gap-2 px-4 py-2 text-sm font-semibold transition-[transform,background-color,filter,box-shadow,border-color,color]";
|
|
60
67
|
const singleButtonVariantClass = variant === 'soft'
|
|
61
68
|
? utils.cn(lib.themeBgColor, lib.themeIconColor, lib.themeBorderColor, "border hover:brightness-95")
|
|
62
69
|
: variant === 'subtle'
|
|
@@ -72,18 +79,41 @@ function XButton(props) {
|
|
|
72
79
|
: variant === 'subtle'
|
|
73
80
|
? utils.cn("bg-transparent hover:bg-neutral-50 dark:hover:bg-neutral-800 sm:border-l", lib.themeIconColor, "border-neutral-200 dark:border-neutral-800")
|
|
74
81
|
: "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700 sm:border-l sm:border-neutral-300 sm:dark:border-neutral-700";
|
|
82
|
+
const splitContainerVariantClass = variant === 'soft'
|
|
83
|
+
? utils.cn('border', lib.themeBorderColor)
|
|
84
|
+
: variant === 'subtle'
|
|
85
|
+
? 'border border-neutral-200 dark:border-neutral-800'
|
|
86
|
+
: null;
|
|
75
87
|
const disabledClass = "opacity-60 cursor-not-allowed";
|
|
76
88
|
if (props.type === 'single') {
|
|
77
89
|
const { button, loadingText, minWidth = 'min-w-[110px]', className = '' } = props;
|
|
78
90
|
const isDisabled = button.disabled || isLoading;
|
|
91
|
+
const isPressed = pressMode !== 'none' && pressedKey === 'single' && !button.disabled;
|
|
79
92
|
const actualLoadingText = loadingText || ((_b = button.text) === null || _b === void 0 ? void 0 : _b.trim()) || 'Loading...';
|
|
80
|
-
return (jsxRuntime.jsx("button", { onClick: () =>
|
|
93
|
+
return (jsxRuntime.jsx("button", Object.assign({ onClick: () => {
|
|
94
|
+
if (!isDisabled && pressMode !== 'none') {
|
|
95
|
+
flash('single');
|
|
96
|
+
}
|
|
97
|
+
handleButtonClick(button.onClick);
|
|
98
|
+
}, disabled: isDisabled }, (pressMode !== 'none' && !isDisabled ? getPressProps('single') : {}), { className: utils.cn("w-full sm:w-auto", minWidth, baseButtonClass, singleButtonVariantClass, "rounded-full", isPressed && getButtonPressClass(), isDisabled && disabledClass, className), title: button.text, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(icons.Loader2Icon, { className: loadingIconClass }), jsxRuntime.jsx("span", { children: actualLoadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderIcon(button.icon), jsxRuntime.jsx("span", { children: button.text })] })) })));
|
|
81
99
|
}
|
|
82
100
|
const { mainButton, menuItems, loadingText, menuWidth = 'w-full sm:w-40', className = '', mainButtonClassName = '', dropdownButtonClassName = '' } = props;
|
|
83
101
|
const isMainDisabled = mainButton.disabled || isLoading;
|
|
102
|
+
const isMainPressed = pressMode !== 'none' && pressedKey === 'main' && !mainButton.disabled;
|
|
103
|
+
const isDropdownPressed = pressMode !== 'none' && pressedKey === 'dropdown' && !isLoading;
|
|
84
104
|
const actualLoadingText = loadingText || ((_c = mainButton.text) === null || _c === void 0 ? void 0 : _c.trim()) || 'Loading...';
|
|
85
|
-
return (jsxRuntime.jsxs("div", { className: utils.cn("relative flex flex-row items-stretch w-full sm:w-
|
|
86
|
-
|
|
105
|
+
return (jsxRuntime.jsxs("div", { className: utils.cn("relative inline-flex flex-row items-stretch w-full sm:w-fit rounded-full gap-0", splitContainerVariantClass, menuOpen && "z-90", className), ref: splitRef, children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => {
|
|
106
|
+
if (!isMainDisabled && pressMode !== 'none') {
|
|
107
|
+
flash('main');
|
|
108
|
+
}
|
|
109
|
+
handleButtonClick(mainButton.onClick);
|
|
110
|
+
}, disabled: isMainDisabled }, (pressMode !== 'none' && !isMainDisabled ? getPressProps('main') : {}), { className: utils.cn("flex-1 min-w-0 sm:min-w-[100px] sm:flex-initial rounded-l-full", baseButtonClass, splitMainButtonVariantClass, isMainPressed && getButtonPressClass(), isMainDisabled && disabledClass, mainButtonClassName), onMouseDown: e => { if (e.button === 2)
|
|
111
|
+
e.preventDefault(); }, title: mainButton.text, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(icons.Loader2Icon, { className: loadingIconClass }), jsxRuntime.jsx("span", { children: actualLoadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderIcon(mainButton.icon), jsxRuntime.jsx("span", { children: mainButton.text })] })) })), jsxRuntime.jsx("button", Object.assign({ type: "button", onClick: () => {
|
|
112
|
+
if (!isLoading && pressMode !== 'none') {
|
|
113
|
+
flash('dropdown');
|
|
114
|
+
}
|
|
115
|
+
setMenuOpen(!menuOpen);
|
|
116
|
+
}, disabled: isLoading }, (pressMode !== 'none' && !isLoading ? getPressProps('dropdown') : {}), { className: utils.cn("w-12 rounded-r-full", baseButtonClass, splitDropdownVariantClass, isDropdownPressed && getButtonPressClass(), isLoading && disabledClass, dropdownButtonClassName), "aria-label": "Open menu", children: jsxRuntime.jsx(icons.ChevronDownIcon, { className: utils.cn(chevronIconClass, menuOpen && "rotate-180", "transition-transform") }) })), menuOpen && (jsxRuntime.jsx("div", { className: utils.cn("absolute top-full right-0 mt-2 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg z-50 overflow-hidden", menuWidth), children: menuItems.map((item, index) => (jsxRuntime.jsxs("button", { type: "button", onClick: () => {
|
|
87
117
|
setMenuOpen(false);
|
|
88
118
|
handleButtonClick(item.onClick);
|
|
89
119
|
}, disabled: item.disabled || isLoading, className: utils.cn("w-full flex items-center gap-2 px-3 py-2 text-sm text-left hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors", item.disabled && disabledClass, item.splitTopBorder && "border-t border-neutral-200 dark:border-neutral-700"), children: [renderIcon(item.icon), jsxRuntime.jsx("span", { className: "flex-1", children: item.text }), item.tag && (jsxRuntime.jsx("span", { className: utils.cn("px-1.5 py-0.5 text-xs rounded", item.tag.color || "bg-blue-100 text-blue-800"), children: item.tag.text }))] }, index))) }))] }));
|
|
@@ -5,12 +5,18 @@ import React__default, { useState, useRef, useEffect } from 'react';
|
|
|
5
5
|
import { Loader2Icon, ChevronDownIcon } from '@windrun-huaiin/base-ui/icons';
|
|
6
6
|
import { themeIconColor, themeBgColor, themeBorderColor, themeMainBgColor } from '@windrun-huaiin/base-ui/lib';
|
|
7
7
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
8
|
+
import { resolvePressFeedbackMode, usePressFeedback } from './use-press-feedback.mjs';
|
|
8
9
|
|
|
10
|
+
const PRESS_FEEDBACK_MS = 180;
|
|
11
|
+
const xButtonPressSubtleClass = 'translate-y-px scale-[0.98] shadow-inner brightness-95';
|
|
12
|
+
const xButtonPressSolidClass = 'translate-y-[2px] scale-[0.95] shadow-[inset_0_2px_4px_rgba(15,23,42,0.18)] brightness-95';
|
|
9
13
|
function XButton(props) {
|
|
10
14
|
var _a, _b, _c;
|
|
11
15
|
const [isLoading, setIsLoading] = useState(false);
|
|
12
16
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
13
|
-
const
|
|
17
|
+
const splitRef = useRef(null);
|
|
18
|
+
const pressMode = resolvePressFeedbackMode(props.pressFeedback);
|
|
19
|
+
const { pressedKey, flash, getPressProps } = usePressFeedback(PRESS_FEEDBACK_MS);
|
|
14
20
|
const { iconClassName } = props;
|
|
15
21
|
const defaultIconClass = "w-5 h-5";
|
|
16
22
|
const variant = (_a = props.variant) !== null && _a !== void 0 ? _a : 'default';
|
|
@@ -28,7 +34,7 @@ function XButton(props) {
|
|
|
28
34
|
useEffect(() => {
|
|
29
35
|
if (props.type === 'split') {
|
|
30
36
|
const handleClickOutside = (event) => {
|
|
31
|
-
if (
|
|
37
|
+
if (splitRef.current && !splitRef.current.contains(event.target)) {
|
|
32
38
|
setMenuOpen(false);
|
|
33
39
|
}
|
|
34
40
|
};
|
|
@@ -54,7 +60,8 @@ function XButton(props) {
|
|
|
54
60
|
setIsLoading(false);
|
|
55
61
|
}
|
|
56
62
|
});
|
|
57
|
-
const
|
|
63
|
+
const getButtonPressClass = () => pressMode === 'solid' ? xButtonPressSolidClass : xButtonPressSubtleClass;
|
|
64
|
+
const baseButtonClass = "flex items-center justify-center gap-2 px-4 py-2 text-sm font-semibold transition-[transform,background-color,filter,box-shadow,border-color,color]";
|
|
58
65
|
const singleButtonVariantClass = variant === 'soft'
|
|
59
66
|
? cn(themeBgColor, themeIconColor, themeBorderColor, "border hover:brightness-95")
|
|
60
67
|
: variant === 'subtle'
|
|
@@ -70,18 +77,41 @@ function XButton(props) {
|
|
|
70
77
|
: variant === 'subtle'
|
|
71
78
|
? cn("bg-transparent hover:bg-neutral-50 dark:hover:bg-neutral-800 sm:border-l", themeIconColor, "border-neutral-200 dark:border-neutral-800")
|
|
72
79
|
: "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700 sm:border-l sm:border-neutral-300 sm:dark:border-neutral-700";
|
|
80
|
+
const splitContainerVariantClass = variant === 'soft'
|
|
81
|
+
? cn('border', themeBorderColor)
|
|
82
|
+
: variant === 'subtle'
|
|
83
|
+
? 'border border-neutral-200 dark:border-neutral-800'
|
|
84
|
+
: null;
|
|
73
85
|
const disabledClass = "opacity-60 cursor-not-allowed";
|
|
74
86
|
if (props.type === 'single') {
|
|
75
87
|
const { button, loadingText, minWidth = 'min-w-[110px]', className = '' } = props;
|
|
76
88
|
const isDisabled = button.disabled || isLoading;
|
|
89
|
+
const isPressed = pressMode !== 'none' && pressedKey === 'single' && !button.disabled;
|
|
77
90
|
const actualLoadingText = loadingText || ((_b = button.text) === null || _b === void 0 ? void 0 : _b.trim()) || 'Loading...';
|
|
78
|
-
return (jsx("button", { onClick: () =>
|
|
91
|
+
return (jsx("button", Object.assign({ onClick: () => {
|
|
92
|
+
if (!isDisabled && pressMode !== 'none') {
|
|
93
|
+
flash('single');
|
|
94
|
+
}
|
|
95
|
+
handleButtonClick(button.onClick);
|
|
96
|
+
}, disabled: isDisabled }, (pressMode !== 'none' && !isDisabled ? getPressProps('single') : {}), { className: cn("w-full sm:w-auto", minWidth, baseButtonClass, singleButtonVariantClass, "rounded-full", isPressed && getButtonPressClass(), isDisabled && disabledClass, className), title: button.text, children: isLoading ? (jsxs(Fragment, { children: [jsx(Loader2Icon, { className: loadingIconClass }), jsx("span", { children: actualLoadingText })] })) : (jsxs(Fragment, { children: [renderIcon(button.icon), jsx("span", { children: button.text })] })) })));
|
|
79
97
|
}
|
|
80
98
|
const { mainButton, menuItems, loadingText, menuWidth = 'w-full sm:w-40', className = '', mainButtonClassName = '', dropdownButtonClassName = '' } = props;
|
|
81
99
|
const isMainDisabled = mainButton.disabled || isLoading;
|
|
100
|
+
const isMainPressed = pressMode !== 'none' && pressedKey === 'main' && !mainButton.disabled;
|
|
101
|
+
const isDropdownPressed = pressMode !== 'none' && pressedKey === 'dropdown' && !isLoading;
|
|
82
102
|
const actualLoadingText = loadingText || ((_c = mainButton.text) === null || _c === void 0 ? void 0 : _c.trim()) || 'Loading...';
|
|
83
|
-
return (jsxs("div", { className: cn("relative flex flex-row items-stretch w-full sm:w-
|
|
84
|
-
|
|
103
|
+
return (jsxs("div", { className: cn("relative inline-flex flex-row items-stretch w-full sm:w-fit rounded-full gap-0", splitContainerVariantClass, menuOpen && "z-90", className), ref: splitRef, children: [jsx("button", Object.assign({ onClick: () => {
|
|
104
|
+
if (!isMainDisabled && pressMode !== 'none') {
|
|
105
|
+
flash('main');
|
|
106
|
+
}
|
|
107
|
+
handleButtonClick(mainButton.onClick);
|
|
108
|
+
}, disabled: isMainDisabled }, (pressMode !== 'none' && !isMainDisabled ? getPressProps('main') : {}), { className: cn("flex-1 min-w-0 sm:min-w-[100px] sm:flex-initial rounded-l-full", baseButtonClass, splitMainButtonVariantClass, isMainPressed && getButtonPressClass(), isMainDisabled && disabledClass, mainButtonClassName), onMouseDown: e => { if (e.button === 2)
|
|
109
|
+
e.preventDefault(); }, title: mainButton.text, children: isLoading ? (jsxs(Fragment, { children: [jsx(Loader2Icon, { className: loadingIconClass }), jsx("span", { children: actualLoadingText })] })) : (jsxs(Fragment, { children: [renderIcon(mainButton.icon), jsx("span", { children: mainButton.text })] })) })), jsx("button", Object.assign({ type: "button", onClick: () => {
|
|
110
|
+
if (!isLoading && pressMode !== 'none') {
|
|
111
|
+
flash('dropdown');
|
|
112
|
+
}
|
|
113
|
+
setMenuOpen(!menuOpen);
|
|
114
|
+
}, disabled: isLoading }, (pressMode !== 'none' && !isLoading ? getPressProps('dropdown') : {}), { className: cn("w-12 rounded-r-full", baseButtonClass, splitDropdownVariantClass, isDropdownPressed && getButtonPressClass(), isLoading && disabledClass, dropdownButtonClassName), "aria-label": "Open menu", children: jsx(ChevronDownIcon, { className: cn(chevronIconClass, menuOpen && "rotate-180", "transition-transform") }) })), menuOpen && (jsx("div", { className: cn("absolute top-full right-0 mt-2 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg z-50 overflow-hidden", menuWidth), children: menuItems.map((item, index) => (jsxs("button", { type: "button", onClick: () => {
|
|
85
115
|
setMenuOpen(false);
|
|
86
116
|
handleButtonClick(item.onClick);
|
|
87
117
|
}, disabled: item.disabled || isLoading, className: cn("w-full flex items-center gap-2 px-3 py-2 text-sm text-left hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors", item.disabled && disabledClass, item.splitTopBorder && "border-t border-neutral-200 dark:border-neutral-700"), children: [renderIcon(item.icon), jsx("span", { className: "flex-1", children: item.text }), item.tag && (jsx("span", { className: cn("px-1.5 py-0.5 text-xs rounded", item.tag.color || "bg-blue-100 text-blue-800"), children: item.tag.text }))] }, index))) }))] }));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PressFeedback } from '../buttons/use-press-feedback';
|
|
2
|
+
import { type RandomCalendarRange } from './random-date-range-dialog';
|
|
3
|
+
export type CalendarDateRangeValue = RandomCalendarRange;
|
|
4
|
+
export type CalendarDateRangeInputProps = {
|
|
5
|
+
value: CalendarDateRangeValue;
|
|
6
|
+
onChange: (value: CalendarDateRangeValue) => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
defaultRangeDays?: number;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
showDayCount?: boolean;
|
|
12
|
+
dayCountUnit?: string;
|
|
13
|
+
themedCalendarIcon?: boolean;
|
|
14
|
+
clearPressFeedback?: PressFeedback;
|
|
15
|
+
onOpenChange?: (open: boolean) => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function CalendarDateRangeInput({ value, onChange, placeholder, defaultRangeDays, disabled, className, showDayCount, dayCountUnit, themedCalendarIcon, clearPressFeedback, onOpenChange, }: CalendarDateRangeInputProps): import("react/jsx-runtime").JSX.Element;
|