@naeil/ui 0.7.1 → 0.8.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 +10 -1
- package/README.md +15 -10
- package/dist/{chunk-QQQQZEPV.js → chunk-KPE4KKYM.js} +111 -14
- package/dist/{chunk-OLKTEYLS.js → chunk-ULZPJI5X.js} +103 -73
- package/dist/{chunk-VWIUN35X.js → chunk-WA5D7UI2.js} +1 -1
- 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-content.d.ts +1 -25
- package/dist/source/components/naeil-header-content.js +0 -28
- package/dist/source/components/naeil-header.d.ts +8 -2
- package/dist/source/components/naeil-header.js +50 -24
- 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 +3 -3
- package/docs/components/application-layout.md +48 -13
- package/docs/components/naeil-footer.md +2 -2
- package/docs/components/naeil-header.md +37 -7
- package/docs/design/v2-migration.md +11 -10
- package/docs/package-release.md +44 -14
- package/package.json +1 -1
- package/src/styles/components.css +113 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.7.
|
|
3
|
+
## 0.7.2 — retired project catalog (candidate)
|
|
4
|
+
|
|
5
|
+
- Narrow the canonical `NaeilHeader` project catalog to naeil-ui. Content Collector, PKM, Sustainability Analyzer and Baby Agent are retired from naeil.dev; their site pages move to `src/app/_archive/` and old URLs temporarily redirect to the project list. The project menu uses one column for a single entry. `projects`, `translations` and `currentProjectKey` overrides are unchanged.
|
|
6
|
+
- Release class `dom`: the standing worker adopts it only through an owner-approved `reviewedMigration` of this exact release; ordinary fix-only patches are unaffected.
|
|
7
|
+
|
|
8
|
+
## 0.7.1 — canonical service chrome (published 2026-10-23)
|
|
9
|
+
|
|
10
|
+
Published to npm at `2026-10-23T01:14:11.387Z`; `latest` was verified as `0.7.1`. This first published 0.7.x release includes the unpublished 0.7.0 scope below. Frozen package source and tag `v0.7.1`: `32108fd8e1cb1db6dae24a09f3ca72ac9a91f618`; archive SHA-256: `ecd656c595fd63516417eef58a5e2624d1f1bf928ba0465761292d05502ab3f8`. Independent download and fresh React/Next registry consumers matched the archive and all 139 files, with types, production builds and browser checks passing. Hosted Docs and standing activation require separate receipts; see the [release policy](docs/package-release.md#publication-and-deployment).
|
|
4
11
|
|
|
5
12
|
- Add canonical `NaeilHeaderAccountControl` for shared login/account/logout presentation while consumers retain session and save guards.
|
|
13
|
+
- Include React-only `NaeilFooter`, canonical full/compact destinations, save-navigation interception and server-safe `/release` package/contract identity from the unpublished 0.7.0 candidate.
|
|
6
14
|
- Preserve existing header slots and imports; include authenticated menu/busy/failure coverage and packed consumer checks.
|
|
15
|
+
- Preserve the approved 1600px/clamp frame, generic Navigation/SiteFooter and legacy Nav/Footer APIs. Bundled guides retain their frozen preparation snapshot; maintained guides and hosted Docs use a separately reviewed source.
|
|
7
16
|
|
|
8
17
|
## 0.7.0 — unpublished candidate
|
|
9
18
|
|
package/README.md
CHANGED
|
@@ -2,24 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
React components, semantic design tokens, and shared styles for naeil products. The approved system uses a neutral light/dark palette, Pretendard, 16px controls, comfortable or compact density, purpose-specific content widths, and restrained motion. Radix supplies interaction behavior.
|
|
4
4
|
|
|
5
|
+
**0.7.1 published and registry-verified — 2026-10-23.** The current published baseline is **0.7.1**. This first published 0.7.x release combines canonical `NaeilFooter`, server-safe `/release` identity and `NaeilHeaderAccountControl`; 0.7.0 remains an unpublished candidate. The frozen package source and `v0.7.1` tag are `32108fd8e1cb1db6dae24a09f3ca72ac9a91f618`. Fresh React/Next registry consumers matched all 139 frozen files and passed types, production builds and browser checks. Maintained guides use separately reviewed source; hosted Docs and standing activation require separate receipts. See the [release policy and receipts](docs/package-release.md#publication-and-deployment).
|
|
6
|
+
|
|
5
7
|
This repository also contains the **naeil.dev brand/example website** and an **optional agent reference workflow**. Choose the path you need:
|
|
6
8
|
|
|
7
9
|
| Path | Start here | Tools / keys |
|
|
8
10
|
| --- | --- | --- |
|
|
9
|
-
| Use the UI in an app | [Consumer guide](docs/design/v2-migration.md) | React 19, Tailwind 4; **no Next.js or API keys** for
|
|
11
|
+
| Use the UI in an app | [Consumer guide](docs/design/v2-migration.md) | React 19, Tailwind 4; **no Next.js or API keys** for shared React entries |
|
|
10
12
|
| Develop the shared UI | Commands below and [contributing](https://github.com/naeil-dev/naeil-ui/blob/81fd395031dc5b20074c4d915a8585360ce2d28a/CONTRIBUTING.md) | Node.js 22+, pnpm; no Supabase or 21st key |
|
|
11
13
|
| Use frontend references with an agent | [Optional workflow setup](https://github.com/naeil-dev/naeil-ui/blob/81fd395031dc5b20074c4d915a8585360ce2d28a/docs/design/frontend-tooling.md) | Supported agent; Python for bundled helpers; your own 21st key only for optional MCP |
|
|
12
14
|
| Develop the brand/example website | [Site setup](https://github.com/naeil-dev/naeil-ui/blob/81fd395031dc5b20074c4d915a8585360ce2d28a/docs/site-development.md) | Next.js and a separate Supabase project for site authentication |
|
|
13
15
|
|
|
14
16
|
## Use the UI
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
Use React 19 / ReactDOM 19 and Tailwind 4. UI v2 names the design generation independently of npm versioning. Install the verified registry release:
|
|
17
19
|
|
|
18
20
|
```sh
|
|
19
|
-
pnpm add --save-exact @naeil/ui@0.
|
|
21
|
+
pnpm add --save-exact @naeil/ui@0.7.1
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
For
|
|
24
|
+
The [npm 0.7.1 archive](https://registry.npmjs.org/@naeil/ui/-/ui-0.7.1.tgz) is byte-identical to the frozen release archive. Its bundled guides retain the preparation snapshot; use these maintained instructions for current installation. For local development, `pnpm build:pkg` and `npm pack --ignore-scripts --pack-destination /tmp` produce `/tmp/naeil-ui-0.7.1.tgz`. A fresh pack of maintained source differs from the frozen release archive; use the exact frozen file for release verification. The [0.6.0 release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) retains its archive, publication and deployment evidence.
|
|
23
25
|
|
|
24
26
|
The preceding [0.5.0 release](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0) was published on 2026-10-15 and introduced the whole shared header. Version 0.6.0 adds the shared 1600px frame; existing applications must upgrade and adopt its layout composition.
|
|
25
27
|
|
|
@@ -30,11 +32,13 @@ Choose the shared application composition before building a header or footer:
|
|
|
30
32
|
| Application need | Shared component |
|
|
31
33
|
| --- | --- |
|
|
32
34
|
| Actual naeil.dev brand, project catalog and controls | `NaeilHeader` from `@naeil/ui/naeil-header` |
|
|
35
|
+
| Owned-service login, account and logout presentation (0.7.1) | `NaeilHeaderAccountControl` from `@naeil/ui/naeil-header`, passed in `authSlot` |
|
|
36
|
+
| Owned naeil service footer (0.7.1) | `NaeilFooter` from `@naeil/ui/naeil-footer` |
|
|
33
37
|
| A product's own menu | `Navigation` from `@naeil/ui/ui` |
|
|
34
|
-
|
|
|
38
|
+
| Different product footer or deliberate content override | `SiteFooter` from `@naeil/ui/ui` |
|
|
35
39
|
| Existing Next/next-intl integration | Preserve `Nav` / `Footer` compatibility adapters |
|
|
36
40
|
|
|
37
|
-
Supply application routing, guards, authentication and menu data through adapters and slots. Keep the shared menu, mobile sheet,
|
|
41
|
+
Pair `NaeilHeader` and `NaeilFooter` in owned services. Supply application routing, guards, authentication and menu data through adapters and slots. `NaeilHeaderAccountControl` receives session state, a busy flag and the guarded logout callback; keep pending state and its request lock above the header so desktop/mobile copies share them. Keep the shared menu, mobile sheet, account presentation and footer implementation in the package. The [application layout guide](docs/components/application-layout.md) provides a starter and consumer-agent instructions. Header, main and footer share a 1600px maximum frame with `clamp(16px, 2.4vw, 40px)` gutters; use `ui-app-frame` on main and keep 640px reading/settings or 480px short-form sections inside it.
|
|
38
42
|
|
|
39
43
|
Import primitives from the dedicated entrypoint:
|
|
40
44
|
|
|
@@ -73,9 +77,9 @@ Mark Japanese content with `lang="ja"`. Keep font OFL notices with redistributed
|
|
|
73
77
|
|
|
74
78
|
Shared CSS follows the OS theme unless the document root has `class="light"` or `class="dark"`. For compact spacing, set `data-ui-density="compact"` on that root; omit it for comfortable spacing. Density changes spacing while retaining 16px control text and minimum mobile touch sizes. React apps can also use `ThemeProvider` from `@naeil/ui/components/theme-provider` with `attribute="class"`.
|
|
75
79
|
|
|
76
|
-
The [component guides](docs/components/README.md) cover exact APIs, state composition and accessibility responsibilities
|
|
80
|
+
The [component guides](docs/components/README.md) cover exact APIs, state composition and accessibility responsibilities, with matching Storybook Docs and runnable examples. Canonical footer and account examples live in the existing NaeilHeader/SiteFooter stories. The [consumer guide](docs/design/v2-migration.md) covers peer dependencies, fonts, themes, density, Select versus DropdownMenu, overrides, and layout ownership. Fonts are delivered by the consumer; shared CSS does not fetch them automatically. `/ui`, `/utils`, `/naeil-header`, `/naeil-footer` and `/release` work without Next.js; the last two are included in 0.7.1. The legacy root remains a framework compatibility entry with static Next/next-intl imports; install those optional peers when using it, Nav, Footer or i18n/routing. ThemeProvider/controls and Toaster retain the React library next-themes. Shared deep imports use the exact [allowlist and migration](docs/design/v2-migration.md#03-package-boundary); website-only paths belong in your application.
|
|
77
81
|
|
|
78
|
-
For the actual naeil.dev branded header, import `NaeilHeader` from `@naeil/ui/naeil-header`; generic `Navigation` from `/ui` remains available for different product menus. The whole header owns its catalog, header translations, controls and responsive styles. Supply routing/dirty guards, an existing next-themes provider and real authentication through its props
|
|
82
|
+
For the actual naeil.dev branded header, import `NaeilHeader` and `NaeilHeaderAccountControl` from `@naeil/ui/naeil-header`; generic `Navigation` from `/ui` remains available for different product menus. The whole header owns its catalog, header translations, controls and responsive styles. Supply routing/dirty guards, an existing next-themes provider and real authentication state/actions through its props. Its five decorative project images reference official `naeil.dev` URLs and require network access; their bytes are excluded and unresolved redistribution provenance remains in [third-party notices](THIRD_PARTY_NOTICES.md). See [NaeilHeader](docs/components/naeil-header.md) for fixed positioning, the shared frame, supported locales and adapter contract, and [NaeilFooter](docs/components/naeil-footer.md) for canonical destinations and save interception.
|
|
79
83
|
|
|
80
84
|
## Develop the shared UI
|
|
81
85
|
|
|
@@ -92,7 +96,8 @@ Storybook uses the actual public components with simulated example data. It need
|
|
|
92
96
|
```sh
|
|
93
97
|
pnpm build:pkg
|
|
94
98
|
pnpm pack --pack-destination /tmp
|
|
95
|
-
# In your consumer app: pnpm add /tmp/naeil-ui-0.
|
|
99
|
+
# In your consumer app: pnpm add /tmp/naeil-ui-0.7.1.tgz
|
|
100
|
+
# This development pack does not replace the frozen release archive.
|
|
96
101
|
```
|
|
97
102
|
|
|
98
103
|
| Command | Purpose |
|
|
@@ -104,7 +109,7 @@ pnpm pack --pack-destination /tmp
|
|
|
104
109
|
| `pnpm build:pkg` / `pnpm check:package` | Build package / inspect package boundary and independently install/build packed React + Next consumers |
|
|
105
110
|
| `pnpm dev` / `pnpm build` | Brand/example website development / build |
|
|
106
111
|
|
|
107
|
-
A [reusable React consumer example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) composes the packaged header, a settings form and footer. [Release policy](docs/package-release.md) and [changelog](CHANGELOG.md) distinguish implementation, publication and deployment. CI verifies the package and separate website/Storybook. Downloadable preview artifacts include notices and local fonts. The current
|
|
112
|
+
A [reusable React consumer example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) composes the packaged header, a settings form and footer. [Release policy](docs/package-release.md) and [changelog](CHANGELOG.md) distinguish implementation, publication and deployment. CI verifies the package and separate website/Storybook. Downloadable preview artifacts include notices and local fonts. The current Storybook source defines 17 Docs pages (15 UI families and Brand / Logo plus NaeilHeader), including Usage, JapaneseApplication, ReferenceFull, ReferenceCompact and AccountStates examples. This does not establish a new hosted deployment. [Hosted Docs](https://naeil-dev.github.io/naeil-ui/) have recorded [0.5.0 verification](https://github.com/naeil-dev/naeil-ui/releases/download/v0.5.0/hosted-docs-verification.json); subsequent deployments require their own source and HTTP checks. The same approved brand is live on [naeil.dev](https://naeil.dev/). See [preview instructions](docs/package-release.md#storybook-preview-artifact) and [support limits](docs/design/public-ui-support.md). Native/manual platform acceptance retains its documented limits; service adoption and continuous rollout have separate [dated records](https://github.com/naeil-dev/naeil-ui/tree/main/docs/rollout).
|
|
108
113
|
|
|
109
114
|
Read [DESIGN.md](DESIGN.md) before shared UI changes. Numeric values live in `src/tokens/`; generated CSS is not edited by hand. Shared styles live in `src/styles/`, site-only styles in `src/app/`. Hero art, 3D scenes, cursor effects, and site content belong to the example website.
|
|
110
115
|
|
|
@@ -343,13 +343,15 @@ var NativeLink = React.forwardRef(
|
|
|
343
343
|
return /* @__PURE__ */ jsx4("a", __spreadValues({ ref }, props));
|
|
344
344
|
}
|
|
345
345
|
);
|
|
346
|
+
var NavigationSheetAction = React.createContext(void 0);
|
|
346
347
|
function DesktopGroup({
|
|
347
348
|
item,
|
|
348
349
|
active,
|
|
349
350
|
open,
|
|
350
351
|
anotherGroupOpen,
|
|
351
352
|
onOpenChange,
|
|
352
|
-
children
|
|
353
|
+
children,
|
|
354
|
+
chrome
|
|
353
355
|
}) {
|
|
354
356
|
var _a;
|
|
355
357
|
const hoverOpened = React.useRef(false);
|
|
@@ -428,7 +430,7 @@ function DesktopGroup({
|
|
|
428
430
|
),
|
|
429
431
|
/* @__PURE__ */ jsx4(Popover.Portal, { children: /* @__PURE__ */ jsx4(
|
|
430
432
|
Popover.Content,
|
|
431
|
-
{
|
|
433
|
+
__spreadProps(__spreadValues({}, chrome), {
|
|
432
434
|
ref: panel,
|
|
433
435
|
"data-slot": "navigation-popover",
|
|
434
436
|
className: "ui-navigation-popover ui-popup",
|
|
@@ -454,7 +456,7 @@ function DesktopGroup({
|
|
|
454
456
|
event.preventDefault();
|
|
455
457
|
},
|
|
456
458
|
children: /* @__PURE__ */ jsx4("nav", { "aria-label": item.label, className: "ui-navigation-menu-grid", children })
|
|
457
|
-
}
|
|
459
|
+
})
|
|
458
460
|
) })
|
|
459
461
|
]
|
|
460
462
|
}
|
|
@@ -470,6 +472,8 @@ function Navigation(_a) {
|
|
|
470
472
|
toolbar,
|
|
471
473
|
mobileToolbar,
|
|
472
474
|
mobileFooter,
|
|
475
|
+
mobileOpen: controlledOpen,
|
|
476
|
+
onMobileOpenChange,
|
|
473
477
|
containerClassName,
|
|
474
478
|
onNavigate,
|
|
475
479
|
className
|
|
@@ -482,18 +486,75 @@ function Navigation(_a) {
|
|
|
482
486
|
"toolbar",
|
|
483
487
|
"mobileToolbar",
|
|
484
488
|
"mobileFooter",
|
|
489
|
+
"mobileOpen",
|
|
490
|
+
"onMobileOpenChange",
|
|
485
491
|
"containerClassName",
|
|
486
492
|
"onNavigate",
|
|
487
493
|
"className"
|
|
488
494
|
]);
|
|
489
|
-
const [
|
|
495
|
+
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
496
|
+
const mobileOpen = controlledOpen != null ? controlledOpen : internalOpen;
|
|
497
|
+
const state = React.useRef({ open: mobileOpen, controlled: controlledOpen !== void 0, onChange: onMobileOpenChange });
|
|
498
|
+
const mounted = React.useRef(false);
|
|
499
|
+
const pendingAction = React.useRef(void 0);
|
|
500
|
+
const cancelAction = React.useCallback(() => {
|
|
501
|
+
var _a2;
|
|
502
|
+
clearTimeout((_a2 = pendingAction.current) == null ? void 0 : _a2.timer);
|
|
503
|
+
pendingAction.current = void 0;
|
|
504
|
+
}, []);
|
|
505
|
+
React.useLayoutEffect(() => {
|
|
506
|
+
const reopening = mobileOpen && !state.current.open;
|
|
507
|
+
state.current = { open: mobileOpen, controlled: controlledOpen !== void 0, onChange: onMobileOpenChange };
|
|
508
|
+
if (reopening) cancelAction();
|
|
509
|
+
});
|
|
510
|
+
React.useLayoutEffect(() => {
|
|
511
|
+
mounted.current = true;
|
|
512
|
+
return () => {
|
|
513
|
+
mounted.current = false;
|
|
514
|
+
cancelAction();
|
|
515
|
+
};
|
|
516
|
+
}, [cancelAction]);
|
|
517
|
+
const setMobileOpen = React.useCallback((next, owner) => {
|
|
518
|
+
var _a2, _b2;
|
|
519
|
+
if (next) cancelAction();
|
|
520
|
+
if (next === state.current.open) return;
|
|
521
|
+
if (pendingAction.current !== owner) cancelAction();
|
|
522
|
+
if (!state.current.controlled) {
|
|
523
|
+
state.current.open = next;
|
|
524
|
+
setInternalOpen(next);
|
|
525
|
+
}
|
|
526
|
+
(_b2 = (_a2 = state.current).onChange) == null ? void 0 : _b2.call(_a2, next);
|
|
527
|
+
}, [cancelAction]);
|
|
528
|
+
const chrome = "data-naeil-chrome" in props ? { "data-naeil-chrome": "", lang: props.lang } : {};
|
|
490
529
|
const [openGroup, setOpenGroup] = React.useState(null);
|
|
491
530
|
const trigger = React.useRef(null);
|
|
492
531
|
const desktop = React.useRef(null);
|
|
532
|
+
const sheetContent = React.useRef(null);
|
|
533
|
+
function settleAction(flow) {
|
|
534
|
+
if (!mounted.current || pendingAction.current !== flow || state.current.open || !flow.menuClosed || !flow.sheetClosed || flow.timer !== void 0) return;
|
|
535
|
+
flow.timer = setTimeout(() => {
|
|
536
|
+
var _a2, _b2;
|
|
537
|
+
flow.timer = void 0;
|
|
538
|
+
if (!mounted.current || pendingAction.current !== flow) return;
|
|
539
|
+
if (state.current.open) {
|
|
540
|
+
cancelAction();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (!flow.menuClosed || !flow.sheetClosed) return;
|
|
544
|
+
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);
|
|
545
|
+
cancelAction();
|
|
546
|
+
if (!target) return;
|
|
547
|
+
target.focus();
|
|
548
|
+
flow.action();
|
|
549
|
+
}, 0);
|
|
550
|
+
}
|
|
551
|
+
const previousHref = React.useRef(currentHref);
|
|
493
552
|
React.useEffect(() => {
|
|
553
|
+
if (previousHref.current === currentHref) return;
|
|
554
|
+
previousHref.current = currentHref;
|
|
494
555
|
setMobileOpen(false);
|
|
495
556
|
setOpenGroup(null);
|
|
496
|
-
}, [currentHref]);
|
|
557
|
+
}, [currentHref, setMobileOpen]);
|
|
497
558
|
React.useEffect(() => {
|
|
498
559
|
const element = desktop.current;
|
|
499
560
|
if (!element || typeof ResizeObserver === "undefined") return;
|
|
@@ -503,7 +564,7 @@ function Navigation(_a) {
|
|
|
503
564
|
});
|
|
504
565
|
observer.observe(element);
|
|
505
566
|
return () => observer.disconnect();
|
|
506
|
-
}, []);
|
|
567
|
+
}, [setMobileOpen]);
|
|
507
568
|
const active = (item) => {
|
|
508
569
|
var _a2;
|
|
509
570
|
return (_a2 = item.active) != null ? _a2 : !item.external && item.href === currentHref;
|
|
@@ -523,8 +584,10 @@ function Navigation(_a) {
|
|
|
523
584
|
),
|
|
524
585
|
onClick: (event) => {
|
|
525
586
|
onNavigate == null ? void 0 : onNavigate(event, item);
|
|
526
|
-
if (event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey)
|
|
527
|
-
close
|
|
587
|
+
if (event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) {
|
|
588
|
+
if (close === "mobile") setMobileOpen(false);
|
|
589
|
+
else if (close === "group") setOpenGroup(null);
|
|
590
|
+
}
|
|
528
591
|
},
|
|
529
592
|
children: [
|
|
530
593
|
item.icon && /* @__PURE__ */ jsx4("span", { className: "ui-navigation-icon", "aria-hidden": "true", children: item.icon }),
|
|
@@ -559,6 +622,7 @@ function Navigation(_a) {
|
|
|
559
622
|
(item) => "items" in item ? /* @__PURE__ */ jsx4(
|
|
560
623
|
DesktopGroup,
|
|
561
624
|
{
|
|
625
|
+
chrome,
|
|
562
626
|
item,
|
|
563
627
|
active: item.items.some(active),
|
|
564
628
|
open: openGroup === item.id,
|
|
@@ -567,7 +631,7 @@ function Navigation(_a) {
|
|
|
567
631
|
(current) => open ? item.id : current === item.id ? null : current
|
|
568
632
|
),
|
|
569
633
|
children: item.items.map(
|
|
570
|
-
(child) => link(child,
|
|
634
|
+
(child) => link(child, "group", true)
|
|
571
635
|
)
|
|
572
636
|
},
|
|
573
637
|
item.id
|
|
@@ -596,12 +660,26 @@ function Navigation(_a) {
|
|
|
596
660
|
),
|
|
597
661
|
/* @__PURE__ */ jsxs2(
|
|
598
662
|
Dialog.Content,
|
|
599
|
-
{
|
|
663
|
+
__spreadProps(__spreadValues({}, chrome), {
|
|
664
|
+
ref: (element) => {
|
|
665
|
+
if (element) sheetContent.current = element;
|
|
666
|
+
},
|
|
600
667
|
"data-slot": "navigation-sheet",
|
|
601
668
|
className: "ui-navigation-sheet ui-popup",
|
|
602
669
|
"aria-describedby": void 0,
|
|
603
670
|
onCloseAutoFocus: (event) => {
|
|
604
671
|
var _a2, _b2;
|
|
672
|
+
if (controlledOpen !== void 0 && (!mounted.current || state.current.open)) {
|
|
673
|
+
event.preventDefault();
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const flow = pendingAction.current;
|
|
677
|
+
if (flow && event.target === flow.sheet) {
|
|
678
|
+
event.preventDefault();
|
|
679
|
+
flow.sheetClosed = true;
|
|
680
|
+
settleAction(flow);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
605
683
|
if (trigger.current && trigger.current.getBoundingClientRect().width === 0) {
|
|
606
684
|
event.preventDefault();
|
|
607
685
|
(_b2 = (_a2 = desktop.current) == null ? void 0 : _a2.querySelector("button, a")) == null ? void 0 : _b2.focus();
|
|
@@ -628,16 +706,34 @@ function Navigation(_a) {
|
|
|
628
706
|
(item) => "items" in item ? /* @__PURE__ */ jsxs2("section", { "aria-label": item.label, children: [
|
|
629
707
|
/* @__PURE__ */ jsx4("h3", { className: "ui-navigation-group-label", children: item.label }),
|
|
630
708
|
item.items.map(
|
|
631
|
-
(child) => link(child,
|
|
709
|
+
(child) => link(child, "mobile", true)
|
|
632
710
|
)
|
|
633
|
-
] }, item.id) : link(item,
|
|
711
|
+
] }, item.id) : link(item, "mobile")
|
|
634
712
|
)
|
|
635
713
|
}
|
|
636
714
|
),
|
|
637
|
-
(mobileToolbar != null ? mobileToolbar : toolbar) && /* @__PURE__ */ jsx4("div", { className: "ui-navigation-mobile-toolbar", children:
|
|
715
|
+
(mobileToolbar != null ? mobileToolbar : toolbar) && /* @__PURE__ */ jsx4("div", { className: "ui-navigation-mobile-toolbar", children: /* @__PURE__ */ jsx4(NavigationSheetAction.Provider, { value: controlledOpen === void 0 ? void 0 : (action) => {
|
|
716
|
+
if (!mounted.current) return { menuClosed: () => {
|
|
717
|
+
}, cancel: () => {
|
|
718
|
+
} };
|
|
719
|
+
cancelAction();
|
|
720
|
+
const flow = { action, sheet: sheetContent.current, menuClosed: false, sheetClosed: false };
|
|
721
|
+
pendingAction.current = flow;
|
|
722
|
+
return {
|
|
723
|
+
menuClosed: () => {
|
|
724
|
+
if (!mounted.current || pendingAction.current !== flow) return;
|
|
725
|
+
flow.menuClosed = true;
|
|
726
|
+
setMobileOpen(false, flow);
|
|
727
|
+
settleAction(flow);
|
|
728
|
+
},
|
|
729
|
+
cancel: () => {
|
|
730
|
+
if (pendingAction.current === flow) cancelAction();
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}, children: mobileToolbar != null ? mobileToolbar : toolbar }) }),
|
|
638
734
|
mobileFooter && /* @__PURE__ */ jsx4("div", { className: "ui-navigation-mobile-footer", children: mobileFooter })
|
|
639
735
|
]
|
|
640
|
-
}
|
|
736
|
+
})
|
|
641
737
|
)
|
|
642
738
|
] })
|
|
643
739
|
] })
|
|
@@ -894,6 +990,7 @@ export {
|
|
|
894
990
|
SelectSeparator,
|
|
895
991
|
SelectScrollUpButton,
|
|
896
992
|
SelectScrollDownButton,
|
|
993
|
+
NavigationSheetAction,
|
|
897
994
|
Navigation,
|
|
898
995
|
DropdownMenu,
|
|
899
996
|
DropdownMenuPortal,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Logo,
|
|
4
4
|
uiRelease
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WA5D7UI2.js";
|
|
6
6
|
import {
|
|
7
7
|
Badge,
|
|
8
8
|
Button,
|
|
@@ -13,12 +13,13 @@ import {
|
|
|
13
13
|
DropdownMenuSeparator,
|
|
14
14
|
DropdownMenuTrigger,
|
|
15
15
|
Navigation,
|
|
16
|
+
NavigationSheetAction,
|
|
16
17
|
Select,
|
|
17
18
|
SelectContent,
|
|
18
19
|
SelectItem,
|
|
19
20
|
SelectTrigger,
|
|
20
21
|
SelectValue
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-KPE4KKYM.js";
|
|
22
23
|
import {
|
|
23
24
|
__objRest,
|
|
24
25
|
__spreadProps,
|
|
@@ -35,40 +36,12 @@ import { Moon, Sun } from "lucide-react";
|
|
|
35
36
|
var headerLocales = ["en", "ko", "ja"];
|
|
36
37
|
var headerLocaleNames = { en: "English", ko: "\uD55C\uAD6D\uC5B4", ja: "\u65E5\u672C\u8A9E" };
|
|
37
38
|
var headerProjects = [
|
|
38
|
-
{
|
|
39
|
-
"key": "cc",
|
|
40
|
-
"href": "/cc",
|
|
41
|
-
"accent": "oklch(0.704 0.140 181)",
|
|
42
|
-
"icon": "coral",
|
|
43
|
-
"status": "active"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"key": "pkm",
|
|
47
|
-
"href": "/pkm",
|
|
48
|
-
"accent": "oklch(0.702 0.183 293)",
|
|
49
|
-
"icon": "jellyfish",
|
|
50
|
-
"status": "coming"
|
|
51
|
-
},
|
|
52
39
|
{
|
|
53
40
|
"key": "naeilUi",
|
|
54
41
|
"href": "/design",
|
|
55
42
|
"accent": "oklch(0.623 0.214 259)",
|
|
56
43
|
"icon": "whale",
|
|
57
44
|
"status": "active"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"key": "sa",
|
|
61
|
-
"href": "/sa",
|
|
62
|
-
"accent": "oklch(0.723 0.219 149)",
|
|
63
|
-
"icon": "fish",
|
|
64
|
-
"status": "active"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"key": "babyAgent",
|
|
68
|
-
"href": "/baby-agent",
|
|
69
|
-
"accent": "oklch(0.769 0.188 70.08)",
|
|
70
|
-
"icon": "turtle",
|
|
71
|
-
"status": "coming"
|
|
72
45
|
}
|
|
73
46
|
];
|
|
74
47
|
var headerContent = {
|
|
@@ -205,6 +178,7 @@ var headerContent = {
|
|
|
205
178
|
|
|
206
179
|
// src/components/naeil-header-account.tsx
|
|
207
180
|
import { UserRound } from "lucide-react";
|
|
181
|
+
import { useContext, useRef } from "react";
|
|
208
182
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
209
183
|
var labels = {
|
|
210
184
|
en: { menu: "Account menu", account: "Account", login: "Log in", logout: "Log out", busy: "Logging out\u2026" },
|
|
@@ -214,10 +188,18 @@ var labels = {
|
|
|
214
188
|
var NativeLink = (props) => /* @__PURE__ */ jsx("a", __spreadValues({}, props));
|
|
215
189
|
function NaeilHeaderAccountControl({ locale, account, Link = NativeLink }) {
|
|
216
190
|
const text = labels[locale];
|
|
191
|
+
const handoff = useContext(NavigationSheetAction);
|
|
192
|
+
const pendingAction = useRef(void 0);
|
|
217
193
|
if (account.status === "signed-out") {
|
|
218
194
|
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 });
|
|
219
195
|
}
|
|
220
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, {
|
|
196
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { onOpenChange: (open) => {
|
|
197
|
+
var _a;
|
|
198
|
+
if (open) {
|
|
199
|
+
(_a = pendingAction.current) == null ? void 0 : _a.cancel();
|
|
200
|
+
pendingAction.current = void 0;
|
|
201
|
+
}
|
|
202
|
+
}, children: [
|
|
221
203
|
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
222
204
|
"button",
|
|
223
205
|
{
|
|
@@ -230,24 +212,46 @@ function NaeilHeaderAccountControl({ locale, account, Link = NativeLink }) {
|
|
|
230
212
|
children: /* @__PURE__ */ jsx("span", { className: "naeil-header-account-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx(UserRound, {}) })
|
|
231
213
|
}
|
|
232
214
|
) }),
|
|
233
|
-
/* @__PURE__ */ jsxs(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
215
|
+
/* @__PURE__ */ jsxs(
|
|
216
|
+
DropdownMenuContent,
|
|
217
|
+
{
|
|
218
|
+
align: "end",
|
|
219
|
+
className: "w-56 max-w-[calc(100vw-2rem)]",
|
|
220
|
+
"data-naeil-account-menu": true,
|
|
221
|
+
"data-naeil-chrome": "",
|
|
222
|
+
lang: locale,
|
|
223
|
+
onCloseAutoFocus: (event) => {
|
|
224
|
+
const flow = pendingAction.current;
|
|
225
|
+
if (!flow || event.target !== flow.menu) return;
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
flow.menu = null;
|
|
228
|
+
flow.menuClosed();
|
|
229
|
+
},
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex min-w-0 flex-col font-normal", children: [
|
|
232
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-base font-medium", children: account.displayName || text.account }),
|
|
233
|
+
account.email && /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-muted-foreground", children: account.email })
|
|
234
|
+
] }),
|
|
235
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
236
|
+
/* @__PURE__ */ jsx(
|
|
237
|
+
DropdownMenuItem,
|
|
238
|
+
{
|
|
239
|
+
className: "min-h-11",
|
|
240
|
+
disabled: account.busy,
|
|
241
|
+
onSelect: (event) => {
|
|
242
|
+
if (account.busy) return;
|
|
243
|
+
if (handoff) {
|
|
244
|
+
const menu = event.target.closest("[data-naeil-account-menu]");
|
|
245
|
+
if (!(menu == null ? void 0 : menu.isConnected)) return;
|
|
246
|
+
pendingAction.current = __spreadProps(__spreadValues({}, handoff(account.onLogout)), { menu });
|
|
247
|
+
} else account.onLogout();
|
|
248
|
+
},
|
|
249
|
+
children: account.busy ? text.busy : text.logout
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
)
|
|
251
255
|
] });
|
|
252
256
|
}
|
|
253
257
|
|
|
@@ -282,7 +286,7 @@ function NaeilHeaderLocaleControl({ locale, onLocaleChange }) {
|
|
|
282
286
|
children: /* @__PURE__ */ jsx2(SelectValue, { children: locale.toUpperCase() })
|
|
283
287
|
}
|
|
284
288
|
),
|
|
285
|
-
/* @__PURE__ */ jsx2(SelectContent, { align: "end", children: headerLocales.map((value) => /* @__PURE__ */ jsx2(SelectItem, { value, className: "min-h-11", lang: value, children: headerLocaleNames[value] }, value)) })
|
|
289
|
+
/* @__PURE__ */ jsx2(SelectContent, { align: "end", "data-naeil-chrome": "", "data-naeil-locale-menu": "", lang: locale, children: headerLocales.map((value) => /* @__PURE__ */ jsx2(SelectItem, { value, className: "min-h-11", lang: value, children: headerLocaleNames[value] }, value)) })
|
|
286
290
|
] });
|
|
287
291
|
}
|
|
288
292
|
var NativeLink2 = (props) => /* @__PURE__ */ jsx2("a", __spreadValues({}, props));
|
|
@@ -292,6 +296,9 @@ function NaeilHeader(_a) {
|
|
|
292
296
|
resolveHref = (path) => `https://naeil.dev/${locale}${path === "/" ? "" : path}`,
|
|
293
297
|
brandHref,
|
|
294
298
|
brandSlot,
|
|
299
|
+
brandSuffix,
|
|
300
|
+
statusSlot,
|
|
301
|
+
appNavigation,
|
|
295
302
|
brandLinkProps,
|
|
296
303
|
brandLinkComponent,
|
|
297
304
|
onLocaleChange,
|
|
@@ -316,6 +323,9 @@ function NaeilHeader(_a) {
|
|
|
316
323
|
"resolveHref",
|
|
317
324
|
"brandHref",
|
|
318
325
|
"brandSlot",
|
|
326
|
+
"brandSuffix",
|
|
327
|
+
"statusSlot",
|
|
328
|
+
"appNavigation",
|
|
319
329
|
"brandLinkProps",
|
|
320
330
|
"brandLinkComponent",
|
|
321
331
|
"onLocaleChange",
|
|
@@ -358,41 +368,61 @@ function NaeilHeader(_a) {
|
|
|
358
368
|
const brandDestination = brandHref != null ? brandHref : resolveHref("/");
|
|
359
369
|
const BrandLink = brandLinkComponent != null ? brandLinkComponent : Link;
|
|
360
370
|
const _a2 = brandLinkProps != null ? brandLinkProps : {}, { onClick: onBrandClick } = _a2, brandProps = __objRest(_a2, ["onClick"]);
|
|
361
|
-
const toolbar = /* @__PURE__ */ jsx2(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
371
|
+
const toolbar = /* @__PURE__ */ jsx2(
|
|
372
|
+
"div",
|
|
373
|
+
{
|
|
374
|
+
"data-app-toolbar": true,
|
|
375
|
+
className: "naeil-header-controls",
|
|
376
|
+
"data-naeil-chrome": toolbarSlot === void 0 ? "" : void 0,
|
|
377
|
+
lang: toolbarSlot === void 0 ? locale : void 0,
|
|
378
|
+
children: toolbarSlot !== void 0 ? toolbarSlot : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
379
|
+
/* @__PURE__ */ jsx2(NaeilHeaderThemeControl, { locale }),
|
|
380
|
+
onLocaleChange && /* @__PURE__ */ jsx2(NaeilHeaderLocaleControl, { locale, onLocaleChange }),
|
|
381
|
+
authSlot && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
382
|
+
/* @__PURE__ */ jsx2("div", { "aria-hidden": "true", className: "naeil-header-separator" }),
|
|
383
|
+
authSlot
|
|
384
|
+
] })
|
|
385
|
+
] })
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
const logo = /* @__PURE__ */ jsx2(Logo, { variant: "horizontal", size: 28, className: "block", label: null });
|
|
389
|
+
const brand = /* @__PURE__ */ jsx2(
|
|
390
|
+
BrandLink,
|
|
391
|
+
__spreadProps(__spreadValues({
|
|
392
|
+
href: brandDestination,
|
|
393
|
+
"data-app-brand": true,
|
|
394
|
+
"aria-label": brandSlot == null ? ".naeil" : void 0
|
|
395
|
+
}, brandProps), {
|
|
396
|
+
onClick: (event) => {
|
|
397
|
+
onBrandClick == null ? void 0 : onBrandClick(event);
|
|
398
|
+
onNavigate == null ? void 0 : onNavigate(event, { id: "brand", href: brandDestination, label: ".naeil" });
|
|
399
|
+
},
|
|
400
|
+
children: brandSlot != null ? brandSlot : brandSuffix ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
401
|
+
logo,
|
|
402
|
+
/* @__PURE__ */ jsx2("span", { className: "naeil-header-brand-suffix", children: brandSuffix })
|
|
403
|
+
] }) : logo
|
|
404
|
+
})
|
|
405
|
+
);
|
|
369
406
|
return /* @__PURE__ */ jsx2(
|
|
370
407
|
Navigation,
|
|
371
408
|
__spreadProps(__spreadValues({}, props), {
|
|
372
409
|
"data-global-header": true,
|
|
373
410
|
"data-naeil-header": true,
|
|
411
|
+
"data-naeil-chrome": "",
|
|
412
|
+
lang: locale,
|
|
374
413
|
"data-naeil-ui-version": uiRelease.version,
|
|
375
414
|
"data-naeil-ui-contract": uiRelease.contractVersion,
|
|
376
415
|
Link,
|
|
377
416
|
currentHref,
|
|
378
417
|
onNavigate,
|
|
379
|
-
brand: /* @__PURE__ */
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
"data-app-brand": true,
|
|
384
|
-
"aria-label": brandSlot == null ? ".naeil" : void 0
|
|
385
|
-
}, brandProps), {
|
|
386
|
-
onClick: (event) => {
|
|
387
|
-
onBrandClick == null ? void 0 : onBrandClick(event);
|
|
388
|
-
onNavigate == null ? void 0 : onNavigate(event, { id: "brand", href: brandDestination, label: ".naeil" });
|
|
389
|
-
},
|
|
390
|
-
children: brandSlot != null ? brandSlot : /* @__PURE__ */ jsx2(Logo, { variant: "horizontal", size: 28, className: "block", label: null })
|
|
391
|
-
})
|
|
392
|
-
),
|
|
418
|
+
brand: statusSlot != null ? /* @__PURE__ */ jsxs2("div", { className: "naeil-header-brand-row", children: [
|
|
419
|
+
brand,
|
|
420
|
+
statusSlot
|
|
421
|
+
] }) : brand,
|
|
393
422
|
items: [
|
|
394
|
-
...showProjects ? [{ id: "projects", label: (_b2 = translations == null ? void 0 : translations.projects) != null ? _b2 : content.nav.projects, items: projectLinks, columns: 2 }] : [],
|
|
423
|
+
...showProjects ? [{ id: "projects", label: (_b2 = translations == null ? void 0 : translations.projects) != null ? _b2 : content.nav.projects, items: projectLinks, columns: projectLinks.length > 1 ? 2 : 1 }] : [],
|
|
395
424
|
...showBlog ? [{ id: "blog", label: (_c = translations == null ? void 0 : translations.blog) != null ? _c : content.nav.blog, href: resolveHref("/blog"), external: projectsExternal }] : [],
|
|
425
|
+
...appNavigation != null ? appNavigation : [],
|
|
396
426
|
...(subNav != null ? subNav : []).map((item) => __spreadProps(__spreadValues({}, item), { id: item.href }))
|
|
397
427
|
],
|
|
398
428
|
labels: labels2 != null ? labels2 : { navigation: content.nav.navigation, menu: content.nav.menu, close: content.nav.closeMenu },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { N as NavigationLink, a as NavigationProps } from '../navigation-
|
|
3
|
-
import { S as SiteFooterProps } from '../site-footer-
|
|
2
|
+
import { N as NavigationLink, a as NavigationProps } from '../navigation-D8N1tMTu.js';
|
|
3
|
+
import { S as SiteFooterProps } from '../site-footer-s113FZgC.js';
|
|
4
4
|
import { ThemeProvider as ThemeProvider$1 } from 'next-themes';
|
|
5
5
|
import * as React$1 from 'react';
|
|
6
6
|
import { SVGProps } from 'react';
|