@photon-ai/pho-ui 2.2.0 → 2.4.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.
- package/dist/chunks/basic-page-c_7Ii5Fr.js +1117 -0
- package/dist/chunks/basic-page-c_7Ii5Fr.js.map +1 -0
- package/dist/chunks/{button-BmhyLgy5.js → button-DNNs5RsT.js} +6 -6
- package/dist/chunks/{button-BmhyLgy5.js.map → button-DNNs5RsT.js.map} +1 -1
- package/dist/chunks/{dialog-DtFO7oy0.js → dialog-8SRNi_rv.js} +3 -3
- package/dist/chunks/{dialog-DtFO7oy0.js.map → dialog-8SRNi_rv.js.map} +1 -1
- package/dist/chunks/{error-Da7P5WZi.js → error-Crs-wqMC.js} +14 -14
- package/dist/chunks/{error-Da7P5WZi.js.map → error-Crs-wqMC.js.map} +1 -1
- package/dist/chunks/form-AiHbbw4m.js +17 -0
- package/dist/chunks/form-AiHbbw4m.js.map +1 -0
- package/dist/chunks/{input-QSAUO30c.js → input-DjvZoF8v.js} +48 -43
- package/dist/chunks/input-DjvZoF8v.js.map +1 -0
- package/dist/chunks/{input-group-5AtpIhC_.js → input-group-fc9_jD4d.js} +44 -38
- package/dist/chunks/input-group-fc9_jD4d.js.map +1 -0
- package/dist/chunks/kbd-DNI10uy7.js +18 -0
- package/dist/chunks/kbd-DNI10uy7.js.map +1 -0
- package/dist/chunks/{menu-Cgj3lq9z.js → menu-BQeprfxB.js} +5 -5
- package/dist/chunks/{menu-Cgj3lq9z.js.map → menu-BQeprfxB.js.map} +1 -1
- package/dist/chunks/scroll-area-Bu7egArU.js +166 -0
- package/dist/chunks/scroll-area-Bu7egArU.js.map +1 -0
- package/dist/chunks/sidebar-DvB-G0BP.js +231 -0
- package/dist/chunks/sidebar-DvB-G0BP.js.map +1 -0
- package/dist/chunks/toast-DoXaRPIZ.js +167 -0
- package/dist/chunks/toast-DoXaRPIZ.js.map +1 -0
- package/dist/components/button.js +1 -1
- package/dist/components/command.js +125 -172
- package/dist/components/command.js.map +1 -1
- package/dist/components/context-menu.js +23 -23
- package/dist/components/dialog.js +1 -1
- package/dist/components/error.js +1 -1
- package/dist/components/field.js +12 -5
- package/dist/components/field.js.map +1 -1
- package/dist/components/form.js +2 -14
- package/dist/components/input-group.js +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/kbd.js +2 -15
- package/dist/components/menu.js +1 -1
- package/dist/components/phone-input.js +1 -1
- package/dist/components/scroll-area.js +3 -2
- package/dist/components/toast.js +3 -57
- package/dist/index.js +65 -58
- package/dist/primitives.js +3 -3
- package/dist/sections/basic-page.js +15 -8
- package/dist/sections/sidebar.js +1 -1
- package/dist/src/components/command/command.d.ts +5 -4
- package/dist/src/components/field/field.d.ts +9 -0
- package/dist/src/components/input/input.d.ts +10 -1
- package/dist/src/components/input-group/input-group.d.ts +9 -2
- package/dist/src/components/scroll-area/index.d.ts +1 -0
- package/dist/src/components/scroll-area/scroll-area-edges.d.ts +11 -1
- package/dist/src/components/scroll-area/scroll-area.d.ts +15 -2
- package/dist/src/components/toast/index.d.ts +1 -1
- package/dist/src/components/toast/toast.d.ts +22 -7
- package/dist/src/sections/basic-page/basic-page.d.ts +210 -30
- package/dist/src/sections/basic-page/index.d.ts +1 -1
- package/dist/src/sections/sidebar/sidebar.d.ts +33 -3
- package/package.json +1 -1
- package/src/styles/pho.css +19 -0
- package/dist/chunks/basic-page-CB36yrpL.js +0 -643
- package/dist/chunks/basic-page-CB36yrpL.js.map +0 -1
- package/dist/chunks/input-QSAUO30c.js.map +0 -1
- package/dist/chunks/input-group-5AtpIhC_.js.map +0 -1
- package/dist/chunks/scroll-area-Cw4JFqNG.js +0 -152
- package/dist/chunks/scroll-area-Cw4JFqNG.js.map +0 -1
- package/dist/chunks/sidebar-BETMObNj.js +0 -186
- package/dist/chunks/sidebar-BETMObNj.js.map +0 -1
- package/dist/components/form.js.map +0 -1
- package/dist/components/kbd.js.map +0 -1
- package/dist/components/toast.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-DtFO7oy0.js","names":[],"sources":["../../src/components/dialog/dialog.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n type ElementType,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from \"react\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { AnimatedHeight } from \"../../utils/animated-height\";\nimport { cn } from \"../../utils/cn\";\nimport { ScrollArea } from \"../scroll-area\";\nimport { Button, type ButtonProps } from \"../button/button\";\nimport { CloseButton } from \"../button/close-button\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\n/* ========================== Async pending context ========================== */\n\n/** Imperative actions exposed by Base UI's `actionsRef`. */\ninterface RootActions {\n unmount: () => void;\n close: () => void;\n}\n\ninterface DialogPendingContextValue {\n /** True while a `Dialog.Action` promise is in flight. */\n pending: boolean;\n beginPending: () => void;\n endPending: () => void;\n /** Close the dialog imperatively (used by `Dialog.Action` on resolve). */\n close: () => void;\n}\n\nconst DialogPendingContext = createContext<DialogPendingContextValue | null>(\n null,\n);\n\n/**\n * Whether the surrounding dialog has a `Dialog.Action` promise in flight —\n * use it to disable secondary actions (Cancel etc.) while work is pending.\n * Returns `false` outside a `Dialog.Root`.\n */\nexport function useDialogPending(): boolean {\n return useContext(DialogPendingContext)?.pending ?? false;\n}\n\ntype RootProps = ComponentProps<typeof BaseDialog.Root>;\n\n/**\n * `Dialog.Root` with async awareness: while a `Dialog.Action` promise is\n * pending, every close request (outside press, Escape, `Dialog.Close`) is\n * cancelled so the dialog stays open until the work settles. Props pass\n * through to Base UI's `Dialog.Root` unchanged.\n */\nfunction Root({ onOpenChange, actionsRef, ...props }: RootProps) {\n const [pending, setPending] = useState(false);\n // Guard reads a ref so a resolve → close() in the same tick isn't blocked\n // by a not-yet-flushed state update.\n const pendingRef = useRef(false);\n const internalActionsRef = useRef<RootActions | null>(null);\n\n // Keep a caller-supplied actionsRef working alongside our internal one.\n useEffect(() => {\n if (!actionsRef) return;\n actionsRef.current = internalActionsRef.current;\n return () => {\n actionsRef.current = null;\n };\n }, [actionsRef]);\n\n const handleOpenChange = useCallback<NonNullable<RootProps[\"onOpenChange\"]>>(\n (open, eventDetails) => {\n if (!open && pendingRef.current) {\n eventDetails.cancel();\n return;\n }\n onOpenChange?.(open, eventDetails);\n },\n [onOpenChange],\n );\n\n const contextValue = useMemo<DialogPendingContextValue>(\n () => ({\n pending,\n beginPending: () => {\n pendingRef.current = true;\n setPending(true);\n },\n endPending: () => {\n pendingRef.current = false;\n setPending(false);\n },\n close: () => internalActionsRef.current?.close(),\n }),\n [pending],\n );\n\n return (\n <DialogPendingContext.Provider value={contextValue}>\n <BaseDialog.Root\n actionsRef={internalActionsRef}\n onOpenChange={handleOpenChange}\n {...props}\n />\n </DialogPendingContext.Provider>\n );\n}\n\nconst Trigger = BaseDialog.Trigger;\nconst Portal = BaseDialog.Portal;\nconst Close = BaseDialog.Close;\n\n/* ============================== Dialog.Action ============================== */\n\nexport interface DialogActionProps extends Omit<ButtonProps, \"onClick\"> {\n /**\n * Confirm handler. Return a promise to keep the dialog open while it runs:\n * the button shows `loading`, all dismissal is locked, and the dialog\n * closes when the promise resolves. On rejection the dialog stays open —\n * surface errors inside the handler. A non-promise return closes\n * immediately, like `Dialog.Close`.\n */\n onClick?: (\n event: ReactMouseEvent<HTMLButtonElement>,\n ) => void | Promise<unknown>;\n}\n\nfunction isThenable(value: unknown): value is Promise<unknown> {\n return (\n value != null && typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\n/**\n * The confirm button of an action row, with native promise support — a\n * `Button` that runs its (possibly async) `onClick` and closes the dialog\n * when the work settles successfully.\n */\nfunction Action({ onClick, loading, disabled, ...props }: DialogActionProps) {\n const pendingContext = useContext(DialogPendingContext);\n const [busy, setBusy] = useState(false);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n function handleClick(event: ReactMouseEvent<HTMLButtonElement>) {\n if (busy || loading) return;\n const result = onClick?.(event);\n if (!isThenable(result)) {\n pendingContext?.close();\n return;\n }\n setBusy(true);\n pendingContext?.beginPending();\n result.then(\n () => {\n pendingContext?.endPending();\n if (mountedRef.current) setBusy(false);\n pendingContext?.close();\n },\n (error: unknown) => {\n pendingContext?.endPending();\n if (mountedRef.current) setBusy(false);\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.error(\"Dialog.Action: async onClick rejected —\", error);\n }\n },\n );\n }\n\n const inert = busy || loading;\n return (\n <Button\n {...props}\n disabled={disabled || inert}\n loading={inert}\n onClick={handleClick}\n />\n );\n}\n\nfunction Backdrop({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Backdrop>) {\n return (\n <BaseDialog.Backdrop\n className={cn(\n \"bg-pho-overlay fixed inset-0 z-50 transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Popup({ className, ...props }: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n // Floating layer: bg-pho-primary + border-pho-secondary hairline, elevation kept.\n \"border-pho-secondary bg-pho-primary shadow-pho-xl fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border outline-none\",\n // Global height cap — the inset panel's footprint (inset-4 /\n // sm:inset-8). A flex column so the body region can shrink and\n // scroll while header and actions stay pinned.\n \"flex max-h-[calc(100dvh-2rem)] flex-col sm:max-h-[calc(100dvh-4rem)]\",\n // Opacity + scale only — leave translate centering alone (no transition-all).\n \"transition-[opacity,scale] duration-200 ease-out\",\n \"data-[starting-style]:scale-95 data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:duration-150\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Title({ className, ...props }: StyledProps<typeof BaseDialog.Title>) {\n return (\n <BaseDialog.Title\n className={cn(\n \"text-pho-primary font-display text-display-sm font-medium text-balance\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Description({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Description>) {\n return (\n <BaseDialog.Description\n className={cn(\n \"text-pho-description text-display-xs font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Header icon chrome: secondary ink, a hair of extra air before the title.\n * Panel dialogs use 32px (`size-8`); fullscreen gates step up to 48px\n * (`size-12`) so the glyph reads at viewport scale.\n */\nfunction DialogHeaderIcon({\n children,\n size = \"md\",\n}: {\n children: ReactNode;\n size?: \"md\" | \"lg\";\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"text-pho-secondary mb-1\",\n size === \"lg\" ? \"[&>svg]:size-12\" : \"[&>svg]:size-8\",\n )}\n >\n {children}\n </span>\n );\n}\n\ninterface ContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /**\n * Decorative icon above the title — typically a Tabler outline icon.\n * Rendered aria-hidden in secondary ink at 32px (`[&>svg]:size-8`). Pass a\n * styled node to override the ink; the size is owned by the slot.\n */\n icon?: ReactNode;\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Consequence bullets rendered as a list at the top of the body — one item\n * per outcome (what is deleted, what survives, who loses access). Use on\n * destructive confirms where the one-line description undersells the\n * impact; keep each item to a single short sentence. Wrap the load-bearing\n * figure in `<strong>` (\"<strong>3 members</strong> lose access\") — the\n * list styles it (secondary ink, medium weight); no classes needed.\n */\n consequences?: ReactNode[];\n /**\n * Final cautionary line under `consequences` — error ink at description\n * size (e.g. \"It cannot be undone. Please be certain.\"). Reserve it for\n * irreversible actions; keep it short.\n */\n warning?: ReactNode;\n /**\n * Footer action row (Cancel / Confirm, etc.). Iron rule: when `actions` is\n * set, the header close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Header close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience panel: renders the portal, backdrop, and styled popup with an\n * optional header (title/description + close) and optional action row. For\n * full control, compose the parts directly (`Dialog.Portal` /\n * `Dialog.Backdrop` / `Dialog.Popup`).\n */\nfunction Content({\n icon,\n title,\n description,\n consequences,\n warning,\n actions,\n showClose = true,\n children,\n className,\n ...props\n}: ContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a header X.\n const showCloseButton = actions == null && showClose;\n const hasHeader = Boolean(icon || title || description || showCloseButton);\n const hasConsequences = consequences != null && consequences.length > 0;\n // The scrollable middle region — actions stay pinned below it.\n const hasScrollBody = hasConsequences || warning != null || children != null;\n\n return (\n <Portal>\n <Backdrop />\n <Popup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex shrink-0 flex-col gap-1 px-6 pt-6\">\n {icon && <DialogHeaderIcon>{icon}</DialogHeaderIcon>}\n {(title || showCloseButton) && (\n // Title row height = title only. Close is out of flow, centered\n // on the row, and may paint outside it (no clip, no stretch).\n <div className=\"relative\">\n {title ? (\n <Title className={showCloseButton ? \"pr-12\" : undefined}>\n {title}\n </Title>\n ) : (\n <div className=\"h-[1.625rem]\" aria-hidden=\"true\" />\n )}\n {showCloseButton && (\n <Close\n render={\n <CloseButton className=\"absolute top-1/2 right-0 z-10 -translate-y-1/2\" />\n }\n />\n )}\n </div>\n )}\n {description && <Description>{description}</Description>}\n </div>\n )}\n {hasScrollBody && (\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content className=\"flex flex-col gap-5 px-6 py-5\">\n {hasConsequences && (\n <ul className=\"text-pho-description text-md [&_strong]:text-pho-secondary space-y-1 [&_strong]:font-medium\">\n {consequences.map((item, index) => (\n // Hand-drawn marker: a 4px dot on the first line's center —\n // quieter than the browser disc, ink tracks the text.\n <li\n key={index}\n className=\"relative pl-4 before:absolute before:top-2.5 before:left-1 before:size-1 before:rounded-full before:bg-current\"\n >\n {item}\n </li>\n ))}\n </ul>\n )}\n {warning != null && (\n <p className=\"text-pho-error text-display-xs font-medium\">\n {warning}\n </p>\n )}\n {children}\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n )}\n {actions != null && (\n <div\n className={cn(\n \"flex shrink-0 items-center justify-end gap-2 px-6 pb-5\",\n !hasScrollBody && \"pt-5\",\n )}\n >\n {actions}\n </div>\n )}\n </Popup>\n </Portal>\n );\n}\n\n/* =========================== Dialog.InsetContent =========================== */\n\n/**\n * Near-fullscreen popup — the viewport minus a breathing ring, floating over\n * a backdrop (the page stays visible through the gap, so it dims like any\n * floating layer). Still a card: border, elevation, rounded corners; the\n * scale gesture stays but shrinks to 98% — at this size 95% reads like a jump.\n */\nfunction InsetPopup({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n \"border-pho-secondary bg-pho-primary shadow-pho-xl fixed inset-4 z-50 flex flex-col overflow-hidden rounded-lg border outline-none sm:inset-8\",\n \"transition-[opacity,scale] duration-200 ease-out\",\n \"data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0 data-[ending-style]:duration-150\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface InsetContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /** Decorative icon above the title — same treatment as `Content`. */\n icon?: ReactNode;\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Bottom action row. Iron rule as `Content`: when `actions` is set, the\n * header close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Header close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience near-fullscreen panel: header pinned on top, body filling and\n * scrolling on its own, optional action row pinned at the bottom. Sits\n * between `Content` (a focused card) and `FullscreenContent` (a page\n * takeover): use it for roomy working surfaces — pickers, editors, previews —\n * that still want the page peeking through an edge.\n */\nfunction InsetContent({\n icon,\n title,\n description,\n actions,\n showClose = true,\n children,\n className,\n ...props\n}: InsetContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a header X.\n const showCloseButton = actions == null && showClose;\n const hasHeader = Boolean(icon || title || description || showCloseButton);\n\n return (\n <Portal>\n <Backdrop />\n <InsetPopup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex shrink-0 flex-col gap-1 px-6 pt-6\">\n {icon && <DialogHeaderIcon>{icon}</DialogHeaderIcon>}\n {(title || showCloseButton) && (\n <div className=\"relative\">\n {title ? (\n <Title className={showCloseButton ? \"pr-12\" : undefined}>\n {title}\n </Title>\n ) : (\n <div className=\"h-[1.625rem]\" aria-hidden=\"true\" />\n )}\n {showCloseButton && (\n <Close\n render={\n <CloseButton className=\"absolute top-1/2 right-0 z-10 -translate-y-1/2\" />\n }\n />\n )}\n </div>\n )}\n {description && <Description>{description}</Description>}\n </div>\n )}\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content className=\"px-6 py-5\">\n {children}\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n {actions != null && (\n <div className=\"flex shrink-0 items-center justify-end gap-2 px-6 pb-5\">\n {actions}\n </div>\n )}\n </InsetPopup>\n </Portal>\n );\n}\n\n/* ========================= Dialog.FullscreenContent ======================== */\n\n/**\n * Full-viewport popup — a page-surface takeover instead of a floating card.\n * No backdrop: the popup itself is the layer. Fade only; scale is a floating-\n * card gesture and reads wrong at viewport size.\n */\nfunction FullscreenPopup({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n \"bg-pho-page fixed inset-0 z-50 overflow-y-auto outline-none\",\n \"transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface FullscreenContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /**\n * Decorative icon above the title — same chrome as `Content`, but 48px\n * (`size-12`) for viewport-scale gates.\n */\n icon?: ReactNode;\n /** Heading of the takeover. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Action row under the body. Iron rule as `Content`: when `actions` is set,\n * the corner close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Corner close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n /**\n * Classes on the viewport-centering frame (the `min-h-full` flex host).\n * Use to drop the default optical `pb-20` when the stack grows (e.g. a\n * captcha) so true center stays put: `frameClassName=\"pb-6\"`.\n */\n frameClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * Convenience full-screen panel: the whole viewport becomes the dialog, with\n * a centered column at the standalone-flow width (28rem — the same measure as\n * onboarding / invite cards). Column height follows its content through\n * `AnimatedHeight` (same spring as the Card shell), so step swaps — phone →\n * OTP → captcha — glide instead of jumping. Use for gates the visitor must\n * deal with before the page behind makes sense (e.g. binding a phone number);\n * prefer `Content` for everything conversational.\n */\nfunction FullscreenContent({\n icon,\n title,\n description,\n actions,\n showClose = true,\n frameClassName,\n children,\n className,\n ...props\n}: FullscreenContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a corner X.\n const showCloseButton = actions == null && showClose;\n\n return (\n <Portal>\n <FullscreenPopup className={className} {...props}>\n {showCloseButton && (\n <Close\n render={<CloseButton className=\"fixed top-4 right-4 z-10\" />}\n />\n )}\n {/* Default `pb-20` optically lifts the column above true center. */}\n <div\n className={cn(\n \"flex min-h-full items-center justify-center px-6 pt-6 pb-20\",\n frameClassName,\n )}\n >\n {/* Width stays on the outer host; AnimatedHeight measures the\n section stack and springs its own height on content changes. */}\n <div className=\"w-full max-w-md\">\n <AnimatedHeight className=\"flex flex-col gap-5\">\n {(icon || title || description) && (\n <div className=\"flex flex-col gap-1\">\n {icon && (\n <DialogHeaderIcon size=\"lg\">{icon}</DialogHeaderIcon>\n )}\n {title && <Title>{title}</Title>}\n {description && <Description>{description}</Description>}\n </div>\n )}\n {children}\n {actions != null && (\n <div className=\"flex items-center justify-end gap-2\">\n {actions}\n </div>\n )}\n </AnimatedHeight>\n </div>\n </div>\n </FullscreenPopup>\n </Portal>\n );\n}\n\n/**\n * Modal dialog built on Base UI `Dialog`. Controlled or uncontrolled via\n * `Dialog.Root` (`open` / `onOpenChange` / `defaultOpen`). `Dialog.Action`\n * adds native promise support: an async confirm keeps the dialog open and\n * undismissable until the work resolves.\n */\nexport const Dialog = {\n Root,\n Trigger,\n Portal,\n Backdrop,\n Popup,\n InsetPopup,\n FullscreenPopup,\n Title,\n Description,\n Close,\n Action,\n Content,\n InsetContent,\n FullscreenContent,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dialog-8SRNi_rv.js","names":[],"sources":["../../src/components/dialog/dialog.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n type ElementType,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from \"react\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { AnimatedHeight } from \"../../utils/animated-height\";\nimport { cn } from \"../../utils/cn\";\nimport { ScrollArea } from \"../scroll-area\";\nimport { Button, type ButtonProps } from \"../button/button\";\nimport { CloseButton } from \"../button/close-button\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\n/* ========================== Async pending context ========================== */\n\n/** Imperative actions exposed by Base UI's `actionsRef`. */\ninterface RootActions {\n unmount: () => void;\n close: () => void;\n}\n\ninterface DialogPendingContextValue {\n /** True while a `Dialog.Action` promise is in flight. */\n pending: boolean;\n beginPending: () => void;\n endPending: () => void;\n /** Close the dialog imperatively (used by `Dialog.Action` on resolve). */\n close: () => void;\n}\n\nconst DialogPendingContext = createContext<DialogPendingContextValue | null>(\n null,\n);\n\n/**\n * Whether the surrounding dialog has a `Dialog.Action` promise in flight —\n * use it to disable secondary actions (Cancel etc.) while work is pending.\n * Returns `false` outside a `Dialog.Root`.\n */\nexport function useDialogPending(): boolean {\n return useContext(DialogPendingContext)?.pending ?? false;\n}\n\ntype RootProps = ComponentProps<typeof BaseDialog.Root>;\n\n/**\n * `Dialog.Root` with async awareness: while a `Dialog.Action` promise is\n * pending, every close request (outside press, Escape, `Dialog.Close`) is\n * cancelled so the dialog stays open until the work settles. Props pass\n * through to Base UI's `Dialog.Root` unchanged.\n */\nfunction Root({ onOpenChange, actionsRef, ...props }: RootProps) {\n const [pending, setPending] = useState(false);\n // Guard reads a ref so a resolve → close() in the same tick isn't blocked\n // by a not-yet-flushed state update.\n const pendingRef = useRef(false);\n const internalActionsRef = useRef<RootActions | null>(null);\n\n // Keep a caller-supplied actionsRef working alongside our internal one.\n useEffect(() => {\n if (!actionsRef) return;\n actionsRef.current = internalActionsRef.current;\n return () => {\n actionsRef.current = null;\n };\n }, [actionsRef]);\n\n const handleOpenChange = useCallback<NonNullable<RootProps[\"onOpenChange\"]>>(\n (open, eventDetails) => {\n if (!open && pendingRef.current) {\n eventDetails.cancel();\n return;\n }\n onOpenChange?.(open, eventDetails);\n },\n [onOpenChange],\n );\n\n const contextValue = useMemo<DialogPendingContextValue>(\n () => ({\n pending,\n beginPending: () => {\n pendingRef.current = true;\n setPending(true);\n },\n endPending: () => {\n pendingRef.current = false;\n setPending(false);\n },\n close: () => internalActionsRef.current?.close(),\n }),\n [pending],\n );\n\n return (\n <DialogPendingContext.Provider value={contextValue}>\n <BaseDialog.Root\n actionsRef={internalActionsRef}\n onOpenChange={handleOpenChange}\n {...props}\n />\n </DialogPendingContext.Provider>\n );\n}\n\nconst Trigger = BaseDialog.Trigger;\nconst Portal = BaseDialog.Portal;\nconst Close = BaseDialog.Close;\n\n/* ============================== Dialog.Action ============================== */\n\nexport interface DialogActionProps extends Omit<ButtonProps, \"onClick\"> {\n /**\n * Confirm handler. Return a promise to keep the dialog open while it runs:\n * the button shows `loading`, all dismissal is locked, and the dialog\n * closes when the promise resolves. On rejection the dialog stays open —\n * surface errors inside the handler. A non-promise return closes\n * immediately, like `Dialog.Close`.\n */\n onClick?: (\n event: ReactMouseEvent<HTMLButtonElement>,\n ) => void | Promise<unknown>;\n}\n\nfunction isThenable(value: unknown): value is Promise<unknown> {\n return (\n value != null && typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\n/**\n * The confirm button of an action row, with native promise support — a\n * `Button` that runs its (possibly async) `onClick` and closes the dialog\n * when the work settles successfully.\n */\nfunction Action({ onClick, loading, disabled, ...props }: DialogActionProps) {\n const pendingContext = useContext(DialogPendingContext);\n const [busy, setBusy] = useState(false);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n function handleClick(event: ReactMouseEvent<HTMLButtonElement>) {\n if (busy || loading) return;\n const result = onClick?.(event);\n if (!isThenable(result)) {\n pendingContext?.close();\n return;\n }\n setBusy(true);\n pendingContext?.beginPending();\n result.then(\n () => {\n pendingContext?.endPending();\n if (mountedRef.current) setBusy(false);\n pendingContext?.close();\n },\n (error: unknown) => {\n pendingContext?.endPending();\n if (mountedRef.current) setBusy(false);\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.error(\"Dialog.Action: async onClick rejected —\", error);\n }\n },\n );\n }\n\n const inert = busy || loading;\n return (\n <Button\n {...props}\n disabled={disabled || inert}\n loading={inert}\n onClick={handleClick}\n />\n );\n}\n\nfunction Backdrop({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Backdrop>) {\n return (\n <BaseDialog.Backdrop\n className={cn(\n \"bg-pho-overlay fixed inset-0 z-50 transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Popup({ className, ...props }: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n // Floating layer: bg-pho-primary + border-pho-secondary hairline, elevation kept.\n \"border-pho-secondary bg-pho-primary shadow-pho-xl fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border outline-none\",\n // Global height cap — the inset panel's footprint (inset-4 /\n // sm:inset-8). A flex column so the body region can shrink and\n // scroll while header and actions stay pinned.\n \"flex max-h-[calc(100dvh-2rem)] flex-col sm:max-h-[calc(100dvh-4rem)]\",\n // Opacity + scale only — leave translate centering alone (no transition-all).\n \"transition-[opacity,scale] duration-200 ease-out\",\n \"data-[starting-style]:scale-95 data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:duration-150\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Title({ className, ...props }: StyledProps<typeof BaseDialog.Title>) {\n return (\n <BaseDialog.Title\n className={cn(\n \"text-pho-primary font-display text-display-sm font-medium text-balance\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Description({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Description>) {\n return (\n <BaseDialog.Description\n className={cn(\n \"text-pho-description text-display-xs font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Header icon chrome: secondary ink, a hair of extra air before the title.\n * Panel dialogs use 32px (`size-8`); fullscreen gates step up to 48px\n * (`size-12`) so the glyph reads at viewport scale.\n */\nfunction DialogHeaderIcon({\n children,\n size = \"md\",\n}: {\n children: ReactNode;\n size?: \"md\" | \"lg\";\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"text-pho-secondary mb-1\",\n size === \"lg\" ? \"[&>svg]:size-12\" : \"[&>svg]:size-8\",\n )}\n >\n {children}\n </span>\n );\n}\n\ninterface ContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /**\n * Decorative icon above the title — typically a Tabler outline icon.\n * Rendered aria-hidden in secondary ink at 32px (`[&>svg]:size-8`). Pass a\n * styled node to override the ink; the size is owned by the slot.\n */\n icon?: ReactNode;\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Consequence bullets rendered as a list at the top of the body — one item\n * per outcome (what is deleted, what survives, who loses access). Use on\n * destructive confirms where the one-line description undersells the\n * impact; keep each item to a single short sentence. Wrap the load-bearing\n * figure in `<strong>` (\"<strong>3 members</strong> lose access\") — the\n * list styles it (secondary ink, medium weight); no classes needed.\n */\n consequences?: ReactNode[];\n /**\n * Final cautionary line under `consequences` — error ink at description\n * size (e.g. \"It cannot be undone. Please be certain.\"). Reserve it for\n * irreversible actions; keep it short.\n */\n warning?: ReactNode;\n /**\n * Footer action row (Cancel / Confirm, etc.). Iron rule: when `actions` is\n * set, the header close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Header close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience panel: renders the portal, backdrop, and styled popup with an\n * optional header (title/description + close) and optional action row. For\n * full control, compose the parts directly (`Dialog.Portal` /\n * `Dialog.Backdrop` / `Dialog.Popup`).\n */\nfunction Content({\n icon,\n title,\n description,\n consequences,\n warning,\n actions,\n showClose = true,\n children,\n className,\n ...props\n}: ContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a header X.\n const showCloseButton = actions == null && showClose;\n const hasHeader = Boolean(icon || title || description || showCloseButton);\n const hasConsequences = consequences != null && consequences.length > 0;\n // The scrollable middle region — actions stay pinned below it.\n const hasScrollBody = hasConsequences || warning != null || children != null;\n\n return (\n <Portal>\n <Backdrop />\n <Popup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex shrink-0 flex-col gap-1 px-6 pt-6\">\n {icon && <DialogHeaderIcon>{icon}</DialogHeaderIcon>}\n {(title || showCloseButton) && (\n // Title row height = title only. Close is out of flow, centered\n // on the row, and may paint outside it (no clip, no stretch).\n <div className=\"relative\">\n {title ? (\n <Title className={showCloseButton ? \"pr-12\" : undefined}>\n {title}\n </Title>\n ) : (\n <div className=\"h-[1.625rem]\" aria-hidden=\"true\" />\n )}\n {showCloseButton && (\n <Close\n render={\n <CloseButton className=\"absolute top-1/2 right-0 z-10 -translate-y-1/2\" />\n }\n />\n )}\n </div>\n )}\n {description && <Description>{description}</Description>}\n </div>\n )}\n {hasScrollBody && (\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content className=\"flex flex-col gap-5 px-6 py-5\">\n {hasConsequences && (\n <ul className=\"text-pho-description text-md [&_strong]:text-pho-secondary space-y-1 [&_strong]:font-medium\">\n {consequences.map((item, index) => (\n // Hand-drawn marker: a 4px dot on the first line's center —\n // quieter than the browser disc, ink tracks the text.\n <li\n key={index}\n className=\"relative pl-4 before:absolute before:top-2.5 before:left-1 before:size-1 before:rounded-full before:bg-current\"\n >\n {item}\n </li>\n ))}\n </ul>\n )}\n {warning != null && (\n <p className=\"text-pho-error text-display-xs font-medium\">\n {warning}\n </p>\n )}\n {children}\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n )}\n {actions != null && (\n <div\n className={cn(\n \"flex shrink-0 items-center justify-end gap-2 px-6 pb-5\",\n !hasScrollBody && \"pt-5\",\n )}\n >\n {actions}\n </div>\n )}\n </Popup>\n </Portal>\n );\n}\n\n/* =========================== Dialog.InsetContent =========================== */\n\n/**\n * Near-fullscreen popup — the viewport minus a breathing ring, floating over\n * a backdrop (the page stays visible through the gap, so it dims like any\n * floating layer). Still a card: border, elevation, rounded corners; the\n * scale gesture stays but shrinks to 98% — at this size 95% reads like a jump.\n */\nfunction InsetPopup({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n \"border-pho-secondary bg-pho-primary shadow-pho-xl fixed inset-4 z-50 flex flex-col overflow-hidden rounded-lg border outline-none sm:inset-8\",\n \"transition-[opacity,scale] duration-200 ease-out\",\n \"data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0 data-[ending-style]:duration-150\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface InsetContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /** Decorative icon above the title — same treatment as `Content`. */\n icon?: ReactNode;\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Bottom action row. Iron rule as `Content`: when `actions` is set, the\n * header close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Header close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience near-fullscreen panel: header pinned on top, body filling and\n * scrolling on its own, optional action row pinned at the bottom. Sits\n * between `Content` (a focused card) and `FullscreenContent` (a page\n * takeover): use it for roomy working surfaces — pickers, editors, previews —\n * that still want the page peeking through an edge.\n */\nfunction InsetContent({\n icon,\n title,\n description,\n actions,\n showClose = true,\n children,\n className,\n ...props\n}: InsetContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a header X.\n const showCloseButton = actions == null && showClose;\n const hasHeader = Boolean(icon || title || description || showCloseButton);\n\n return (\n <Portal>\n <Backdrop />\n <InsetPopup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex shrink-0 flex-col gap-1 px-6 pt-6\">\n {icon && <DialogHeaderIcon>{icon}</DialogHeaderIcon>}\n {(title || showCloseButton) && (\n <div className=\"relative\">\n {title ? (\n <Title className={showCloseButton ? \"pr-12\" : undefined}>\n {title}\n </Title>\n ) : (\n <div className=\"h-[1.625rem]\" aria-hidden=\"true\" />\n )}\n {showCloseButton && (\n <Close\n render={\n <CloseButton className=\"absolute top-1/2 right-0 z-10 -translate-y-1/2\" />\n }\n />\n )}\n </div>\n )}\n {description && <Description>{description}</Description>}\n </div>\n )}\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content className=\"px-6 py-5\">\n {children}\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n {actions != null && (\n <div className=\"flex shrink-0 items-center justify-end gap-2 px-6 pb-5\">\n {actions}\n </div>\n )}\n </InsetPopup>\n </Portal>\n );\n}\n\n/* ========================= Dialog.FullscreenContent ======================== */\n\n/**\n * Full-viewport popup — a page-surface takeover instead of a floating card.\n * No backdrop: the popup itself is the layer. Fade only; scale is a floating-\n * card gesture and reads wrong at viewport size.\n */\nfunction FullscreenPopup({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n \"bg-pho-page fixed inset-0 z-50 overflow-y-auto outline-none\",\n \"transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\ninterface FullscreenContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /**\n * Decorative icon above the title — same chrome as `Content`, but 48px\n * (`size-12`) for viewport-scale gates.\n */\n icon?: ReactNode;\n /** Heading of the takeover. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /**\n * Action row under the body. Iron rule as `Content`: when `actions` is set,\n * the corner close button is never shown — dismiss via the actions.\n */\n actions?: ReactNode;\n /**\n * Corner close control. Only applies when `actions` is absent.\n * @default true\n */\n showClose?: boolean;\n /**\n * Classes on the viewport-centering frame (the `min-h-full` flex host).\n * Use to drop the default optical `pb-20` when the stack grows (e.g. a\n * captcha) so true center stays put: `frameClassName=\"pb-6\"`.\n */\n frameClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * Convenience full-screen panel: the whole viewport becomes the dialog, with\n * a centered column at the standalone-flow width (28rem — the same measure as\n * onboarding / invite cards). Column height follows its content through\n * `AnimatedHeight` (same spring as the Card shell), so step swaps — phone →\n * OTP → captcha — glide instead of jumping. Use for gates the visitor must\n * deal with before the page behind makes sense (e.g. binding a phone number);\n * prefer `Content` for everything conversational.\n */\nfunction FullscreenContent({\n icon,\n title,\n description,\n actions,\n showClose = true,\n frameClassName,\n children,\n className,\n ...props\n}: FullscreenContentProps) {\n // Iron rule: action dialogs dismiss via their actions — never a corner X.\n const showCloseButton = actions == null && showClose;\n\n return (\n <Portal>\n <FullscreenPopup className={className} {...props}>\n {showCloseButton && (\n <Close\n render={<CloseButton className=\"fixed top-4 right-4 z-10\" />}\n />\n )}\n {/* Default `pb-20` optically lifts the column above true center. */}\n <div\n className={cn(\n \"flex min-h-full items-center justify-center px-6 pt-6 pb-20\",\n frameClassName,\n )}\n >\n {/* Width stays on the outer host; AnimatedHeight measures the\n section stack and springs its own height on content changes. */}\n <div className=\"w-full max-w-md\">\n <AnimatedHeight className=\"flex flex-col gap-5\">\n {(icon || title || description) && (\n <div className=\"flex flex-col gap-1\">\n {icon && (\n <DialogHeaderIcon size=\"lg\">{icon}</DialogHeaderIcon>\n )}\n {title && <Title>{title}</Title>}\n {description && <Description>{description}</Description>}\n </div>\n )}\n {children}\n {actions != null && (\n <div className=\"flex items-center justify-end gap-2\">\n {actions}\n </div>\n )}\n </AnimatedHeight>\n </div>\n </div>\n </FullscreenPopup>\n </Portal>\n );\n}\n\n/**\n * Modal dialog built on Base UI `Dialog`. Controlled or uncontrolled via\n * `Dialog.Root` (`open` / `onOpenChange` / `defaultOpen`). `Dialog.Action`\n * adds native promise support: an async confirm keeps the dialog open and\n * undismissable until the work resolves.\n */\nexport const Dialog = {\n Root,\n Trigger,\n Portal,\n Backdrop,\n Popup,\n InsetPopup,\n FullscreenPopup,\n Title,\n Description,\n Close,\n Action,\n Content,\n InsetContent,\n FullscreenContent,\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { t as h } from "./cn-ChIgwEl3.js";
|
|
3
|
-
import { a as x, o as R } from "./button-
|
|
3
|
+
import { a as x, o as R } from "./button-DNNs5RsT.js";
|
|
4
4
|
import { t as C } from "./createReactComponent-BqCmnqfX.js";
|
|
5
5
|
import { t as S } from "./IconInfoCircle-L0zfhg7R.js";
|
|
6
6
|
import { t as g } from "./IconAlertCircleFilled-VxKU8TYK.js";
|
|
@@ -37,7 +37,7 @@ function D(t) {
|
|
|
37
37
|
}
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
|
-
var
|
|
40
|
+
var $ = "Something went wrong.", f = {
|
|
41
41
|
400: "Bad Request",
|
|
42
42
|
401: "Unauthorized",
|
|
43
43
|
403: "Forbidden",
|
|
@@ -54,7 +54,7 @@ var j = "Something went wrong.", f = {
|
|
|
54
54
|
502: "Bad Gateway",
|
|
55
55
|
503: "Service Unavailable",
|
|
56
56
|
504: "Gateway Timeout"
|
|
57
|
-
},
|
|
57
|
+
}, j = {
|
|
58
58
|
400: "The request was malformed or incomplete.",
|
|
59
59
|
401: "You need to sign in again.",
|
|
60
60
|
403: "You don't have permission to do that.",
|
|
@@ -72,15 +72,15 @@ var j = "Something went wrong.", f = {
|
|
|
72
72
|
503: "The service is temporarily unavailable.",
|
|
73
73
|
504: "The gateway timed out waiting for an upstream server."
|
|
74
74
|
};
|
|
75
|
-
function
|
|
75
|
+
function B(t) {
|
|
76
76
|
return typeof t.code == "string" && t.code !== "";
|
|
77
77
|
}
|
|
78
78
|
function v(t, e) {
|
|
79
79
|
if (f[t]) return f[t];
|
|
80
80
|
if (e && e !== "Something went wrong." && e !== "Request failed") return e.replace(/\.$/, "");
|
|
81
81
|
}
|
|
82
|
-
function
|
|
83
|
-
return
|
|
82
|
+
function F(t) {
|
|
83
|
+
return j[t] ?? "The request failed with an unexpected status.";
|
|
84
84
|
}
|
|
85
85
|
function u(t) {
|
|
86
86
|
if (!t) return t;
|
|
@@ -89,11 +89,11 @@ function u(t) {
|
|
|
89
89
|
}
|
|
90
90
|
function y(t) {
|
|
91
91
|
const e = D(t);
|
|
92
|
-
if (e == null) return { title:
|
|
92
|
+
if (e == null) return { title: $ };
|
|
93
93
|
const r = [e.detail, e.remediation?.action].filter((i) => typeof i == "string" && i !== "").join(" ") || void 0;
|
|
94
|
-
return !
|
|
94
|
+
return !B(e) && r == null ? {
|
|
95
95
|
title: u(v(e.status, e.title) ?? "Something went wrong."),
|
|
96
|
-
description:
|
|
96
|
+
description: F(e.status),
|
|
97
97
|
requestId: e.requestId,
|
|
98
98
|
problem: e
|
|
99
99
|
} : {
|
|
@@ -107,7 +107,7 @@ function y(t) {
|
|
|
107
107
|
function d(t) {
|
|
108
108
|
return typeof t == "string" ? u(t) : t;
|
|
109
109
|
}
|
|
110
|
-
var T = "Learn more",
|
|
110
|
+
var T = "Learn more", U = "Try again";
|
|
111
111
|
function M({ error: t, children: e, className: r, ...i }) {
|
|
112
112
|
if (t == null && e == null) return null;
|
|
113
113
|
const o = y(t);
|
|
@@ -217,7 +217,7 @@ function G({ error: t, title: e, description: r, icon: i, onRetry: o, actions: N
|
|
|
217
217
|
const a = y(t), p = d(r ?? a.description), I = p != null || a.requestId != null ? /* @__PURE__ */ s(c, { children: [p, a.requestId != null && /* @__PURE__ */ n(b, { requestId: a.requestId })] }) : void 0, E = N ?? (o != null || a.documentation != null ? /* @__PURE__ */ s(c, { children: [o != null && /* @__PURE__ */ n(x, {
|
|
218
218
|
size: "sm",
|
|
219
219
|
onClick: o,
|
|
220
|
-
children:
|
|
220
|
+
children: U
|
|
221
221
|
}), a.documentation != null && /* @__PURE__ */ n(R, {
|
|
222
222
|
size: "sm",
|
|
223
223
|
variant: "outlined",
|
|
@@ -244,9 +244,9 @@ export {
|
|
|
244
244
|
D as l,
|
|
245
245
|
G as n,
|
|
246
246
|
v as o,
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
$ as r,
|
|
248
|
+
F as s,
|
|
249
249
|
M as t
|
|
250
250
|
};
|
|
251
251
|
|
|
252
|
-
//# sourceMappingURL=error-
|
|
252
|
+
//# sourceMappingURL=error-Crs-wqMC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-Da7P5WZi.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertTriangle.mjs","../../src/components/error/problem.ts","../../src/components/error/error.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M12 9v4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M12 16h.01\", \"key\": \"svg-2\" }]];\nconst IconAlertTriangle = createReactComponent(\"outline\", \"alert-triangle\", \"AlertTriangle\", __iconNode);\n\nexport { __iconNode, IconAlertTriangle as default };\n//# sourceMappingURL=IconAlertTriangle.mjs.map\n","/**\n * Client-side mirror of the `@photon-hq/error` response contract — the\n * RFC 9457 Problem Details body every Photon service returns for 4xx / 5xx.\n * Machine logic branches on `code`; `title` / `detail` / `remediation` are\n * the human-readable copy, already reviewed server-side and safe to render.\n *\n * Fetch layers may also hand us a thin status-only shape when the response\n * was not ok but the body was not a contract problem — `title` + `status`\n * still count so the UI can disclose the HTTP code we already know.\n */\n\nexport interface ProblemRemediation {\n /** What the user can do about it — a full sentence. */\n action?: string;\n /** Docs URL explaining the problem. */\n documentation?: string;\n}\n\nexport interface Problem {\n /** Stable machine-readable key (`RATE_LIMITED`) — the field to branch on. */\n code?: string;\n /** Human summary of the problem type (\"Too Many Requests\"). */\n title: string;\n /** HTTP status (4xx / 5xx). */\n status: number;\n /** Problem type URI (`urn:photon:problem:*` or an https URL). */\n type?: string;\n /** Occurrence-specific human sentence. */\n detail?: string;\n /** Correlation id for support (`X-Request-ID`). */\n requestId?: string;\n remediation?: ProblemRemediation;\n /** RFC 9457 extensions (`issues`, `actualVersion`, …). */\n [extension: string]: unknown;\n}\n\n/** Structural check: `title` + a 4xx/5xx status. `code` is optional. */\nexport function isProblem(value: unknown): value is Problem {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.title === \"string\" &&\n typeof v.status === \"number\" &&\n Number.isInteger(v.status) &&\n v.status >= 400 &&\n v.status <= 599 &&\n (v.code === undefined || typeof v.code === \"string\")\n );\n}\n\n/**\n * Find the problem a caught value carries — the value itself, one typed onto\n * an error as a `problem` field (dashboard's `ApiError` shape), or one\n * wrapped in an `Error` `cause` chain (how fetch layers usually rethrow a\n * parsed body). Returns `null` when there is none.\n */\nexport function parseProblem(error: unknown): Problem | null {\n let current: unknown = error;\n for (\n let depth = 0;\n depth < 8 && typeof current === \"object\" && current !== null;\n depth++\n ) {\n if (isProblem(current)) return current;\n const carried = (current as { problem?: unknown }).problem;\n if (isProblem(carried)) return carried;\n current = (current as { cause?: unknown }).cause;\n }\n return null;\n}\n\nexport interface ErrorDescription {\n /** Short name of the failure — the problem `title`, or a generic fallback. */\n title: string;\n /** Sentence(s) to show — `detail` and `remediation.action` joined. */\n description?: string;\n /** Docs URL from `remediation.documentation`. */\n documentation?: string;\n /** Correlation id for support. */\n requestId?: string;\n /** The matched problem, when the input carried one. */\n problem?: Problem;\n}\n\n/**\n * Anything that isn't a problem renders as this — raw `Error` messages are\n * not contract copy and never shown (the same fail-closed stance as the\n * server side of `@photon-hq/error`). Always a full sentence (trailing\n * period).\n */\nexport const GENERIC_ERROR_TITLE = \"Something went wrong.\";\n\n/** Standard reason phrases for statuses we commonly surface without a body. */\nconst HTTP_REASON_PHRASE: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 408: \"Request Timeout\",\n 409: \"Conflict\",\n 410: \"Gone\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 422: \"Unprocessable Entity\",\n 429: \"Too Many Requests\",\n 500: \"Internal Server Error\",\n 501: \"Not Implemented\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Gateway Timeout\",\n};\n\n/** One-sentence explanations when the server gave a status but no problem copy. */\nconst HTTP_STATUS_EXPLANATION: Record<number, string> = {\n 400: \"The request was malformed or incomplete.\",\n 401: \"You need to sign in again.\",\n 403: \"You don't have permission to do that.\",\n 404: \"The requested resource was not found.\",\n 408: \"The request timed out before the server responded.\",\n 409: \"The request conflicts with the current state.\",\n 410: \"That resource is no longer available.\",\n 413: \"The request was too large for the server to accept.\",\n 415: \"That media type isn't supported.\",\n 422: \"The request was understood but couldn't be processed.\",\n 429: \"Request rate limit exceeded. Wait a moment before trying again.\",\n 500: \"The server hit an unexpected error.\",\n 501: \"The server doesn't support that operation.\",\n 502: \"The gateway got an invalid response from an upstream server.\",\n 503: \"The service is temporarily unavailable.\",\n 504: \"The gateway timed out waiting for an upstream server.\",\n};\n\nfunction hasContractCode(problem: Problem): boolean {\n return typeof problem.code === \"string\" && problem.code !== \"\";\n}\n\n/** Reason phrase for a status — map first, then a non-generic `problem.title`. */\nexport function httpReasonPhrase(\n status: number,\n fallbackTitle?: string,\n): string | undefined {\n if (HTTP_REASON_PHRASE[status]) return HTTP_REASON_PHRASE[status];\n if (\n fallbackTitle &&\n fallbackTitle !== GENERIC_ERROR_TITLE &&\n fallbackTitle !== \"Request failed\"\n ) {\n return fallbackTitle.replace(/\\.$/, \"\");\n }\n return undefined;\n}\n\n/** Human sentence for a bare HTTP status, when the body carried no copy. */\nexport function httpStatusExplanation(status: number): string {\n return (\n HTTP_STATUS_EXPLANATION[status] ??\n \"The request failed with an unexpected status.\"\n );\n}\n\n/**\n * Sentence-case the first character. Server `detail` / `title` sometimes\n * arrive fully lowercase; UI copy always leads with a capital.\n */\nexport function capitalizeFirst(text: string): string {\n if (!text) return text;\n const first = text.charAt(0);\n const upper = first.toUpperCase();\n if (first === upper) return text;\n return upper + text.slice(1);\n}\n\n/**\n * Turn a caught value into displayable copy. `Error` and `ErrorView` call\n * this internally; use it directly for other surfaces (a toast, a banner).\n *\n * Status-only failures (HTTP not ok, but no contract `code` / copy) still\n * keep `problem` so the details panel can show the status. Title becomes the\n * reason phrase (e.g. \"Bad Gateway\"); description is a short explanation —\n * not just the numeric code.\n */\nexport function describeError(error: unknown): ErrorDescription {\n const problem = parseProblem(error);\n if (problem == null) return { title: GENERIC_ERROR_TITLE };\n\n const description =\n [problem.detail, problem.remediation?.action]\n .filter((part): part is string => typeof part === \"string\" && part !== \"\")\n .join(\" \") || undefined;\n\n if (!hasContractCode(problem) && description == null) {\n const reason = httpReasonPhrase(problem.status, problem.title);\n return {\n title: capitalizeFirst(reason ?? GENERIC_ERROR_TITLE),\n description: httpStatusExplanation(problem.status),\n requestId: problem.requestId,\n problem,\n };\n }\n\n return {\n title: capitalizeFirst(problem.title || GENERIC_ERROR_TITLE),\n description: description ? capitalizeFirst(description) : undefined,\n documentation: problem.remediation?.documentation,\n requestId: problem.requestId,\n problem,\n };\n}\n","import {\n useEffect,\n useRef,\n useState,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconAlertTriangle,\n IconInfoCircle,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button, ButtonLink } from \"../button/button\";\nimport { Popover } from \"../popover/popover\";\nimport { StatusView, type StatusViewSize } from \"../status-view/status-view\";\nimport {\n capitalizeFirst,\n describeError,\n httpReasonPhrase,\n type ErrorDescription,\n} from \"./problem\";\n\n/** Capitalize string copy; leave custom ReactNode overrides alone. */\nfunction displayCopy(value: ReactNode): ReactNode {\n return typeof value === \"string\" ? capitalizeFirst(value) : value;\n}\n\n/**\n * Design notes:\n * - ONE input: `error` is the caught value, whatever it is — a Problem\n * Details body from `@photon-hq/error`, an `Error` carrying one as\n * `cause`, or something unknown. The component derives its copy via\n * `describeError`; unknowns fall back to a generic line rather than\n * leaking raw messages.\n * - TWO presentations, one per altitude: `Error` is the inline line for\n * forms, cards, and action rows; `ErrorView` is the region / page state\n * built on StatusView (same `sm` / `base` size axis).\n * - Both render `null` when there is nothing to show, so they can sit\n * unconditionally in JSX (`<Error error={mutation.error} />`).\n * - Client-side *validation* is not this component's job — that belongs to\n * `Field.Error`, wired to control validity.\n */\n\nconst DOC_LINK_LABEL = \"Learn more\";\nconst RETRY_LABEL = \"Try again\";\n\ninterface ErrorBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n}\n\nexport interface ErrorProps\n extends ErrorBaseProps, Omit<ComponentProps<\"div\">, keyof ErrorBaseProps> {}\n\n/**\n * Error — one line of red ink naming a server failure (4xx / 5xx), placed\n * inline: under a form, in a card, beside the action that failed. Give it the\n * caught value and it renders the problem's `detail` (falling back to `title`,\n * then to a generic line), plus a docs link when the problem carries one.\n * Renders nothing while `error` is nullish, so it can stay mounted.\n *\n * For a region or page that failed to load, use {@link ErrorView}.\n */\nexport function Error({ error, children, className, ...props }: ErrorProps) {\n if (error == null && children == null) return null;\n const copy = describeError(error);\n return (\n <div\n role=\"alert\"\n className={cn(\n \"text-pho-error flex min-w-0 items-start gap-1.5 text-base\",\n className,\n )}\n {...props}\n >\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n <span className=\"min-w-0\">\n {displayCopy(children ?? copy.description ?? copy.title)}\n {copy.documentation != null && (\n <>\n {\" \"}\n <a\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline underline-offset-2\"\n >\n {DOC_LINK_LABEL}\n </a>\n </>\n )}\n {/* Trailing whitespace collapses when the details render null. */}{\" \"}\n <ErrorDetails copy={copy} />\n </span>\n </div>\n );\n}\n\nError.displayName = \"Error\";\n\n/**\n * Hover / focus disclosure on the inline line — the request's fuller story:\n * problem `title` (with the same alert icon + `text-pho-error` ink as the\n * inline line), the untruncated description, then `status` + `code` and the\n * copyable request id grouped below for a support handoff. Renders only when\n * the caught value actually carried a problem (or at least a request id); a\n * generic unknown has nothing more to disclose. Built on `Popover` rather\n * than `Tooltip` because the panel holds an interactive control (the copy\n * button) — `openOnHover` keeps the tooltip-like gesture.\n */\nfunction ErrorDetails({ copy }: { copy: ErrorDescription }) {\n const { problem, requestId } = copy;\n if (problem == null && requestId == null) return null;\n\n // Contract problems show `status CODE`; status-only shows `status Reason`.\n let statusLine: string | null = null;\n if (problem != null) {\n if (problem.code) {\n statusLine = `${problem.status} ${problem.code}`;\n } else {\n const reason = httpReasonPhrase(problem.status, problem.title);\n statusLine = reason\n ? `${problem.status} ${reason}`\n : String(problem.status);\n }\n }\n\n return (\n <Popover.Root>\n <Popover.Trigger\n openOnHover\n delay={150}\n aria-label=\"Error details\"\n className=\"outline-pho-brand inline-flex size-4 shrink-0 translate-y-[3px] cursor-help items-center justify-center rounded-full opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2\"\n >\n <IconInfoCircle className=\"size-3.5\" />\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Positioner align=\"start\">\n <Popover.Popup className=\"text-pho-error\">\n <Popover.Title className=\"text-pho-error flex items-start gap-1.5\">\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n {copy.title}\n </Popover.Title>\n {copy.description != null && (\n <Popover.Description className=\"text-pho-error\">\n {copy.description}\n </Popover.Description>\n )}\n {(statusLine != null || requestId != null) && (\n <div className=\"text-pho-error mt-1 font-mono text-[10px] leading-snug\">\n {statusLine != null && <p>{statusLine}</p>}\n {requestId != null && (\n <RequestId requestId={requestId} className=\"mt-0\" />\n )}\n </div>\n )}\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** Request-id footnote — click copies the id for support handoff. */\nfunction RequestId({\n requestId,\n className,\n}: {\n requestId: string;\n className?: string;\n}) {\n const [copied, setCopied] = useState(false);\n const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n },\n [],\n );\n\n return (\n <span\n className={cn(\"mt-1 block font-mono text-[10px] leading-snug\", className)}\n >\n Request ID:{\" \"}\n <button\n type=\"button\"\n className=\"outline-pho-brand inline cursor-pointer underline-offset-2 select-all hover:underline focus-visible:underline focus-visible:outline-2 focus-visible:-outline-offset-1\"\n aria-label={\n copied ? \"Request ID copied\" : `Copy request ID ${requestId}`\n }\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(requestId);\n } catch {\n return;\n }\n setCopied(true);\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n copiedTimer.current = setTimeout(() => setCopied(false), 1500);\n }}\n >\n {requestId}\n </button>\n <output className=\"sr-only\" aria-live=\"polite\">\n {copied ? \"Copied\" : \"\"}\n </output>\n </span>\n );\n}\n\ninterface ErrorViewBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived title. */\n title?: ReactNode;\n /** Override the derived description. */\n description?: ReactNode;\n /** Override the warning glyph. */\n icon?: ReactNode;\n /** Offer a retry — renders a \"Try again\" button. */\n onRetry?: () => void;\n /** Replace the built-in action row entirely. */\n actions?: ReactNode;\n /** Size variant — StatusView's axis. @default \"base\" */\n size?: StatusViewSize;\n}\n\nexport interface ErrorViewProps\n extends\n ErrorViewBaseProps,\n Omit<ComponentProps<\"div\">, keyof ErrorViewBaseProps | \"children\"> {}\n\n/**\n * ErrorView — the failed state of a region or page, built on StatusView:\n * warning icon over the problem's `title`, its `detail` and remediation\n * below, an action row (`onRetry` and the problem's docs link), and the\n * request id as a footnote for support. Announces via `role=\"alert\"`.\n *\n * Use `size=\"sm\"` inside cards and panels, the default `base` for whole\n * pages. Renders nothing while `error` is nullish (unless given a static\n * `title`), so it can sit unconditionally next to a query.\n */\nexport function ErrorView({\n error,\n title,\n description,\n icon,\n onRetry,\n actions,\n size = \"base\",\n ...props\n}: ErrorViewProps) {\n if (error == null && title == null) return null;\n const copy = describeError(error);\n\n const resolvedDescription = displayCopy(description ?? copy.description);\n const body =\n resolvedDescription != null || copy.requestId != null ? (\n <>\n {resolvedDescription}\n {copy.requestId != null && <RequestId requestId={copy.requestId} />}\n </>\n ) : undefined;\n\n const resolvedActions =\n actions ??\n (onRetry != null || copy.documentation != null ? (\n <>\n {onRetry != null && (\n <Button size=\"sm\" onClick={onRetry}>\n {RETRY_LABEL}\n </Button>\n )}\n {copy.documentation != null && (\n <ButtonLink\n size=\"sm\"\n variant=\"outlined\"\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {DOC_LINK_LABEL}\n </ButtonLink>\n )}\n </>\n ) : undefined);\n\n return (\n <StatusView\n role=\"alert\"\n size={size}\n icon={icon ?? <IconAlertTriangle />}\n title={displayCopy(title ?? copy.title)}\n description={body}\n actions={resolvedActions}\n {...props}\n />\n );\n}\n\nErrorView.displayName = \"ErrorView\";\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"error-Crs-wqMC.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertTriangle.mjs","../../src/components/error/problem.ts","../../src/components/error/error.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M12 9v4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M12 16h.01\", \"key\": \"svg-2\" }]];\nconst IconAlertTriangle = createReactComponent(\"outline\", \"alert-triangle\", \"AlertTriangle\", __iconNode);\n\nexport { __iconNode, IconAlertTriangle as default };\n//# sourceMappingURL=IconAlertTriangle.mjs.map\n","/**\n * Client-side mirror of the `@photon-hq/error` response contract — the\n * RFC 9457 Problem Details body every Photon service returns for 4xx / 5xx.\n * Machine logic branches on `code`; `title` / `detail` / `remediation` are\n * the human-readable copy, already reviewed server-side and safe to render.\n *\n * Fetch layers may also hand us a thin status-only shape when the response\n * was not ok but the body was not a contract problem — `title` + `status`\n * still count so the UI can disclose the HTTP code we already know.\n */\n\nexport interface ProblemRemediation {\n /** What the user can do about it — a full sentence. */\n action?: string;\n /** Docs URL explaining the problem. */\n documentation?: string;\n}\n\nexport interface Problem {\n /** Stable machine-readable key (`RATE_LIMITED`) — the field to branch on. */\n code?: string;\n /** Human summary of the problem type (\"Too Many Requests\"). */\n title: string;\n /** HTTP status (4xx / 5xx). */\n status: number;\n /** Problem type URI (`urn:photon:problem:*` or an https URL). */\n type?: string;\n /** Occurrence-specific human sentence. */\n detail?: string;\n /** Correlation id for support (`X-Request-ID`). */\n requestId?: string;\n remediation?: ProblemRemediation;\n /** RFC 9457 extensions (`issues`, `actualVersion`, …). */\n [extension: string]: unknown;\n}\n\n/** Structural check: `title` + a 4xx/5xx status. `code` is optional. */\nexport function isProblem(value: unknown): value is Problem {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.title === \"string\" &&\n typeof v.status === \"number\" &&\n Number.isInteger(v.status) &&\n v.status >= 400 &&\n v.status <= 599 &&\n (v.code === undefined || typeof v.code === \"string\")\n );\n}\n\n/**\n * Find the problem a caught value carries — the value itself, one typed onto\n * an error as a `problem` field (dashboard's `ApiError` shape), or one\n * wrapped in an `Error` `cause` chain (how fetch layers usually rethrow a\n * parsed body). Returns `null` when there is none.\n */\nexport function parseProblem(error: unknown): Problem | null {\n let current: unknown = error;\n for (\n let depth = 0;\n depth < 8 && typeof current === \"object\" && current !== null;\n depth++\n ) {\n if (isProblem(current)) return current;\n const carried = (current as { problem?: unknown }).problem;\n if (isProblem(carried)) return carried;\n current = (current as { cause?: unknown }).cause;\n }\n return null;\n}\n\nexport interface ErrorDescription {\n /** Short name of the failure — the problem `title`, or a generic fallback. */\n title: string;\n /** Sentence(s) to show — `detail` and `remediation.action` joined. */\n description?: string;\n /** Docs URL from `remediation.documentation`. */\n documentation?: string;\n /** Correlation id for support. */\n requestId?: string;\n /** The matched problem, when the input carried one. */\n problem?: Problem;\n}\n\n/**\n * Anything that isn't a problem renders as this — raw `Error` messages are\n * not contract copy and never shown (the same fail-closed stance as the\n * server side of `@photon-hq/error`). Always a full sentence (trailing\n * period).\n */\nexport const GENERIC_ERROR_TITLE = \"Something went wrong.\";\n\n/** Standard reason phrases for statuses we commonly surface without a body. */\nconst HTTP_REASON_PHRASE: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 408: \"Request Timeout\",\n 409: \"Conflict\",\n 410: \"Gone\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 422: \"Unprocessable Entity\",\n 429: \"Too Many Requests\",\n 500: \"Internal Server Error\",\n 501: \"Not Implemented\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Gateway Timeout\",\n};\n\n/** One-sentence explanations when the server gave a status but no problem copy. */\nconst HTTP_STATUS_EXPLANATION: Record<number, string> = {\n 400: \"The request was malformed or incomplete.\",\n 401: \"You need to sign in again.\",\n 403: \"You don't have permission to do that.\",\n 404: \"The requested resource was not found.\",\n 408: \"The request timed out before the server responded.\",\n 409: \"The request conflicts with the current state.\",\n 410: \"That resource is no longer available.\",\n 413: \"The request was too large for the server to accept.\",\n 415: \"That media type isn't supported.\",\n 422: \"The request was understood but couldn't be processed.\",\n 429: \"Request rate limit exceeded. Wait a moment before trying again.\",\n 500: \"The server hit an unexpected error.\",\n 501: \"The server doesn't support that operation.\",\n 502: \"The gateway got an invalid response from an upstream server.\",\n 503: \"The service is temporarily unavailable.\",\n 504: \"The gateway timed out waiting for an upstream server.\",\n};\n\nfunction hasContractCode(problem: Problem): boolean {\n return typeof problem.code === \"string\" && problem.code !== \"\";\n}\n\n/** Reason phrase for a status — map first, then a non-generic `problem.title`. */\nexport function httpReasonPhrase(\n status: number,\n fallbackTitle?: string,\n): string | undefined {\n if (HTTP_REASON_PHRASE[status]) return HTTP_REASON_PHRASE[status];\n if (\n fallbackTitle &&\n fallbackTitle !== GENERIC_ERROR_TITLE &&\n fallbackTitle !== \"Request failed\"\n ) {\n return fallbackTitle.replace(/\\.$/, \"\");\n }\n return undefined;\n}\n\n/** Human sentence for a bare HTTP status, when the body carried no copy. */\nexport function httpStatusExplanation(status: number): string {\n return (\n HTTP_STATUS_EXPLANATION[status] ??\n \"The request failed with an unexpected status.\"\n );\n}\n\n/**\n * Sentence-case the first character. Server `detail` / `title` sometimes\n * arrive fully lowercase; UI copy always leads with a capital.\n */\nexport function capitalizeFirst(text: string): string {\n if (!text) return text;\n const first = text.charAt(0);\n const upper = first.toUpperCase();\n if (first === upper) return text;\n return upper + text.slice(1);\n}\n\n/**\n * Turn a caught value into displayable copy. `Error` and `ErrorView` call\n * this internally; use it directly for other surfaces (a toast, a banner).\n *\n * Status-only failures (HTTP not ok, but no contract `code` / copy) still\n * keep `problem` so the details panel can show the status. Title becomes the\n * reason phrase (e.g. \"Bad Gateway\"); description is a short explanation —\n * not just the numeric code.\n */\nexport function describeError(error: unknown): ErrorDescription {\n const problem = parseProblem(error);\n if (problem == null) return { title: GENERIC_ERROR_TITLE };\n\n const description =\n [problem.detail, problem.remediation?.action]\n .filter((part): part is string => typeof part === \"string\" && part !== \"\")\n .join(\" \") || undefined;\n\n if (!hasContractCode(problem) && description == null) {\n const reason = httpReasonPhrase(problem.status, problem.title);\n return {\n title: capitalizeFirst(reason ?? GENERIC_ERROR_TITLE),\n description: httpStatusExplanation(problem.status),\n requestId: problem.requestId,\n problem,\n };\n }\n\n return {\n title: capitalizeFirst(problem.title || GENERIC_ERROR_TITLE),\n description: description ? capitalizeFirst(description) : undefined,\n documentation: problem.remediation?.documentation,\n requestId: problem.requestId,\n problem,\n };\n}\n","import {\n useEffect,\n useRef,\n useState,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconAlertTriangle,\n IconInfoCircle,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button, ButtonLink } from \"../button/button\";\nimport { Popover } from \"../popover/popover\";\nimport { StatusView, type StatusViewSize } from \"../status-view/status-view\";\nimport {\n capitalizeFirst,\n describeError,\n httpReasonPhrase,\n type ErrorDescription,\n} from \"./problem\";\n\n/** Capitalize string copy; leave custom ReactNode overrides alone. */\nfunction displayCopy(value: ReactNode): ReactNode {\n return typeof value === \"string\" ? capitalizeFirst(value) : value;\n}\n\n/**\n * Design notes:\n * - ONE input: `error` is the caught value, whatever it is — a Problem\n * Details body from `@photon-hq/error`, an `Error` carrying one as\n * `cause`, or something unknown. The component derives its copy via\n * `describeError`; unknowns fall back to a generic line rather than\n * leaking raw messages.\n * - TWO presentations, one per altitude: `Error` is the inline line for\n * forms, cards, and action rows; `ErrorView` is the region / page state\n * built on StatusView (same `sm` / `base` size axis).\n * - Both render `null` when there is nothing to show, so they can sit\n * unconditionally in JSX (`<Error error={mutation.error} />`).\n * - Client-side *validation* is not this component's job — that belongs to\n * `Field.Error`, wired to control validity.\n */\n\nconst DOC_LINK_LABEL = \"Learn more\";\nconst RETRY_LABEL = \"Try again\";\n\ninterface ErrorBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n}\n\nexport interface ErrorProps\n extends ErrorBaseProps, Omit<ComponentProps<\"div\">, keyof ErrorBaseProps> {}\n\n/**\n * Error — one line of red ink naming a server failure (4xx / 5xx), placed\n * inline: under a form, in a card, beside the action that failed. Give it the\n * caught value and it renders the problem's `detail` (falling back to `title`,\n * then to a generic line), plus a docs link when the problem carries one.\n * Renders nothing while `error` is nullish, so it can stay mounted.\n *\n * For a region or page that failed to load, use {@link ErrorView}.\n */\nexport function Error({ error, children, className, ...props }: ErrorProps) {\n if (error == null && children == null) return null;\n const copy = describeError(error);\n return (\n <div\n role=\"alert\"\n className={cn(\n \"text-pho-error flex min-w-0 items-start gap-1.5 text-base\",\n className,\n )}\n {...props}\n >\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n <span className=\"min-w-0\">\n {displayCopy(children ?? copy.description ?? copy.title)}\n {copy.documentation != null && (\n <>\n {\" \"}\n <a\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline underline-offset-2\"\n >\n {DOC_LINK_LABEL}\n </a>\n </>\n )}\n {/* Trailing whitespace collapses when the details render null. */}{\" \"}\n <ErrorDetails copy={copy} />\n </span>\n </div>\n );\n}\n\nError.displayName = \"Error\";\n\n/**\n * Hover / focus disclosure on the inline line — the request's fuller story:\n * problem `title` (with the same alert icon + `text-pho-error` ink as the\n * inline line), the untruncated description, then `status` + `code` and the\n * copyable request id grouped below for a support handoff. Renders only when\n * the caught value actually carried a problem (or at least a request id); a\n * generic unknown has nothing more to disclose. Built on `Popover` rather\n * than `Tooltip` because the panel holds an interactive control (the copy\n * button) — `openOnHover` keeps the tooltip-like gesture.\n */\nfunction ErrorDetails({ copy }: { copy: ErrorDescription }) {\n const { problem, requestId } = copy;\n if (problem == null && requestId == null) return null;\n\n // Contract problems show `status CODE`; status-only shows `status Reason`.\n let statusLine: string | null = null;\n if (problem != null) {\n if (problem.code) {\n statusLine = `${problem.status} ${problem.code}`;\n } else {\n const reason = httpReasonPhrase(problem.status, problem.title);\n statusLine = reason\n ? `${problem.status} ${reason}`\n : String(problem.status);\n }\n }\n\n return (\n <Popover.Root>\n <Popover.Trigger\n openOnHover\n delay={150}\n aria-label=\"Error details\"\n className=\"outline-pho-brand inline-flex size-4 shrink-0 translate-y-[3px] cursor-help items-center justify-center rounded-full opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2\"\n >\n <IconInfoCircle className=\"size-3.5\" />\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Positioner align=\"start\">\n <Popover.Popup className=\"text-pho-error\">\n <Popover.Title className=\"text-pho-error flex items-start gap-1.5\">\n <span aria-hidden className=\"flex h-[1lh] shrink-0 items-center\">\n <IconAlertCircleFilled className=\"size-4\" />\n </span>\n {copy.title}\n </Popover.Title>\n {copy.description != null && (\n <Popover.Description className=\"text-pho-error\">\n {copy.description}\n </Popover.Description>\n )}\n {(statusLine != null || requestId != null) && (\n <div className=\"text-pho-error mt-1 font-mono text-[10px] leading-snug\">\n {statusLine != null && <p>{statusLine}</p>}\n {requestId != null && (\n <RequestId requestId={requestId} className=\"mt-0\" />\n )}\n </div>\n )}\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** Request-id footnote — click copies the id for support handoff. */\nfunction RequestId({\n requestId,\n className,\n}: {\n requestId: string;\n className?: string;\n}) {\n const [copied, setCopied] = useState(false);\n const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n },\n [],\n );\n\n return (\n <span\n className={cn(\"mt-1 block font-mono text-[10px] leading-snug\", className)}\n >\n Request ID:{\" \"}\n <button\n type=\"button\"\n className=\"outline-pho-brand inline cursor-pointer underline-offset-2 select-all hover:underline focus-visible:underline focus-visible:outline-2 focus-visible:-outline-offset-1\"\n aria-label={\n copied ? \"Request ID copied\" : `Copy request ID ${requestId}`\n }\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(requestId);\n } catch {\n return;\n }\n setCopied(true);\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n copiedTimer.current = setTimeout(() => setCopied(false), 1500);\n }}\n >\n {requestId}\n </button>\n <output className=\"sr-only\" aria-live=\"polite\">\n {copied ? \"Copied\" : \"\"}\n </output>\n </span>\n );\n}\n\ninterface ErrorViewBaseProps {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived title. */\n title?: ReactNode;\n /** Override the derived description. */\n description?: ReactNode;\n /** Override the warning glyph. */\n icon?: ReactNode;\n /** Offer a retry — renders a \"Try again\" button. */\n onRetry?: () => void;\n /** Replace the built-in action row entirely. */\n actions?: ReactNode;\n /** Size variant — StatusView's axis. @default \"base\" */\n size?: StatusViewSize;\n}\n\nexport interface ErrorViewProps\n extends\n ErrorViewBaseProps,\n Omit<ComponentProps<\"div\">, keyof ErrorViewBaseProps | \"children\"> {}\n\n/**\n * ErrorView — the failed state of a region or page, built on StatusView:\n * warning icon over the problem's `title`, its `detail` and remediation\n * below, an action row (`onRetry` and the problem's docs link), and the\n * request id as a footnote for support. Announces via `role=\"alert\"`.\n *\n * Use `size=\"sm\"` inside cards and panels, the default `base` for whole\n * pages. Renders nothing while `error` is nullish (unless given a static\n * `title`), so it can sit unconditionally next to a query.\n */\nexport function ErrorView({\n error,\n title,\n description,\n icon,\n onRetry,\n actions,\n size = \"base\",\n ...props\n}: ErrorViewProps) {\n if (error == null && title == null) return null;\n const copy = describeError(error);\n\n const resolvedDescription = displayCopy(description ?? copy.description);\n const body =\n resolvedDescription != null || copy.requestId != null ? (\n <>\n {resolvedDescription}\n {copy.requestId != null && <RequestId requestId={copy.requestId} />}\n </>\n ) : undefined;\n\n const resolvedActions =\n actions ??\n (onRetry != null || copy.documentation != null ? (\n <>\n {onRetry != null && (\n <Button size=\"sm\" onClick={onRetry}>\n {RETRY_LABEL}\n </Button>\n )}\n {copy.documentation != null && (\n <ButtonLink\n size=\"sm\"\n variant=\"outlined\"\n href={copy.documentation}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {DOC_LINK_LABEL}\n </ButtonLink>\n )}\n </>\n ) : undefined);\n\n return (\n <StatusView\n role=\"alert\"\n size={size}\n icon={icon ?? <IconAlertTriangle />}\n title={displayCopy(title ?? copy.title)}\n description={body}\n actions={resolvedActions}\n {...props}\n />\n );\n}\n\nErrorView.displayName = \"ErrorView\";\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as m } from "./cn-ChIgwEl3.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
5
|
+
import { Form as e } from "@base-ui/react/form";
|
|
6
|
+
function a({ className: o, ...r }) {
|
|
7
|
+
return /* @__PURE__ */ t(e, {
|
|
8
|
+
className: m("flex flex-col gap-5", o),
|
|
9
|
+
...r
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
a.displayName = "Form";
|
|
13
|
+
export {
|
|
14
|
+
a as t
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=form-AiHbbw4m.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-AiHbbw4m.js","names":[],"sources":["../../src/components/form/form.tsx"],"sourcesContent":["import { type ComponentProps } from \"react\";\nimport { Form as BaseForm } from \"@base-ui/react/form\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface FormProps extends Omit<\n ComponentProps<typeof BaseForm>,\n \"className\"\n> {\n className?: string;\n}\n\n/**\n * Form wrapper built on Base UI `Form`. Coordinates the contained `Field`s —\n * pass server-side `errors` and they surface on the matching fields, and\n * `onClearErrors` clears them as the user edits.\n */\nexport function Form({ className, ...props }: FormProps) {\n return (\n <BaseForm className={cn(\"flex flex-col gap-5\", className)} {...props} />\n );\n}\n\nForm.displayName = \"Form\";\n"],"mappings":";;;;;AAgBA,SAAgB,EAAK,EAAE,WAAA,GAAW,GAAG,EAAA,GAAoB;AACvD,SACE,gBAAA,EAAC,GAAD;AAAA,IAAU,WAAW,EAAG,uBAAuB,CAAS;AAAA,IAAG,GAAI;AAAA,EAAQ,CAAA;AAE3E;AAEA,EAAK,cAAc"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as
|
|
2
|
+
import { t as s } from "./cn-ChIgwEl3.js";
|
|
3
3
|
import { t as f } from "./createReactComponent-BqCmnqfX.js";
|
|
4
|
-
import { t as
|
|
5
|
-
import { useState as
|
|
6
|
-
import { jsx as e, jsxs as
|
|
7
|
-
import { Field as
|
|
8
|
-
var
|
|
4
|
+
import { t as D } from "./IconAlertCircleFilled-VxKU8TYK.js";
|
|
5
|
+
import { useState as R } from "react";
|
|
6
|
+
import { jsx as e, jsxs as i } from "react/jsx-runtime";
|
|
7
|
+
import { Field as o } from "@base-ui/react/field";
|
|
8
|
+
var k = [
|
|
9
9
|
["path", {
|
|
10
10
|
d: "M10.585 10.587a2 2 0 0 0 2.829 2.828",
|
|
11
11
|
key: "svg-0"
|
|
@@ -18,87 +18,92 @@ var D = [
|
|
|
18
18
|
d: "M3 3l18 18",
|
|
19
19
|
key: "svg-2"
|
|
20
20
|
}]
|
|
21
|
-
],
|
|
21
|
+
], C = f("outline", "eye-off", "EyeOff", k), L = [["path", {
|
|
22
22
|
d: "M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0",
|
|
23
23
|
key: "svg-0"
|
|
24
24
|
}], ["path", {
|
|
25
25
|
d: "M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6",
|
|
26
26
|
key: "svg-1"
|
|
27
|
-
}]],
|
|
27
|
+
}]], O = f("outline", "eye", "Eye", L), M = {
|
|
28
28
|
sm: "px-2.5 py-1.5 text-md md:text-base",
|
|
29
29
|
md: "px-2.5 py-1.5 text-md",
|
|
30
30
|
lg: "px-3 py-2 text-md"
|
|
31
|
-
},
|
|
31
|
+
}, T = {
|
|
32
32
|
sm: "pl-8.5",
|
|
33
33
|
md: "pl-9.5",
|
|
34
34
|
lg: "pl-10"
|
|
35
|
-
},
|
|
35
|
+
}, P = {
|
|
36
36
|
sm: "left-3 size-4",
|
|
37
37
|
md: "left-3 size-5",
|
|
38
38
|
lg: "left-3.5 size-5"
|
|
39
|
-
},
|
|
39
|
+
}, Z = {
|
|
40
40
|
sm: "rounded-base",
|
|
41
41
|
md: "rounded-base",
|
|
42
42
|
lg: "squircle rounded-base"
|
|
43
|
-
},
|
|
43
|
+
}, j = {
|
|
44
44
|
sm: "",
|
|
45
45
|
md: "",
|
|
46
|
-
lg:
|
|
47
|
-
},
|
|
46
|
+
lg: s("isolate before:pointer-events-none before:absolute before:-z-10 before:content-['']", "before:bg-pho-primary before:inset-px before:rounded-[calc(var(--radius-base)-1px)]", "data-[invalid]:before:inset-[1.5px] data-[invalid]:before:rounded-[calc(var(--radius-base)-1.5px)]", "has-[input:focus-visible]:before:inset-0.5 has-[input:focus-visible]:before:rounded-[calc(var(--radius-base)-2px)]", "before:transition-[inset,border-radius] before:duration-100 before:ease-linear")
|
|
47
|
+
}, F = {
|
|
48
48
|
sm: "autofill:rounded-[calc(var(--radius-base)-2px)]",
|
|
49
49
|
md: "autofill:rounded-[calc(var(--radius-base)-2px)]",
|
|
50
50
|
lg: "autofill:rounded-[calc(var(--radius-base)-2px)]"
|
|
51
51
|
};
|
|
52
|
-
function
|
|
53
|
-
const [
|
|
54
|
-
return /* @__PURE__ */
|
|
55
|
-
disabled:
|
|
52
|
+
function G({ label: l, hint: d, size: a = "md", invalid: r, icon: n, labelTrailing: b, trailing: c, disabled: h, required: p, type: u = "text", className: x, wrapperClassName: v, id: g, ref: y, ...N }) {
|
|
53
|
+
const [m, I] = R(!1), t = u === "password", _ = t && m ? "text" : u, E = !!n, w = t || r, A = c != null && !r && !t;
|
|
54
|
+
return /* @__PURE__ */ i(o.Root, {
|
|
55
|
+
disabled: h,
|
|
56
56
|
className: "group/field flex w-full flex-col gap-1.5",
|
|
57
57
|
children: [
|
|
58
|
-
l && /* @__PURE__ */
|
|
58
|
+
l && /* @__PURE__ */ i("div", {
|
|
59
59
|
className: "flex items-center justify-between gap-2",
|
|
60
|
-
children: [/* @__PURE__ */
|
|
60
|
+
children: [/* @__PURE__ */ i(o.Label, {
|
|
61
61
|
className: "text-pho-secondary flex cursor-default items-center gap-0.5 text-base font-medium",
|
|
62
|
-
children: [l,
|
|
62
|
+
children: [l, p && /* @__PURE__ */ e("span", {
|
|
63
63
|
className: "text-pho-brand",
|
|
64
64
|
children: "*"
|
|
65
65
|
})]
|
|
66
|
-
}),
|
|
66
|
+
}), b]
|
|
67
67
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
"data-invalid":
|
|
70
|
-
className:
|
|
68
|
+
/* @__PURE__ */ i("div", {
|
|
69
|
+
"data-invalid": r || void 0,
|
|
70
|
+
className: s("bg-pho-primary ring-pho-primary relative flex w-full items-center p-0.5 ring-1 transition-shadow duration-100 ease-linear ring-inset", Z[a], j[a], "has-[input:focus-visible]:ring-pho-brand has-[input:focus-visible]:ring-2", "data-[invalid]:ring-pho-error-subtle data-[invalid]:has-[input:focus-visible]:ring-pho-error data-[invalid]:ring-[1.5px]", "group-data-[disabled]/field:cursor-not-allowed group-data-[disabled]/field:opacity-50", v),
|
|
71
71
|
children: [
|
|
72
|
-
n && /* @__PURE__ */ e(n, { className:
|
|
73
|
-
/* @__PURE__ */ e(
|
|
74
|
-
ref:
|
|
75
|
-
id:
|
|
76
|
-
type:
|
|
77
|
-
required:
|
|
78
|
-
...
|
|
79
|
-
className:
|
|
72
|
+
n && /* @__PURE__ */ e(n, { className: s("text-pho-secondary pointer-events-none absolute", P[a]) }),
|
|
73
|
+
/* @__PURE__ */ e(o.Control, {
|
|
74
|
+
ref: y,
|
|
75
|
+
id: g,
|
|
76
|
+
type: _,
|
|
77
|
+
required: p,
|
|
78
|
+
...N,
|
|
79
|
+
className: s("text-pho-primary placeholder:text-pho-placeholder m-0 w-full bg-transparent outline-none disabled:cursor-not-allowed", M[a], F[a], E && T[a], w && "pr-8.5", x)
|
|
80
80
|
}),
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
r && !t && /* @__PURE__ */ e(D, { className: "text-pho-error pointer-events-none absolute right-3 size-4" }),
|
|
82
|
+
A && /* @__PURE__ */ e("span", {
|
|
83
|
+
className: "text-pho-description pointer-events-none absolute right-3 flex items-center",
|
|
84
|
+
children: c
|
|
85
|
+
}),
|
|
86
|
+
t && /* @__PURE__ */ e("button", {
|
|
83
87
|
type: "button",
|
|
84
88
|
tabIndex: -1,
|
|
85
89
|
"aria-label": "Toggle password visibility",
|
|
86
|
-
onClick: () =>
|
|
90
|
+
onClick: () => I((S) => !S),
|
|
87
91
|
className: "text-pho-secondary hover:text-pho-secondary-hover absolute right-3 flex cursor-pointer items-center transition duration-100 ease-linear focus:outline-none",
|
|
88
|
-
children:
|
|
92
|
+
children: m ? /* @__PURE__ */ e(C, { className: "size-4" }) : /* @__PURE__ */ e(O, { className: "size-4" })
|
|
89
93
|
})
|
|
90
94
|
]
|
|
91
95
|
}),
|
|
92
|
-
d && /* @__PURE__ */ e(
|
|
93
|
-
className:
|
|
96
|
+
d && /* @__PURE__ */ e(o.Description, {
|
|
97
|
+
className: s("text-pho-description text-base", r && "text-pho-error"),
|
|
94
98
|
children: d
|
|
95
|
-
})
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ e(o.Error, { className: "text-pho-error text-base" })
|
|
96
101
|
]
|
|
97
102
|
});
|
|
98
103
|
}
|
|
99
|
-
|
|
104
|
+
G.displayName = "Input";
|
|
100
105
|
export {
|
|
101
|
-
|
|
106
|
+
G as t
|
|
102
107
|
};
|
|
103
108
|
|
|
104
|
-
//# sourceMappingURL=input-
|
|
109
|
+
//# sourceMappingURL=input-DjvZoF8v.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-DjvZoF8v.js","names":["__iconNode"],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconEyeOff.mjs","../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconEye.mjs","../../src/components/input/input.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M10.585 10.587a2 2 0 0 0 2.829 2.828\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M3 3l18 18\", \"key\": \"svg-2\" }]];\nconst IconEyeOff = createReactComponent(\"outline\", \"eye-off\", \"EyeOff\", __iconNode);\n\nexport { __iconNode, IconEyeOff as default };\n//# sourceMappingURL=IconEyeOff.mjs.map\n","/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6\", \"key\": \"svg-1\" }]];\nconst IconEye = createReactComponent(\"outline\", \"eye\", \"Eye\", __iconNode);\n\nexport { __iconNode, IconEye as default };\n//# sourceMappingURL=IconEye.mjs.map\n","import {\n useState,\n type ComponentType,\n type InputHTMLAttributes,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconEye,\n IconEyeOff,\n} from \"@tabler/icons-react\";\nimport { Field } from \"@base-ui/react/field\";\nimport { cn } from \"../../utils/cn\";\n\nexport type InputSize = \"sm\" | \"md\" | \"lg\";\n\n// Control padding per size. The wrapper carries a 2px inset (`p-0.5`) so the\n// control's border box — and the browser's autofill fill it paints — sits\n// inside the ring instead of over it; these paddings are the documented\n// effective padding (sm/md 12/8px, lg 14/10px) minus that 2px inset, so the\n// text position and total field height are unchanged.\nconst SIZE_PAD: Record<InputSize, string> = {\n // 16px on small viewports — iOS zooms into any focused field under 16px.\n sm: \"px-2.5 py-1.5 text-md md:text-base\",\n md: \"px-2.5 py-1.5 text-md\",\n lg: \"px-3 py-2 text-md\",\n};\n// Effective icon clearance minus the wrapper's 2px inset.\nconst LEADING_PAD: Record<InputSize, string> = {\n sm: \"pl-8.5\",\n md: \"pl-9.5\",\n lg: \"pl-10\",\n};\n// Icons are absolutely positioned, so they're placed from the wrapper's\n// padding edge — which, with no border, is the visible field edge; the\n// wrapper's `p-0.5` does not offset them.\nconst LEADING_ICON_POS: Record<InputSize, string> = {\n sm: \"left-3 size-4\",\n md: \"left-3 size-5\",\n lg: \"left-3.5 size-5\",\n};\n// Wrapper corner radius — `rounded-base` (12px) at every size; `lg` also gets\n// `squircle` superellipse smoothing (the ring is a box-shadow on this wrapper,\n// so it follows the border-radius automatically).\nconst SIZE_RADIUS: Record<InputSize, string> = {\n sm: \"rounded-base\",\n md: \"rounded-base\",\n lg: \"squircle rounded-base\",\n};\n// Concentric inner-curve cover, `lg` only. Nested corner radii must follow\n// innerRadius = outerRadius − inset, so the hairline's inner edge at a 12px\n// outer radius is 11px at rest (ring-1) and 10px when focused (ring-2).\n// Browsers derive that automatically for plain round corners, but with\n// `corner-shape: superellipse(...)` shipping engines still scale (rather than\n// offset) the inner shadow curve, which renders the ring visibly thicker at\n// the four corners (see w3c/csswg-drafts#12961). This ::before repaints the\n// field surface over everything inside the correct concentric curve — a round\n// radius is the right parallel-offset approximation of the mild superellipse —\n// so the ring reads as a uniform hairline in every engine. `isolate` +\n// `-z-10` slot the cover above the wrapper's background/ring but below the\n// control's text, icons, and autofill fill. `sm`/`md` have no corner-shape,\n// so the native (already concentric) rendering is kept there.\n// The ::before is absolutely positioned, so its inset is measured from the\n// wrapper's border box (border 0): 1px in at rest, 2px in when focused.\nconst SIZE_RING_COVER: Record<InputSize, string> = {\n sm: \"\",\n md: \"\",\n lg: cn(\n \"isolate before:pointer-events-none before:absolute before:-z-10 before:content-['']\",\n \"before:bg-pho-primary before:inset-px before:rounded-[calc(var(--radius-base)-1px)]\",\n // Invalid sits between rest (1px) and focus (2px) — keep the cover concentric.\n \"data-[invalid]:before:inset-[1.5px] data-[invalid]:before:rounded-[calc(var(--radius-base)-1.5px)]\",\n \"has-[input:focus-visible]:before:inset-0.5 has-[input:focus-visible]:before:rounded-[calc(var(--radius-base)-2px)]\",\n \"before:transition-[inset,border-radius] before:duration-100 before:ease-linear\",\n ),\n};\n// The control's border box sits 2px inside the wrapper (its `p-0.5` inset),\n// clear of both ring widths, so the browser's autofill fill paints inside the\n// hairline instead of over it. Rounding follows inner = outer − gap, derived\n// from the token with calc. Round (not squircle) — the parallel offset of\n// the wrapper's mild superellipse is effectively circular.\nconst SIZE_AUTOFILL_RADIUS: Record<InputSize, string> = {\n sm: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n md: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n lg: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n};\n\nexport interface InputProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"size\"\n> {\n /** Field label (associated for accessibility). */\n label?: ReactNode;\n /** Helper text below the field; rendered in the error color when `invalid`. */\n hint?: ReactNode;\n /** Size variant. @default \"md\" */\n size?: InputSize;\n /** Render the invalid state (ring + icon + error-colored hint). */\n invalid?: boolean;\n /** Leading icon component (e.g. a Tabler icon). */\n icon?: ComponentType<{ className?: string }>;\n /** Node rendered at the right edge of the label row (e.g. \"Forgot?\"). */\n labelTrailing?: ReactNode;\n /**\n * Quiet word or glyph inside the field at its trailing edge (an\n * auto-save's `Saved`, a live character count). Non-interactive overlay:\n * it never takes the pointer, the invalid icon takes the corner over\n * it, and the input reserves nothing — content that shows only\n * sometimes brings its own surface wash (as `Saved` does) so long text\n * fades beneath it instead of colliding.\n */\n trailing?: ReactNode;\n /** Class applied to the input wrapper. */\n wrapperClassName?: string;\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * Text input built on Base UI `Field` (label/description a11y wiring) with\n * Pho's bordered surface, leading icon, invalid state, and a built-in password\n * reveal toggle.\n */\nexport function Input({\n label,\n hint,\n size = \"md\",\n invalid,\n icon: Icon,\n labelTrailing,\n trailing,\n disabled,\n required,\n type = \"text\",\n className,\n wrapperClassName,\n id,\n ref,\n ...props\n}: InputProps) {\n const [reveal, setReveal] = useState(false);\n const isPassword = type === \"password\";\n const resolvedType = isPassword && reveal ? \"text\" : type;\n const hasLeadingIcon = Boolean(Icon);\n const hasTrailingIcon = isPassword || invalid;\n const hasTrailing = trailing != null && !invalid && !isPassword;\n\n return (\n <Field.Root\n disabled={disabled}\n className=\"group/field flex w-full flex-col gap-1.5\"\n >\n {label && (\n <div className=\"flex items-center justify-between gap-2\">\n <Field.Label className=\"text-pho-secondary flex cursor-default items-center gap-0.5 text-base font-medium\">\n {label}\n {required && <span className=\"text-pho-brand\">*</span>}\n </Field.Label>\n {labelTrailing}\n </div>\n )}\n\n <div\n data-invalid={invalid || undefined}\n className={cn(\n // Flat field surface — hairline ring only; elevation shadows are\n // reserved for floating layers (menus/popovers/dialogs). `p-0.5`\n // insets the control 2px (the focused ring width) so its background\n // and the browser's autofill fill nest inside the ring; the size\n // maps compensate so effective padding/height are unchanged.\n \"bg-pho-primary ring-pho-primary relative flex w-full items-center p-0.5 ring-1 transition-shadow duration-100 ease-linear ring-inset\",\n SIZE_RADIUS[size],\n SIZE_RING_COVER[size],\n \"has-[input:focus-visible]:ring-pho-brand has-[input:focus-visible]:ring-2\",\n // Invalid is mid-weight: thicker than the 1px rest hairline, thinner\n // than the 2px focus ring. Rest uses the subtle error ink; focus\n // steps up to the solid error (same hue, full opacity).\n \"data-[invalid]:ring-pho-error-subtle data-[invalid]:has-[input:focus-visible]:ring-pho-error data-[invalid]:ring-[1.5px]\",\n \"group-data-[disabled]/field:cursor-not-allowed group-data-[disabled]/field:opacity-50\",\n wrapperClassName,\n )}\n >\n {Icon && (\n <Icon\n className={cn(\n \"text-pho-secondary pointer-events-none absolute\",\n LEADING_ICON_POS[size],\n )}\n />\n )}\n\n <Field.Control\n ref={ref}\n id={id}\n type={resolvedType}\n required={required}\n {...props}\n className={cn(\n \"text-pho-primary placeholder:text-pho-placeholder m-0 w-full bg-transparent outline-none disabled:cursor-not-allowed\",\n SIZE_PAD[size],\n SIZE_AUTOFILL_RADIUS[size],\n hasLeadingIcon && LEADING_PAD[size],\n // 34px + the wrapper's 2px inset = the previous 36px clearance.\n hasTrailingIcon && \"pr-8.5\",\n className,\n )}\n />\n\n {invalid && !isPassword && (\n <IconAlertCircleFilled className=\"text-pho-error pointer-events-none absolute right-3 size-4\" />\n )}\n {hasTrailing && (\n <span className=\"text-pho-description pointer-events-none absolute right-3 flex items-center\">\n {trailing}\n </span>\n )}\n\n {isPassword && (\n <button\n type=\"button\"\n // Reachable by click but skipped in tab order so Tab moves field→field.\n tabIndex={-1}\n aria-label=\"Toggle password visibility\"\n onClick={() => setReveal((v) => !v)}\n className=\"text-pho-secondary hover:text-pho-secondary-hover absolute right-3 flex cursor-pointer items-center transition duration-100 ease-linear focus:outline-none\"\n >\n {reveal ? (\n <IconEyeOff className=\"size-4\" />\n ) : (\n <IconEye className=\"size-4\" />\n )}\n </button>\n )}\n </div>\n\n {hint && (\n <Field.Description\n className={cn(\n \"text-pho-description text-base\",\n invalid && \"text-pho-error\",\n )}\n >\n {hint}\n </Field.Description>\n )}\n {/* Validation and server errors routed here by Field / Form. */}\n <Field.Error className=\"text-pho-error text-base\" />\n </Field.Root>\n );\n}\n\nInput.displayName = \"Input\";\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
|