@windrun-huaiin/third-ui 10.1.0 → 10.1.2
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/clerk/clerk-user-client.js +1 -1
- package/dist/clerk/clerk-user-client.mjs +1 -1
- package/dist/fuma/base/custom-header.d.ts +16 -1
- package/dist/fuma/base/custom-header.js +99 -18
- package/dist/fuma/base/custom-header.mjs +105 -24
- package/dist/fuma/base/custom-home-layout.d.ts +11 -1
- package/dist/fuma/base/custom-home-layout.js +2 -2
- package/dist/fuma/base/custom-home-layout.mjs +2 -2
- package/dist/main/credit/credit-nav-button.js +1 -1
- package/dist/main/credit/credit-nav-button.mjs +1 -1
- package/package.json +3 -3
- package/src/clerk/clerk-user-client.tsx +1 -1
- package/src/fuma/base/custom-header.tsx +229 -82
- package/src/fuma/base/custom-home-layout.tsx +21 -1
- package/src/main/credit/credit-nav-button.tsx +1 -1
|
@@ -7,7 +7,7 @@ var server = require('@windrun-huaiin/base-ui/components/server');
|
|
|
7
7
|
var signupButtonWithFingerprintClient = require('./signup-button-with-fingerprint-client.js');
|
|
8
8
|
|
|
9
9
|
function ClerkUserClient({ data }) {
|
|
10
|
-
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 h-10
|
|
10
|
+
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 h-10 mr-3 sm:mr-2", children: [jsxRuntime.jsx(nextjs.ClerkLoading, { children: jsxRuntime.jsx("div", { className: "w-20 h-9 px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-sm" }) }), jsxRuntime.jsxs(nextjs.ClerkLoaded, { children: [jsxRuntime.jsxs(nextjs.SignedOut, { children: [jsxRuntime.jsx(nextjs.SignInButton, { mode: data.clerkAuthInModal ? 'modal' : 'redirect', children: jsxRuntime.jsx("button", { className: "w-16 sm:w-20 h-8 sm:h-9 px-1.5 sm:px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-xs sm:text-sm whitespace-nowrap", children: data.signIn }) }), data.showSignUp && (jsxRuntime.jsx(signupButtonWithFingerprintClient.SignUpButtonWithFingerprint, { mode: data.clerkAuthInModal ? 'modal' : 'redirect', signUp: data.signUp }))] }), jsxRuntime.jsx(nextjs.SignedIn, { children: jsxRuntime.jsx(nextjs.UserButton, { appearance: {
|
|
11
11
|
elements: {
|
|
12
12
|
userButtonAvatarBox: "w-8 h-8 border",
|
|
13
13
|
}
|
|
@@ -5,7 +5,7 @@ import { globalLucideIcons } from '@windrun-huaiin/base-ui/components/server';
|
|
|
5
5
|
import { SignUpButtonWithFingerprint } from './signup-button-with-fingerprint-client.mjs';
|
|
6
6
|
|
|
7
7
|
function ClerkUserClient({ data }) {
|
|
8
|
-
return (jsxs("div", { className: "flex items-center gap-2 h-10
|
|
8
|
+
return (jsxs("div", { className: "flex items-center gap-2 h-10 mr-3 sm:mr-2", children: [jsx(ClerkLoading, { children: jsx("div", { className: "w-20 h-9 px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-sm" }) }), jsxs(ClerkLoaded, { children: [jsxs(SignedOut, { children: [jsx(SignInButton, { mode: data.clerkAuthInModal ? 'modal' : 'redirect', children: jsx("button", { className: "w-16 sm:w-20 h-8 sm:h-9 px-1.5 sm:px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-xs sm:text-sm whitespace-nowrap", children: data.signIn }) }), data.showSignUp && (jsx(SignUpButtonWithFingerprint, { mode: data.clerkAuthInModal ? 'modal' : 'redirect', signUp: data.signUp }))] }), jsx(SignedIn, { children: jsx(UserButton, { appearance: {
|
|
9
9
|
elements: {
|
|
10
10
|
userButtonAvatarBox: "w-8 h-8 border",
|
|
11
11
|
}
|
|
@@ -34,5 +34,20 @@ export interface CustomHomeHeaderProps extends HomeLayoutProps {
|
|
|
34
34
|
* @defaultValue true
|
|
35
35
|
*/
|
|
36
36
|
floating?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Control order of action items on desktop.
|
|
39
|
+
*/
|
|
40
|
+
desktopActionsOrder?: DesktopAction[];
|
|
41
|
+
/**
|
|
42
|
+
* Control order of quick actions on the mobile bar.
|
|
43
|
+
*/
|
|
44
|
+
mobileBarActionsOrder?: MobileBarAction[];
|
|
45
|
+
/**
|
|
46
|
+
* Control order of utilities inside the mobile dropdown.
|
|
47
|
+
*/
|
|
48
|
+
mobileMenuActionsOrder?: MobileMenuAction[];
|
|
37
49
|
}
|
|
38
|
-
export
|
|
50
|
+
export type DesktopAction = 'search' | 'theme' | 'i18n' | 'secondary' | 'github';
|
|
51
|
+
export type MobileBarAction = 'pinned' | 'search' | 'menu';
|
|
52
|
+
export type MobileMenuAction = 'secondary' | 'github' | 'separator' | 'i18n' | 'theme';
|
|
53
|
+
export declare function CustomHomeHeader({ nav, i18n, links, githubUrl, themeSwitch, searchToggle, bannerHeight, headerHeight, maxContentWidth, navbarClassName, floating, desktopActionsOrder, mobileBarActionsOrder, mobileMenuActionsOrder, }: CustomHomeHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -18,30 +18,108 @@ var button = require('fumadocs-ui/components/ui/button');
|
|
|
18
18
|
var layout = require('fumadocs-ui/contexts/layout');
|
|
19
19
|
var i18n = require('fumadocs-ui/contexts/i18n');
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
const DEFAULT_DESKTOP_ACTIONS = [
|
|
22
|
+
'search',
|
|
23
|
+
'theme',
|
|
24
|
+
'i18n',
|
|
25
|
+
'secondary',
|
|
26
|
+
];
|
|
27
|
+
const DEFAULT_MOBILE_BAR_ACTIONS = [
|
|
28
|
+
'pinned',
|
|
29
|
+
'search',
|
|
30
|
+
'menu',
|
|
31
|
+
];
|
|
32
|
+
const DEFAULT_MOBILE_MENU_ACTIONS = [
|
|
33
|
+
'secondary',
|
|
34
|
+
'separator',
|
|
35
|
+
'i18n',
|
|
36
|
+
'theme',
|
|
37
|
+
];
|
|
38
|
+
function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitch = {}, searchToggle: searchToggle$1 = {}, bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, navbarClassName, floating = false, desktopActionsOrder = DEFAULT_DESKTOP_ACTIONS, mobileBarActionsOrder = DEFAULT_MOBILE_BAR_ACTIONS, mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS, }) {
|
|
22
39
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
23
40
|
const finalLinks = React.useMemo(() => shared.getLinks(links, githubUrl), [links, githubUrl]);
|
|
24
41
|
const navItems = finalLinks.filter((item) => { var _a; return ['nav', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
|
|
25
42
|
const menuItems = finalLinks.filter((item) => { var _a; return ['menu', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
|
|
26
43
|
const mobilePinnedItems = navItems.filter((item) => isSecondary(item) && isMobilePinned(item));
|
|
27
44
|
const filteredMenuItems = menuItems.filter((item) => !isMobilePinned(item));
|
|
28
|
-
|
|
45
|
+
const primaryMenuItems = filteredMenuItems.filter((item) => !isSecondary(item));
|
|
46
|
+
const secondaryMenuItems = filteredMenuItems.filter(isSecondary);
|
|
47
|
+
const desktopSecondaryItems = navItems.filter(isSecondary);
|
|
48
|
+
const desktopActionsIncludeGithub = desktopActionsOrder.includes('github');
|
|
49
|
+
const githubDesktopItem = desktopActionsIncludeGithub
|
|
50
|
+
? desktopSecondaryItems.find((item) => isGithubItem(item, githubUrl))
|
|
51
|
+
: undefined;
|
|
52
|
+
const desktopSecondaryDisplayItems = desktopActionsIncludeGithub && githubDesktopItem
|
|
53
|
+
? desktopSecondaryItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
54
|
+
: desktopSecondaryItems;
|
|
55
|
+
const desktopActionNodes = {
|
|
56
|
+
search: searchToggle$1.enabled !== false
|
|
57
|
+
? (_b = (_a = searchToggle$1.components) === null || _a === void 0 ? void 0 : _a.lg) !== null && _b !== void 0 ? _b : (jsxRuntime.jsx(searchToggle.LargeSearchToggle, { className: "w-full rounded-full ps-2.5 max-w-[240px]", hideIfDisabled: true }))
|
|
58
|
+
: null,
|
|
59
|
+
theme: themeSwitch.enabled !== false
|
|
60
|
+
? (_c = themeSwitch.component) !== null && _c !== void 0 ? _c : jsxRuntime.jsx(themeToggle.ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
|
|
61
|
+
: null,
|
|
62
|
+
i18n: i18n ? (jsxRuntime.jsx(CompactLanguageToggle, { children: jsxRuntime.jsx(server.globalLucideIcons.Languages, { className: "size-5" }) })) : null,
|
|
63
|
+
secondary: desktopSecondaryDisplayItems.length ? (jsxRuntime.jsx("ul", { className: "flex flex-row gap-2 items-center empty:hidden", children: desktopSecondaryDisplayItems.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: cn.cn(item.type === 'icon' && [
|
|
64
|
+
'-mx-1',
|
|
65
|
+
i === 0 && 'ms-0',
|
|
66
|
+
i === desktopSecondaryDisplayItems.length - 1 && 'me-0',
|
|
67
|
+
]) }, i))) })) : null,
|
|
68
|
+
github: githubDesktopItem ? (jsxRuntime.jsx(NavbarLinkItem, { item: githubDesktopItem, className: cn.cn(githubDesktopItem.type === 'icon' && '-mx-1') })) : null,
|
|
69
|
+
};
|
|
70
|
+
const mobileMenuActionsIncludeGithub = mobileMenuActionsOrder.includes('github');
|
|
71
|
+
const githubMobileMenuItem = mobileMenuActionsIncludeGithub
|
|
72
|
+
? secondaryMenuItems.find((item) => isGithubItem(item, githubUrl))
|
|
73
|
+
: undefined;
|
|
74
|
+
const secondaryMenuDisplayItems = mobileMenuActionsIncludeGithub && githubMobileMenuItem
|
|
75
|
+
? secondaryMenuItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
76
|
+
: secondaryMenuItems;
|
|
77
|
+
const mobileMenuActionNodes = {
|
|
78
|
+
secondary: secondaryMenuDisplayItems.length ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: secondaryMenuDisplayItems.map((item, i) => (jsxRuntime.jsx(MenuLinkItem, { item: item, className: "-me-1.5" }, i))) })) : null,
|
|
79
|
+
github: githubMobileMenuItem ? (jsxRuntime.jsx(MenuLinkItem, { item: githubMobileMenuItem, className: "-me-1.5" })) : null,
|
|
80
|
+
separator: jsxRuntime.jsx("div", { role: "separator", className: "flex-1" }),
|
|
81
|
+
i18n: i18n ? (jsxRuntime.jsxs(CompactLanguageToggle, { children: [jsxRuntime.jsx(server.globalLucideIcons.Languages, { className: "size-5" }), jsxRuntime.jsx(languageToggle.LanguageToggleText, {}), jsxRuntime.jsx(server.globalLucideIcons.ChevronDown, { className: "size-3 text-fd-muted-foreground" })] })) : null,
|
|
82
|
+
theme: themeSwitch.enabled !== false
|
|
83
|
+
? (_d = themeSwitch.component) !== null && _d !== void 0 ? _d : jsxRuntime.jsx(themeToggle.ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
|
|
84
|
+
: null,
|
|
85
|
+
};
|
|
86
|
+
const shouldRenderMobileUtilities = mobileMenuActionsOrder.some((action) => action !== 'separator' && Boolean(mobileMenuActionNodes[action]));
|
|
87
|
+
const renderMobileMenuAction = (action) => {
|
|
88
|
+
if (action === 'separator' && !shouldRenderMobileUtilities)
|
|
89
|
+
return null;
|
|
90
|
+
return mobileMenuActionNodes[action];
|
|
91
|
+
};
|
|
92
|
+
const menuNode = (jsxRuntime.jsxs(Menu, { children: [jsxRuntime.jsx(MenuTrigger, { "aria-label": "Toggle Menu", className: cn.cn(button.buttonVariants({
|
|
93
|
+
size: 'icon',
|
|
94
|
+
color: 'ghost',
|
|
95
|
+
className: 'group [&_svg]:size-5.5',
|
|
96
|
+
})), enableHover: nav.enableHoverToOpen, children: jsxRuntime.jsx(server.globalLucideIcons.ChevronDown, { className: "transition-transform duration-300 group-data-[state=open]:rotate-180" }) }), jsxRuntime.jsxs(MenuContent, { className: "sm:flex-row sm:items-center sm:justify-end", children: [primaryMenuItems.map((item, i) => (jsxRuntime.jsx(MenuLinkItem, { item: item, className: "sm:hidden" }, i))), shouldRenderMobileUtilities ? (jsxRuntime.jsx("div", { className: "-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2", children: mobileMenuActionsOrder.map((action) => {
|
|
97
|
+
const node = renderMobileMenuAction(action);
|
|
98
|
+
if (!node)
|
|
99
|
+
return null;
|
|
100
|
+
return (jsxRuntime.jsx(React.Fragment, { children: node }, `mobile-menu-${action}`));
|
|
101
|
+
}) })) : null] })] }));
|
|
102
|
+
const mobilePinnedNode = mobilePinnedItems.length > 0 ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: mobilePinnedItems.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: "max-sm:-mr-1" }, `mobile-pinned-${i}`))) })) : null;
|
|
103
|
+
const mobileSearchNode = searchToggle$1.enabled !== false
|
|
104
|
+
? (_f = (_e = searchToggle$1.components) === null || _e === void 0 ? void 0 : _e.sm) !== null && _f !== void 0 ? _f : (jsxRuntime.jsx(searchToggle.SearchToggle, { className: "p-2", hideIfDisabled: true }))
|
|
105
|
+
: null;
|
|
106
|
+
const mobileBarNodes = {
|
|
107
|
+
pinned: mobilePinnedNode,
|
|
108
|
+
search: mobileSearchNode,
|
|
109
|
+
menu: menuNode,
|
|
110
|
+
};
|
|
111
|
+
const getMobileBarNode = (action) => { var _a; return (_a = mobileBarNodes[action]) !== null && _a !== void 0 ? _a : null; };
|
|
112
|
+
return (jsxRuntime.jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsxRuntime.jsx(Link, { href: (_g = nav.url) !== null && _g !== void 0 ? _g : '/', className: "inline-flex items-center gap-2.5 font-semibold", children: nav.title }), nav.children, jsxRuntime.jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
|
|
29
113
|
.filter((item) => !isSecondary(item))
|
|
30
|
-
.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }), jsxRuntime.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
color: 'ghost',
|
|
40
|
-
className: 'group [&_svg]:size-5.5',
|
|
41
|
-
})), enableHover: nav.enableHoverToOpen, children: jsxRuntime.jsx(server.globalLucideIcons.ChevronDown, { className: "transition-transform duration-300 group-data-[state=open]:rotate-180" }) }), jsxRuntime.jsxs(MenuContent, { className: "sm:flex-row sm:items-center sm:justify-end", children: [filteredMenuItems
|
|
42
|
-
.filter((item) => !isSecondary(item))
|
|
43
|
-
.map((item, i) => (jsxRuntime.jsx(MenuLinkItem, { item: item, className: "sm:hidden" }, i))), jsxRuntime.jsxs("div", { className: "-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2", children: [filteredMenuItems.filter(isSecondary).map((item, i) => (jsxRuntime.jsx(MenuLinkItem, { item: item, className: "-me-1.5" }, i))), jsxRuntime.jsx("div", { role: "separator", className: "flex-1" }), i18n ? (jsxRuntime.jsxs(CompactLanguageToggle, { children: [jsxRuntime.jsx(server.globalLucideIcons.Languages, { className: "size-5" }), jsxRuntime.jsx(languageToggle.LanguageToggleText, {}), jsxRuntime.jsx(server.globalLucideIcons.ChevronDown, { className: "size-3 text-fd-muted-foreground" })] })) : null, themeSwitch.enabled !== false &&
|
|
44
|
-
((_g = themeSwitch.component) !== null && _g !== void 0 ? _g : (jsxRuntime.jsx(themeToggle.ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })))] })] })] })] })] }));
|
|
114
|
+
.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }), jsxRuntime.jsx("div", { className: "flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden", children: desktopActionsOrder.map((action) => {
|
|
115
|
+
const node = desktopActionNodes[action];
|
|
116
|
+
if (!node)
|
|
117
|
+
return null;
|
|
118
|
+
return (jsxRuntime.jsx(React.Fragment, { children: node }, `desktop-${action}`));
|
|
119
|
+
}) }), jsxRuntime.jsx("ul", { className: "flex flex-row items-center ms-auto -me-1.5 lg:hidden", children: mobileBarActionsOrder.map((action) => {
|
|
120
|
+
const node = getMobileBarNode(action);
|
|
121
|
+
return node ? (jsxRuntime.jsx(React.Fragment, { children: node }, `mobile-bar-${action}`)) : null;
|
|
122
|
+
}) })] }));
|
|
45
123
|
}
|
|
46
124
|
function CustomNavbar(_a) {
|
|
47
125
|
var { bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, className, style, floating = false } = _a, props = tslib_es6.__rest(_a, ["bannerHeight", "headerHeight", "maxContentWidth", "className", "style", "floating"]);
|
|
@@ -143,7 +221,7 @@ function CompactLanguageToggle(_a) {
|
|
|
143
221
|
throw new Error('Missing `<I18nProvider />`');
|
|
144
222
|
return (jsxRuntime.jsxs(popover.Popover, { children: [jsxRuntime.jsx(popover.PopoverTrigger, Object.assign({ "aria-label": context.text.chooseLanguage }, props, { className: cn.cn(button.buttonVariants({
|
|
145
223
|
color: 'ghost',
|
|
146
|
-
className: 'gap-1.5 py-1.5
|
|
224
|
+
className: 'gap-1.5 py-1.5 px-1',
|
|
147
225
|
}), props.className), children: props.children })), jsxRuntime.jsxs(popover.PopoverContent, { className: cn.cn('flex min-w-[150px] z-1001 flex-col overflow-x-hidden rounded-xl border bg-fd-popover/60 p-0 text-fd-popover-foreground backdrop-blur-lg', contentClassName), children: [jsxRuntime.jsx("p", { className: "mb-1 p-2 text-xs font-medium text-fd-muted-foreground", children: context.text.chooseLanguage }), context.locales.map((item) => (jsxRuntime.jsx("button", { type: "button", className: cn.cn('p-2 text-start text-sm', item.locale === context.locale
|
|
148
226
|
? 'bg-fd-primary/10 font-medium text-fd-primary'
|
|
149
227
|
: 'hover:bg-fd-accent hover:text-fd-accent-foreground'), onClick: () => {
|
|
@@ -151,6 +229,9 @@ function CompactLanguageToggle(_a) {
|
|
|
151
229
|
(_a = context.onChange) === null || _a === void 0 ? void 0 : _a.call(context, item.locale);
|
|
152
230
|
}, children: item.name }, item.locale)))] })] }));
|
|
153
231
|
}
|
|
232
|
+
function isGithubItem(item, githubUrl) {
|
|
233
|
+
return Boolean(githubUrl && item.type === 'icon' && item.url === githubUrl);
|
|
234
|
+
}
|
|
154
235
|
function isSecondary(item) {
|
|
155
236
|
if ('secondary' in item && item.secondary != null)
|
|
156
237
|
return item.secondary;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { __rest } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
|
|
3
|
-
import {
|
|
4
|
-
import { useMemo,
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { useMemo, Fragment as Fragment$1, useState } from 'react';
|
|
5
5
|
import { cva } from '../../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs';
|
|
6
6
|
import Link from 'fumadocs-core/link';
|
|
7
7
|
import { globalLucideIcons } from '@windrun-huaiin/base-ui/components/server';
|
|
@@ -10,36 +10,114 @@ import { cn } from 'fumadocs-ui/utils/cn';
|
|
|
10
10
|
import { LargeSearchToggle, SearchToggle } from 'fumadocs-ui/components/layout/search-toggle';
|
|
11
11
|
import { ThemeToggle } from 'fumadocs-ui/components/layout/theme-toggle';
|
|
12
12
|
import { LanguageToggleText } from 'fumadocs-ui/components/layout/language-toggle';
|
|
13
|
-
import { NavigationMenuItem,
|
|
13
|
+
import { NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger, NavigationMenuContent, NavigationMenu, NavigationMenuList, NavigationMenuViewport } from 'fumadocs-ui/components/ui/navigation-menu';
|
|
14
14
|
import { Popover, PopoverTrigger, PopoverContent } from 'fumadocs-ui/components/ui/popover';
|
|
15
15
|
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
|
|
16
16
|
import { useNav } from 'fumadocs-ui/contexts/layout';
|
|
17
17
|
import { useI18n } from 'fumadocs-ui/contexts/i18n';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const DEFAULT_DESKTOP_ACTIONS = [
|
|
20
|
+
'search',
|
|
21
|
+
'theme',
|
|
22
|
+
'i18n',
|
|
23
|
+
'secondary',
|
|
24
|
+
];
|
|
25
|
+
const DEFAULT_MOBILE_BAR_ACTIONS = [
|
|
26
|
+
'pinned',
|
|
27
|
+
'search',
|
|
28
|
+
'menu',
|
|
29
|
+
];
|
|
30
|
+
const DEFAULT_MOBILE_MENU_ACTIONS = [
|
|
31
|
+
'secondary',
|
|
32
|
+
'separator',
|
|
33
|
+
'i18n',
|
|
34
|
+
'theme',
|
|
35
|
+
];
|
|
36
|
+
function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitch = {}, searchToggle = {}, bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, navbarClassName, floating = false, desktopActionsOrder = DEFAULT_DESKTOP_ACTIONS, mobileBarActionsOrder = DEFAULT_MOBILE_BAR_ACTIONS, mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS, }) {
|
|
20
37
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
21
38
|
const finalLinks = useMemo(() => getLinks(links, githubUrl), [links, githubUrl]);
|
|
22
39
|
const navItems = finalLinks.filter((item) => { var _a; return ['nav', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
|
|
23
40
|
const menuItems = finalLinks.filter((item) => { var _a; return ['menu', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
|
|
24
41
|
const mobilePinnedItems = navItems.filter((item) => isSecondary(item) && isMobilePinned(item));
|
|
25
42
|
const filteredMenuItems = menuItems.filter((item) => !isMobilePinned(item));
|
|
26
|
-
|
|
43
|
+
const primaryMenuItems = filteredMenuItems.filter((item) => !isSecondary(item));
|
|
44
|
+
const secondaryMenuItems = filteredMenuItems.filter(isSecondary);
|
|
45
|
+
const desktopSecondaryItems = navItems.filter(isSecondary);
|
|
46
|
+
const desktopActionsIncludeGithub = desktopActionsOrder.includes('github');
|
|
47
|
+
const githubDesktopItem = desktopActionsIncludeGithub
|
|
48
|
+
? desktopSecondaryItems.find((item) => isGithubItem(item, githubUrl))
|
|
49
|
+
: undefined;
|
|
50
|
+
const desktopSecondaryDisplayItems = desktopActionsIncludeGithub && githubDesktopItem
|
|
51
|
+
? desktopSecondaryItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
52
|
+
: desktopSecondaryItems;
|
|
53
|
+
const desktopActionNodes = {
|
|
54
|
+
search: searchToggle.enabled !== false
|
|
55
|
+
? (_b = (_a = searchToggle.components) === null || _a === void 0 ? void 0 : _a.lg) !== null && _b !== void 0 ? _b : (jsx(LargeSearchToggle, { className: "w-full rounded-full ps-2.5 max-w-[240px]", hideIfDisabled: true }))
|
|
56
|
+
: null,
|
|
57
|
+
theme: themeSwitch.enabled !== false
|
|
58
|
+
? (_c = themeSwitch.component) !== null && _c !== void 0 ? _c : jsx(ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
|
|
59
|
+
: null,
|
|
60
|
+
i18n: i18n ? (jsx(CompactLanguageToggle, { children: jsx(globalLucideIcons.Languages, { className: "size-5" }) })) : null,
|
|
61
|
+
secondary: desktopSecondaryDisplayItems.length ? (jsx("ul", { className: "flex flex-row gap-2 items-center empty:hidden", children: desktopSecondaryDisplayItems.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: cn(item.type === 'icon' && [
|
|
62
|
+
'-mx-1',
|
|
63
|
+
i === 0 && 'ms-0',
|
|
64
|
+
i === desktopSecondaryDisplayItems.length - 1 && 'me-0',
|
|
65
|
+
]) }, i))) })) : null,
|
|
66
|
+
github: githubDesktopItem ? (jsx(NavbarLinkItem, { item: githubDesktopItem, className: cn(githubDesktopItem.type === 'icon' && '-mx-1') })) : null,
|
|
67
|
+
};
|
|
68
|
+
const mobileMenuActionsIncludeGithub = mobileMenuActionsOrder.includes('github');
|
|
69
|
+
const githubMobileMenuItem = mobileMenuActionsIncludeGithub
|
|
70
|
+
? secondaryMenuItems.find((item) => isGithubItem(item, githubUrl))
|
|
71
|
+
: undefined;
|
|
72
|
+
const secondaryMenuDisplayItems = mobileMenuActionsIncludeGithub && githubMobileMenuItem
|
|
73
|
+
? secondaryMenuItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
74
|
+
: secondaryMenuItems;
|
|
75
|
+
const mobileMenuActionNodes = {
|
|
76
|
+
secondary: secondaryMenuDisplayItems.length ? (jsx(Fragment, { children: secondaryMenuDisplayItems.map((item, i) => (jsx(MenuLinkItem, { item: item, className: "-me-1.5" }, i))) })) : null,
|
|
77
|
+
github: githubMobileMenuItem ? (jsx(MenuLinkItem, { item: githubMobileMenuItem, className: "-me-1.5" })) : null,
|
|
78
|
+
separator: jsx("div", { role: "separator", className: "flex-1" }),
|
|
79
|
+
i18n: i18n ? (jsxs(CompactLanguageToggle, { children: [jsx(globalLucideIcons.Languages, { className: "size-5" }), jsx(LanguageToggleText, {}), jsx(globalLucideIcons.ChevronDown, { className: "size-3 text-fd-muted-foreground" })] })) : null,
|
|
80
|
+
theme: themeSwitch.enabled !== false
|
|
81
|
+
? (_d = themeSwitch.component) !== null && _d !== void 0 ? _d : jsx(ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
|
|
82
|
+
: null,
|
|
83
|
+
};
|
|
84
|
+
const shouldRenderMobileUtilities = mobileMenuActionsOrder.some((action) => action !== 'separator' && Boolean(mobileMenuActionNodes[action]));
|
|
85
|
+
const renderMobileMenuAction = (action) => {
|
|
86
|
+
if (action === 'separator' && !shouldRenderMobileUtilities)
|
|
87
|
+
return null;
|
|
88
|
+
return mobileMenuActionNodes[action];
|
|
89
|
+
};
|
|
90
|
+
const menuNode = (jsxs(Menu, { children: [jsx(MenuTrigger, { "aria-label": "Toggle Menu", className: cn(buttonVariants({
|
|
91
|
+
size: 'icon',
|
|
92
|
+
color: 'ghost',
|
|
93
|
+
className: 'group [&_svg]:size-5.5',
|
|
94
|
+
})), enableHover: nav.enableHoverToOpen, children: jsx(globalLucideIcons.ChevronDown, { className: "transition-transform duration-300 group-data-[state=open]:rotate-180" }) }), jsxs(MenuContent, { className: "sm:flex-row sm:items-center sm:justify-end", children: [primaryMenuItems.map((item, i) => (jsx(MenuLinkItem, { item: item, className: "sm:hidden" }, i))), shouldRenderMobileUtilities ? (jsx("div", { className: "-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2", children: mobileMenuActionsOrder.map((action) => {
|
|
95
|
+
const node = renderMobileMenuAction(action);
|
|
96
|
+
if (!node)
|
|
97
|
+
return null;
|
|
98
|
+
return (jsx(Fragment$1, { children: node }, `mobile-menu-${action}`));
|
|
99
|
+
}) })) : null] })] }));
|
|
100
|
+
const mobilePinnedNode = mobilePinnedItems.length > 0 ? (jsx(Fragment, { children: mobilePinnedItems.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: "max-sm:-mr-1" }, `mobile-pinned-${i}`))) })) : null;
|
|
101
|
+
const mobileSearchNode = searchToggle.enabled !== false
|
|
102
|
+
? (_f = (_e = searchToggle.components) === null || _e === void 0 ? void 0 : _e.sm) !== null && _f !== void 0 ? _f : (jsx(SearchToggle, { className: "p-2", hideIfDisabled: true }))
|
|
103
|
+
: null;
|
|
104
|
+
const mobileBarNodes = {
|
|
105
|
+
pinned: mobilePinnedNode,
|
|
106
|
+
search: mobileSearchNode,
|
|
107
|
+
menu: menuNode,
|
|
108
|
+
};
|
|
109
|
+
const getMobileBarNode = (action) => { var _a; return (_a = mobileBarNodes[action]) !== null && _a !== void 0 ? _a : null; };
|
|
110
|
+
return (jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsx(Link, { href: (_g = nav.url) !== null && _g !== void 0 ? _g : '/', className: "inline-flex items-center gap-2.5 font-semibold", children: nav.title }), nav.children, jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
|
|
27
111
|
.filter((item) => !isSecondary(item))
|
|
28
|
-
.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }),
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
color: 'ghost',
|
|
38
|
-
className: 'group [&_svg]:size-5.5',
|
|
39
|
-
})), enableHover: nav.enableHoverToOpen, children: jsx(globalLucideIcons.ChevronDown, { className: "transition-transform duration-300 group-data-[state=open]:rotate-180" }) }), jsxs(MenuContent, { className: "sm:flex-row sm:items-center sm:justify-end", children: [filteredMenuItems
|
|
40
|
-
.filter((item) => !isSecondary(item))
|
|
41
|
-
.map((item, i) => (jsx(MenuLinkItem, { item: item, className: "sm:hidden" }, i))), jsxs("div", { className: "-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2", children: [filteredMenuItems.filter(isSecondary).map((item, i) => (jsx(MenuLinkItem, { item: item, className: "-me-1.5" }, i))), jsx("div", { role: "separator", className: "flex-1" }), i18n ? (jsxs(CompactLanguageToggle, { children: [jsx(globalLucideIcons.Languages, { className: "size-5" }), jsx(LanguageToggleText, {}), jsx(globalLucideIcons.ChevronDown, { className: "size-3 text-fd-muted-foreground" })] })) : null, themeSwitch.enabled !== false &&
|
|
42
|
-
((_g = themeSwitch.component) !== null && _g !== void 0 ? _g : (jsx(ThemeToggle, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })))] })] })] })] })] }));
|
|
112
|
+
.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }), jsx("div", { className: "flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden", children: desktopActionsOrder.map((action) => {
|
|
113
|
+
const node = desktopActionNodes[action];
|
|
114
|
+
if (!node)
|
|
115
|
+
return null;
|
|
116
|
+
return (jsx(Fragment$1, { children: node }, `desktop-${action}`));
|
|
117
|
+
}) }), jsx("ul", { className: "flex flex-row items-center ms-auto -me-1.5 lg:hidden", children: mobileBarActionsOrder.map((action) => {
|
|
118
|
+
const node = getMobileBarNode(action);
|
|
119
|
+
return node ? (jsx(Fragment$1, { children: node }, `mobile-bar-${action}`)) : null;
|
|
120
|
+
}) })] }));
|
|
43
121
|
}
|
|
44
122
|
function CustomNavbar(_a) {
|
|
45
123
|
var { bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, className, style, floating = false } = _a, props = __rest(_a, ["bannerHeight", "headerHeight", "maxContentWidth", "className", "style", "floating"]);
|
|
@@ -96,10 +174,10 @@ function NavbarLinkItem(_a) {
|
|
|
96
174
|
const children = item.items.map((child, j) => {
|
|
97
175
|
var _a, _b;
|
|
98
176
|
if (child.type === 'custom') {
|
|
99
|
-
return jsx(Fragment, { children: child.children }, j);
|
|
177
|
+
return jsx(Fragment$1, { children: child.children }, j);
|
|
100
178
|
}
|
|
101
179
|
const _c = (_a = child.menu) !== null && _a !== void 0 ? _a : {}, { banner = child.icon ? (jsx("div", { className: "w-fit rounded-md border bg-fd-muted p-1 [&_svg]:size-4", children: child.icon })) : null } = _c, rest = __rest(_c, ["banner"]);
|
|
102
|
-
return (jsx(NavigationMenuLink, { asChild: true, children: jsx(Link, Object.assign({ href: child.url, external: child.external }, rest, { className: cn('flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground', rest.className), children: (_b = rest.children) !== null && _b !== void 0 ? _b : (jsxs(Fragment
|
|
180
|
+
return (jsx(NavigationMenuLink, { asChild: true, children: jsx(Link, Object.assign({ href: child.url, external: child.external }, rest, { className: cn('flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground', rest.className), children: (_b = rest.children) !== null && _b !== void 0 ? _b : (jsxs(Fragment, { children: [banner, jsx("p", { className: "text-[15px] font-medium", children: child.text }), jsx("p", { className: "text-sm text-fd-muted-foreground empty:hidden", children: child.description })] })) })) }, `${j}-${child.url}`));
|
|
103
181
|
});
|
|
104
182
|
return (jsxs(NavigationMenuItem, { children: [jsx(NavigationMenuTrigger, Object.assign({}, props, { className: cn(navItemVariants(), 'rounded-md', props.className), children: item.url ? (jsx(Link, { href: item.url, external: item.external, children: item.text })) : (item.text) })), jsx(NavigationMenuContent, { className: "grid grid-cols-1 gap-2 p-4 md:grid-cols-2 lg:grid-cols-3", children: children })] }));
|
|
105
183
|
}
|
|
@@ -112,7 +190,7 @@ function MenuLinkItem(_a) {
|
|
|
112
190
|
if (item.type === 'custom')
|
|
113
191
|
return jsx("div", { className: cn('grid', props.className), children: item.children });
|
|
114
192
|
if (item.type === 'menu') {
|
|
115
|
-
const header = (jsxs(Fragment
|
|
193
|
+
const header = (jsxs(Fragment, { children: [item.icon, item.text] }));
|
|
116
194
|
return (jsxs("div", { className: cn('mb-4 flex flex-col', props.className), children: [jsx("p", { className: "mb-1 text-sm text-fd-muted-foreground", children: item.url ? (jsx(NavigationMenuLink, { asChild: true, children: jsx(Link, { href: item.url, external: item.external, children: header }) })) : (header) }), item.items.map((child, i) => (jsx(MenuLinkItem, { item: child }, i)))] }));
|
|
117
195
|
}
|
|
118
196
|
return (jsx(NavigationMenuLink, { asChild: true, children: jsxs(BaseLinkItem, { item: item, className: cn({
|
|
@@ -141,7 +219,7 @@ function CompactLanguageToggle(_a) {
|
|
|
141
219
|
throw new Error('Missing `<I18nProvider />`');
|
|
142
220
|
return (jsxs(Popover, { children: [jsx(PopoverTrigger, Object.assign({ "aria-label": context.text.chooseLanguage }, props, { className: cn(buttonVariants({
|
|
143
221
|
color: 'ghost',
|
|
144
|
-
className: 'gap-1.5 py-1.5
|
|
222
|
+
className: 'gap-1.5 py-1.5 px-1',
|
|
145
223
|
}), props.className), children: props.children })), jsxs(PopoverContent, { className: cn('flex min-w-[150px] z-1001 flex-col overflow-x-hidden rounded-xl border bg-fd-popover/60 p-0 text-fd-popover-foreground backdrop-blur-lg', contentClassName), children: [jsx("p", { className: "mb-1 p-2 text-xs font-medium text-fd-muted-foreground", children: context.text.chooseLanguage }), context.locales.map((item) => (jsx("button", { type: "button", className: cn('p-2 text-start text-sm', item.locale === context.locale
|
|
146
224
|
? 'bg-fd-primary/10 font-medium text-fd-primary'
|
|
147
225
|
: 'hover:bg-fd-accent hover:text-fd-accent-foreground'), onClick: () => {
|
|
@@ -149,6 +227,9 @@ function CompactLanguageToggle(_a) {
|
|
|
149
227
|
(_a = context.onChange) === null || _a === void 0 ? void 0 : _a.call(context, item.locale);
|
|
150
228
|
}, children: item.name }, item.locale)))] })] }));
|
|
151
229
|
}
|
|
230
|
+
function isGithubItem(item, githubUrl) {
|
|
231
|
+
return Boolean(githubUrl && item.type === 'icon' && item.url === githubUrl);
|
|
232
|
+
}
|
|
152
233
|
function isSecondary(item) {
|
|
153
234
|
if ('secondary' in item && item.secondary != null)
|
|
154
235
|
return item.secondary;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import { type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
|
|
3
|
+
import { type DesktopAction, type MobileBarAction, type MobileMenuAction } from './custom-header';
|
|
3
4
|
export interface CustomHomeLayoutProps {
|
|
4
5
|
locale: string;
|
|
5
6
|
options: HomeLayoutProps;
|
|
@@ -63,6 +64,15 @@ export interface CustomHomeLayoutProps {
|
|
|
63
64
|
* Additional styles merged on top of the computed layout style.
|
|
64
65
|
*/
|
|
65
66
|
style?: CSSProperties;
|
|
67
|
+
/**
|
|
68
|
+
* Customize the order of header action items.
|
|
69
|
+
*/
|
|
70
|
+
actionOrders?: HeaderActionOrders;
|
|
66
71
|
children?: ReactNode;
|
|
67
72
|
}
|
|
68
|
-
export
|
|
73
|
+
export interface HeaderActionOrders {
|
|
74
|
+
desktop?: DesktopAction[];
|
|
75
|
+
mobileBar?: MobileBarAction[];
|
|
76
|
+
mobileMenu?: MobileMenuAction[];
|
|
77
|
+
}
|
|
78
|
+
export declare function CustomHomeLayout({ locale, options, children, showBanner, bannerHeight, headerHeight, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter, showGoToTop, style, floatingNav, actionOrders, }: CustomHomeLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -8,7 +8,7 @@ var footer = require('../../main/footer.js');
|
|
|
8
8
|
var goToTop = require('../../main/go-to-top.js');
|
|
9
9
|
var customHeader = require('./custom-header.js');
|
|
10
10
|
|
|
11
|
-
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, }) {
|
|
11
|
+
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, }) {
|
|
12
12
|
const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
|
|
13
13
|
const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
|
|
14
14
|
const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-nav-height': `${headerHeight}rem`, paddingTop: floatingNav
|
|
@@ -16,7 +16,7 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
|
|
|
16
16
|
: `${resolvedPaddingTop}rem` }, style);
|
|
17
17
|
const { nav } = options, homeLayoutProps = tslib_es6.__rest(options, ["nav"]);
|
|
18
18
|
const navOptions = nav !== null && nav !== void 0 ? nav : {};
|
|
19
|
-
const header = (jsxRuntime.jsx(customHeader.CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav })));
|
|
19
|
+
const header = (jsxRuntime.jsx(customHeader.CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
|
|
20
20
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsxRuntime.jsx(fumaBannerSuit.FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxRuntime.jsxs(home.HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer$1 !== null && footer$1 !== void 0 ? footer$1 : jsxRuntime.jsx(footer.Footer, { locale: locale }) : null, showGoToTop ? goToTop$1 !== null && goToTop$1 !== void 0 ? goToTop$1 : jsxRuntime.jsx(goToTop.GoToTop, {}) : null] }))] }));
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -6,7 +6,7 @@ import { Footer } from '../../main/footer.mjs';
|
|
|
6
6
|
import { GoToTop } from '../../main/go-to-top.mjs';
|
|
7
7
|
import { CustomHomeHeader } from './custom-header.mjs';
|
|
8
8
|
|
|
9
|
-
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, }) {
|
|
9
|
+
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, }) {
|
|
10
10
|
const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
|
|
11
11
|
const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
|
|
12
12
|
const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-nav-height': `${headerHeight}rem`, paddingTop: floatingNav
|
|
@@ -14,7 +14,7 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
|
|
|
14
14
|
: `${resolvedPaddingTop}rem` }, style);
|
|
15
15
|
const { nav } = options, homeLayoutProps = __rest(options, ["nav"]);
|
|
16
16
|
const navOptions = nav !== null && nav !== void 0 ? nav : {};
|
|
17
|
-
const header = (jsx(CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav })));
|
|
17
|
+
const header = (jsx(CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
|
|
18
18
|
return (jsxs(Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsx(FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxs(HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer !== null && footer !== void 0 ? footer : jsx(Footer, { locale: locale }) : null, showGoToTop ? goToTop !== null && goToTop !== void 0 ? goToTop : jsx(GoToTop, {}) : null] }))] }));
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -106,7 +106,7 @@ function CreditNavButton({ locale, totalBalance, totalLabel, children, }) {
|
|
|
106
106
|
closePricingModal,
|
|
107
107
|
}), [closeMenu, isMobile, openPricingModal, closePricingModal]);
|
|
108
108
|
const isOnetimeModal = pricingModal.mode === 'onetime';
|
|
109
|
-
return (jsxRuntime.jsxs(CreditNavPopoverContext.Provider, { value: contextValue, children: [jsxRuntime.jsxs(ui.DropdownMenu, { modal: false, open: open, onOpenChange: setOpen, children: [jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: jsxRuntime.jsxs("button", { type: "button", "aria-label": `${formattedBalance} ${totalLabel}`, className: utils.cn('group
|
|
109
|
+
return (jsxRuntime.jsxs(CreditNavPopoverContext.Provider, { value: contextValue, children: [jsxRuntime.jsxs(ui.DropdownMenu, { modal: false, open: open, onOpenChange: setOpen, children: [jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: jsxRuntime.jsxs("button", { type: "button", "aria-label": `${formattedBalance} ${totalLabel}`, className: utils.cn('group mx-2 sm:mx-1 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white pl-2 pr-4 py-1.5 text-sm font-semibold text-slate-700 shadow-sm transition-all dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100', 'hover:border-transparent hover:bg-linear-to-r hover:from-purple-500/90 hover:to-pink-500/90 hover:text-white hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-purple-500 dark:hover:from-purple-500 dark:hover:to-pink-500'), ref: triggerRef, children: [jsxRuntime.jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-br from-purple-400/20 to-pink-500/20 text-purple-600 transition-transform group-hover:scale-105 dark:text-purple-200", children: jsxRuntime.jsx(server.globalLucideIcons.Gem, { className: "h-3.5 w-3.5" }) }), jsxRuntime.jsxs("span", { className: "relative flex items-center", children: [jsxRuntime.jsx("span", { className: "text-base font-semibold leading-none", children: formattedBalance }), jsxRuntime.jsx("span", { className: "sr-only", children: ` ${totalLabel}` })] })] }) }), jsxRuntime.jsx(ui.DropdownMenuContent, { forceMount: true, sideOffset: 12, align: "end", className: "z-50 border-0 bg-transparent p-0 shadow-none mx-4 sm:mx-2 md:mx-1", children: jsxRuntime.jsx("div", { className: "w-[90vw] max-w-[90vw] max-h-[80vh] overflow-y-auto overflow-x-hidden rounded-3xl bg-transparent sm:w-[410px] sm:max-h-[90vh] sm:max-w-[95vw]", ref: contentRef, children: children }) })] }), pricingModal.modalMoneyPriceData && pricingModal.pricingContext ? (jsxRuntime.jsx(ui.AlertDialog, { open: pricingModal.open, onOpenChange: (open) => setPricingModal((prev) => (Object.assign(Object.assign({}, prev), { open }))), children: jsxRuntime.jsxs(ui.AlertDialogContent, { className: "mt-5 sm:mt-6 md:mt-10 lg:mt-15 w-[95vw] max-w-[1200px] overflow-hidden border border-slate-200 bg-white p-0 shadow-[0_32px_90px_rgba(15,23,42,0.25)] ring-1 ring-black/5 dark:border-white/12 dark:bg-[#0f1222] dark:shadow-[0_40px_120px_rgba(0,0,0,0.6)] dark:ring-white/10", children: [jsxRuntime.jsxs(ui.AlertDialogHeader, { className: "flex flex-row items-center justify-between border-b border-slate-200 px-6 pt-4 pb-1 dark:border-slate-800", children: [jsxRuntime.jsx(ui.AlertDialogTitle, { asChild: true, children: jsxRuntime.jsxs("div", { className: "flex flex-wrap items-baseline gap-3 text-slate-900 dark:text-white", children: [jsxRuntime.jsx("span", { className: "text-2xl font-semibold leading-tight", children: pricingModal.modalMoneyPriceData.title }), pricingModal.modalMoneyPriceData.subtitle ? (jsxRuntime.jsx("span", { className: "text-sm font-medium text-slate-500 dark:text-slate-300", children: pricingModal.modalMoneyPriceData.subtitle })) : null] }) }), jsxRuntime.jsx("button", { type: "button", className: "rounded-full p-2 text-gray-400 transition hover:bg-gray-400 hover:text-gray-400 dark:text-white/80 dark:hover:bg-white/80 dark:hover:text-white/80", onClick: closePricingModal, children: jsxRuntime.jsx(server.globalLucideIcons.X, { className: "h-6 w-6" }) })] }), jsxRuntime.jsx("div", { className: "max-h-[60vh] sm:max-h-[80vh] overflow-y-auto px-4 pt-2 pb-6", children: jsxRuntime.jsx("div", { className: "mx-auto w-full", children: jsxRuntime.jsx(moneyPriceInteractive.MoneyPriceInteractive, { data: pricingModal.modalMoneyPriceData, config: pricingModal.pricingContext.moneyPriceConfig, checkoutApiEndpoint: pricingModal.pricingContext.checkoutApiEndpoint, customerPortalApiEndpoint: pricingModal.pricingContext.customerPortalApiEndpoint, enableSubscriptionUpgrade: pricingModal.pricingContext.enableSubscriptionUpgrade, initialBillingType: isOnetimeModal ? 'onetime' : undefined, disableAutoDetectBilling: isOnetimeModal, initUserContext: pricingModal.pricingContext.initUserContext }, pricingModal.mode) }) })] }) })) : null] }));
|
|
110
110
|
}
|
|
111
111
|
const CreditNavPopoverContext = React.createContext(null);
|
|
112
112
|
function useCreditNavPopover() {
|
|
@@ -104,7 +104,7 @@ function CreditNavButton({ locale, totalBalance, totalLabel, children, }) {
|
|
|
104
104
|
closePricingModal,
|
|
105
105
|
}), [closeMenu, isMobile, openPricingModal, closePricingModal]);
|
|
106
106
|
const isOnetimeModal = pricingModal.mode === 'onetime';
|
|
107
|
-
return (jsxs(CreditNavPopoverContext.Provider, { value: contextValue, children: [jsxs(DropdownMenu, { modal: false, open: open, onOpenChange: setOpen, children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs("button", { type: "button", "aria-label": `${formattedBalance} ${totalLabel}`, className: cn('group
|
|
107
|
+
return (jsxs(CreditNavPopoverContext.Provider, { value: contextValue, children: [jsxs(DropdownMenu, { modal: false, open: open, onOpenChange: setOpen, children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs("button", { type: "button", "aria-label": `${formattedBalance} ${totalLabel}`, className: cn('group mx-2 sm:mx-1 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white pl-2 pr-4 py-1.5 text-sm font-semibold text-slate-700 shadow-sm transition-all dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100', 'hover:border-transparent hover:bg-linear-to-r hover:from-purple-500/90 hover:to-pink-500/90 hover:text-white hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-purple-500 dark:hover:from-purple-500 dark:hover:to-pink-500'), ref: triggerRef, children: [jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-br from-purple-400/20 to-pink-500/20 text-purple-600 transition-transform group-hover:scale-105 dark:text-purple-200", children: jsx(globalLucideIcons.Gem, { className: "h-3.5 w-3.5" }) }), jsxs("span", { className: "relative flex items-center", children: [jsx("span", { className: "text-base font-semibold leading-none", children: formattedBalance }), jsx("span", { className: "sr-only", children: ` ${totalLabel}` })] })] }) }), jsx(DropdownMenuContent, { forceMount: true, sideOffset: 12, align: "end", className: "z-50 border-0 bg-transparent p-0 shadow-none mx-4 sm:mx-2 md:mx-1", children: jsx("div", { className: "w-[90vw] max-w-[90vw] max-h-[80vh] overflow-y-auto overflow-x-hidden rounded-3xl bg-transparent sm:w-[410px] sm:max-h-[90vh] sm:max-w-[95vw]", ref: contentRef, children: children }) })] }), pricingModal.modalMoneyPriceData && pricingModal.pricingContext ? (jsx(AlertDialog, { open: pricingModal.open, onOpenChange: (open) => setPricingModal((prev) => (Object.assign(Object.assign({}, prev), { open }))), children: jsxs(AlertDialogContent, { className: "mt-5 sm:mt-6 md:mt-10 lg:mt-15 w-[95vw] max-w-[1200px] overflow-hidden border border-slate-200 bg-white p-0 shadow-[0_32px_90px_rgba(15,23,42,0.25)] ring-1 ring-black/5 dark:border-white/12 dark:bg-[#0f1222] dark:shadow-[0_40px_120px_rgba(0,0,0,0.6)] dark:ring-white/10", children: [jsxs(AlertDialogHeader, { className: "flex flex-row items-center justify-between border-b border-slate-200 px-6 pt-4 pb-1 dark:border-slate-800", children: [jsx(AlertDialogTitle, { asChild: true, children: jsxs("div", { className: "flex flex-wrap items-baseline gap-3 text-slate-900 dark:text-white", children: [jsx("span", { className: "text-2xl font-semibold leading-tight", children: pricingModal.modalMoneyPriceData.title }), pricingModal.modalMoneyPriceData.subtitle ? (jsx("span", { className: "text-sm font-medium text-slate-500 dark:text-slate-300", children: pricingModal.modalMoneyPriceData.subtitle })) : null] }) }), jsx("button", { type: "button", className: "rounded-full p-2 text-gray-400 transition hover:bg-gray-400 hover:text-gray-400 dark:text-white/80 dark:hover:bg-white/80 dark:hover:text-white/80", onClick: closePricingModal, children: jsx(globalLucideIcons.X, { className: "h-6 w-6" }) })] }), jsx("div", { className: "max-h-[60vh] sm:max-h-[80vh] overflow-y-auto px-4 pt-2 pb-6", children: jsx("div", { className: "mx-auto w-full", children: jsx(MoneyPriceInteractive, { data: pricingModal.modalMoneyPriceData, config: pricingModal.pricingContext.moneyPriceConfig, checkoutApiEndpoint: pricingModal.pricingContext.checkoutApiEndpoint, customerPortalApiEndpoint: pricingModal.pricingContext.customerPortalApiEndpoint, enableSubscriptionUpgrade: pricingModal.pricingContext.enableSubscriptionUpgrade, initialBillingType: isOnetimeModal ? 'onetime' : undefined, disableAutoDetectBilling: isOnetimeModal, initUserContext: pricingModal.pricingContext.initUserContext }, pricingModal.mode) }) })] }) })) : null] }));
|
|
108
108
|
}
|
|
109
109
|
const CreditNavPopoverContext = createContext(null);
|
|
110
110
|
function useCreditNavPopover() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/third-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.2",
|
|
4
4
|
"description": "Third-party integrated UI components for windrun-huaiin projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"react-medium-image-zoom": "^5.2.14",
|
|
82
82
|
"swiper": "^12.0.3",
|
|
83
83
|
"zod": "^4.1.12",
|
|
84
|
-
"@windrun-huaiin/
|
|
85
|
-
"@windrun-huaiin/
|
|
84
|
+
"@windrun-huaiin/base-ui": "^10.1.0",
|
|
85
|
+
"@windrun-huaiin/lib": "^10.1.0"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
88
|
"clsx": "^2.1.1",
|
|
@@ -16,7 +16,7 @@ interface ClerkUserData {
|
|
|
16
16
|
|
|
17
17
|
export function ClerkUserClient({ data }: { data: ClerkUserData }) {
|
|
18
18
|
return (
|
|
19
|
-
<div className="flex items-center gap-2 h-10
|
|
19
|
+
<div className="flex items-center gap-2 h-10 mr-3 sm:mr-2">
|
|
20
20
|
<ClerkLoading>
|
|
21
21
|
<div className="w-20 h-9 px-2 border border-gray-300 rounded-full hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-800 text-center text-sm"></div>
|
|
22
22
|
</ClerkLoading>
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type ComponentProps,
|
|
5
5
|
type CSSProperties,
|
|
6
6
|
Fragment,
|
|
7
|
+
type ReactNode,
|
|
7
8
|
useMemo,
|
|
8
9
|
useState,
|
|
9
10
|
} from 'react';
|
|
@@ -79,8 +80,52 @@ export interface CustomHomeHeaderProps extends HomeLayoutProps {
|
|
|
79
80
|
* @defaultValue true
|
|
80
81
|
*/
|
|
81
82
|
floating?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Control order of action items on desktop.
|
|
85
|
+
*/
|
|
86
|
+
desktopActionsOrder?: DesktopAction[];
|
|
87
|
+
/**
|
|
88
|
+
* Control order of quick actions on the mobile bar.
|
|
89
|
+
*/
|
|
90
|
+
mobileBarActionsOrder?: MobileBarAction[];
|
|
91
|
+
/**
|
|
92
|
+
* Control order of utilities inside the mobile dropdown.
|
|
93
|
+
*/
|
|
94
|
+
mobileMenuActionsOrder?: MobileMenuAction[];
|
|
82
95
|
}
|
|
83
96
|
|
|
97
|
+
export type DesktopAction =
|
|
98
|
+
| 'search'
|
|
99
|
+
| 'theme'
|
|
100
|
+
| 'i18n'
|
|
101
|
+
| 'secondary'
|
|
102
|
+
| 'github';
|
|
103
|
+
export type MobileBarAction = 'pinned' | 'search' | 'menu';
|
|
104
|
+
export type MobileMenuAction =
|
|
105
|
+
| 'secondary'
|
|
106
|
+
| 'github'
|
|
107
|
+
| 'separator'
|
|
108
|
+
| 'i18n'
|
|
109
|
+
| 'theme';
|
|
110
|
+
|
|
111
|
+
const DEFAULT_DESKTOP_ACTIONS: DesktopAction[] = [
|
|
112
|
+
'search',
|
|
113
|
+
'theme',
|
|
114
|
+
'i18n',
|
|
115
|
+
'secondary',
|
|
116
|
+
];
|
|
117
|
+
const DEFAULT_MOBILE_BAR_ACTIONS: MobileBarAction[] = [
|
|
118
|
+
'pinned',
|
|
119
|
+
'search',
|
|
120
|
+
'menu',
|
|
121
|
+
];
|
|
122
|
+
const DEFAULT_MOBILE_MENU_ACTIONS: MobileMenuAction[] = [
|
|
123
|
+
'secondary',
|
|
124
|
+
'separator',
|
|
125
|
+
'i18n',
|
|
126
|
+
'theme',
|
|
127
|
+
];
|
|
128
|
+
|
|
84
129
|
export function CustomHomeHeader({
|
|
85
130
|
nav = {},
|
|
86
131
|
i18n = false,
|
|
@@ -93,6 +138,9 @@ export function CustomHomeHeader({
|
|
|
93
138
|
maxContentWidth = 1400,
|
|
94
139
|
navbarClassName,
|
|
95
140
|
floating = false,
|
|
141
|
+
desktopActionsOrder = DEFAULT_DESKTOP_ACTIONS,
|
|
142
|
+
mobileBarActionsOrder = DEFAULT_MOBILE_BAR_ACTIONS,
|
|
143
|
+
mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS,
|
|
96
144
|
}: CustomHomeHeaderProps) {
|
|
97
145
|
const finalLinks = useMemo(
|
|
98
146
|
() => getLinks(links, githubUrl),
|
|
@@ -108,10 +156,167 @@ export function CustomHomeHeader({
|
|
|
108
156
|
const mobilePinnedItems = navItems.filter(
|
|
109
157
|
(item) => isSecondary(item) && isMobilePinned(item),
|
|
110
158
|
);
|
|
111
|
-
const filteredMenuItems = menuItems.filter(
|
|
112
|
-
|
|
159
|
+
const filteredMenuItems = menuItems.filter((item) => !isMobilePinned(item));
|
|
160
|
+
const primaryMenuItems = filteredMenuItems.filter((item) => !isSecondary(item));
|
|
161
|
+
const secondaryMenuItems = filteredMenuItems.filter(isSecondary);
|
|
162
|
+
const desktopSecondaryItems = navItems.filter(isSecondary);
|
|
163
|
+
const desktopActionsIncludeGithub = desktopActionsOrder.includes('github');
|
|
164
|
+
const githubDesktopItem = desktopActionsIncludeGithub
|
|
165
|
+
? desktopSecondaryItems.find((item) => isGithubItem(item, githubUrl))
|
|
166
|
+
: undefined;
|
|
167
|
+
const desktopSecondaryDisplayItems =
|
|
168
|
+
desktopActionsIncludeGithub && githubDesktopItem
|
|
169
|
+
? desktopSecondaryItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
170
|
+
: desktopSecondaryItems;
|
|
171
|
+
|
|
172
|
+
const desktopActionNodes: Record<DesktopAction, ReactNode> = {
|
|
173
|
+
search:
|
|
174
|
+
searchToggle.enabled !== false
|
|
175
|
+
? searchToggle.components?.lg ?? (
|
|
176
|
+
<LargeSearchToggle
|
|
177
|
+
className="w-full rounded-full ps-2.5 max-w-[240px]"
|
|
178
|
+
hideIfDisabled
|
|
179
|
+
/>
|
|
180
|
+
)
|
|
181
|
+
: null,
|
|
182
|
+
theme:
|
|
183
|
+
themeSwitch.enabled !== false
|
|
184
|
+
? themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />
|
|
185
|
+
: null,
|
|
186
|
+
i18n: i18n ? (
|
|
187
|
+
<CompactLanguageToggle>
|
|
188
|
+
<icons.Languages className="size-5" />
|
|
189
|
+
</CompactLanguageToggle>
|
|
190
|
+
) : null,
|
|
191
|
+
secondary: desktopSecondaryDisplayItems.length ? (
|
|
192
|
+
<ul className="flex flex-row gap-2 items-center empty:hidden">
|
|
193
|
+
{desktopSecondaryDisplayItems.map((item, i) => (
|
|
194
|
+
<NavbarLinkItem
|
|
195
|
+
key={i}
|
|
196
|
+
item={item}
|
|
197
|
+
className={cn(
|
|
198
|
+
item.type === 'icon' && [
|
|
199
|
+
'-mx-1',
|
|
200
|
+
i === 0 && 'ms-0',
|
|
201
|
+
i === desktopSecondaryDisplayItems.length - 1 && 'me-0',
|
|
202
|
+
],
|
|
203
|
+
)}
|
|
204
|
+
/>
|
|
205
|
+
))}
|
|
206
|
+
</ul>
|
|
207
|
+
) : null,
|
|
208
|
+
github: githubDesktopItem ? (
|
|
209
|
+
<NavbarLinkItem
|
|
210
|
+
item={githubDesktopItem}
|
|
211
|
+
className={cn(githubDesktopItem.type === 'icon' && '-mx-1')}
|
|
212
|
+
/>
|
|
213
|
+
) : null,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const mobileMenuActionsIncludeGithub =
|
|
217
|
+
mobileMenuActionsOrder.includes('github');
|
|
218
|
+
const githubMobileMenuItem = mobileMenuActionsIncludeGithub
|
|
219
|
+
? secondaryMenuItems.find((item) => isGithubItem(item, githubUrl))
|
|
220
|
+
: undefined;
|
|
221
|
+
const secondaryMenuDisplayItems =
|
|
222
|
+
mobileMenuActionsIncludeGithub && githubMobileMenuItem
|
|
223
|
+
? secondaryMenuItems.filter((item) => !isGithubItem(item, githubUrl))
|
|
224
|
+
: secondaryMenuItems;
|
|
225
|
+
|
|
226
|
+
const mobileMenuActionNodes: Record<MobileMenuAction, ReactNode> = {
|
|
227
|
+
secondary: secondaryMenuDisplayItems.length ? (
|
|
228
|
+
<>
|
|
229
|
+
{secondaryMenuDisplayItems.map((item, i) => (
|
|
230
|
+
<MenuLinkItem key={i} item={item} className="-me-1.5" />
|
|
231
|
+
))}
|
|
232
|
+
</>
|
|
233
|
+
) : null,
|
|
234
|
+
github: githubMobileMenuItem ? (
|
|
235
|
+
<MenuLinkItem item={githubMobileMenuItem} className="-me-1.5" />
|
|
236
|
+
) : null,
|
|
237
|
+
separator: <div role="separator" className="flex-1" />,
|
|
238
|
+
i18n: i18n ? (
|
|
239
|
+
<CompactLanguageToggle>
|
|
240
|
+
<icons.Languages className="size-5" />
|
|
241
|
+
<LanguageToggleText />
|
|
242
|
+
<icons.ChevronDown className="size-3 text-fd-muted-foreground" />
|
|
243
|
+
</CompactLanguageToggle>
|
|
244
|
+
) : null,
|
|
245
|
+
theme:
|
|
246
|
+
themeSwitch.enabled !== false
|
|
247
|
+
? themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />
|
|
248
|
+
: null,
|
|
249
|
+
};
|
|
250
|
+
const shouldRenderMobileUtilities = mobileMenuActionsOrder.some(
|
|
251
|
+
(action) => action !== 'separator' && Boolean(mobileMenuActionNodes[action]),
|
|
252
|
+
);
|
|
253
|
+
const renderMobileMenuAction = (action: MobileMenuAction) => {
|
|
254
|
+
if (action === 'separator' && !shouldRenderMobileUtilities) return null;
|
|
255
|
+
return mobileMenuActionNodes[action];
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const menuNode = (
|
|
259
|
+
<Menu>
|
|
260
|
+
<MenuTrigger
|
|
261
|
+
aria-label="Toggle Menu"
|
|
262
|
+
className={cn(
|
|
263
|
+
buttonVariants({
|
|
264
|
+
size: 'icon',
|
|
265
|
+
color: 'ghost',
|
|
266
|
+
className: 'group [&_svg]:size-5.5',
|
|
267
|
+
}),
|
|
268
|
+
)}
|
|
269
|
+
enableHover={nav.enableHoverToOpen}
|
|
270
|
+
>
|
|
271
|
+
<icons.ChevronDown className="transition-transform duration-300 group-data-[state=open]:rotate-180" />
|
|
272
|
+
</MenuTrigger>
|
|
273
|
+
<MenuContent className="sm:flex-row sm:items-center sm:justify-end">
|
|
274
|
+
{primaryMenuItems.map((item, i) => (
|
|
275
|
+
<MenuLinkItem key={i} item={item} className="sm:hidden" />
|
|
276
|
+
))}
|
|
277
|
+
{shouldRenderMobileUtilities ? (
|
|
278
|
+
<div className="-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2">
|
|
279
|
+
{mobileMenuActionsOrder.map((action) => {
|
|
280
|
+
const node = renderMobileMenuAction(action);
|
|
281
|
+
if (!node) return null;
|
|
282
|
+
return (
|
|
283
|
+
<Fragment key={`mobile-menu-${action}`}>
|
|
284
|
+
{node}
|
|
285
|
+
</Fragment>
|
|
286
|
+
);
|
|
287
|
+
})}
|
|
288
|
+
</div>
|
|
289
|
+
) : null}
|
|
290
|
+
</MenuContent>
|
|
291
|
+
</Menu>
|
|
113
292
|
);
|
|
114
293
|
|
|
294
|
+
const mobilePinnedNode =
|
|
295
|
+
mobilePinnedItems.length > 0 ? (
|
|
296
|
+
<>
|
|
297
|
+
{mobilePinnedItems.map((item, i) => (
|
|
298
|
+
<NavbarLinkItem
|
|
299
|
+
key={`mobile-pinned-${i}`}
|
|
300
|
+
item={item}
|
|
301
|
+
className="max-sm:-mr-1"
|
|
302
|
+
/>
|
|
303
|
+
))}
|
|
304
|
+
</>
|
|
305
|
+
) : null;
|
|
306
|
+
const mobileSearchNode =
|
|
307
|
+
searchToggle.enabled !== false
|
|
308
|
+
? searchToggle.components?.sm ?? (
|
|
309
|
+
<SearchToggle className="p-2" hideIfDisabled />
|
|
310
|
+
)
|
|
311
|
+
: null;
|
|
312
|
+
const mobileBarNodes: Record<MobileBarAction, ReactNode> = {
|
|
313
|
+
pinned: mobilePinnedNode,
|
|
314
|
+
search: mobileSearchNode,
|
|
315
|
+
menu: menuNode,
|
|
316
|
+
};
|
|
317
|
+
const getMobileBarNode = (action: MobileBarAction) =>
|
|
318
|
+
mobileBarNodes[action] ?? null;
|
|
319
|
+
|
|
115
320
|
return (
|
|
116
321
|
<CustomNavbar
|
|
117
322
|
bannerHeight={bannerHeight}
|
|
@@ -135,87 +340,23 @@ export function CustomHomeHeader({
|
|
|
135
340
|
))}
|
|
136
341
|
</ul>
|
|
137
342
|
<div className="flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden">
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
{i18n && (
|
|
148
|
-
<CompactLanguageToggle>
|
|
149
|
-
<icons.Languages className="size-5" />
|
|
150
|
-
</CompactLanguageToggle>
|
|
151
|
-
)}
|
|
152
|
-
<ul className="flex flex-row gap-2 items-center empty:hidden">
|
|
153
|
-
{navItems.filter(isSecondary).map((item, i) => (
|
|
154
|
-
<NavbarLinkItem
|
|
155
|
-
key={i}
|
|
156
|
-
item={item}
|
|
157
|
-
className={cn(
|
|
158
|
-
item.type === 'icon' && [
|
|
159
|
-
'-mx-1',
|
|
160
|
-
i === 0 && 'ms-0',
|
|
161
|
-
i === navItems.length - 1 && 'me-0',
|
|
162
|
-
],
|
|
163
|
-
)}
|
|
164
|
-
/>
|
|
165
|
-
))}
|
|
166
|
-
</ul>
|
|
343
|
+
{desktopActionsOrder.map((action) => {
|
|
344
|
+
const node = desktopActionNodes[action];
|
|
345
|
+
if (!node) return null;
|
|
346
|
+
return (
|
|
347
|
+
<Fragment key={`desktop-${action}`}>
|
|
348
|
+
{node}
|
|
349
|
+
</Fragment>
|
|
350
|
+
);
|
|
351
|
+
})}
|
|
167
352
|
</div>
|
|
168
353
|
<ul className="flex flex-row items-center ms-auto -me-1.5 lg:hidden">
|
|
169
|
-
{
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
))}
|
|
176
|
-
{searchToggle.enabled !== false &&
|
|
177
|
-
(searchToggle.components?.sm ?? (
|
|
178
|
-
<SearchToggle className="p-2" hideIfDisabled />
|
|
179
|
-
))}
|
|
180
|
-
<Menu>
|
|
181
|
-
<MenuTrigger
|
|
182
|
-
aria-label="Toggle Menu"
|
|
183
|
-
className={cn(
|
|
184
|
-
buttonVariants({
|
|
185
|
-
size: 'icon',
|
|
186
|
-
color: 'ghost',
|
|
187
|
-
className: 'group [&_svg]:size-5.5',
|
|
188
|
-
}),
|
|
189
|
-
)}
|
|
190
|
-
enableHover={nav.enableHoverToOpen}
|
|
191
|
-
>
|
|
192
|
-
<icons.ChevronDown className="transition-transform duration-300 group-data-[state=open]:rotate-180" />
|
|
193
|
-
</MenuTrigger>
|
|
194
|
-
<MenuContent className="sm:flex-row sm:items-center sm:justify-end">
|
|
195
|
-
{filteredMenuItems
|
|
196
|
-
.filter((item) => !isSecondary(item))
|
|
197
|
-
.map((item, i) => (
|
|
198
|
-
<MenuLinkItem key={i} item={item} className="sm:hidden" />
|
|
199
|
-
))}
|
|
200
|
-
<div className="-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2">
|
|
201
|
-
{filteredMenuItems.filter(isSecondary).map((item, i) => (
|
|
202
|
-
<MenuLinkItem key={i} item={item} className="-me-1.5" />
|
|
203
|
-
))}
|
|
204
|
-
<div role="separator" className="flex-1" />
|
|
205
|
-
{i18n ? (
|
|
206
|
-
<CompactLanguageToggle>
|
|
207
|
-
<icons.Languages className="size-5" />
|
|
208
|
-
<LanguageToggleText />
|
|
209
|
-
<icons.ChevronDown className="size-3 text-fd-muted-foreground" />
|
|
210
|
-
</CompactLanguageToggle>
|
|
211
|
-
) : null}
|
|
212
|
-
{themeSwitch.enabled !== false &&
|
|
213
|
-
(themeSwitch.component ?? (
|
|
214
|
-
<ThemeToggle mode={themeSwitch?.mode} />
|
|
215
|
-
))}
|
|
216
|
-
</div>
|
|
217
|
-
</MenuContent>
|
|
218
|
-
</Menu>
|
|
354
|
+
{mobileBarActionsOrder.map((action) => {
|
|
355
|
+
const node = getMobileBarNode(action);
|
|
356
|
+
return node ? (
|
|
357
|
+
<Fragment key={`mobile-bar-${action}`}>{node}</Fragment>
|
|
358
|
+
) : null;
|
|
359
|
+
})}
|
|
219
360
|
</ul>
|
|
220
361
|
</CustomNavbar>
|
|
221
362
|
);
|
|
@@ -525,7 +666,7 @@ function CompactLanguageToggle({
|
|
|
525
666
|
className={cn(
|
|
526
667
|
buttonVariants({
|
|
527
668
|
color: 'ghost',
|
|
528
|
-
className: 'gap-1.5 py-1.5
|
|
669
|
+
className: 'gap-1.5 py-1.5 px-1',
|
|
529
670
|
}),
|
|
530
671
|
props.className,
|
|
531
672
|
)}
|
|
@@ -563,6 +704,12 @@ function CompactLanguageToggle({
|
|
|
563
704
|
);
|
|
564
705
|
}
|
|
565
706
|
|
|
707
|
+
function isGithubItem(item: LinkItemType, githubUrl?: string): boolean {
|
|
708
|
+
return Boolean(
|
|
709
|
+
githubUrl && item.type === 'icon' && item.url === githubUrl,
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
566
713
|
function isSecondary(item: LinkItemType): boolean {
|
|
567
714
|
if ('secondary' in item && item.secondary != null) return item.secondary;
|
|
568
715
|
|
|
@@ -3,7 +3,13 @@ import { HomeLayout, type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
|
|
|
3
3
|
import { FumaBannerSuit } from '@third-ui/fuma/fuma-banner-suit';
|
|
4
4
|
import { Footer } from '@third-ui/main/footer';
|
|
5
5
|
import { GoToTop } from '@third-ui/main/go-to-top';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
NavbarCSSVars,
|
|
8
|
+
CustomHomeHeader,
|
|
9
|
+
type DesktopAction,
|
|
10
|
+
type MobileBarAction,
|
|
11
|
+
type MobileMenuAction,
|
|
12
|
+
} from './custom-header';
|
|
7
13
|
|
|
8
14
|
// - bannerHeight/headerHeight 换成你项目期望的 rem 值即可(如果没有 Banner 就把 bannerHeight 设成 0)。
|
|
9
15
|
// - layoutStyle 同时把变量传给 HomeLayout 的 main 元素,这样内容整体会往下错开,不需要 has-banner/no-banner class。
|
|
@@ -73,9 +79,19 @@ export interface CustomHomeLayoutProps {
|
|
|
73
79
|
* Additional styles merged on top of the computed layout style.
|
|
74
80
|
*/
|
|
75
81
|
style?: CSSProperties;
|
|
82
|
+
/**
|
|
83
|
+
* Customize the order of header action items.
|
|
84
|
+
*/
|
|
85
|
+
actionOrders?: HeaderActionOrders;
|
|
76
86
|
children?: ReactNode;
|
|
77
87
|
}
|
|
78
88
|
|
|
89
|
+
export interface HeaderActionOrders {
|
|
90
|
+
desktop?: DesktopAction[];
|
|
91
|
+
mobileBar?: MobileBarAction[];
|
|
92
|
+
mobileMenu?: MobileMenuAction[];
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
export function CustomHomeLayout({
|
|
80
96
|
locale,
|
|
81
97
|
options,
|
|
@@ -92,6 +108,7 @@ export function CustomHomeLayout({
|
|
|
92
108
|
showGoToTop = true,
|
|
93
109
|
style,
|
|
94
110
|
floatingNav = false,
|
|
111
|
+
actionOrders,
|
|
95
112
|
}: CustomHomeLayoutProps) {
|
|
96
113
|
const resolvedBannerHeight = bannerHeight ?? (showBanner ? 3 : 0.5);
|
|
97
114
|
const resolvedPaddingTop =
|
|
@@ -116,6 +133,9 @@ export function CustomHomeLayout({
|
|
|
116
133
|
headerHeight={headerHeight}
|
|
117
134
|
navbarClassName={navbarClassName}
|
|
118
135
|
floating={floatingNav}
|
|
136
|
+
desktopActionsOrder={actionOrders?.desktop}
|
|
137
|
+
mobileBarActionsOrder={actionOrders?.mobileBar}
|
|
138
|
+
mobileMenuActionsOrder={actionOrders?.mobileMenu}
|
|
119
139
|
/>
|
|
120
140
|
);
|
|
121
141
|
|
|
@@ -181,7 +181,7 @@ export function CreditNavButton({
|
|
|
181
181
|
type="button"
|
|
182
182
|
aria-label={`${formattedBalance} ${totalLabel}`}
|
|
183
183
|
className={cn(
|
|
184
|
-
'group
|
|
184
|
+
'group mx-2 sm:mx-1 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white pl-2 pr-4 py-1.5 text-sm font-semibold text-slate-700 shadow-sm transition-all dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100',
|
|
185
185
|
'hover:border-transparent hover:bg-linear-to-r hover:from-purple-500/90 hover:to-pink-500/90 hover:text-white hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-purple-500 dark:hover:from-purple-500 dark:hover:to-pink-500',
|
|
186
186
|
)}
|
|
187
187
|
ref={triggerRef}
|