Clean up Dialog internals

This commit is contained in:
Mats Bosson 2026-03-29 05:54:12 +07:00
parent 87af246d71
commit 8ab23a1722
6 changed files with 22 additions and 32 deletions

View File

@ -1,6 +1,6 @@
// packages/core/src/components/dialog/dialog-close.tsx
import type { Component, JSX } from "solid-js";
import { splitProps } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
import { Dynamic } from "solid-js/web";
import { useInternalDialogContext } from "./dialog-context";
@ -20,8 +20,9 @@ export function DialogClose(props: DialogCloseProps): JSX.Element {
ctx.setOpen(false);
};
const closeProps = mergeProps(rest, { onClick: handleClick });
return (
<Dynamic component={local.as ?? "button"} onClick={handleClick} {...rest}>
<Dynamic component={local.as ?? "button"} {...closeProps}>
{local.children}
</Dynamic>
);

View File

@ -3,7 +3,6 @@ import type { JSX } from "solid-js";
import { Show, createEffect, onCleanup, splitProps } from "solid-js";
import { createDismiss } from "../../utilities/dismiss/create-dismiss";
import { createFocusTrap } from "../../utilities/focus-trap/create-focus-trap";
import { Portal } from "../../utilities/portal/portal";
import { createScrollLock } from "../../utilities/scroll-lock/create-scroll-lock";
import { useInternalDialogContext } from "./dialog-context";
@ -22,7 +21,8 @@ export interface DialogContentProps extends JSX.DialogHtmlAttributes<HTMLDialogE
}
/**
* Dialog content panel. Portals to body, manages focus trap, scroll lock, and dismiss.
* Dialog content panel. Manages focus trap, scroll lock, and dismiss.
* Renders inline; wrap with Dialog.Portal to control portaling.
* Only renders when open (unless forceMount is set).
*/
export function DialogContent(props: DialogContentProps): JSX.Element {
@ -61,19 +61,17 @@ export function DialogContent(props: DialogContentProps): JSX.Element {
return (
<Show when={local.forceMount || ctx.isOpen()}>
<Portal>
<dialog
ref={contentRef}
id={ctx.contentId()}
aria-modal={ctx.modal() || undefined}
aria-labelledby={ctx.titleId()}
aria-describedby={ctx.descriptionId()}
data-state={ctx.isOpen() ? "open" : "closed"}
{...rest}
>
{local.children}
</dialog>
</Portal>
<dialog
ref={contentRef}
id={ctx.contentId()}
aria-modal={ctx.modal() || undefined}
aria-labelledby={ctx.titleId() || undefined}
aria-describedby={ctx.descriptionId() || undefined}
data-state={ctx.isOpen() ? "open" : "closed"}
{...rest}
>
{local.children}
</dialog>
</Show>
);
}

View File

@ -16,9 +16,6 @@ export interface InternalDialogContextValue {
/** Registered ID from Dialog.Description — used for aria-describedby */
descriptionId: Accessor<string | undefined>;
setDescriptionId: (id: string | undefined) => void;
/** Whether Dialog.Trigger has been explicitly rendered */
hasTrigger: Accessor<boolean>;
setHasTrigger: (has: boolean) => void;
}
const InternalDialogContext = createContext<InternalDialogContextValue>();

View File

@ -1,6 +1,5 @@
// packages/core/src/components/dialog/dialog-portal.tsx
import type { JSX } from "solid-js";
import { splitProps } from "solid-js";
import { Portal } from "../../utilities/portal/portal";
export interface DialogPortalProps {
@ -11,10 +10,9 @@ export interface DialogPortalProps {
/** Renders children into a portal (defaults to document.body). */
export function DialogPortal(props: DialogPortalProps): JSX.Element {
const [local, rest] = splitProps(props, ["target", "children"]);
return local.target !== undefined ? (
<Portal target={local.target}>{local.children}</Portal>
return props.target !== undefined ? (
<Portal target={props.target}>{props.children}</Portal>
) : (
<Portal>{local.children}</Portal>
<Portal>{props.children}</Portal>
);
}

View File

@ -1,6 +1,6 @@
// packages/core/src/components/dialog/dialog-root.tsx
import type { JSX } from "solid-js";
import { createSignal } from "solid-js";
import { createUniqueId } from "solid-js";
import {
type CreateDisclosureStateOptions,
createDisclosureState,
@ -44,11 +44,9 @@ export function DialogRoot(props: DialogRootProps): JSX.Element {
},
} as CreateDisclosureStateOptions);
const contentId = `pettyui-dialog-${Math.random().toString(36).slice(2, 9)}`;
const contentId = createUniqueId();
const [titleId, setTitleId] = createRegisterId();
const [descriptionId, setDescriptionId] = createRegisterId();
const [hasTrigger, setHasTrigger] = createSignal(false);
const internalCtx: InternalDialogContextValue = {
isOpen: disclosure.isOpen,
setOpen: (open) => (open ? disclosure.open() : disclosure.close()),
@ -58,8 +56,6 @@ export function DialogRoot(props: DialogRootProps): JSX.Element {
setTitleId,
descriptionId,
setDescriptionId,
hasTrigger,
setHasTrigger,
};
return (

View File

@ -1,10 +1,10 @@
// packages/core/src/components/dialog/index.ts
import { DialogClose } from "./dialog-close";
import { DialogContent } from "./dialog-content";
import { useDialogContext } from "./dialog-context";
import { DialogDescription } from "./dialog-description";
import { DialogOverlay } from "./dialog-overlay";
import { DialogPortal } from "./dialog-portal";
// packages/core/src/components/dialog/index.ts
import { DialogRoot } from "./dialog-root";
import { DialogTitle } from "./dialog-title";
import { DialogTrigger } from "./dialog-trigger";