harebear-ui 0.9.0 → 0.10.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/components/admin/app-nav.d.ts +11 -1
- package/dist/components/admin/app-nav.js +26 -9
- package/dist/components/admin/checklist-editor.d.ts +20 -0
- package/dist/components/admin/checklist-editor.js +69 -0
- package/dist/components/admin/comment-thread.d.ts +26 -0
- package/dist/components/admin/comment-thread.js +68 -0
- package/dist/components/admin/copy-field.d.ts +3 -1
- package/dist/components/admin/copy-field.js +10 -5
- package/dist/components/admin/date-time-picker.js +1 -1
- package/dist/components/admin/markdown-editor.d.ts +2 -5
- package/dist/components/admin/markdown-editor.js +13 -9
- package/dist/components/admin/page-header.d.ts +3 -1
- package/dist/components/admin/page-header.js +17 -9
- package/dist/components/admin/page.d.ts +16 -0
- package/dist/components/admin/page.js +42 -0
- package/dist/components/admin/prompt-dialog.d.ts +19 -0
- package/dist/components/admin/prompt-dialog.js +83 -0
- package/dist/components/admin/settings.d.ts +4 -2
- package/dist/components/admin/settings.js +37 -30
- package/dist/components/admin/time-zone-picker.d.ts +12 -0
- package/dist/components/admin/time-zone-picker.js +31 -0
- package/dist/components/admin/user-menu.d.ts +3 -1
- package/dist/components/admin/user-menu.js +21 -10
- package/dist/components/ai/assistant.js +1 -1
- package/dist/components/ai/code-block.js +3 -3
- package/dist/components/ai/markdown.d.ts +13 -0
- package/dist/components/ai/markdown.js +41 -0
- package/dist/components/auth/auth.d.ts +41 -5
- package/dist/components/auth/auth.js +165 -155
- package/dist/components/data/day-strip.d.ts +6 -1
- package/dist/components/data/day-strip.js +15 -5
- package/dist/components/layout/collapse.js +1 -1
- package/dist/components/layout/text.d.ts +3 -3
- package/dist/components/layout/text.js +6 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/check-circle.js +1 -1
- package/dist/components/ui/code-block.js +2 -2
- package/dist/components/ui/combobox.js +1 -0
- package/dist/components/ui/input-group.js +1 -1
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/sonner.d.ts +2 -2
- package/dist/components/ui/sonner.js +1 -1
- package/dist/components/ui/toggle.js +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.js +9 -3
- package/dist/lib/{date.js → internal/date.js} +1 -1
- package/dist/lib/{toast-clearance.js → internal/toast-clearance.js} +1 -1
- package/dist/styles.css +6 -1
- package/docs/COMPONENTS.md +150 -26
- package/docs/ROADMAP.md +1 -0
- package/llms.txt +7 -6
- package/package.json +3 -1
- package/skills/harebear-ui/SKILL.md +5 -4
|
@@ -29,8 +29,8 @@ function SettingsSection({ title, description, className, children, ...props })
|
|
|
29
29
|
})]
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
/** One setting: label and description at the left, a control at the right, optional expandable content. */
|
|
33
|
-
function SettingsRow({ label, description, htmlFor, control, content, open: openProp, defaultOpen = false, onOpenChange, expandLabel = "Change", className, ...props }) {
|
|
32
|
+
/** One setting: label and description at the left, a control at the right, optional body and expandable content. */
|
|
33
|
+
function SettingsRow({ label, description, htmlFor, control, body, content, open: openProp, defaultOpen = false, onOpenChange, expandLabel = "Change", className, ...props }) {
|
|
34
34
|
const [openState, setOpenState] = React.useState(defaultOpen);
|
|
35
35
|
const open = openProp ?? openState;
|
|
36
36
|
const setOpen = (next) => {
|
|
@@ -44,35 +44,42 @@ function SettingsRow({ label, description, htmlFor, control, content, open: open
|
|
|
44
44
|
"data-state": content ? open ? "open" : "closed" : void 0,
|
|
45
45
|
className: cn("py-3.5", className),
|
|
46
46
|
...props,
|
|
47
|
-
children: [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ jsxs("div", {
|
|
49
|
+
className: "flex flex-wrap items-center gap-x-4 gap-y-2",
|
|
50
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
51
|
+
className: "flex min-w-0 flex-1 basis-48 flex-col",
|
|
52
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
53
|
+
htmlFor,
|
|
54
|
+
className: "text-sm font-medium",
|
|
55
|
+
children: label
|
|
56
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
57
|
+
className: "text-[13px] text-muted-foreground",
|
|
58
|
+
children: description
|
|
59
|
+
})]
|
|
60
|
+
}), control ?? (content ? /* @__PURE__ */ jsx(Button, {
|
|
61
|
+
type: "button",
|
|
62
|
+
variant: "outline",
|
|
63
|
+
size: "sm",
|
|
64
|
+
"aria-expanded": open,
|
|
65
|
+
"aria-controls": contentId,
|
|
66
|
+
onClick: () => setOpen(!open),
|
|
67
|
+
children: open ? "Cancel" : expandLabel
|
|
68
|
+
}) : null)]
|
|
69
|
+
}),
|
|
70
|
+
body && /* @__PURE__ */ jsx("div", {
|
|
71
|
+
className: "pt-3",
|
|
72
|
+
children: body
|
|
73
|
+
}),
|
|
74
|
+
content && /* @__PURE__ */ jsx(Collapse, {
|
|
75
|
+
open,
|
|
76
|
+
id: contentId,
|
|
77
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
78
|
+
className: "pt-3.5",
|
|
79
|
+
children: content
|
|
80
|
+
})
|
|
74
81
|
})
|
|
75
|
-
|
|
82
|
+
]
|
|
76
83
|
});
|
|
77
84
|
}
|
|
78
85
|
export { SettingsRow, SettingsSection };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface TimeZonePickerProps {
|
|
3
|
+
/** IANA name, e.g. "America/Vancouver". */
|
|
4
|
+
value: string;
|
|
5
|
+
onValueChange: (value: string) => void;
|
|
6
|
+
/** id of the search input, for a FieldLabel or SettingsRow `htmlFor`. */
|
|
7
|
+
id?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Searchable IANA time zone list (Intl.supportedValuesOf("timeZone")), built on Combobox. */
|
|
11
|
+
declare function TimeZonePicker({ value, onValueChange, id, className }: TimeZonePickerProps): React.JSX.Element;
|
|
12
|
+
export { TimeZonePicker };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "../ui/combobox.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
// src/components/admin/time-zone-picker.tsx
|
|
7
|
+
const label = (zone) => zone.replaceAll("_", " ");
|
|
8
|
+
/** Searchable IANA time zone list (Intl.supportedValuesOf("timeZone")), built on Combobox. */
|
|
9
|
+
function TimeZonePicker({ value, onValueChange, id, className }) {
|
|
10
|
+
const zones = React.useMemo(() => {
|
|
11
|
+
const all = Intl.supportedValuesOf("timeZone");
|
|
12
|
+
return value && !all.includes(value) ? [value, ...all] : all;
|
|
13
|
+
}, [value]);
|
|
14
|
+
return /* @__PURE__ */ jsxs(Combobox, {
|
|
15
|
+
items: zones,
|
|
16
|
+
value,
|
|
17
|
+
onValueChange: (next) => next && onValueChange(next),
|
|
18
|
+
itemToStringLabel: label,
|
|
19
|
+
children: [/* @__PURE__ */ jsx(ComboboxInput, {
|
|
20
|
+
id,
|
|
21
|
+
placeholder: "Search time zones",
|
|
22
|
+
showTrigger: false,
|
|
23
|
+
className: cn("w-full", className)
|
|
24
|
+
}), /* @__PURE__ */ jsxs(ComboboxContent, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: "No matching time zone." }), /* @__PURE__ */ jsx(ComboboxList, { children: (zone) => /* @__PURE__ */ jsx(ComboboxItem, {
|
|
25
|
+
value: zone,
|
|
26
|
+
className: "pointer-coarse:min-h-11",
|
|
27
|
+
children: label(zone)
|
|
28
|
+
}, zone) })] })]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export { TimeZonePicker };
|
|
@@ -15,11 +15,13 @@ export interface UserMenuProps {
|
|
|
15
15
|
onSignOut?: () => void;
|
|
16
16
|
/** Default "Sign out". */
|
|
17
17
|
signOutLabel?: React.ReactNode;
|
|
18
|
+
/** Trigger is just the avatar (44px on touch screens) and the menu opens below it, e.g. in a phone page header. */
|
|
19
|
+
compact?: boolean;
|
|
18
20
|
className?: string;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* The signed-in user as a menu button, for a sidebar footer or top bar: theme choice, app items such as
|
|
22
24
|
* Settings, and sign out. In a sidebar icon rail the trigger is just the avatar and the menu opens to the side.
|
|
23
25
|
*/
|
|
24
|
-
declare function UserMenu({ name, subtitle, avatarUrl, color, children, themeSwitch, onSignOut, signOutLabel, className }: UserMenuProps): React.JSX.Element;
|
|
26
|
+
declare function UserMenu({ name, subtitle, avatarUrl, color, children, themeSwitch, onSignOut, signOutLabel, compact, className }: UserMenuProps): React.JSX.Element;
|
|
25
27
|
export { UserMenu };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { categoryBg } from "./category.js";
|
|
3
|
+
import { UserChip, initials } from "./user-chip.js";
|
|
3
4
|
import { useTheme } from "../theme/theme-provider.js";
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar.js";
|
|
4
6
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
|
|
5
7
|
import { useSidebarOptional } from "../ui/sidebar.js";
|
|
6
8
|
import { cn } from "../../lib/utils.js";
|
|
@@ -28,26 +30,35 @@ const themes = [
|
|
|
28
30
|
* The signed-in user as a menu button, for a sidebar footer or top bar: theme choice, app items such as
|
|
29
31
|
* Settings, and sign out. In a sidebar icon rail the trigger is just the avatar and the menu opens to the side.
|
|
30
32
|
*/
|
|
31
|
-
function UserMenu({ name, subtitle, avatarUrl, color, children, themeSwitch = true, onSignOut, signOutLabel = "Sign out", className }) {
|
|
33
|
+
function UserMenu({ name, subtitle, avatarUrl, color, children, themeSwitch = true, onSignOut, signOutLabel = "Sign out", compact = false, className }) {
|
|
32
34
|
const { mode, setMode } = useTheme();
|
|
33
35
|
const sidebar = useSidebarOptional();
|
|
34
36
|
const rail = sidebar?.state === "collapsed" && !sidebar.isMobile;
|
|
35
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */
|
|
37
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
36
38
|
"data-slot": "user-menu-trigger",
|
|
37
|
-
"aria-label": `Account menu for ${name}
|
|
38
|
-
className: cn("flex w-full min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring
|
|
39
|
-
children:
|
|
39
|
+
"aria-label": compact ? `Account menu for ${name}` : rail ? name : void 0,
|
|
40
|
+
className: cn(compact ? "grid size-10 shrink-0 place-items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background pointer-coarse:size-11" : "flex w-full min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:bg-muted group-data-[collapsible=icon]:mx-auto group-data-[collapsible=icon]:size-10 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:rounded-full group-data-[collapsible=icon]:p-0", className),
|
|
41
|
+
children: compact || rail ? /* @__PURE__ */ jsxs(Avatar, {
|
|
42
|
+
"aria-hidden": "true",
|
|
43
|
+
children: [avatarUrl && /* @__PURE__ */ jsx(AvatarImage, {
|
|
44
|
+
src: avatarUrl,
|
|
45
|
+
alt: ""
|
|
46
|
+
}), /* @__PURE__ */ jsx(AvatarFallback, {
|
|
47
|
+
"data-initials": initials(name),
|
|
48
|
+
className: cn("after:content-[attr(data-initials)]", color && [categoryBg[color], "text-category-foreground"])
|
|
49
|
+
})]
|
|
50
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(UserChip, {
|
|
40
51
|
name,
|
|
41
52
|
subtitle,
|
|
42
53
|
avatarUrl,
|
|
43
54
|
color,
|
|
44
55
|
className: "flex-1"
|
|
45
|
-
}), /* @__PURE__ */ jsx(ChevronsUpDownIcon, { className: "size-4 shrink-0 text-muted-foreground group-data-[collapsible=icon]:hidden" })]
|
|
56
|
+
}), /* @__PURE__ */ jsx(ChevronsUpDownIcon, { className: "size-4 shrink-0 text-muted-foreground group-data-[collapsible=icon]:hidden" })] })
|
|
46
57
|
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
47
58
|
"data-slot": "user-menu",
|
|
48
|
-
side: rail ? "right" : "top",
|
|
49
|
-
align: rail ? "end" : "start",
|
|
50
|
-
className: "w-(--radix-dropdown-menu-trigger-width) min-w-60",
|
|
59
|
+
side: compact ? "bottom" : rail ? "right" : "top",
|
|
60
|
+
align: compact || rail ? "end" : "start",
|
|
61
|
+
className: compact ? "min-w-60" : "w-(--radix-dropdown-menu-trigger-width) min-w-60",
|
|
51
62
|
children: [
|
|
52
63
|
/* @__PURE__ */ jsxs(DropdownMenuLabel, {
|
|
53
64
|
className: "flex min-w-0 flex-col font-normal",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { AskBox } from "./ask-box.js";
|
|
3
3
|
import { AssistantAvatar, ChatMessage, ChatThread, TypingIndicator } from "./chat.js";
|
|
4
4
|
import { Button } from "../ui/button.js";
|
|
5
|
-
import { useToastClearance } from "../../lib/toast-clearance.js";
|
|
5
|
+
import { useToastClearance } from "../../lib/internal/toast-clearance.js";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -30,9 +30,9 @@ function CodeSnippet({ code, language, highlight = true, lineNumbers, hideCopy,
|
|
|
30
30
|
type: "button",
|
|
31
31
|
variant: "ghost",
|
|
32
32
|
size: "sm",
|
|
33
|
-
className: "ms-auto",
|
|
33
|
+
className: "ms-auto pointer-coarse:h-11",
|
|
34
34
|
onClick: () => copy(code),
|
|
35
|
-
"aria-label": "Copy code",
|
|
35
|
+
"aria-label": copied ? void 0 : "Copy code",
|
|
36
36
|
children: [copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}), copied ? "Copied" : "Copy"]
|
|
37
37
|
})]
|
|
38
38
|
}), /* @__PURE__ */ jsx(CodeBlockContent, {
|
|
@@ -106,7 +106,7 @@ function withTips(children) {
|
|
|
106
106
|
function Prose({ className, children, ...props }) {
|
|
107
107
|
return /* @__PURE__ */ jsx("div", {
|
|
108
108
|
"data-slot": "prose",
|
|
109
|
-
className: cn("flex min-w-0 flex-col gap-3.5 text-[15px] leading-[1.65] wrap-anywhere text-foreground", "[&_ol]:flex [&_ol]:list-decimal [&_ol]:flex-col [&_ol]:gap-2 [&_ol]:pl-5.5", "[&_ul]:flex [&_ul]:list-disc [&_ul]:flex-col [&_ul]:gap-2 [&_ul]:pl-5.5", "[&_strong]:font-semibold [&_a]:text-primary [&_a]:underline-offset-4 hover:[&_a]:
|
|
109
|
+
className: cn("flex min-w-0 flex-col gap-3.5 text-[15px] leading-[1.65] wrap-anywhere text-foreground", "[&_ol]:flex [&_ol]:list-decimal [&_ol]:flex-col [&_ol]:gap-2 [&_ol]:pl-5.5", "[&_ul]:flex [&_ul]:list-disc [&_ul]:flex-col [&_ul]:gap-2 [&_ul]:pl-5.5", "[&_strong]:font-semibold [&_a]:text-primary [&_a]:underline [&_a]:decoration-primary/40 [&_a]:underline-offset-4 hover:[&_a]:decoration-primary [&_a]:rounded-sm [&_a:focus-visible]:outline-2 [&_a:focus-visible]:outline-offset-2 [&_a:focus-visible]:outline-ring", "[&_h1]:text-xl [&_h1]:font-semibold [&_h2]:text-lg [&_h2]:font-semibold [&_h3]:font-semibold [&_h4]:font-semibold", "[&_blockquote]:border-l-2 [&_blockquote]:pl-4 [&_blockquote]:text-muted-foreground", "[&_:not(pre)>code]:rounded-[6px] [&_:not(pre)>code]:bg-muted [&_:not(pre)>code]:px-1.5 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-[12.5px]", "[&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-4.5 [&_pre]:font-mono [&_pre]:text-[13px]", "[&_table]:w-full [&_table]:text-sm [&_td]:border-b [&_td]:py-2 [&_th]:border-b [&_th]:py-2 [&_th]:text-left [&_th]:font-medium", className),
|
|
110
110
|
...props,
|
|
111
111
|
children: withTips(children)
|
|
112
112
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface MarkdownProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
3
|
+
/** The Markdown source. GitHub flavoured (tables, task lists, strikethrough, autolinks). Raw HTML is dropped. */
|
|
4
|
+
children: string;
|
|
5
|
+
/** Component for in-app links (href without a scheme, e.g. "/lists/2"), e.g. Next.js or React Router Link. Gets `href`. */
|
|
6
|
+
linkComponent?: React.ElementType;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Renders Markdown safely inside Prose: no raw HTML, unsafe URLs (javascript:) removed, `> Tip:` blockquotes as
|
|
10
|
+
* a Callout. Other props go to the Prose wrapper.
|
|
11
|
+
*/
|
|
12
|
+
declare function Markdown({ children, linkComponent: Link, ...props }: MarkdownProps): React.JSX.Element;
|
|
13
|
+
export { Markdown };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Prose, ProseBlockquote } from "./code-block.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import ReactMarkdown from "react-markdown";
|
|
6
|
+
import remarkGfm from "remark-gfm";
|
|
7
|
+
// src/components/ai/markdown.tsx
|
|
8
|
+
const plugins = [remarkGfm];
|
|
9
|
+
const SCHEME = /^([a-z][a-z\d+.-]*:|\/\/)/i;
|
|
10
|
+
/**
|
|
11
|
+
* Renders Markdown safely inside Prose: no raw HTML, unsafe URLs (javascript:) removed, `> Tip:` blockquotes as
|
|
12
|
+
* a Callout. Other props go to the Prose wrapper.
|
|
13
|
+
*/
|
|
14
|
+
function Markdown({ children, linkComponent: Link, ...props }) {
|
|
15
|
+
const components = React.useMemo(() => ({
|
|
16
|
+
blockquote: ProseBlockquote,
|
|
17
|
+
a: ({ node: _node, href = "", ...rest }) => Link && !SCHEME.test(href) ? /* @__PURE__ */ jsx(Link, {
|
|
18
|
+
href,
|
|
19
|
+
...rest
|
|
20
|
+
}) : /* @__PURE__ */ jsx("a", {
|
|
21
|
+
href,
|
|
22
|
+
...rest
|
|
23
|
+
}),
|
|
24
|
+
input: ({ node: _node, checked, ...rest }) => /* @__PURE__ */ jsx("input", {
|
|
25
|
+
...rest,
|
|
26
|
+
checked,
|
|
27
|
+
"aria-label": checked ? "Done" : "Not done"
|
|
28
|
+
})
|
|
29
|
+
}), [Link]);
|
|
30
|
+
return /* @__PURE__ */ jsx(Prose, {
|
|
31
|
+
"data-slot": "markdown",
|
|
32
|
+
...props,
|
|
33
|
+
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
34
|
+
remarkPlugins: plugins,
|
|
35
|
+
components,
|
|
36
|
+
skipHtml: true,
|
|
37
|
+
children
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export { Markdown };
|
|
@@ -33,6 +33,23 @@ declare function GitHubIcon(props: React.ComponentProps<"svg">): React.JSX.Eleme
|
|
|
33
33
|
declare function GoogleIcon(props: React.ComponentProps<"svg">): React.JSX.Element;
|
|
34
34
|
/** Apple mark, for AuthProvider icons. */
|
|
35
35
|
declare function AppleIcon(props: React.ComponentProps<"svg">): React.JSX.Element;
|
|
36
|
+
export interface AuthFormProps extends Omit<React.ComponentProps<"form">, "onSubmit" | "title"> {
|
|
37
|
+
title: React.ReactNode;
|
|
38
|
+
description?: React.ReactNode;
|
|
39
|
+
/** The fields. Give each a `name`; they arrive in onSubmit's FormData. */
|
|
40
|
+
children?: React.ReactNode;
|
|
41
|
+
/** Form-level error, e.g. "Invalid email or password". */
|
|
42
|
+
error?: React.ReactNode;
|
|
43
|
+
submitLabel: React.ReactNode;
|
|
44
|
+
/** Shows a spinner on the submit button and disables every control. */
|
|
45
|
+
loading?: boolean;
|
|
46
|
+
/** Called with the form's data once the browser's validation passes. Omit to submit natively (e.g. to an `action`). */
|
|
47
|
+
onSubmit?: (data: FormData) => void | Promise<void>;
|
|
48
|
+
/** Under the submit button: provider buttons, a "Sign in" link. */
|
|
49
|
+
footer?: React.ReactNode;
|
|
50
|
+
}
|
|
51
|
+
/** The shell LoginForm, SignupForm and NewPasswordForm share: heading, your fields, error, submit button. */
|
|
52
|
+
declare function AuthForm({ title, description, children, error, submitLabel, loading, onSubmit, footer, className, ...props }: AuthFormProps): React.JSX.Element;
|
|
36
53
|
interface AuthFormBase extends Omit<React.ComponentProps<"form">, "onSubmit" | "title"> {
|
|
37
54
|
title?: React.ReactNode;
|
|
38
55
|
description?: React.ReactNode;
|
|
@@ -51,7 +68,7 @@ export interface LoginValues {
|
|
|
51
68
|
/** Absent in "email-only" mode. */
|
|
52
69
|
password?: string;
|
|
53
70
|
}
|
|
54
|
-
export interface LoginFormProps extends AuthFormBase {
|
|
71
|
+
export interface LoginFormProps extends Omit<AuthFormBase, "children"> {
|
|
55
72
|
/** Called with the entered values. Omit to let the form submit natively (e.g. to an `action`). */
|
|
56
73
|
onSubmit?: (values: LoginValues) => void | Promise<void>;
|
|
57
74
|
/** "email-only" drops the password field for magic-link or OTP flows (shadcn login-05). */
|
|
@@ -61,14 +78,17 @@ export interface LoginFormProps extends AuthFormBase {
|
|
|
61
78
|
submitLabel?: React.ReactNode;
|
|
62
79
|
}
|
|
63
80
|
/** Email + password sign-in form with optional SSO buttons. Pair with AuthLayout. */
|
|
64
|
-
declare function LoginForm({ title, description, onSubmit, mode, providers, onProviderClick, providersFirst, forgotPasswordHref, signupHref, submitLabel,
|
|
81
|
+
declare function LoginForm({ title, description, onSubmit, mode, providers, onProviderClick, providersFirst, forgotPasswordHref, signupHref, submitLabel, ...props }: LoginFormProps): React.JSX.Element;
|
|
65
82
|
export interface SignupValues {
|
|
66
83
|
name?: string;
|
|
67
84
|
email: string;
|
|
68
85
|
password: string;
|
|
69
86
|
}
|
|
70
87
|
export interface SignupFormProps extends AuthFormBase {
|
|
71
|
-
|
|
88
|
+
/** `data` holds every field, including your own `children` fields. */
|
|
89
|
+
onSubmit?: (values: SignupValues, data: FormData) => void | Promise<void>;
|
|
90
|
+
/** Extra fields shown before the name field (e.g. a household name). Give each a `name`; read it from `data`. */
|
|
91
|
+
children?: React.ReactNode;
|
|
72
92
|
/** Ask for a full name. Default true. */
|
|
73
93
|
nameField?: boolean;
|
|
74
94
|
/** Ask for the password twice; mismatches are reported before onSubmit. Default true. */
|
|
@@ -81,5 +101,21 @@ export interface SignupFormProps extends AuthFormBase {
|
|
|
81
101
|
submitLabel?: React.ReactNode;
|
|
82
102
|
}
|
|
83
103
|
/** Account creation form: name, email, password (+ confirm), optional SSO. Pair with AuthLayout. */
|
|
84
|
-
declare function SignupForm({ title, description, onSubmit, nameField, confirmPassword, passwordHint, minPasswordLength, providers, onProviderClick, providersFirst, loginHref, submitLabel,
|
|
85
|
-
export
|
|
104
|
+
declare function SignupForm({ title, description, onSubmit, children, nameField, confirmPassword, passwordHint, minPasswordLength, providers, onProviderClick, providersFirst, loginHref, submitLabel, ...props }: SignupFormProps): React.JSX.Element;
|
|
105
|
+
export interface NewPasswordFormProps extends Omit<React.ComponentProps<"form">, "onSubmit" | "title" | "children">, Pick<AuthFormProps, "description" | "error" | "loading"> {
|
|
106
|
+
/** Default "Choose a new password". */
|
|
107
|
+
title?: React.ReactNode;
|
|
108
|
+
/** Called once both fields match. */
|
|
109
|
+
onSubmit?: (values: {
|
|
110
|
+
password: string;
|
|
111
|
+
}) => void | Promise<void>;
|
|
112
|
+
/** Minimum length enforced by the browser. Default 8. */
|
|
113
|
+
minPasswordLength?: number;
|
|
114
|
+
/** Hint under the password field. Default "At least N characters." */
|
|
115
|
+
passwordHint?: React.ReactNode;
|
|
116
|
+
/** Default "Save password". */
|
|
117
|
+
submitLabel?: React.ReactNode;
|
|
118
|
+
}
|
|
119
|
+
/** Password reset or first password: new password + confirm. Pair with AuthLayout. */
|
|
120
|
+
declare function NewPasswordForm({ title, onSubmit, minPasswordLength, passwordHint, submitLabel, ...props }: NewPasswordFormProps): React.JSX.Element;
|
|
121
|
+
export { AppleIcon, AuthForm, AuthLayout, GitHubIcon, GoogleIcon, LoginForm, NewPasswordForm, SignupForm };
|