@naeil/ui 0.7.2 → 0.9.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/CHANGELOG.md +17 -1
- package/DESIGN.md +5 -1
- package/README.md +6 -6
- package/dist/{chunk-7JBLPGZ4.js → chunk-JDHGKM25.js} +1 -1
- package/dist/{chunk-QQQQZEPV.js → chunk-KPE4KKYM.js} +111 -14
- package/dist/{chunk-MGCCKOGU.js → chunk-XRO3NJAB.js} +102 -44
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +3 -3
- package/dist/components/naeil-footer.d.ts +2 -2
- package/dist/components/naeil-footer.js +3 -1
- package/dist/components/naeil-header.d.ts +8 -2
- package/dist/components/naeil-header.js +3 -3
- package/dist/components/ui/index.d.ts +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/lib/ui-release.d.ts +1 -1
- package/dist/lib/ui-release.js +1 -1
- package/dist/{navigation-CgPeL9tF.d.ts → navigation-D8N1tMTu.d.ts} +4 -2
- package/dist/{site-footer-vXDa_6yr.d.ts → site-footer-s113FZgC.d.ts} +1 -1
- package/dist/source/components/naeil-footer.js +2 -0
- package/dist/source/components/naeil-header-account.js +54 -19
- package/dist/source/components/naeil-header.d.ts +8 -2
- package/dist/source/components/naeil-header.js +49 -23
- package/dist/source/components/ui/navigation.d.ts +8 -2
- package/dist/source/components/ui/navigation.js +112 -15
- package/dist/source/lib/ui-release.d.ts +1 -1
- package/dist/source/lib/ui-release.js +1 -1
- package/docs/components/README.md +1 -1
- package/docs/components/application-layout.md +28 -7
- package/docs/components/common.md +2 -2
- package/docs/components/naeil-header.md +39 -5
- package/docs/design/v2-migration.md +8 -4
- package/docs/package-release.md +25 -4
- package/package.json +1 -1
- package/src/styles/components.css +130 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { b as NavigationLinkComponent, a as NavigationProps, c as NavigationLinkProps, N as NavigationLink } from '../navigation-
|
|
3
|
+
import { b as NavigationLinkComponent, a as NavigationProps, c as NavigationItem, d as NavigationLinkProps, N as NavigationLink } from '../navigation-D8N1tMTu.js';
|
|
4
4
|
import { N as NaeilHeaderLocale } from '../naeil-header-content-BvISJ4RV.js';
|
|
5
5
|
|
|
6
6
|
type NaeilHeaderAccount = {
|
|
@@ -29,6 +29,12 @@ interface NaeilHeaderProps extends Omit<NavigationProps, "brand" | "items" | "to
|
|
|
29
29
|
resolveHref?: (path: string) => string;
|
|
30
30
|
brandHref?: string;
|
|
31
31
|
brandSlot?: ReactNode;
|
|
32
|
+
/** Ignored when brandSlot replaces the complete brand content. */
|
|
33
|
+
brandSuffix?: string;
|
|
34
|
+
/** After the brand link, outside it; rendered once at every width. */
|
|
35
|
+
statusSlot?: ReactNode;
|
|
36
|
+
/** Product menu data, after catalog/blog and before flat subNav links. */
|
|
37
|
+
appNavigation?: NavigationItem[];
|
|
32
38
|
brandLinkProps?: Omit<NavigationLinkProps, "href" | "children">;
|
|
33
39
|
/** Omit when the application does not support changing its UI language. */
|
|
34
40
|
onLocaleChange?: (locale: NaeilHeaderLocale) => void;
|
|
@@ -67,6 +73,6 @@ declare function NaeilHeaderLocaleControl({ locale, onLocaleChange }: {
|
|
|
67
73
|
onLocaleChange: (locale: NaeilHeaderLocale) => void;
|
|
68
74
|
}): react_jsx_runtime.JSX.Element;
|
|
69
75
|
/** One complete, opt-in branded composition shared by naeil.dev and applications. */
|
|
70
|
-
declare function NaeilHeader({ locale, resolveHref, brandHref, brandSlot, brandLinkProps, brandLinkComponent, onLocaleChange, authSlot, toolbarSlot, projects, projectsExternal, translations, showProjects, showBlog, subNav, currentProjectKey, currentProjectHref, labels, Link, currentHref, onNavigate, className, containerClassName, ...props }: NaeilHeaderProps): react_jsx_runtime.JSX.Element;
|
|
76
|
+
declare function NaeilHeader({ locale, resolveHref, brandHref, brandSlot, brandSuffix, statusSlot, appNavigation, brandLinkProps, brandLinkComponent, onLocaleChange, authSlot, toolbarSlot, projects, projectsExternal, translations, showProjects, showBlog, subNav, currentProjectKey, currentProjectHref, labels, Link, currentHref, onNavigate, className, containerClassName, ...props }: NaeilHeaderProps): react_jsx_runtime.JSX.Element;
|
|
71
77
|
|
|
72
78
|
export { NaeilHeader, type NaeilHeaderAccount, NaeilHeaderAccountControl, type NaeilHeaderAccountControlProps, NaeilHeaderLocale, NaeilHeaderLocaleControl, type NaeilHeaderProps, NaeilHeaderThemeControl };
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
NaeilHeaderAccountControl,
|
|
6
6
|
NaeilHeaderLocaleControl,
|
|
7
7
|
NaeilHeaderThemeControl
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-XRO3NJAB.js";
|
|
9
|
+
import "../chunk-JDHGKM25.js";
|
|
10
|
+
import "../chunk-KPE4KKYM.js";
|
|
11
11
|
import "../chunk-JBTDRVZ7.js";
|
|
12
12
|
export {
|
|
13
13
|
NaeilHeader,
|
|
@@ -4,8 +4,8 @@ import { Avatar as Avatar$1, Dialog as Dialog$1, DropdownMenu as DropdownMenu$1,
|
|
|
4
4
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
5
|
import { VariantProps } from 'class-variance-authority';
|
|
6
6
|
import { ToasterProps } from 'sonner';
|
|
7
|
-
export {
|
|
8
|
-
export { a as SiteFooter, S as SiteFooterProps } from '../../site-footer-
|
|
7
|
+
export { e as Navigation, f as NavigationGroup, c as NavigationItem, N as NavigationLink, b as NavigationLinkComponent, d as NavigationLinkProps, a as NavigationProps } from '../../navigation-D8N1tMTu.js';
|
|
8
|
+
export { a as SiteFooter, S as SiteFooterProps } from '../../site-footer-s113FZgC.js';
|
|
9
9
|
|
|
10
10
|
declare function Avatar({ className, size, ...props }: React.ComponentProps<typeof Avatar$1.Root> & {
|
|
11
11
|
size?: "default" | "sm" | "lg";
|
package/dist/lib/ui-release.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const uiRelease: Readonly<{version: "0.
|
|
1
|
+
export declare const uiRelease: Readonly<{version: "0.9.0"; contractVersion: "1"}>;
|
package/dist/lib/ui-release.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const uiRelease = Object.freeze({"version":"0.
|
|
1
|
+
export const uiRelease = Object.freeze({"version":"0.9.0","contractVersion":"1"});
|
|
@@ -38,11 +38,13 @@ interface NavigationProps extends Omit<React.ComponentPropsWithoutRef<"header">,
|
|
|
38
38
|
toolbar?: React.ReactNode;
|
|
39
39
|
mobileToolbar?: React.ReactNode;
|
|
40
40
|
mobileFooter?: React.ReactNode;
|
|
41
|
+
mobileOpen?: boolean;
|
|
42
|
+
onMobileOpenChange?: (open: boolean) => void;
|
|
41
43
|
/** Use the same product-owned frame class on header, footer and main. */
|
|
42
44
|
containerClassName?: string;
|
|
43
45
|
/** Preserve modified clicks; applications may preventDefault for client routing/guards. */
|
|
44
46
|
onNavigate?: (event: React.MouseEvent<HTMLAnchorElement>, item: NavigationLink) => void;
|
|
45
47
|
}
|
|
46
|
-
declare function Navigation({ brand, items, labels, Link, currentHref, toolbar, mobileToolbar, mobileFooter, containerClassName, onNavigate, className, ...props }: NavigationProps): react_jsx_runtime.JSX.Element;
|
|
48
|
+
declare function Navigation({ brand, items, labels, Link, currentHref, toolbar, mobileToolbar, mobileFooter, mobileOpen: controlledOpen, onMobileOpenChange, containerClassName, onNavigate, className, ...props }: NavigationProps): react_jsx_runtime.JSX.Element;
|
|
47
49
|
|
|
48
|
-
export { type NavigationLink as N, type NavigationProps as a, type NavigationLinkComponent as b, type
|
|
50
|
+
export { type NavigationLink as N, type NavigationProps as a, type NavigationLinkComponent as b, type NavigationItem as c, type NavigationLinkProps as d, Navigation as e, type NavigationGroup as f };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { N as NavigationLink, b as NavigationLinkComponent, a as NavigationProps } from './navigation-
|
|
3
|
+
import { N as NavigationLink, b as NavigationLinkComponent, a as NavigationProps } from './navigation-D8N1tMTu.js';
|
|
4
4
|
|
|
5
5
|
interface SiteFooterProps extends React.ComponentPropsWithoutRef<"footer"> {
|
|
6
6
|
brand?: React.ReactNode;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
3
5
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -15,9 +17,12 @@ var __spreadValues = (a, b) => {
|
|
|
15
17
|
}
|
|
16
18
|
return a;
|
|
17
19
|
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
18
21
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
22
|
import { UserRound } from "lucide-react";
|
|
23
|
+
import { useContext, useRef } from "react";
|
|
20
24
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem } from "./ui/dropdown-menu.js";
|
|
25
|
+
import { NavigationSheetAction } from "./ui/navigation.js";
|
|
21
26
|
const labels = {
|
|
22
27
|
en: { menu: "Account menu", account: "Account", login: "Log in", logout: "Log out", busy: "Logging out\u2026" },
|
|
23
28
|
ko: { menu: "\uACC4\uC815 \uBA54\uB274", account: "\uACC4\uC815", login: "\uB85C\uADF8\uC778", logout: "\uB85C\uADF8\uC544\uC6C3", busy: "\uB85C\uADF8\uC544\uC6C3 \uC911\u2026" },
|
|
@@ -26,10 +31,18 @@ const labels = {
|
|
|
26
31
|
const NativeLink = (props) => /* @__PURE__ */ jsx("a", __spreadValues({}, props));
|
|
27
32
|
function NaeilHeaderAccountControl({ locale, account, Link = NativeLink }) {
|
|
28
33
|
const text = labels[locale];
|
|
34
|
+
const handoff = useContext(NavigationSheetAction);
|
|
35
|
+
const pendingAction = useRef(void 0);
|
|
29
36
|
if (account.status === "signed-out") {
|
|
30
37
|
return /* @__PURE__ */ jsx(Link, { href: account.loginHref, className: "ui-navigation-link ui-focus naeil-header-login", "data-naeil-account-state": "signed-out", children: text.login });
|
|
31
38
|
}
|
|
32
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, {
|
|
39
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { onOpenChange: (open) => {
|
|
40
|
+
var _a;
|
|
41
|
+
if (open) {
|
|
42
|
+
(_a = pendingAction.current) == null ? void 0 : _a.cancel();
|
|
43
|
+
pendingAction.current = void 0;
|
|
44
|
+
}
|
|
45
|
+
}, children: [
|
|
33
46
|
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
34
47
|
"button",
|
|
35
48
|
{
|
|
@@ -42,24 +55,46 @@ function NaeilHeaderAccountControl({ locale, account, Link = NativeLink }) {
|
|
|
42
55
|
children: /* @__PURE__ */ jsx("span", { className: "naeil-header-account-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx(UserRound, {}) })
|
|
43
56
|
}
|
|
44
57
|
) }),
|
|
45
|
-
/* @__PURE__ */ jsxs(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
/* @__PURE__ */ jsxs(
|
|
59
|
+
DropdownMenuContent,
|
|
60
|
+
{
|
|
61
|
+
align: "end",
|
|
62
|
+
className: "w-56 max-w-[calc(100vw-2rem)]",
|
|
63
|
+
"data-naeil-account-menu": true,
|
|
64
|
+
"data-naeil-chrome": "",
|
|
65
|
+
lang: locale,
|
|
66
|
+
onCloseAutoFocus: (event) => {
|
|
67
|
+
const flow = pendingAction.current;
|
|
68
|
+
if (!flow || event.target !== flow.menu) return;
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
flow.menu = null;
|
|
71
|
+
flow.menuClosed();
|
|
72
|
+
},
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex min-w-0 flex-col font-normal", children: [
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-base font-medium", children: account.displayName || text.account }),
|
|
76
|
+
account.email && /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-muted-foreground", children: account.email })
|
|
77
|
+
] }),
|
|
78
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
DropdownMenuItem,
|
|
81
|
+
{
|
|
82
|
+
className: "min-h-11",
|
|
83
|
+
disabled: account.busy,
|
|
84
|
+
onSelect: (event) => {
|
|
85
|
+
if (account.busy) return;
|
|
86
|
+
if (handoff) {
|
|
87
|
+
const menu = event.target.closest("[data-naeil-account-menu]");
|
|
88
|
+
if (!(menu == null ? void 0 : menu.isConnected)) return;
|
|
89
|
+
pendingAction.current = __spreadProps(__spreadValues({}, handoff(account.onLogout)), { menu });
|
|
90
|
+
} else account.onLogout();
|
|
91
|
+
},
|
|
92
|
+
children: account.busy ? text.busy : text.logout
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
)
|
|
63
98
|
] });
|
|
64
99
|
}
|
|
65
100
|
export {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { NavigationProps, NavigationLinkProps, NavigationLink, NavigationLinkComponent } from './ui/navigation.js';
|
|
3
|
+
import { NavigationProps, NavigationItem, NavigationLinkProps, NavigationLink, NavigationLinkComponent } from './ui/navigation.js';
|
|
4
4
|
import { NaeilHeaderLocale } from './naeil-header-content.js';
|
|
5
5
|
export { NaeilHeaderAccount, NaeilHeaderAccountControl, NaeilHeaderAccountControlProps } from './naeil-header-account.js';
|
|
6
6
|
|
|
@@ -10,6 +10,12 @@ interface NaeilHeaderProps extends Omit<NavigationProps, "brand" | "items" | "to
|
|
|
10
10
|
resolveHref?: (path: string) => string;
|
|
11
11
|
brandHref?: string;
|
|
12
12
|
brandSlot?: ReactNode;
|
|
13
|
+
/** Ignored when brandSlot replaces the complete brand content. */
|
|
14
|
+
brandSuffix?: string;
|
|
15
|
+
/** After the brand link, outside it; rendered once at every width. */
|
|
16
|
+
statusSlot?: ReactNode;
|
|
17
|
+
/** Product menu data, after catalog/blog and before flat subNav links. */
|
|
18
|
+
appNavigation?: NavigationItem[];
|
|
13
19
|
brandLinkProps?: Omit<NavigationLinkProps, "href" | "children">;
|
|
14
20
|
/** Omit when the application does not support changing its UI language. */
|
|
15
21
|
onLocaleChange?: (locale: NaeilHeaderLocale) => void;
|
|
@@ -48,6 +54,6 @@ declare function NaeilHeaderLocaleControl({ locale, onLocaleChange }: {
|
|
|
48
54
|
onLocaleChange: (locale: NaeilHeaderLocale) => void;
|
|
49
55
|
}): react_jsx_runtime.JSX.Element;
|
|
50
56
|
/** One complete, opt-in branded composition shared by naeil.dev and applications. */
|
|
51
|
-
declare function NaeilHeader({ locale, resolveHref, brandHref, brandSlot, brandLinkProps, brandLinkComponent, onLocaleChange, authSlot, toolbarSlot, projects, projectsExternal, translations, showProjects, showBlog, subNav, currentProjectKey, currentProjectHref, labels, Link, currentHref, onNavigate, className, containerClassName, ...props }: NaeilHeaderProps): react_jsx_runtime.JSX.Element;
|
|
57
|
+
declare function NaeilHeader({ locale, resolveHref, brandHref, brandSlot, brandSuffix, statusSlot, appNavigation, brandLinkProps, brandLinkComponent, onLocaleChange, authSlot, toolbarSlot, projects, projectsExternal, translations, showProjects, showBlog, subNav, currentProjectKey, currentProjectHref, labels, Link, currentHref, onNavigate, className, containerClassName, ...props }: NaeilHeaderProps): react_jsx_runtime.JSX.Element;
|
|
52
58
|
|
|
53
59
|
export { NaeilHeader, NaeilHeaderLocale, NaeilHeaderLocaleControl, type NaeilHeaderProps, NaeilHeaderThemeControl };
|
|
@@ -72,7 +72,7 @@ function NaeilHeaderLocaleControl({ locale, onLocaleChange }) {
|
|
|
72
72
|
children: /* @__PURE__ */ jsx(SelectValue, { children: locale.toUpperCase() })
|
|
73
73
|
}
|
|
74
74
|
),
|
|
75
|
-
/* @__PURE__ */ jsx(SelectContent, { align: "end", children: headerLocales.map((value) => /* @__PURE__ */ jsx(SelectItem, { value, className: "min-h-11", lang: value, children: headerLocaleNames[value] }, value)) })
|
|
75
|
+
/* @__PURE__ */ jsx(SelectContent, { align: "end", "data-naeil-chrome": "", "data-naeil-locale-menu": "", lang: locale, children: headerLocales.map((value) => /* @__PURE__ */ jsx(SelectItem, { value, className: "min-h-11", lang: value, children: headerLocaleNames[value] }, value)) })
|
|
76
76
|
] });
|
|
77
77
|
}
|
|
78
78
|
const NativeLink = (props) => /* @__PURE__ */ jsx("a", __spreadValues({}, props));
|
|
@@ -82,6 +82,9 @@ function NaeilHeader(_a) {
|
|
|
82
82
|
resolveHref = (path) => `https://naeil.dev/${locale}${path === "/" ? "" : path}`,
|
|
83
83
|
brandHref,
|
|
84
84
|
brandSlot,
|
|
85
|
+
brandSuffix,
|
|
86
|
+
statusSlot,
|
|
87
|
+
appNavigation,
|
|
85
88
|
brandLinkProps,
|
|
86
89
|
brandLinkComponent,
|
|
87
90
|
onLocaleChange,
|
|
@@ -106,6 +109,9 @@ function NaeilHeader(_a) {
|
|
|
106
109
|
"resolveHref",
|
|
107
110
|
"brandHref",
|
|
108
111
|
"brandSlot",
|
|
112
|
+
"brandSuffix",
|
|
113
|
+
"statusSlot",
|
|
114
|
+
"appNavigation",
|
|
109
115
|
"brandLinkProps",
|
|
110
116
|
"brandLinkComponent",
|
|
111
117
|
"onLocaleChange",
|
|
@@ -148,41 +154,61 @@ function NaeilHeader(_a) {
|
|
|
148
154
|
const brandDestination = brandHref != null ? brandHref : resolveHref("/");
|
|
149
155
|
const BrandLink = brandLinkComponent != null ? brandLinkComponent : Link;
|
|
150
156
|
const _a2 = brandLinkProps != null ? brandLinkProps : {}, { onClick: onBrandClick } = _a2, brandProps = __objRest(_a2, ["onClick"]);
|
|
151
|
-
const toolbar = /* @__PURE__ */ jsx(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
const toolbar = /* @__PURE__ */ jsx(
|
|
158
|
+
"div",
|
|
159
|
+
{
|
|
160
|
+
"data-app-toolbar": true,
|
|
161
|
+
className: "naeil-header-controls",
|
|
162
|
+
"data-naeil-chrome": toolbarSlot === void 0 ? "" : void 0,
|
|
163
|
+
lang: toolbarSlot === void 0 ? locale : void 0,
|
|
164
|
+
children: toolbarSlot !== void 0 ? toolbarSlot : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
165
|
+
/* @__PURE__ */ jsx(NaeilHeaderThemeControl, { locale }),
|
|
166
|
+
onLocaleChange && /* @__PURE__ */ jsx(NaeilHeaderLocaleControl, { locale, onLocaleChange }),
|
|
167
|
+
authSlot && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
168
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "naeil-header-separator" }),
|
|
169
|
+
authSlot
|
|
170
|
+
] })
|
|
171
|
+
] })
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
const logo = /* @__PURE__ */ jsx(Logo, { variant: "horizontal", size: 28, className: "block", label: null });
|
|
175
|
+
const brand = /* @__PURE__ */ jsx(
|
|
176
|
+
BrandLink,
|
|
177
|
+
__spreadProps(__spreadValues({
|
|
178
|
+
href: brandDestination,
|
|
179
|
+
"data-app-brand": true,
|
|
180
|
+
"aria-label": brandSlot == null ? ".naeil" : void 0
|
|
181
|
+
}, brandProps), {
|
|
182
|
+
onClick: (event) => {
|
|
183
|
+
onBrandClick == null ? void 0 : onBrandClick(event);
|
|
184
|
+
onNavigate == null ? void 0 : onNavigate(event, { id: "brand", href: brandDestination, label: ".naeil" });
|
|
185
|
+
},
|
|
186
|
+
children: brandSlot != null ? brandSlot : brandSuffix ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
187
|
+
logo,
|
|
188
|
+
/* @__PURE__ */ jsx("span", { className: "naeil-header-brand-suffix", children: brandSuffix })
|
|
189
|
+
] }) : logo
|
|
190
|
+
})
|
|
191
|
+
);
|
|
159
192
|
return /* @__PURE__ */ jsx(
|
|
160
193
|
Navigation,
|
|
161
194
|
__spreadProps(__spreadValues({}, props), {
|
|
162
195
|
"data-global-header": true,
|
|
163
196
|
"data-naeil-header": true,
|
|
197
|
+
"data-naeil-chrome": "",
|
|
198
|
+
lang: locale,
|
|
164
199
|
"data-naeil-ui-version": uiRelease.version,
|
|
165
200
|
"data-naeil-ui-contract": uiRelease.contractVersion,
|
|
166
201
|
Link,
|
|
167
202
|
currentHref,
|
|
168
203
|
onNavigate,
|
|
169
|
-
brand: /* @__PURE__ */
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"data-app-brand": true,
|
|
174
|
-
"aria-label": brandSlot == null ? ".naeil" : void 0
|
|
175
|
-
}, brandProps), {
|
|
176
|
-
onClick: (event) => {
|
|
177
|
-
onBrandClick == null ? void 0 : onBrandClick(event);
|
|
178
|
-
onNavigate == null ? void 0 : onNavigate(event, { id: "brand", href: brandDestination, label: ".naeil" });
|
|
179
|
-
},
|
|
180
|
-
children: brandSlot != null ? brandSlot : /* @__PURE__ */ jsx(Logo, { variant: "horizontal", size: 28, className: "block", label: null })
|
|
181
|
-
})
|
|
182
|
-
),
|
|
204
|
+
brand: statusSlot != null ? /* @__PURE__ */ jsxs("div", { className: "naeil-header-brand-row", children: [
|
|
205
|
+
brand,
|
|
206
|
+
statusSlot
|
|
207
|
+
] }) : brand,
|
|
183
208
|
items: [
|
|
184
209
|
...showProjects ? [{ id: "projects", label: (_b2 = translations == null ? void 0 : translations.projects) != null ? _b2 : content.nav.projects, items: projectLinks, columns: projectLinks.length > 1 ? 2 : 1 }] : [],
|
|
185
210
|
...showBlog ? [{ id: "blog", label: (_c = translations == null ? void 0 : translations.blog) != null ? _c : content.nav.blog, href: resolveHref("/blog"), external: projectsExternal }] : [],
|
|
211
|
+
...appNavigation != null ? appNavigation : [],
|
|
186
212
|
...(subNav != null ? subNav : []).map((item) => __spreadProps(__spreadValues({}, item), { id: item.href }))
|
|
187
213
|
],
|
|
188
214
|
labels: labels != null ? labels : { navigation: content.nav.navigation, menu: content.nav.menu, close: content.nav.closeMenu },
|
|
@@ -38,11 +38,17 @@ interface NavigationProps extends Omit<react.ComponentPropsWithoutRef<"header">,
|
|
|
38
38
|
toolbar?: react.ReactNode;
|
|
39
39
|
mobileToolbar?: react.ReactNode;
|
|
40
40
|
mobileFooter?: react.ReactNode;
|
|
41
|
+
mobileOpen?: boolean;
|
|
42
|
+
onMobileOpenChange?: (open: boolean) => void;
|
|
41
43
|
/** Use the same product-owned frame class on header, footer and main. */
|
|
42
44
|
containerClassName?: string;
|
|
43
45
|
/** Preserve modified clicks; applications may preventDefault for client routing/guards. */
|
|
44
46
|
onNavigate?: (event: react.MouseEvent<HTMLAnchorElement>, item: NavigationLink) => void;
|
|
45
47
|
}
|
|
46
|
-
declare
|
|
48
|
+
declare const NavigationSheetAction: react.Context<((action: () => void) => {
|
|
49
|
+
menuClosed: () => void;
|
|
50
|
+
cancel: () => void;
|
|
51
|
+
}) | undefined>;
|
|
52
|
+
declare function Navigation({ brand, items, labels, Link, currentHref, toolbar, mobileToolbar, mobileFooter, mobileOpen: controlledOpen, onMobileOpenChange, containerClassName, onNavigate, className, ...props }: NavigationProps): react_jsx_runtime.JSX.Element;
|
|
47
53
|
|
|
48
|
-
export { Navigation, type NavigationGroup, type NavigationItem, type NavigationLink, type NavigationLinkComponent, type NavigationLinkProps, type NavigationProps };
|
|
54
|
+
export { Navigation, type NavigationGroup, type NavigationItem, type NavigationLink, type NavigationLinkComponent, type NavigationLinkProps, type NavigationProps, NavigationSheetAction };
|
|
@@ -40,13 +40,15 @@ const NativeLink = React.forwardRef(
|
|
|
40
40
|
return /* @__PURE__ */ jsx("a", __spreadValues({ ref }, props));
|
|
41
41
|
}
|
|
42
42
|
);
|
|
43
|
+
const NavigationSheetAction = React.createContext(void 0);
|
|
43
44
|
function DesktopGroup({
|
|
44
45
|
item,
|
|
45
46
|
active,
|
|
46
47
|
open,
|
|
47
48
|
anotherGroupOpen,
|
|
48
49
|
onOpenChange,
|
|
49
|
-
children
|
|
50
|
+
children,
|
|
51
|
+
chrome
|
|
50
52
|
}) {
|
|
51
53
|
var _a;
|
|
52
54
|
const hoverOpened = React.useRef(false);
|
|
@@ -125,7 +127,7 @@ function DesktopGroup({
|
|
|
125
127
|
),
|
|
126
128
|
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
127
129
|
Popover.Content,
|
|
128
|
-
{
|
|
130
|
+
__spreadProps(__spreadValues({}, chrome), {
|
|
129
131
|
ref: panel,
|
|
130
132
|
"data-slot": "navigation-popover",
|
|
131
133
|
className: "ui-navigation-popover ui-popup",
|
|
@@ -151,7 +153,7 @@ function DesktopGroup({
|
|
|
151
153
|
event.preventDefault();
|
|
152
154
|
},
|
|
153
155
|
children: /* @__PURE__ */ jsx("nav", { "aria-label": item.label, className: "ui-navigation-menu-grid", children })
|
|
154
|
-
}
|
|
156
|
+
})
|
|
155
157
|
) })
|
|
156
158
|
]
|
|
157
159
|
}
|
|
@@ -167,6 +169,8 @@ function Navigation(_a) {
|
|
|
167
169
|
toolbar,
|
|
168
170
|
mobileToolbar,
|
|
169
171
|
mobileFooter,
|
|
172
|
+
mobileOpen: controlledOpen,
|
|
173
|
+
onMobileOpenChange,
|
|
170
174
|
containerClassName,
|
|
171
175
|
onNavigate,
|
|
172
176
|
className
|
|
@@ -179,18 +183,75 @@ function Navigation(_a) {
|
|
|
179
183
|
"toolbar",
|
|
180
184
|
"mobileToolbar",
|
|
181
185
|
"mobileFooter",
|
|
186
|
+
"mobileOpen",
|
|
187
|
+
"onMobileOpenChange",
|
|
182
188
|
"containerClassName",
|
|
183
189
|
"onNavigate",
|
|
184
190
|
"className"
|
|
185
191
|
]);
|
|
186
|
-
const [
|
|
192
|
+
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
193
|
+
const mobileOpen = controlledOpen != null ? controlledOpen : internalOpen;
|
|
194
|
+
const state = React.useRef({ open: mobileOpen, controlled: controlledOpen !== void 0, onChange: onMobileOpenChange });
|
|
195
|
+
const mounted = React.useRef(false);
|
|
196
|
+
const pendingAction = React.useRef(void 0);
|
|
197
|
+
const cancelAction = React.useCallback(() => {
|
|
198
|
+
var _a2;
|
|
199
|
+
clearTimeout((_a2 = pendingAction.current) == null ? void 0 : _a2.timer);
|
|
200
|
+
pendingAction.current = void 0;
|
|
201
|
+
}, []);
|
|
202
|
+
React.useLayoutEffect(() => {
|
|
203
|
+
const reopening = mobileOpen && !state.current.open;
|
|
204
|
+
state.current = { open: mobileOpen, controlled: controlledOpen !== void 0, onChange: onMobileOpenChange };
|
|
205
|
+
if (reopening) cancelAction();
|
|
206
|
+
});
|
|
207
|
+
React.useLayoutEffect(() => {
|
|
208
|
+
mounted.current = true;
|
|
209
|
+
return () => {
|
|
210
|
+
mounted.current = false;
|
|
211
|
+
cancelAction();
|
|
212
|
+
};
|
|
213
|
+
}, [cancelAction]);
|
|
214
|
+
const setMobileOpen = React.useCallback((next, owner) => {
|
|
215
|
+
var _a2, _b2;
|
|
216
|
+
if (next) cancelAction();
|
|
217
|
+
if (next === state.current.open) return;
|
|
218
|
+
if (pendingAction.current !== owner) cancelAction();
|
|
219
|
+
if (!state.current.controlled) {
|
|
220
|
+
state.current.open = next;
|
|
221
|
+
setInternalOpen(next);
|
|
222
|
+
}
|
|
223
|
+
(_b2 = (_a2 = state.current).onChange) == null ? void 0 : _b2.call(_a2, next);
|
|
224
|
+
}, [cancelAction]);
|
|
225
|
+
const chrome = "data-naeil-chrome" in props ? { "data-naeil-chrome": "", lang: props.lang } : {};
|
|
187
226
|
const [openGroup, setOpenGroup] = React.useState(null);
|
|
188
227
|
const trigger = React.useRef(null);
|
|
189
228
|
const desktop = React.useRef(null);
|
|
229
|
+
const sheetContent = React.useRef(null);
|
|
230
|
+
function settleAction(flow) {
|
|
231
|
+
if (!mounted.current || pendingAction.current !== flow || state.current.open || !flow.menuClosed || !flow.sheetClosed || flow.timer !== void 0) return;
|
|
232
|
+
flow.timer = setTimeout(() => {
|
|
233
|
+
var _a2, _b2;
|
|
234
|
+
flow.timer = void 0;
|
|
235
|
+
if (!mounted.current || pendingAction.current !== flow) return;
|
|
236
|
+
if (state.current.open) {
|
|
237
|
+
cancelAction();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (!flow.menuClosed || !flow.sheetClosed) return;
|
|
241
|
+
const target = [trigger.current, (_b2 = (_a2 = desktop.current) == null ? void 0 : _a2.parentElement) == null ? void 0 : _b2.querySelector(".ui-navigation-toolbar [data-naeil-account-trigger]")].find((element) => (element == null ? void 0 : element.isConnected) && element.getBoundingClientRect().width && element.getBoundingClientRect().height);
|
|
242
|
+
cancelAction();
|
|
243
|
+
if (!target) return;
|
|
244
|
+
target.focus();
|
|
245
|
+
flow.action();
|
|
246
|
+
}, 0);
|
|
247
|
+
}
|
|
248
|
+
const previousHref = React.useRef(currentHref);
|
|
190
249
|
React.useEffect(() => {
|
|
250
|
+
if (previousHref.current === currentHref) return;
|
|
251
|
+
previousHref.current = currentHref;
|
|
191
252
|
setMobileOpen(false);
|
|
192
253
|
setOpenGroup(null);
|
|
193
|
-
}, [currentHref]);
|
|
254
|
+
}, [currentHref, setMobileOpen]);
|
|
194
255
|
React.useEffect(() => {
|
|
195
256
|
const element = desktop.current;
|
|
196
257
|
if (!element || typeof ResizeObserver === "undefined") return;
|
|
@@ -200,7 +261,7 @@ function Navigation(_a) {
|
|
|
200
261
|
});
|
|
201
262
|
observer.observe(element);
|
|
202
263
|
return () => observer.disconnect();
|
|
203
|
-
}, []);
|
|
264
|
+
}, [setMobileOpen]);
|
|
204
265
|
const active = (item) => {
|
|
205
266
|
var _a2;
|
|
206
267
|
return (_a2 = item.active) != null ? _a2 : !item.external && item.href === currentHref;
|
|
@@ -220,8 +281,10 @@ function Navigation(_a) {
|
|
|
220
281
|
),
|
|
221
282
|
onClick: (event) => {
|
|
222
283
|
onNavigate == null ? void 0 : onNavigate(event, item);
|
|
223
|
-
if (event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey)
|
|
224
|
-
close
|
|
284
|
+
if (event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) {
|
|
285
|
+
if (close === "mobile") setMobileOpen(false);
|
|
286
|
+
else if (close === "group") setOpenGroup(null);
|
|
287
|
+
}
|
|
225
288
|
},
|
|
226
289
|
children: [
|
|
227
290
|
item.icon && /* @__PURE__ */ jsx("span", { className: "ui-navigation-icon", "aria-hidden": "true", children: item.icon }),
|
|
@@ -256,6 +319,7 @@ function Navigation(_a) {
|
|
|
256
319
|
(item) => "items" in item ? /* @__PURE__ */ jsx(
|
|
257
320
|
DesktopGroup,
|
|
258
321
|
{
|
|
322
|
+
chrome,
|
|
259
323
|
item,
|
|
260
324
|
active: item.items.some(active),
|
|
261
325
|
open: openGroup === item.id,
|
|
@@ -264,7 +328,7 @@ function Navigation(_a) {
|
|
|
264
328
|
(current) => open ? item.id : current === item.id ? null : current
|
|
265
329
|
),
|
|
266
330
|
children: item.items.map(
|
|
267
|
-
(child) => link(child,
|
|
331
|
+
(child) => link(child, "group", true)
|
|
268
332
|
)
|
|
269
333
|
},
|
|
270
334
|
item.id
|
|
@@ -293,12 +357,26 @@ function Navigation(_a) {
|
|
|
293
357
|
),
|
|
294
358
|
/* @__PURE__ */ jsxs(
|
|
295
359
|
Dialog.Content,
|
|
296
|
-
{
|
|
360
|
+
__spreadProps(__spreadValues({}, chrome), {
|
|
361
|
+
ref: (element) => {
|
|
362
|
+
if (element) sheetContent.current = element;
|
|
363
|
+
},
|
|
297
364
|
"data-slot": "navigation-sheet",
|
|
298
365
|
className: "ui-navigation-sheet ui-popup",
|
|
299
366
|
"aria-describedby": void 0,
|
|
300
367
|
onCloseAutoFocus: (event) => {
|
|
301
368
|
var _a2, _b2;
|
|
369
|
+
if (controlledOpen !== void 0 && (!mounted.current || state.current.open)) {
|
|
370
|
+
event.preventDefault();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const flow = pendingAction.current;
|
|
374
|
+
if (flow && event.target === flow.sheet) {
|
|
375
|
+
event.preventDefault();
|
|
376
|
+
flow.sheetClosed = true;
|
|
377
|
+
settleAction(flow);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
302
380
|
if (trigger.current && trigger.current.getBoundingClientRect().width === 0) {
|
|
303
381
|
event.preventDefault();
|
|
304
382
|
(_b2 = (_a2 = desktop.current) == null ? void 0 : _a2.querySelector("button, a")) == null ? void 0 : _b2.focus();
|
|
@@ -325,16 +403,34 @@ function Navigation(_a) {
|
|
|
325
403
|
(item) => "items" in item ? /* @__PURE__ */ jsxs("section", { "aria-label": item.label, children: [
|
|
326
404
|
/* @__PURE__ */ jsx("h3", { className: "ui-navigation-group-label", children: item.label }),
|
|
327
405
|
item.items.map(
|
|
328
|
-
(child) => link(child,
|
|
406
|
+
(child) => link(child, "mobile", true)
|
|
329
407
|
)
|
|
330
|
-
] }, item.id) : link(item,
|
|
408
|
+
] }, item.id) : link(item, "mobile")
|
|
331
409
|
)
|
|
332
410
|
}
|
|
333
411
|
),
|
|
334
|
-
(mobileToolbar != null ? mobileToolbar : toolbar) && /* @__PURE__ */ jsx("div", { className: "ui-navigation-mobile-toolbar", children:
|
|
412
|
+
(mobileToolbar != null ? mobileToolbar : toolbar) && /* @__PURE__ */ jsx("div", { className: "ui-navigation-mobile-toolbar", children: /* @__PURE__ */ jsx(NavigationSheetAction.Provider, { value: controlledOpen === void 0 ? void 0 : (action) => {
|
|
413
|
+
if (!mounted.current) return { menuClosed: () => {
|
|
414
|
+
}, cancel: () => {
|
|
415
|
+
} };
|
|
416
|
+
cancelAction();
|
|
417
|
+
const flow = { action, sheet: sheetContent.current, menuClosed: false, sheetClosed: false };
|
|
418
|
+
pendingAction.current = flow;
|
|
419
|
+
return {
|
|
420
|
+
menuClosed: () => {
|
|
421
|
+
if (!mounted.current || pendingAction.current !== flow) return;
|
|
422
|
+
flow.menuClosed = true;
|
|
423
|
+
setMobileOpen(false, flow);
|
|
424
|
+
settleAction(flow);
|
|
425
|
+
},
|
|
426
|
+
cancel: () => {
|
|
427
|
+
if (pendingAction.current === flow) cancelAction();
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}, children: mobileToolbar != null ? mobileToolbar : toolbar }) }),
|
|
335
431
|
mobileFooter && /* @__PURE__ */ jsx("div", { className: "ui-navigation-mobile-footer", children: mobileFooter })
|
|
336
432
|
]
|
|
337
|
-
}
|
|
433
|
+
})
|
|
338
434
|
)
|
|
339
435
|
] })
|
|
340
436
|
] })
|
|
@@ -343,5 +439,6 @@ function Navigation(_a) {
|
|
|
343
439
|
);
|
|
344
440
|
}
|
|
345
441
|
export {
|
|
346
|
-
Navigation
|
|
442
|
+
Navigation,
|
|
443
|
+
NavigationSheetAction
|
|
347
444
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Component usage guides
|
|
2
2
|
|
|
3
|
-
These maintained guides cover
|
|
3
|
+
These maintained guides cover published exact **@naeil/ui 0.8.0** and the unpublished **0.9.0 frame candidate**. On 2026-10-29 registry tags were latest=0.7.2 and next=0.8.0; see [release policy and identities](../package-release.md#publication-and-deployment). [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` for the current 17 Docs pages (15 UI and 2 Brand) and choose Docs or Usage. The [hosted Storybook](https://naeil-dev.github.io/naeil-ui/) has recorded [0.5.0 verification](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0); each subsequent deployment requires its own receipt. Select chooses a value; DropdownMenu chooses an action.
|
|
4
4
|
|
|
5
5
|
Start application layouts with the [shared navigation/footer selection and frame guide](application-layout.md). It includes a starter, the 1600px frame contract and instructions to place in consumer repositories so agents reuse the shared implementation.
|
|
6
6
|
|