@xforge-dev/forge-kit 0.20.0 → 0.22.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/app-switcher.js +1 -1
- package/dist/forge-header.d.ts +16 -4
- package/dist/forge-header.js +27 -8
- package/dist/support-button.js +1 -1
- package/package.json +1 -1
package/dist/app-switcher.js
CHANGED
|
@@ -11,7 +11,7 @@ export function AppSwitcher({ current, account, syncError, showAccount = true, c
|
|
|
11
11
|
const currentApp = FORGE_APPS.find((a) => a.id === current);
|
|
12
12
|
const liveUrl = useCurrentUrl(currentApp?.url ?? accountUrl('/'));
|
|
13
13
|
const next = currentUrl ?? liveUrl;
|
|
14
|
-
return (_jsxs(Menu.Root, { children: [_jsxs(Menu.Trigger, { className: "flex items-center gap-2 rounded-md px-2 py-1 font-semibold hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring", children: [_jsx(ForgeLogo, { brand: currentApp?.id ?? 'xforge', variant: "icon", className: "h-8", alt: "" }), _jsx("span", { children: currentApp?.name ?? 'xForge' }), _jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" }), syncError && (_jsx("span", { role: "img", "aria-label": "Not synced", title: "Not synced", className: "h-2 w-2 rounded-full bg-destructive" }))] }), _jsx(Menu.Portal, { children: _jsxs(Menu.Content, { align: "start", sideOffset: 6, className: "z-50 w-72 rounded-md border bg-popover p-1 text-popover-foreground shadow-md", children: [_jsxs(Menu.Label, { className: "flex items-center gap-2 px-2 py-1.5 text-xs font-medium text-muted-foreground", children: [_jsx(ForgeLogo, { brand: "xforge", variant: "icon", className: "h-4", alt: "" }), "xForge apps"] }), FORGE_APPS.map((app) => {
|
|
14
|
+
return (_jsxs(Menu.Root, { children: [_jsxs(Menu.Trigger, { className: "flex items-center gap-2 rounded-md px-2 py-1 font-semibold hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring", children: [_jsx(ForgeLogo, { brand: currentApp?.id ?? 'xforge', variant: "icon", className: "h-8", alt: "" }), _jsx("span", { className: "max-[440px]:sr-only", children: currentApp?.name ?? 'xForge' }), _jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" }), syncError && (_jsx("span", { role: "img", "aria-label": "Not synced", title: "Not synced", className: "h-2 w-2 rounded-full bg-destructive" }))] }), _jsx(Menu.Portal, { children: _jsxs(Menu.Content, { align: "start", sideOffset: 6, className: "z-50 w-72 rounded-md border bg-popover p-1 text-popover-foreground shadow-md", children: [_jsxs(Menu.Label, { className: "flex items-center gap-2 px-2 py-1.5 text-xs font-medium text-muted-foreground", children: [_jsx(ForgeLogo, { brand: "xforge", variant: "icon", className: "h-4", alt: "" }), "xForge apps"] }), FORGE_APPS.map((app) => {
|
|
15
15
|
const isCurrent = app.id === current;
|
|
16
16
|
const disabled = app.status === 'soon';
|
|
17
17
|
return (_jsx(Menu.Item, { asChild: true, disabled: disabled, children: _jsxs("a", { href: disabled ? undefined : app.url, "aria-current": isCurrent ? 'page' : undefined, className: item, children: [_jsx(ForgeLogo, { brand: app.id, variant: "icon", className: "h-9 shrink-0", alt: "" }), _jsxs("span", { className: "flex-1", children: [_jsx("span", { className: "block font-medium", children: app.name }), _jsx("span", { className: "block text-xs text-muted-foreground", children: disabled ? 'Coming soon' : app.description })] }), isCurrent && _jsx(Check, { className: "h-4 w-4" })] }) }, app.id));
|
package/dist/forge-header.d.ts
CHANGED
|
@@ -4,16 +4,24 @@ import { type ForgeAppId } from './apps.js';
|
|
|
4
4
|
import type { ForgeAccount } from './use-forge-user.js';
|
|
5
5
|
export type ForgeNavLink = {
|
|
6
6
|
href: string;
|
|
7
|
-
label
|
|
7
|
+
/** Usually text; a node for a link that needs emphasis inside its label (e.g. a footnote link). */
|
|
8
|
+
label: ReactNode;
|
|
8
9
|
icon?: LucideIcon;
|
|
10
|
+
/** Full-colour art (e.g. a game icon) in place of `icon`: drawn in the icon box (24px in menus) and left untinted. */
|
|
11
|
+
media?: ReactNode;
|
|
9
12
|
active?: boolean;
|
|
10
13
|
description?: string;
|
|
11
14
|
};
|
|
12
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* A run of links inside a nav group, under an optional heading (text, or e.g. an avatar and a name). `rail` indents
|
|
17
|
+
* the links behind a line, kept within the heading's width, to show they all belong to it (e.g. one character's
|
|
18
|
+
* pages); the active link lights its stretch of the line.
|
|
19
|
+
*/
|
|
13
20
|
export type ForgeNavSection = {
|
|
14
21
|
key?: string;
|
|
15
22
|
title?: ReactNode;
|
|
16
23
|
items: ForgeNavLink[];
|
|
24
|
+
rail?: boolean;
|
|
17
25
|
};
|
|
18
26
|
/** A header menu of related pages; active when one of its links is. */
|
|
19
27
|
export type ForgeNavGroup = {
|
|
@@ -30,6 +38,8 @@ export type ForgeHeaderProps = {
|
|
|
30
38
|
syncError?: boolean;
|
|
31
39
|
/** Page links and groups of them: inline (groups as menus) on desktop, in the account panel on mobile. */
|
|
32
40
|
nav?: ForgeNavItem[];
|
|
41
|
+
/** False keeps `nav` out of the account panel, for an app that draws its own mobile navigation. Defaults to true. */
|
|
42
|
+
panelNav?: boolean;
|
|
33
43
|
/** Header buttons shown left of the avatar. */
|
|
34
44
|
actions?: ReactNode;
|
|
35
45
|
/** Shown in the panel under the username, supporter badge and Discord name (e.g. time zone). */
|
|
@@ -51,7 +61,7 @@ export type ForgeHeaderProps = {
|
|
|
51
61
|
* The header every xForge app shares: app switcher, page links, app buttons,
|
|
52
62
|
* and an avatar that opens the account panel with the theme settings.
|
|
53
63
|
*/
|
|
54
|
-
export declare function ForgeHeader({ current, account, syncError, nav, actions, accountDetails, guestDetails, accountActions, panel, onBeforeSignOut, containerClassName, currentUrl, }: ForgeHeaderProps): import("react").JSX.Element;
|
|
64
|
+
export declare function ForgeHeader({ current, account, syncError, nav, panelNav, actions, accountDetails, guestDetails, accountActions, panel, onBeforeSignOut, containerClassName, currentUrl, }: ForgeHeaderProps): import("react").JSX.Element;
|
|
55
65
|
export declare function ForgeAvatar({ account, className }: {
|
|
56
66
|
account: ForgeAccount | null;
|
|
57
67
|
className?: string;
|
|
@@ -68,6 +78,8 @@ export declare function ForgePanelSection({ title, description, children, }: {
|
|
|
68
78
|
}): import("react").JSX.Element;
|
|
69
79
|
export type ForgePanelItemProps = {
|
|
70
80
|
icon?: LucideIcon;
|
|
81
|
+
/** Full-colour art in place of `icon` (see ForgeNavLink). */
|
|
82
|
+
media?: ReactNode;
|
|
71
83
|
label: ReactNode;
|
|
72
84
|
description?: ReactNode;
|
|
73
85
|
/** In-app path (client navigation) or, with `external`, a full URL. */
|
|
@@ -81,7 +93,7 @@ export type ForgePanelItemProps = {
|
|
|
81
93
|
keepOpen?: boolean;
|
|
82
94
|
} & Omit<ComponentPropsWithRef<'button'>, 'children' | 'onClick' | 'disabled'>;
|
|
83
95
|
/** A row in the account panel: a link or a button that closes the panel. */
|
|
84
|
-
export declare function ForgePanelItem({ icon
|
|
96
|
+
export declare function ForgePanelItem({ icon, media, label, description, href, external, onClick, disabled, active, tone, keepOpen, className: extraClassName, ...buttonProps }: ForgePanelItemProps): import("react").JSX.Element;
|
|
85
97
|
/** A labelled on/off switch row for panel sections. */
|
|
86
98
|
export declare function ForgeSwitchRow({ icon: Icon, label, description, checked, onCheckedChange, }: {
|
|
87
99
|
icon?: LucideIcon;
|
package/dist/forge-header.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as Dialog from '@radix-ui/react-dialog';
|
|
|
4
4
|
import * as Menu from '@radix-ui/react-dropdown-menu';
|
|
5
5
|
import Link from 'next/link.js';
|
|
6
6
|
import { useState } from 'react';
|
|
7
|
-
import {
|
|
7
|
+
import { ChevronDown, ExternalLink, LogIn, LogOut, Moon, RefreshCw, Sun, UserRound, X } from 'lucide-react';
|
|
8
8
|
import { accountUrl } from './account-url.js';
|
|
9
9
|
import { AppSwitcher } from './app-switcher.js';
|
|
10
10
|
import { FORGE_APPS } from './apps.js';
|
|
@@ -12,6 +12,23 @@ import { ForgeSupporterBadge } from './support-button.js';
|
|
|
12
12
|
import { FORGE_COLOR_THEMES } from './theme.js';
|
|
13
13
|
import { useForgeTheme } from './theme-provider.js';
|
|
14
14
|
import { useCurrentUrl } from './use-current-url.js';
|
|
15
|
+
/** The active link's stretch of a section's rail, on a `relative` row inset 4px from a 2px line. */
|
|
16
|
+
const railMark = 'before:absolute before:inset-y-1 before:-left-1.5 before:w-0.5 before:rounded-full before:bg-primary';
|
|
17
|
+
/** A link's leading art or icon; `icon` keeps the 16px tinted look, `media` gets a 20px untinted box. */
|
|
18
|
+
function LinkLead({ icon: Icon, media, tint }) {
|
|
19
|
+
if (media)
|
|
20
|
+
return _jsx("span", { "aria-hidden": true, className: "flex size-5 shrink-0 items-center justify-center", children: media });
|
|
21
|
+
return Icon ? _jsx(Icon, { "aria-hidden": true, className: `mt-0.5 h-4 w-4 shrink-0 ${tint ? 'text-muted-foreground' : ''}` }) : null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A menu link's lead: one 24px box for both kinds, so labels line up. Art fills it untinted; a line icon sits at 18px
|
|
25
|
+
* in the muted colour.
|
|
26
|
+
*/
|
|
27
|
+
function MenuLead({ icon: Icon, media }) {
|
|
28
|
+
if (!media && !Icon)
|
|
29
|
+
return null;
|
|
30
|
+
return (_jsx("span", { "aria-hidden": true, className: "flex size-6 shrink-0 items-center justify-center", children: media ?? (Icon && _jsx(Icon, { strokeWidth: 2, className: "size-[18px] text-muted-foreground" })) }));
|
|
31
|
+
}
|
|
15
32
|
export const isNavGroup = (item) => 'sections' in item;
|
|
16
33
|
const navPill = (active) => active
|
|
17
34
|
? 'rounded-lg bg-primary-tint px-3 py-1.5 text-sm font-semibold text-primary-text'
|
|
@@ -20,15 +37,17 @@ const navPill = (active) => active
|
|
|
20
37
|
* The header every xForge app shares: app switcher, page links, app buttons,
|
|
21
38
|
* and an avatar that opens the account panel with the theme settings.
|
|
22
39
|
*/
|
|
23
|
-
export function ForgeHeader({ current, account, syncError, nav = [], actions, accountDetails, guestDetails, accountActions, panel, onBeforeSignOut, containerClassName = 'max-w-6xl', currentUrl, }) {
|
|
40
|
+
export function ForgeHeader({ current, account, syncError, nav = [], panelNav = true, actions, accountDetails, guestDetails, accountActions, panel, onBeforeSignOut, containerClassName = 'max-w-6xl', currentUrl, }) {
|
|
24
41
|
const [open, setOpen] = useState(false);
|
|
25
|
-
return (_jsx("header", { className: "sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/80", children: _jsxs("div", { className: `mx-auto flex h-14 w-full items-center gap-2 px-4 sm:gap-4 ${containerClassName}`, children: [_jsx(AppSwitcher, { current: current, account: account, showAccount: false, currentUrl: currentUrl }), _jsx("nav", { className: "hidden flex-1 items-center gap-1 sm:flex", children: nav.map((item) => isNavGroup(item) ? (_jsx(NavMenu, { group: item }, item.label)) : (_jsx(Link, { href: item.href, "aria-current": item.active ? 'page' : undefined, className: navPill(item.active), children: item.label }, item.href))) }), _jsx("div", { className: "flex-1 sm:hidden" }), actions && _jsx("div", { className: "flex items-center gap-2", children: actions }), _jsxs(Dialog.Root, { open: open, onOpenChange: setOpen, children: [_jsxs(Dialog.Trigger, { "aria-label": "Account and settings", className: "relative shrink-0 rounded-full focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: [_jsx(ForgeAvatar, { account: account, className: "h-8 w-8" }), syncError && (_jsx("span", { role: "img", "aria-label": "Not synced", title: "Not synced", className: "absolute -right-0.5 -top-0.5 h-2.5 w-2.5 rounded-full border-2 border-background bg-destructive" }))] }), _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/60" }), _jsx(Dialog.Content, { className: "fixed inset-y-0 right-0 z-50 flex w-[88vw] max-w-sm flex-col border-l bg-background text-foreground shadow-lg outline-hidden", children: _jsx(AccountPanel, { current: current, account: account, syncError: syncError, nav: nav, accountDetails: accountDetails, guestDetails: guestDetails, accountActions: accountActions, panel: panel, onBeforeSignOut: onBeforeSignOut, currentUrl: currentUrl }) })] })] })] }) }));
|
|
42
|
+
return (_jsx("header", { className: "sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/80", children: _jsxs("div", { className: `mx-auto flex h-14 w-full items-center gap-2 px-4 sm:gap-4 ${containerClassName}`, children: [_jsx(AppSwitcher, { current: current, account: account, showAccount: false, currentUrl: currentUrl }), _jsx("nav", { className: "hidden flex-1 items-center gap-1 sm:flex", children: nav.map((item) => isNavGroup(item) ? (_jsx(NavMenu, { group: item }, item.label)) : (_jsx(Link, { href: item.href, "aria-current": item.active ? 'page' : undefined, className: navPill(item.active), children: item.label }, item.href))) }), _jsx("div", { className: "flex-1 sm:hidden" }), actions && _jsx("div", { className: "flex items-center gap-2", children: actions }), _jsxs(Dialog.Root, { open: open, onOpenChange: setOpen, children: [_jsxs(Dialog.Trigger, { "aria-label": "Account and settings", className: "relative shrink-0 rounded-full focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: [_jsx(ForgeAvatar, { account: account, className: "h-8 w-8" }), syncError && (_jsx("span", { role: "img", "aria-label": "Not synced", title: "Not synced", className: "absolute -right-0.5 -top-0.5 h-2.5 w-2.5 rounded-full border-2 border-background bg-destructive" }))] }), _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/60" }), _jsx(Dialog.Content, { className: "fixed inset-y-0 right-0 z-50 flex w-[88vw] max-w-sm flex-col border-l bg-background text-foreground shadow-lg outline-hidden", children: _jsx(AccountPanel, { current: current, account: account, syncError: syncError, nav: panelNav ? nav : [], accountDetails: accountDetails, guestDetails: guestDetails, accountActions: accountActions, panel: panel, onBeforeSignOut: onBeforeSignOut, currentUrl: currentUrl }) })] })] })] }) }));
|
|
26
43
|
}
|
|
27
|
-
const menuItem = 'flex cursor-pointer select-none items-
|
|
44
|
+
const menuItem = 'flex cursor-pointer select-none items-center gap-3 rounded-sm px-2 py-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground';
|
|
45
|
+
/** The page on screen: the hover fill plus a 2px accent bar down its left edge. */
|
|
46
|
+
const menuActive = 'bg-accent shadow-[inset_2px_0_0_var(--primary)]';
|
|
28
47
|
/** A nav group on desktop: a pill that opens its links, sections split by a seam. */
|
|
29
48
|
function NavMenu({ group }) {
|
|
30
49
|
const sections = group.sections.filter((s) => s.items.length > 0);
|
|
31
|
-
return (_jsxs(Menu.Root, { modal: false, children: [_jsxs(Menu.Trigger, { className: `group flex items-center gap-1 ${navPill(group.active)} focus-visible:outline-2 focus-visible:outline-ring`, children: [group.label, _jsx(ChevronDown, { "aria-hidden": true, className: "h-3.5 w-3.5 opacity-70 transition-transform duration-200 group-data-[state=open]:rotate-180" })] }), _jsx(Menu.Portal, { children: _jsx(Menu.Content, { align: "start", sideOffset: 6, className: "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] w-
|
|
50
|
+
return (_jsxs(Menu.Root, { modal: false, children: [_jsxs(Menu.Trigger, { className: `group flex items-center gap-1 ${navPill(group.active)} focus-visible:outline-2 focus-visible:outline-ring`, children: [group.label, _jsx(ChevronDown, { "aria-hidden": true, className: "h-3.5 w-3.5 opacity-70 transition-transform duration-200 group-data-[state=open]:rotate-180" })] }), _jsx(Menu.Portal, { children: _jsx(Menu.Content, { align: "start", sideOffset: 6, className: "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] w-72 overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md", children: sections.map((section, i) => (_jsxs(Menu.Group, { children: [i > 0 && _jsx(Menu.Separator, { className: "-mx-1 my-1 h-px bg-border" }), section.title && (_jsx(Menu.Label, { className: "px-2 pb-1 pt-1.5 text-xs font-medium text-muted-foreground", children: section.title })), _jsx("div", { className: section.rail ? 'ml-3.5 mr-2 border-l-2 pl-1' : undefined, children: section.items.map(({ href, label, icon, media, active, description }) => (_jsx(Menu.Item, { asChild: true, children: _jsxs(Link, { href: href, "aria-current": active ? 'page' : undefined, className: `${menuItem} ${active ? menuActive : ''}`, children: [_jsx(MenuLead, { icon: icon, media: media }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block font-semibold", children: label }), description && _jsx("span", { className: "block text-xs text-muted-foreground", children: description })] })] }) }, href))) })] }, section.key ?? i))) }) })] }));
|
|
32
51
|
}
|
|
33
52
|
function initials(name) {
|
|
34
53
|
return name.slice(0, 2).toUpperCase();
|
|
@@ -65,7 +84,7 @@ function PanelNavGroup({ group }) {
|
|
|
65
84
|
return (_jsxs("div", { role: "group", "aria-label": group.label, className: "pt-2", children: [_jsxs("p", { className: "flex items-center gap-2 px-2 pb-1 text-xs font-medium text-muted-foreground", children: [Icon && _jsx(Icon, { "aria-hidden": true, className: "h-3.5 w-3.5" }), group.label] }), group.sections.map((section, i) => (_jsxs("div", { className: "space-y-1", children: [section.title && section.items.length > 0 && (
|
|
66
85
|
// A text title sits over the links' labels; a rich one (an avatar and name) brings its own leading
|
|
67
86
|
// media, so it lines up with their icons instead.
|
|
68
|
-
_jsx("div", { className: `px-2 pb-0.5 pt-1.5 text-xs text-muted-foreground ${typeof section.title === 'string' ? 'pl-9' : ''}`, children: section.title })), section.items.map(({ href, label, icon, active }) => (_jsx(ForgePanelItem, { icon: icon, label: label, href: href, active: active }, href)))] }, section.key ?? i)))] }));
|
|
87
|
+
_jsx("div", { className: `px-2 pb-0.5 pt-1.5 text-xs text-muted-foreground ${typeof section.title === 'string' ? 'pl-9' : ''}`, children: section.title })), _jsx("div", { className: section.rail ? 'ml-4 mr-2 space-y-1 border-l-2 pl-1' : 'space-y-1', children: section.items.map(({ href, label, icon, media, active }) => (_jsx(ForgePanelItem, { icon: icon, media: media, label: label, href: href, active: active, className: section.rail ? `relative ${active ? railMark : ''}` : '' }, href))) })] }, section.key ?? i)))] }));
|
|
69
88
|
}
|
|
70
89
|
/** Discord's mark in `currentColor`; give it Discord's blurple (`text-[#5865F2]`) next to a Discord name. */
|
|
71
90
|
export function DiscordIcon({ className }) {
|
|
@@ -80,9 +99,9 @@ export function ForgePanelSection({ title, description, children, }) {
|
|
|
80
99
|
return (_jsxs("section", { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: title }), description && _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })] }), _jsx("div", { className: "space-y-1", children: children })] }));
|
|
81
100
|
}
|
|
82
101
|
/** A row in the account panel: a link or a button that closes the panel. */
|
|
83
|
-
export function ForgePanelItem({ icon
|
|
102
|
+
export function ForgePanelItem({ icon, media, label, description, href, external, onClick, disabled, active, tone = 'default', keepOpen, className: extraClassName = '', ...buttonProps }) {
|
|
84
103
|
const className = `flex w-full items-start gap-3 rounded-md px-2 py-2 text-left text-sm transition-colors hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 ${tone === 'destructive' ? 'text-destructive' : ''} ${active ? 'bg-accent font-medium' : ''} ${extraClassName}`;
|
|
85
|
-
const body = (_jsxs(_Fragment, { children: [
|
|
104
|
+
const body = (_jsxs(_Fragment, { children: [_jsx(LinkLead, { icon: icon, media: media }), _jsxs("span", { className: "flex-1", children: [_jsx("span", { className: "block font-medium", children: label }), description && _jsx("span", { className: "block text-xs text-muted-foreground", children: description })] }), external && href?.startsWith('http') && tone === 'default' && (_jsx(ExternalLink, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }))] }));
|
|
86
105
|
const element = href ? (external ? (_jsx("a", { href: href, className: className, "aria-current": active ? 'page' : undefined, children: body })) : (_jsx(Link, { href: href, className: className, "aria-current": active ? 'page' : undefined, children: body }))) : (_jsx("button", { type: "button", ...buttonProps, onClick: onClick, disabled: disabled, className: className, children: body }));
|
|
87
106
|
return keepOpen ? element : _jsx(Dialog.Close, { asChild: true, children: element });
|
|
88
107
|
}
|
package/dist/support-button.js
CHANGED
|
@@ -39,5 +39,5 @@ export function ForgeSupporterBadge({ tier }) {
|
|
|
39
39
|
}
|
|
40
40
|
/** Header heart button that opens the "show your support" dialog every xForge app shares. */
|
|
41
41
|
export function ForgeSupportButton({ children }) {
|
|
42
|
-
return (_jsxs(Dialog.Root, { children: [_jsx(Dialog.Trigger, { "aria-label": "Support the project", className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md transition-colors hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring", children: _jsx(Heart, { className: "h-4 w-4
|
|
42
|
+
return (_jsxs(Dialog.Root, { children: [_jsx(Dialog.Trigger, { "aria-label": "Support the project", title: "Support the project", className: "group inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-destructive focus-visible:outline-2 focus-visible:outline-ring data-[state=open]:text-destructive", children: _jsx(Heart, { "aria-hidden": true, className: "h-4 w-4 motion-safe:group-hover:animate-forge-heartbeat" }) }), _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/60" }), _jsxs(Dialog.Content, { className: "fixed left-1/2 top-1/2 z-50 max-h-[90svh] w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border bg-background p-6 text-foreground shadow-lg outline-hidden", children: [_jsx(Dialog.Close, { "aria-label": "Close", className: "absolute right-4 top-4 rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring", children: _jsx(X, { className: "h-4 w-4" }) }), _jsx(Dialog.Title, { className: "text-center text-2xl font-semibold", children: "Looking to show your support?" }), _jsxs("div", { className: "space-y-6 py-4 text-center text-muted-foreground", children: [_jsx(Dialog.Description, { children: "This is a free, fan-made project. If you found it useful and want to support development, you can do so below." }), _jsx("a", { href: BUY_ME_A_COFFEE_URL, target: "_blank", rel: "noopener noreferrer", className: "inline-block", children: _jsx("img", { src: BUY_ME_A_COFFEE_BUTTON, alt: "Buy Me A Coffee", width: 145, height: 40, className: "mx-auto h-10 w-auto" }) }), _jsxs("div", { className: "space-y-2 border-t pt-4", children: [_jsx("p", { className: "text-xs font-medium uppercase tracking-wide text-foreground", children: "Supporter Badges" }), _jsx("p", { className: "text-xs", children: "Donate and reach out \u2014 I'll assign your badge manually." }), _jsx("div", { className: "flex flex-col gap-2 pt-1", children: FORGE_SPONSOR_TIERS.map(({ Icon, label, threshold, className }) => (_jsxs("div", { className: `flex items-center justify-between rounded-lg border px-3 py-2 ${className}`, children: [_jsxs("span", { className: "flex items-center gap-1.5 text-sm font-medium", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }), label] }), _jsx("span", { className: "text-xs font-semibold opacity-80", children: threshold })] }, label))) })] }), children] })] })] })] }));
|
|
43
43
|
}
|