@rovula/ui 0.1.24 → 0.1.26

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.
@@ -6,7 +6,6 @@ import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, A
6
6
  import { useControlledForm, Field } from "@/components/Form";
7
7
  import { TextInput } from "@/components/TextInput/TextInput";
8
8
  export const ConfirmDialog = ({ open, onOpenChange, title, description, children, confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, onClose, trigger, typeToConfirm, hideCancelButton = false, testId, cancelClassName, confirmClassName, }) => {
9
- const formId = React.useId();
10
9
  const requiresInput = !!typeToConfirm;
11
10
  const validationSchema = React.useMemo(() => yup.object({
12
11
  confirmInput: yup
@@ -33,13 +32,19 @@ export const ConfirmDialog = ({ open, onOpenChange, title, description, children
33
32
  onCancel === null || onCancel === void 0 ? void 0 : onCancel();
34
33
  onClose === null || onClose === void 0 ? void 0 : onClose();
35
34
  };
36
- return (_jsxs(AlertDialog, { open: open, onOpenChange: handleOpenChange, children: [trigger && _jsx(AlertDialogTrigger, { asChild: true, children: trigger }), _jsxs(AlertDialogContent, { "data-testid": testId, children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { "data-testid": testId && `${testId}-title`, children: title }), description && (_jsx(AlertDialogDescription, { "data-testid": testId && `${testId}-description`, children: description }))] }), children, requiresInput && (_jsxs(FormRoot, { id: formId, className: "flex flex-col gap-4 w-full", onSubmit: () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), children: [_jsxs("p", { className: "typography-small1 text-text-contrast-max", children: ["Type \u201C", typeToConfirm, "\u201D to proceed."] }), _jsx(Field, { name: "confirmInput", component: TextInput, componentProps: {
35
+ const handleSubmit = () => {
36
+ methods.reset();
37
+ onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm();
38
+ };
39
+ return (_jsxs(AlertDialog, { open: open, onOpenChange: handleOpenChange, children: [trigger && _jsx(AlertDialogTrigger, { asChild: true, children: trigger }), _jsxs(AlertDialogContent, { "data-testid": testId, children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { "data-testid": testId && `${testId}-title`, children: title }), description && (_jsx(AlertDialogDescription, { "data-testid": testId && `${testId}-description`, children: description }))] }), children, requiresInput && (_jsxs(FormRoot, { className: "flex flex-col gap-4 w-full", onSubmit: handleSubmit, children: [_jsxs("p", { className: "typography-small1 text-text-contrast-max", children: ["Type \u201C", typeToConfirm, "\u201D to proceed."] }), _jsx(Field, { name: "confirmInput", component: TextInput, componentProps: {
37
40
  label: "Type to confirm",
38
41
  required: true,
39
42
  hasClearIcon: true,
40
43
  keepFooterSpace: true,
41
44
  fullwidth: true,
42
45
  testId: testId && `${testId}-type-to-confirm-input`,
43
- } })] })), _jsxs(AlertDialogFooter, { children: [!hideCancelButton && (_jsx(AlertDialogCancel, { className: cancelClassName, "data-testid": testId && `${testId}-cancel-button`, onClick: handleCancel, children: cancelLabel })), _jsx(AlertDialogAction, { type: "submit", form: requiresInput ? formId : undefined, disabled: requiresInput && !isFormValid, onClick: requiresInput ? undefined : () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), className: confirmClassName, "data-testid": testId && `${testId}-confirm-button`, children: confirmLabel })] })] })] }));
46
+ } })] })), _jsxs(AlertDialogFooter, { children: [!hideCancelButton && (_jsx(AlertDialogCancel, { className: cancelClassName, "data-testid": testId && `${testId}-cancel-button`, onClick: handleCancel, children: cancelLabel })), _jsx(AlertDialogAction, { type: "button", disabled: requiresInput && !isFormValid, onClick: requiresInput
47
+ ? () => methods.handleSubmit(handleSubmit)()
48
+ : () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), className: confirmClassName, "data-testid": testId && `${testId}-confirm-button`, children: confirmLabel })] })] })] }));
44
49
  };
45
50
  ConfirmDialog.displayName = "ConfirmDialog";
@@ -86,7 +86,12 @@ function renderMenuItems(items, selectedValues, onSelect) {
86
86
  // - Full a11y / WAI-ARIA
87
87
  // - Sub-menu support via DropdownMenuSub
88
88
  // ---------------------------------------------------------------------------
89
- export const Menu = ({ trigger, items, selectedValues = [], onSelect, header, open, onOpenChange, align = "start", side = "bottom", sideOffset = 4, contentClassName, testId, }) => (_jsxs(DropdownMenuRoot, { open: open, onOpenChange: onOpenChange, children: [trigger && (_jsx(DropdownMenuTrigger, { asChild: true, children: trigger })), _jsxs(DropdownMenuContent, { align: align, side: side, sideOffset: sideOffset, className: contentClassName, "data-testid": testId, children: [header && (_jsx("div", { className: "sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]", children: header })), renderMenuItems(items, selectedValues, onSelect)] })] }));
89
+ export const Menu = ({ trigger, items, selectedValues = [], onSelect, header, open, onOpenChange, align = "start", side = "bottom", sideOffset = 4, contentClassName, testId, }) => (
90
+ // Stop click events from bubbling through React's portal event system.
91
+ // DropdownMenuContent renders in a DOM portal (document.body) but React
92
+ // synthetic events still bubble through the component tree, so clicks on
93
+ // menu items reach ancestor onClick handlers (e.g. a clickable card).
94
+ _jsx("div", { onClick: (e) => e.stopPropagation(), children: _jsxs(DropdownMenuRoot, { open: open, onOpenChange: onOpenChange, children: [trigger && (_jsx(DropdownMenuTrigger, { asChild: true, children: trigger })), _jsxs(DropdownMenuContent, { align: align, side: side, sideOffset: sideOffset, className: contentClassName, "data-testid": testId, children: [header && (_jsx("div", { className: "sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]", children: header })), renderMenuItems(items, selectedValues, onSelect)] })] }) }));
90
95
  Menu.displayName = "Menu";
91
96
  // ---------------------------------------------------------------------------
92
97
  // Re-exports — backward compat for consumers using Menu's sub-components
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
@@ -63,7 +63,6 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
63
63
  cancelClassName,
64
64
  confirmClassName,
65
65
  }) => {
66
- const formId = React.useId();
67
66
  const requiresInput = !!typeToConfirm;
68
67
 
69
68
  const validationSchema = React.useMemo(
@@ -104,6 +103,11 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
104
103
  onClose?.();
105
104
  };
106
105
 
106
+ const handleSubmit = () => {
107
+ methods.reset();
108
+ onConfirm?.();
109
+ };
110
+
107
111
  return (
108
112
  <AlertDialog open={open} onOpenChange={handleOpenChange}>
109
113
  {trigger && <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>}
@@ -113,7 +117,9 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
113
117
  {title}
114
118
  </AlertDialogTitle>
115
119
  {description && (
116
- <AlertDialogDescription data-testid={testId && `${testId}-description`}>
120
+ <AlertDialogDescription
121
+ data-testid={testId && `${testId}-description`}
122
+ >
117
123
  {description}
118
124
  </AlertDialogDescription>
119
125
  )}
@@ -123,9 +129,8 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
123
129
 
124
130
  {requiresInput && (
125
131
  <FormRoot
126
- id={formId}
127
132
  className="flex flex-col gap-4 w-full"
128
- onSubmit={() => onConfirm?.()}
133
+ onSubmit={handleSubmit}
129
134
  >
130
135
  <p className="typography-small1 text-text-contrast-max">
131
136
  Type &ldquo;{typeToConfirm}&rdquo; to proceed.
@@ -156,10 +161,13 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
156
161
  </AlertDialogCancel>
157
162
  )}
158
163
  <AlertDialogAction
159
- type="submit"
160
- form={requiresInput ? formId : undefined}
164
+ type="button"
161
165
  disabled={requiresInput && !isFormValid}
162
- onClick={requiresInput ? undefined : () => onConfirm?.()}
166
+ onClick={
167
+ requiresInput
168
+ ? () => methods.handleSubmit(handleSubmit)()
169
+ : () => onConfirm?.()
170
+ }
163
171
  className={confirmClassName}
164
172
  data-testid={testId && `${testId}-confirm-button`}
165
173
  >
@@ -255,25 +255,31 @@ export const Menu = ({
255
255
  contentClassName,
256
256
  testId,
257
257
  }: MenuProps) => (
258
- <DropdownMenuRoot open={open} onOpenChange={onOpenChange}>
259
- {trigger && (
260
- <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
261
- )}
262
- <DropdownMenuContent
263
- align={align}
264
- side={side}
265
- sideOffset={sideOffset}
266
- className={contentClassName}
267
- data-testid={testId}
268
- >
269
- {header && (
270
- <div className="sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]">
271
- {header}
272
- </div>
258
+ // Stop click events from bubbling through React's portal event system.
259
+ // DropdownMenuContent renders in a DOM portal (document.body) but React
260
+ // synthetic events still bubble through the component tree, so clicks on
261
+ // menu items reach ancestor onClick handlers (e.g. a clickable card).
262
+ <div onClick={(e) => e.stopPropagation()}>
263
+ <DropdownMenuRoot open={open} onOpenChange={onOpenChange}>
264
+ {trigger && (
265
+ <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
273
266
  )}
274
- {renderMenuItems(items, selectedValues, onSelect)}
275
- </DropdownMenuContent>
276
- </DropdownMenuRoot>
267
+ <DropdownMenuContent
268
+ align={align}
269
+ side={side}
270
+ sideOffset={sideOffset}
271
+ className={contentClassName}
272
+ data-testid={testId}
273
+ >
274
+ {header && (
275
+ <div className="sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]">
276
+ {header}
277
+ </div>
278
+ )}
279
+ {renderMenuItems(items, selectedValues, onSelect)}
280
+ </DropdownMenuContent>
281
+ </DropdownMenuRoot>
282
+ </div>
277
283
  );
278
284
 
279
285
  Menu.displayName = "Menu";