From 4e711d8f5dc3693f1764e8f9cb511a8d33944735 Mon Sep 17 00:00:00 2001 From: Mats Bosson Date: Sun, 29 Mar 2026 05:32:45 +0700 Subject: [PATCH] Dialog types and context --- .../src/components/dialog/dialog-context.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/core/src/components/dialog/dialog-context.ts diff --git a/packages/core/src/components/dialog/dialog-context.ts b/packages/core/src/components/dialog/dialog-context.ts new file mode 100644 index 0000000..062979c --- /dev/null +++ b/packages/core/src/components/dialog/dialog-context.ts @@ -0,0 +1,71 @@ +// packages/core/src/components/dialog/dialog-context.ts +import type { Accessor } from "solid-js"; +import { createContext, useContext } from "solid-js"; + +// ─── Internal Context (used only by Dialog parts) ────────────────────────── + +export interface InternalDialogContextValue { + isOpen: Accessor; + setOpen: (open: boolean) => void; + modal: Accessor; + /** SSR-safe ID for Dialog.Content element */ + contentId: Accessor; + /** Registered ID from Dialog.Title — used for aria-labelledby */ + titleId: Accessor; + setTitleId: (id: string | undefined) => void; + /** Registered ID from Dialog.Description — used for aria-describedby */ + descriptionId: Accessor; + setDescriptionId: (id: string | undefined) => void; + /** Whether Dialog.Trigger has been explicitly rendered */ + hasTrigger: Accessor; + setHasTrigger: (has: boolean) => void; +} + +const InternalDialogContext = createContext(); + +/** + * Returns the internal Dialog context value. + * Throws if used outside of a Dialog root. + */ +export function useInternalDialogContext(): InternalDialogContextValue { + const ctx = useContext(InternalDialogContext); + if (!ctx) { + throw new Error( + "[PettyUI] Dialog parts must be used inside .\n" + + " Fix: Wrap your Dialog.Content, Dialog.Trigger, etc. inside .\n" + + " Docs: https://pettyui.dev/components/dialog#composition", + ); + } + return ctx; +} + +export const InternalDialogContextProvider = InternalDialogContext.Provider; + +// ─── Public Context (exported via Dialog.useContext) ─────────────────────── + +export interface DialogContextValue { + /** Whether the dialog is currently open. */ + open: Accessor; + /** Whether the dialog renders as a modal (blocks outside interaction). */ + modal: Accessor; +} + +const DialogContext = createContext(); + +/** + * Returns the public Dialog context value. + * Throws if used outside of a Dialog root. + */ +export function useDialogContext(): DialogContextValue { + const ctx = useContext(DialogContext); + if (!ctx) { + throw new Error( + "[PettyUI] Dialog.useContext() was called outside of a .\n" + + " Fix: Call Dialog.useContext() inside a component rendered within .\n" + + " Docs: https://pettyui.dev/components/dialog#context", + ); + } + return ctx; +} + +export const DialogContextProvider = DialogContext.Provider;