@ceed/ads 1.38.0 → 1.39.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.
@@ -4,6 +4,12 @@ export interface DialogFrameProps {
4
4
  children: React.ReactNode;
5
5
  actions: React.ReactNode;
6
6
  fullscreen?: boolean;
7
+ /**
8
+ * When provided, the dialog root renders as a `<form>` and this handler runs on submit.
9
+ * This enables native submit-on-Enter and lets a `type="submit"` button in `actions`
10
+ * submit the fields in the content area. When omitted, the root stays a `<div>`.
11
+ */
12
+ onSubmit?: React.FormEventHandler<HTMLFormElement>;
7
13
  }
8
14
  declare const DialogFrame: React.ForwardRefExoticComponent<Omit<Omit<Pick<{
9
15
  children?: React.ReactNode;
@@ -18,5 +24,5 @@ declare const DialogFrame: React.ForwardRefExoticComponent<Omit<Omit<Pick<{
18
24
  variant?: import("@mui/types").OverridableStringUnion<import("@mui/joy").VariantProp, import("@mui/joy").ModalDialogPropsVariantOverrides> | undefined;
19
25
  } & import("@mui/joy").ModalDialogSlotsAndSlotProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
20
26
  ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
21
- }, "children" | "layout" | "color" | "maxWidth" | "minWidth" | "variant" | "sx" | "size" | "invertedColors" | "orientation" | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "style" | "title" | "children" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "layout" | "color" | "content" | "maxWidth" | "minWidth" | "translate" | "ref" | "slot" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "variant" | "size" | "invertedColors" | "orientation" | keyof import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "title" | "children"> & DialogFrameProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
27
+ }, "children" | "layout" | "color" | "maxWidth" | "minWidth" | "variant" | "sx" | "size" | "invertedColors" | "orientation" | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "style" | "title" | "children" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "layout" | "color" | "content" | "maxWidth" | "minWidth" | "translate" | "ref" | "slot" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "variant" | "size" | "invertedColors" | "orientation" | keyof import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "title" | "children" | "onSubmit"> & DialogFrameProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
22
28
  export { DialogFrame };
@@ -204,41 +204,45 @@ function DeleteConfirmation({ itemName, open, onClose, onDelete }) {
204
204
 
205
205
  ## Form Dialog
206
206
 
207
+ > ✅ **Best Practice** ✅
208
+ >
209
+ > For any dialog that collects input and submits, **always pass `onSubmit`** to DialogFrame — never wire the confirm button with `onClick` alone. When `onSubmit` is provided, DialogFrame renders its root as a `<form>`, so the fields in the content area and the `type="submit"` button in `actions` belong to the same form. This is what enables **submit-on-Enter** (pressing Enter inside an input submits) plus native constraint validation (`required`, `type="email"`, etc.).
210
+ >
211
+ > An `onClick` handler on the action button cannot do this: the button lives in `actions`, a sibling of the content area, so without a wrapping `<form>` there is no implicit submit and Enter does nothing.
212
+
207
213
  ```tsx
208
214
  function QuickAddDialog({ open, onClose, onSubmit }) {
209
- const [name, setName] = useState('');
210
- const [email, setEmail] = useState('');
211
-
212
- const handleSubmit = () => {
213
- onSubmit({ name, email });
214
- setName('');
215
- setEmail('');
215
+ const handleSubmit = (event) => {
216
+ event.preventDefault();
217
+ const formData = new FormData(event.currentTarget);
218
+ onSubmit({ name: formData.get('name'), email: formData.get('email') });
216
219
  onClose();
217
220
  };
218
221
 
219
222
  return (
220
223
  <Modal open={open} onClose={onClose}>
224
+ {/* onSubmit makes the root a <form>; Enter in any field submits */}
221
225
  <DialogFrame
222
226
  title="Add New User"
227
+ onSubmit={handleSubmit}
223
228
  actions={
224
229
  <>
225
230
  <Button variant="plain" color="neutral" onClick={onClose}>
226
231
  Cancel
227
232
  </Button>
228
- <Button onClick={handleSubmit} disabled={!name || !email}>
229
- Add User
230
- </Button>
233
+ {/* type="submit" ties this button to the DialogFrame form */}
234
+ <Button type="submit">Add User</Button>
231
235
  </>
232
236
  }
233
237
  >
234
238
  <Stack gap={2}>
235
239
  <FormControl>
236
240
  <FormLabel>Name</FormLabel>
237
- <Input value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter name" autoFocus />
241
+ <Input name="name" placeholder="Enter name" autoFocus required />
238
242
  </FormControl>
239
243
  <FormControl>
240
244
  <FormLabel>Email</FormLabel>
241
- <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter email" />
245
+ <Input type="email" name="email" placeholder="Enter email" required />
242
246
  </FormControl>
243
247
  </Stack>
244
248
  </DialogFrame>
@@ -398,13 +402,14 @@ function SelectOptionDialog({ open, onClose, options, onSelect }) {
398
402
 
399
403
  ### Key Props
400
404
 
401
- | Prop | Type | Default | Description |
402
- | ------------ | -------------------------------- | ------- | -------------------------------------- |
403
- | `title` | `string` | - | Dialog title displayed in the header |
404
- | `children` | `ReactNode` | - | Dialog body content |
405
- | `actions` | `ReactNode` | - | Action buttons displayed in the footer |
406
- | `fullscreen` | `boolean` | `false` | Enable fullscreen mode |
407
- | `onKeyDown` | `(event: KeyboardEvent) => void` | - | Keyboard event handler |
405
+ | Prop | Type | Default | Description |
406
+ | ------------ | --------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
407
+ | `title` | `string` | - | Dialog title displayed in the header |
408
+ | `children` | `ReactNode` | - | Dialog body content |
409
+ | `actions` | `ReactNode` | - | Action buttons displayed in the footer |
410
+ | `fullscreen` | `boolean` | `false` | Enable fullscreen mode |
411
+ | `onSubmit` | `(event: FormEvent<HTMLFormElement>) => void` | - | When provided, the dialog root renders as a `<form>`, enabling submit-on-Enter and a `type="submit"` action button. Omit it and the root stays a `<div>` |
412
+ | `onKeyDown` | `(event: KeyboardEvent) => void` | - | Keyboard event handler |
408
413
 
409
414
  ### Structure
410
415
 
@@ -572,6 +577,25 @@ DialogFrame inherits accessibility features from Modal:
572
577
  />
573
578
  ```
574
579
 
580
+ 5. **Use `onSubmit` for input dialogs**: For any dialog that collects values and submits, pass `onSubmit` and mark the confirm button `type="submit"`. This renders the root as a `<form>`, giving you submit-on-Enter and native validation for free.
581
+
582
+ ```tsx
583
+ // ✅ Good: onSubmit + type="submit" — Enter submits, `required` is enforced
584
+ <DialogFrame
585
+ onSubmit={handleSubmit}
586
+ actions={
587
+ <>
588
+ <Button variant="plain" color="neutral" onClick={onClose}>
589
+ Cancel
590
+ </Button>
591
+ <Button type="submit">Save</Button>
592
+ </>
593
+ }
594
+ >
595
+ <Input name="email" required />
596
+ </DialogFrame>
597
+ ```
598
+
575
599
  ### ❌ Don't
576
600
 
577
601
  1. **Don't use for notifications**: Use Toast instead
@@ -607,6 +631,20 @@ showToast({ message: 'Item saved!' });
607
631
 
608
632
  4. **Don't block critical workflows**: Important features should be accessible
609
633
 
634
+ 5. **Don't submit input dialogs with `onClick` only**: Pressing Enter in a field won't submit, because the button in `actions` sits outside any form.
635
+
636
+ ```tsx
637
+ // ❌ Bad: onClick-only — Enter in the input does nothing
638
+ <DialogFrame actions={<Button onClick={handleSubmit}>Save</Button>}>
639
+ <Input value={value} onChange={...} />
640
+ </DialogFrame>
641
+
642
+ // ✅ Good: onSubmit + type="submit" — Enter submits
643
+ <DialogFrame onSubmit={handleSubmit} actions={<Button type="submit">Save</Button>}>
644
+ <Input name="value" />
645
+ </DialogFrame>
646
+ ```
647
+
610
648
  ## Performance Considerations
611
649
 
612
650
  ### Lazy Load Dialog Content