@naeil/ui 0.6.0 → 0.7.1

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/contracts/naeil-chrome.v1.json +12 -0
  3. package/dist/chunk-HSTDPY56.js +74 -0
  4. package/dist/chunk-OLKTEYLS.js +413 -0
  5. package/dist/{chunk-CE73AEJZ.js → chunk-QQQQZEPV.js} +249 -3
  6. package/dist/{chunk-VL7FS4QM.js → chunk-VWIUN35X.js} +4 -343
  7. package/dist/components/index.d.ts +2 -2
  8. package/dist/components/index.js +10 -8
  9. package/dist/components/naeil-footer.d.ts +16 -0
  10. package/dist/components/naeil-footer.js +76 -0
  11. package/dist/components/naeil-header.d.ts +23 -5
  12. package/dist/components/naeil-header.js +5 -2
  13. package/dist/components/ui/index.d.ts +2 -2
  14. package/dist/components/ui/index.js +6 -6
  15. package/dist/lib/ui-release.d.ts +1 -0
  16. package/dist/lib/ui-release.js +1 -0
  17. package/dist/naeil-header-content-BvISJ4RV.d.ts +4 -0
  18. package/dist/{navigation-CL10a6Eb.d.ts → navigation-CgPeL9tF.d.ts} +1 -1
  19. package/dist/{site-footer-B67meCDv.d.ts → site-footer-vXDa_6yr.d.ts} +4 -2
  20. package/dist/source/components/naeil-footer.d.ts +16 -0
  21. package/dist/source/components/naeil-footer.js +95 -0
  22. package/dist/source/components/naeil-header-account.d.ts +26 -0
  23. package/dist/source/components/naeil-header-account.js +67 -0
  24. package/dist/source/components/naeil-header.d.ts +2 -1
  25. package/dist/source/components/naeil-header.js +5 -0
  26. package/dist/source/components/ui/site-footer.d.ts +4 -2
  27. package/dist/source/components/ui/site-footer.js +6 -2
  28. package/dist/source/lib/ui-release.d.ts +6 -0
  29. package/dist/source/lib/ui-release.js +4 -0
  30. package/docs/components/README.md +2 -1
  31. package/docs/components/application-layout.md +8 -7
  32. package/docs/components/naeil-footer.md +20 -0
  33. package/docs/components/naeil-header.md +21 -5
  34. package/docs/components/navigation.md +3 -1
  35. package/docs/package-release.md +3 -1
  36. package/package.json +16 -4
  37. package/src/styles/components.css +35 -0
  38. package/dist/chunk-QSTGUEEI.js +0 -323
@@ -0,0 +1,20 @@
1
+ # NaeilFooter — canonical service footer
2
+
3
+ New in the **0.7.0 candidate** (not published). React-only opt-in entry:
4
+
5
+ ```tsx
6
+ import { NaeilFooter } from '@naeil/ui/naeil-footer';
7
+ <NaeilFooter locale="ja" variant="compact" Link={GuardedLink} />
8
+ ```
9
+
10
+ Use with [NaeilHeader](naeil-header.md) on owned user-facing services. Both `full` and `compact` contain the .naeil home link, Projects, Blog and the official naeil GitHub link. Full also displays the existing tagline. Compact reduces spacing/description, not the destinations. No social account or legal claim is invented. The website's former generic X homepage icon is not a canonical account link.
11
+
12
+ `locale` selects `en`, `ko`, or `ja`. `resolveHref(path)` defaults to official localized naeil.dev destinations; a local website router may resolve unlocalized paths itself. `Link` handles **every** canonical anchor, including absolute GitHub URLs. It must forward href, target, rel, onClick, className and children. GitHub opens a new tab with noopener/noreferrer. The shared implementation does not itself trigger navigation or await saves.
13
+
14
+ `onNavigate(event, item)` reaches the brand and all group links. An application can synchronously preventDefault, flush its draft, and navigate only after successful saving. Preserve defaultPrevented, modified/middle clicks and target behavior according to the application's established guard. Do not run both adapter and onNavigate guards twice. Native explicit `external:true` links in generic SiteFooter bypass Link for compatibility, but **still call onNavigate**. This closes the former missing interception path.
15
+
16
+ `variant`, `className`, `containerClassName`, standard footer attributes, `meta` and children remain available. Product status/legal information belongs in meta; no private build data belongs in the UI. Canonical content has no arbitrary replacement groups/brand. Existing generic SiteFooter and Next/next-intl Footer keep their override APIs. Record intentional departures from the canonical composition as rollout exceptions; same component imports alone are not adoption evidence.
17
+
18
+ Footer owns its internal 1600px/clamp frame. Do not wrap it in an additional padded container. Its `data-naeil-footer`, `data-naeil-ui-version` and `data-naeil-ui-contract` identify the actual rendering. `@naeil/ui/release` exports `uiRelease` for the application-owned public artifact identity endpoint. That endpoint exposes only schemaVersion, appId, artifactId, sourceRevision and ui. Release version is not proof of deployment without the matching app artifact and rendered checks.
19
+
20
+ Actual shared examples: **Brand / NaeilHeader / ReferenceFull, ReferenceCompact, JapaneseApplication**. Full legacy Footer examples remain compatibility checks. Consumer saves, failures, auth, real supported locales and modified clicks require product tests.
@@ -1,14 +1,17 @@
1
1
  # NaeilHeader — branded whole-header composition
2
2
 
3
- This guide targets the **@naeil/ui 0.6.0**. See the [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) for archive and publication evidence. Import this opt-in React-only entry when an application inherits the actual naeil.dev header. Generic `Navigation` from `/ui` remains available for different product menus.
3
+ This guide includes the **@naeil/ui 0.7.1 candidate**, whose unchanged frame comes from published 0.6.0. See the [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) for archive and publication evidence. Import this opt-in React-only entry when an application inherits the actual naeil.dev header. Generic `Navigation` from `/ui` remains available for different product menus.
4
4
 
5
5
  ```tsx
6
- import { NaeilHeader } from '@naeil/ui/naeil-header';
6
+ import { NaeilHeader, NaeilHeaderAccountControl } from '@naeil/ui/naeil-header';
7
7
  import '@naeil/ui/globals.css';
8
8
 
9
9
  // Inside the consumer's existing next-themes ThemeProvider:
10
10
  <NaeilHeader locale="ja" Link={GuardedLink} currentHref={pathname}
11
- authSlot={<LogoutButton />} />
11
+ authSlot={<NaeilHeaderAccountControl locale="ja" Link={GuardedLink}
12
+ account={authenticated
13
+ ? { status: 'signed-in', onLogout: () => { void guardedLogout(); }, busy: leaving }
14
+ : { status: 'signed-out', loginHref: '/login' }} />} />
12
15
  ```
13
16
 
14
17
  Load shared styles once. Apps that already own their Tailwind/base styles may import both `@naeil/ui/theme.css` and `@naeil/ui/components.css` instead of globals. Tailwind 4 consumers must register the installed `dist` with `@source`; see [setup](../design/v2-migration.md#시작하기).
@@ -19,7 +22,7 @@ The module owns the brand, five-project catalog and header-only translations, pr
19
22
 
20
23
  Consumers provide an existing next-themes provider, routing/dirty-save guard, current locale and real authentication behavior. Default destinations point to `https://naeil.dev/{locale}`; the site's localized Link instead uses `resolveHref={path => path}`. A supplied `Link` handles default official destinations, including cross-origin URLs. `onNavigate` can intercept brand and menu links; honor modified clicks and target behavior. Explicit `external: true` project overrides and `projectsExternal` use native anchors, so intercept them with `onNavigate` when a dirty guard is required.
21
24
 
22
- Only provide `onLocaleChange` when the application's interface supports all three canonical locales (`en`, `ko`, `ja`). Omitting it removes the locale control; the component does not create application translations or shared preferences. `authSlot` contains the consumer's real session action and must retain at least 44px touch targets. It does not integrate site SSO, share credentials or create accounts. Theme preferences remain consumer-origin state. Without a working provider, rendering alone does not establish theme switching.
25
+ Only provide `onLocaleChange` when the application's interface supports all three canonical locales (`en`, `ko`, `ja`). Omitting it removes the locale control; the component does not create application translations or shared preferences. `authSlot` uses `NaeilHeaderAccountControl` in owned applications: a shared44px neutral account trigger and localized Radix logout menu. Consumers supply their real session state/callbacks, not alternative account markup. The legacy slot remains public for deliberate compatibility overrides. It does not integrate site SSO, share credentials or create accounts. Theme preferences remain consumer-origin state. Without a working provider, rendering alone does not establish theme switching.
23
26
 
24
27
  | Prop | Contract |
25
28
  | --- | --- |
@@ -27,7 +30,7 @@ Only provide `onLocaleChange` when the application's interface supports all thre
27
30
  | `resolveHref` | Resolves canonical unlocalized paths; defaults to the official localized site. |
28
31
  | `Link`, `currentHref`, `onNavigate` | Routing adapter, active destination and navigation interception inherited from Navigation. |
29
32
  | `onLocaleChange` | Optional supported-language callback; omit for a single-language application. |
30
- | `authSlot` | Consumer-owned authentication action in the default toolbar. |
33
+ | `authSlot` | Canonical `NaeilHeaderAccountControl`, with consumer-owned auth state/actions. Legacy arbitrary markup remains supported. |
31
34
  | `brandHref`, `brandSlot`, `brandLinkProps`, `brandLinkComponent` | Explicit brand destination/content/anchor overrides; default accessible brand name is `.naeil`. |
32
35
  | `projects`, `projectsExternal`, `showProjects`, `showBlog` | Legacy catalog/visibility/native-link overrides; defaults use the complete canonical catalog. |
33
36
  | `subNav`, `currentProjectKey`, `currentProjectHref` | Legacy extra links and current-project destination. |
@@ -37,6 +40,17 @@ Only provide `onLocaleChange` when the application's interface supports all thre
37
40
 
38
41
  Standard header attributes and the remaining inherited [Navigation props](navigation.md) remain available. Compatibility overrides preserve existing consumers; new applications normally use the canonical composition.
39
42
 
43
+ ## Account presentation
44
+
45
+ `NaeilHeaderAccountControl` accepts `locale`, optional routing `Link`, and `account`:
46
+
47
+ - `{ status: 'signed-out', loginHref }` renders the shared login link through that adapter.
48
+ - `{ status: 'signed-in', onLogout, busy?, displayName?, email? }` renders the same account icon in every app; optional identity appears only inside its menu. There is no shared identity or avatar fetch.
49
+ - `onLogout` starts the app's guarded action; the app handles async errors, cancellation, session endpoints and redirects. `busy` disables the menu action while keeping the trigger available, with `aria-busy`, for inspection and focus restoration. Cancel/failure clears busy and permits retry.
50
+ - Callback invocation closes the menu and returns focus to the trigger. Mobile navigation may remain open during a failed action. The account menu uses the existing Radix overlay/inert implementation.
51
+
52
+ Keep errors visible in the consumer's recovery UI or toast. The package never calls an authentication endpoint. Compare signed-in and signed-out render states separately; anonymous-only acceptance cannot establish authenticated parity. `AccountStates` is a synthetic Storybook example for pending/cancel/error/retry.
53
+
40
54
  ## Frame and content clearance
41
55
 
42
56
  The fixed header is 65px: the current 64px navigation token plus a 1px border. Clear it in the application's main, sticky regions and focus/anchor scroll margins with `calc(var(--ui-navigation-height) + 1px)`. The header, main and footer now share a 1600px maximum outer frame, including responsive `clamp(16px, 2.4vw, 40px)` gutters. Put `ui-app-frame` on main and reading/settings or short-form sections inside it; the header and shared footer apply their own internal frame. Do not wrap them in another padded frame. Apply deliberate `--ui-width-frame` / `--ui-frame-gutter` overrides on a common ancestor. Remove old `max-w-7xl` or `px-6` overrides to adopt the new defaults. This width update supersedes the previous 1280px/24px branded frame. See the [application layout starter](application-layout.md).
@@ -48,3 +62,5 @@ The five decorative images reference `https://naeil.dev/images/{coral,jellyfish,
48
62
  This entry requires no Next.js, next-intl, site session/auth backend, full application message catalog, hero or site public folder. Generic `/ui` imports do not import its catalog. The local Storybook provides **Brand / NaeilHeader / Docs, Usage, JapaneseApplication**; the Japanese example omits unsupported locale switching and uses a consumer-owned auth slot. Candidate examples do not establish hosted deployment.
49
63
 
50
64
  Verify actual packed consumption, Storybook behavior and rendered comparisons at matching viewport/language/theme. Source imports alone do not prove visual parity. Native Safari, devices, assistive technology, full OAuth and unresolved accessibility observations retain the [documented support limits](../design/public-ui-support.md).
65
+
66
+ Pair owned-service headers with [NaeilFooter](naeil-footer.md). The 0.7.1 candidate emits package/contract identifiers for actual rendered validation.
@@ -40,7 +40,7 @@ Desktop groups open on mouse hover without moving keyboard focus. Leaving both t
40
40
 
41
41
  Container queries select desktop layout at 48rem of available header content width. Navigation, main and footer share a maximum 1600px outer frame and `clamp(16px, 2.4vw, 40px)` gutters by default. Use `ui-app-frame` on main; do not wrap Navigation or SiteFooter in another padded frame. Reading/settings sections inside main retain their 640px cap. The [application layout guide](application-layout.md) covers composition and consistent root token overrides. Header height uses `ui.navigation-height` (64px); toolbar controls should remain short, with volatile counts in the content area. Navigation itself is in document flow; `className` can make it sticky or fixed. Fixed consumers must reserve its height in page composition. A narrow form must not constrain the header. Explicit className/style remain consumer overrides.
42
42
 
43
- `SiteFooter` supports `brand`, `description`, `groups: [{ id, label, links }]`, `meta`, children, `Link`, `containerClassName` and `variant="full|compact"`. Each link group is a labeled navigation landmark. Translated titles, destinations, legal/version text and social links belong to the consumer. A product may place a compact footer in its mobile sheet; a full marketing footer is not mandatory in a dashboard.
43
+ `SiteFooter` supports `brand`, `description`, `groups: [{ id, label, links }]`, `meta`, children, `Link`, `containerClassName`, `variant="full|compact"` and `onNavigate(event, item)` (0.7.0 candidate, including explicit external anchors). Each link group is a labeled navigation landmark. Translated titles, destinations, legal/version text and social links belong to the consumer. A product may place a compact footer in its mobile sheet; a full marketing footer is not mandatory in a dashboard.
44
44
 
45
45
  Desktop reserves `ui.navigation-brand-width` (160px) for the brand, followed by the existing 16px row gap. With matching frame, viewport and font conditions, `.naeil` and `.naeil Home` therefore start their first menu at the same position. Mobile keeps intrinsic brand width. For a larger custom brand, override `--ui-navigation-brand-width` on the header or shared product frame; use the same value across related compositions and ensure the brand fits the reserved area.
46
46
 
@@ -55,3 +55,5 @@ Existing `Nav` and `Footer` imports and props remain. `Nav` adapts the canonical
55
55
  `Nav`/`Footer` continue to need Next/next-intl as documented. React-only services use this core for their own product menus or `/naeil-header` for the canonical branded composition. ThemeProvider, ThemeToggleIcon, LocaleSwitcher and Logo remain available through their existing React-safe deep imports. Reusing controls does not synchronize theme/language/authentication between origins.
56
56
 
57
57
  Storybook: `UI/Navigation` (website, Home composition, long labels), `UI/SiteFooter` (full and compact). Naeil Home is a synthetic composition based on the inspected live menu, not proof of a deployed migration. The repository task note `docs/design/navigation-footer-integration.md` tracks rollout; it is not included in the npm package.
58
+
59
+ Owned naeil services use [NaeilFooter](naeil-footer.md) for the common content preset. Generic and legacy content overrides remain supported.
@@ -1,11 +1,13 @@
1
1
  # Package verification and release policy
2
2
 
3
- This policy covers **@naeil/ui 0.6.0** and its shared application frame. The [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) records the frozen archive and actual publication/deployment outcomes. Verified 0.5.0, 0.4.0 and 0.3.0 evidence remains dated history. UI v2 names a design generation, not an npm major. Future breaking changes require an explicit migration and version decision. Version-scoped installation is in the [README](../README.md#use-the-ui).
3
+ The current implementation candidate is **@naeil/ui 0.7.0**, adding the canonical footer and release identity contract. It is unpublished and requires an explicit migration/release decision. The completed **0.6.0** [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0) remains the evidence for the published application frame; do not republish it. Verified 0.5.0, 0.4.0 and 0.3.0 evidence remains dated history. UI v2 names a design generation, not an npm major. Future breaking changes require an explicit migration and version decision. Version-scoped installation is in the [README](../README.md#use-the-ui).
4
4
 
5
5
  ## Architecture
6
6
 
7
7
  One repository contains the shared package and the separate Next.js brand/example website. `@naeil/ui/ui` provides React primitives and generic Navigation/SiteFooter. The opt-in React-only `@naeil/ui/naeil-header` provides the complete actual naeil.dev branded header; see [its consumer contract](components/naeil-header.md). The legacy root is a framework compatibility entry with static Next/next-intl imports, even when selecting only Logo or a heading; React-only consumers use the documented shared deep modules. Next and next-intl are optional peers, not optional behavior inside the root. Supply them when importing that entry, Nav, Footer, or i18n/routing. Theme controls and Toaster use the framework-independent React library next-themes, retained as a runtime dependency.
8
8
 
9
+ Owned services use `@naeil/ui/naeil-footer` for the canonical localized destinations in full/compact variants; generic SiteFooter and legacy Footer retain their APIs. Footer navigation adapters preserve application save/flush decisions and modified-click behavior. `@naeil/ui/release` is server-safe metadata; it contains no application artifact, credential or environment. Applications compile their own allowlisted artifact/source identity. See [the footer contract](components/naeil-footer.md). The release coordinator remains disabled until its concrete policy, source mappings, immutable artifacts, deployment bridges and recovery targets are reviewed. Publishing a package, creating an update PR or merging it never establishes complete consumer rollout.
10
+
9
11
  Website tools remain development dependencies so maintainers can develop both products. npm tarball consumers install only component runtime dependencies and their host React/ReactDOM. Build entries, package exports and packed checks constrain the public boundary. There is no bundled website action, Supabase helper, Three/hero/cursor module, blog engine or full application message catalog. The opt-in header includes only its three header dictionaries and canonical project catalog. Its five decorative images reference official `https://naeil.dev/images/*.png` URLs and require network access; no image bytes are bundled, and unresolved artwork redistribution provenance remains in [third-party notices](../THIRD_PARTY_NOTICES.md).
10
12
 
11
13
  ## Checks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naeil/ui",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": [
@@ -137,12 +137,20 @@
137
137
  "./naeil-header": {
138
138
  "types": "./dist/components/naeil-header.d.ts",
139
139
  "import": "./dist/components/naeil-header.js"
140
+ },
141
+ "./naeil-footer": {
142
+ "types": "./dist/components/naeil-footer.d.ts",
143
+ "import": "./dist/components/naeil-footer.js"
144
+ },
145
+ "./release": {
146
+ "types": "./dist/lib/ui-release.d.ts",
147
+ "import": "./dist/lib/ui-release.js"
140
148
  }
141
149
  },
142
150
  "scripts": {
143
151
  "dev": "next dev",
144
152
  "build": "next build",
145
- "build:pkg": "pnpm check:brand && tsup && tsup --config tsup.deep.config.ts && tsx scripts/fix-deep-imports.ts && pnpm build:tokens",
153
+ "build:pkg": "pnpm build:release && pnpm check:brand && tsup && tsup --config tsup.deep.config.ts && tsx scripts/fix-deep-imports.ts && pnpm build:tokens && node scripts/build-release-entry.mjs",
146
154
  "start": "next start",
147
155
  "lint": "eslint",
148
156
  "test": "vitest run",
@@ -157,7 +165,10 @@
157
165
  "check:consumers": "tsx scripts/check-consumers.ts",
158
166
  "preview:storybook": "tsx scripts/serve-storybook.ts",
159
167
  "build:brand": "tsx scripts/build-brand.ts",
160
- "check:brand": "tsx scripts/build-brand.ts --check"
168
+ "check:brand": "tsx scripts/build-brand.ts --check",
169
+ "build:release": "node scripts/build-ui-release.mjs",
170
+ "rollout": "node scripts/rollout/rollout.mjs",
171
+ "test:rollout": "node --test scripts/rollout/*.test.mjs scripts/verify-ui-production.test.mjs"
161
172
  },
162
173
  "peerDependencies": {
163
174
  "next": "^15 || ^16",
@@ -234,7 +245,8 @@
234
245
  "docs/design/component-demand.md",
235
246
  "docs/design/public-ui-stage4-verification.md",
236
247
  "docs/design/public-ui-final-verification.md",
237
- "docs/design/public-ui-release-validation.md"
248
+ "docs/design/public-ui-release-validation.md",
249
+ "contracts/"
238
250
  ],
239
251
  "pnpm": {
240
252
  "onlyBuiltDependencies": [
@@ -28,6 +28,41 @@
28
28
  height: var(--spacing-4-value);
29
29
  background: var(--border);
30
30
  }
31
+ .naeil-header-account-trigger {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ flex: none;
36
+ width: var(--ui-touch-target);
37
+ height: var(--ui-touch-target);
38
+ padding: 0;
39
+ border: 0;
40
+ border-radius: var(--radius-md-value);
41
+ background: transparent;
42
+ color: var(--foreground);
43
+ cursor: pointer;
44
+ }
45
+ .naeil-header-account-trigger:hover,
46
+ .naeil-header-account-trigger[data-state="open"] {
47
+ background: var(--surface-subtle);
48
+ }
49
+ .naeil-header-account-avatar {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ width: var(--spacing-8-value);
54
+ height: var(--spacing-8-value);
55
+ border-radius: 50%;
56
+ background: var(--secondary);
57
+ color: var(--secondary-foreground);
58
+ }
59
+ .naeil-header-account-avatar svg {
60
+ width: var(--spacing-4-value);
61
+ height: var(--spacing-4-value);
62
+ }
63
+ .naeil-header-login {
64
+ font-weight: var(--font-weight-medium);
65
+ }
31
66
  .naeil-header-project-icon {
32
67
  display: flex;
33
68
  align-items: center;
@@ -1,323 +0,0 @@
1
- "use client";
2
- import {
3
- useOverlayRef,
4
- useRadixHiddenInert
5
- } from "./chunk-CE73AEJZ.js";
6
- import {
7
- __objRest,
8
- __spreadProps,
9
- __spreadValues,
10
- cn
11
- } from "./chunk-JBTDRVZ7.js";
12
-
13
- // src/components/ui/site-footer.tsx
14
- import { jsx, jsxs } from "react/jsx-runtime";
15
- function SiteFooter(_a) {
16
- var _b = _a, {
17
- brand,
18
- description,
19
- groups = [],
20
- Link,
21
- meta,
22
- children,
23
- containerClassName,
24
- className,
25
- variant = "full"
26
- } = _b, props = __objRest(_b, [
27
- "brand",
28
- "description",
29
- "groups",
30
- "Link",
31
- "meta",
32
- "children",
33
- "containerClassName",
34
- "className",
35
- "variant"
36
- ]);
37
- return /* @__PURE__ */ jsx(
38
- "footer",
39
- __spreadProps(__spreadValues({
40
- "data-slot": "site-footer",
41
- "data-variant": variant,
42
- className: cn("ui-site-footer", className)
43
- }, props), {
44
- children: /* @__PURE__ */ jsxs("div", { className: cn("ui-site-footer-frame", containerClassName), children: [
45
- /* @__PURE__ */ jsxs("div", { className: "ui-site-footer-brand", children: [
46
- brand,
47
- description && /* @__PURE__ */ jsx("div", { className: "ui-site-footer-description", children: description }),
48
- children
49
- ] }),
50
- groups.map((group) => /* @__PURE__ */ jsxs("nav", { "aria-label": group.label, children: [
51
- /* @__PURE__ */ jsx("h2", { className: "ui-site-footer-label", children: group.label }),
52
- group.links.map((item) => {
53
- const Component = !item.external && Link ? Link : "a";
54
- return /* @__PURE__ */ jsx(
55
- Component,
56
- {
57
- href: item.href,
58
- target: item.target,
59
- rel: item.target === "_blank" ? "noopener noreferrer" : void 0,
60
- className: "ui-site-footer-link ui-focus",
61
- children: item.label
62
- },
63
- item.id
64
- );
65
- })
66
- ] }, group.id)),
67
- meta && /* @__PURE__ */ jsx("div", { className: "ui-site-footer-meta", children: meta })
68
- ] })
69
- })
70
- );
71
- }
72
-
73
- // src/components/ui/dropdown-menu.tsx
74
- import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
75
- import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
76
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
77
- function DropdownMenu(_a) {
78
- var props = __objRest(_a, []);
79
- return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Root, __spreadValues({ "data-slot": "dropdown-menu" }, props));
80
- }
81
- function DropdownMenuPortal(_a) {
82
- var props = __objRest(_a, []);
83
- return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Portal, __spreadValues({ "data-slot": "dropdown-menu-portal" }, props));
84
- }
85
- function DropdownMenuTrigger(_a) {
86
- var props = __objRest(_a, []);
87
- return /* @__PURE__ */ jsx2(
88
- DropdownMenuPrimitive.Trigger,
89
- __spreadValues({
90
- "data-slot": "dropdown-menu-trigger"
91
- }, props)
92
- );
93
- }
94
- function DropdownMenuContent(_a) {
95
- var _b = _a, {
96
- ref,
97
- className,
98
- sideOffset = 4
99
- } = _b, props = __objRest(_b, [
100
- "ref",
101
- "className",
102
- "sideOffset"
103
- ]);
104
- const [content, contentRef] = useOverlayRef(ref);
105
- useRadixHiddenInert(content);
106
- return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx2(
107
- DropdownMenuPrimitive.Content,
108
- __spreadValues({
109
- ref: contentRef,
110
- "data-slot": "dropdown-menu-content",
111
- sideOffset,
112
- className: cn(
113
- "ui-menu ui-popup z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border-strong p-1 shadow-md",
114
- className
115
- )
116
- }, props)
117
- ) });
118
- }
119
- function DropdownMenuGroup(_a) {
120
- var props = __objRest(_a, []);
121
- return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Group, __spreadValues({ "data-slot": "dropdown-menu-group" }, props));
122
- }
123
- function DropdownMenuItem(_a) {
124
- var _b = _a, {
125
- className,
126
- inset,
127
- variant = "default"
128
- } = _b, props = __objRest(_b, [
129
- "className",
130
- "inset",
131
- "variant"
132
- ]);
133
- return /* @__PURE__ */ jsx2(
134
- DropdownMenuPrimitive.Item,
135
- __spreadValues({
136
- "data-slot": "dropdown-menu-item",
137
- "data-inset": inset,
138
- "data-variant": variant,
139
- className: cn(
140
- "focus:bg-surface-subtle data-[variant=destructive]:text-error data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-error data-[variant=destructive]:*:[svg]:!text-error [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-base outline-hidden select-none transition-colors duration-(--ui-motion-fast) data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
141
- className
142
- )
143
- }, props)
144
- );
145
- }
146
- function DropdownMenuCheckboxItem(_a) {
147
- var _b = _a, {
148
- className,
149
- children,
150
- checked
151
- } = _b, props = __objRest(_b, [
152
- "className",
153
- "children",
154
- "checked"
155
- ]);
156
- return /* @__PURE__ */ jsxs2(
157
- DropdownMenuPrimitive.CheckboxItem,
158
- __spreadProps(__spreadValues({
159
- "data-slot": "dropdown-menu-checkbox-item",
160
- className: cn(
161
- "focus:bg-surface-subtle relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-base outline-hidden select-none transition-colors duration-(--ui-motion-fast) data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
162
- className
163
- ),
164
- checked
165
- }, props), {
166
- children: [
167
- /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(CheckIcon, { className: "size-4" }) }) }),
168
- children
169
- ]
170
- })
171
- );
172
- }
173
- function DropdownMenuRadioGroup(_a) {
174
- var props = __objRest(_a, []);
175
- return /* @__PURE__ */ jsx2(
176
- DropdownMenuPrimitive.RadioGroup,
177
- __spreadValues({
178
- "data-slot": "dropdown-menu-radio-group"
179
- }, props)
180
- );
181
- }
182
- function DropdownMenuRadioItem(_a) {
183
- var _b = _a, {
184
- className,
185
- children
186
- } = _b, props = __objRest(_b, [
187
- "className",
188
- "children"
189
- ]);
190
- return /* @__PURE__ */ jsxs2(
191
- DropdownMenuPrimitive.RadioItem,
192
- __spreadProps(__spreadValues({
193
- "data-slot": "dropdown-menu-radio-item",
194
- className: cn(
195
- "focus:bg-surface-subtle relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-base outline-hidden select-none transition-colors duration-(--ui-motion-fast) data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
196
- className
197
- )
198
- }, props), {
199
- children: [
200
- /* @__PURE__ */ jsx2("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(CircleIcon, { className: "size-2 fill-current" }) }) }),
201
- children
202
- ]
203
- })
204
- );
205
- }
206
- function DropdownMenuLabel(_a) {
207
- var _b = _a, {
208
- className,
209
- inset
210
- } = _b, props = __objRest(_b, [
211
- "className",
212
- "inset"
213
- ]);
214
- return /* @__PURE__ */ jsx2(
215
- DropdownMenuPrimitive.Label,
216
- __spreadValues({
217
- "data-slot": "dropdown-menu-label",
218
- "data-inset": inset,
219
- className: cn(
220
- "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
221
- className
222
- )
223
- }, props)
224
- );
225
- }
226
- function DropdownMenuSeparator(_a) {
227
- var _b = _a, {
228
- className
229
- } = _b, props = __objRest(_b, [
230
- "className"
231
- ]);
232
- return /* @__PURE__ */ jsx2(
233
- DropdownMenuPrimitive.Separator,
234
- __spreadValues({
235
- "data-slot": "dropdown-menu-separator",
236
- className: cn("bg-border-subtle -mx-1 my-1 h-px", className)
237
- }, props)
238
- );
239
- }
240
- function DropdownMenuShortcut(_a) {
241
- var _b = _a, {
242
- className
243
- } = _b, props = __objRest(_b, [
244
- "className"
245
- ]);
246
- return /* @__PURE__ */ jsx2(
247
- "span",
248
- __spreadValues({
249
- "data-slot": "dropdown-menu-shortcut",
250
- className: cn(
251
- "text-muted-foreground ml-auto text-xs tracking-widest",
252
- className
253
- )
254
- }, props)
255
- );
256
- }
257
- function DropdownMenuSub(_a) {
258
- var props = __objRest(_a, []);
259
- return /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Sub, __spreadValues({ "data-slot": "dropdown-menu-sub" }, props));
260
- }
261
- function DropdownMenuSubTrigger(_a) {
262
- var _b = _a, {
263
- className,
264
- inset,
265
- children
266
- } = _b, props = __objRest(_b, [
267
- "className",
268
- "inset",
269
- "children"
270
- ]);
271
- return /* @__PURE__ */ jsxs2(
272
- DropdownMenuPrimitive.SubTrigger,
273
- __spreadProps(__spreadValues({
274
- "data-slot": "dropdown-menu-sub-trigger",
275
- "data-inset": inset,
276
- className: cn(
277
- "focus:bg-surface-subtle data-[state=open]:bg-surface-subtle [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-base outline-hidden select-none transition-colors duration-(--ui-motion-fast) data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
278
- className
279
- )
280
- }, props), {
281
- children: [
282
- children,
283
- /* @__PURE__ */ jsx2(ChevronRightIcon, { className: "ml-auto size-4" })
284
- ]
285
- })
286
- );
287
- }
288
- function DropdownMenuSubContent(_a) {
289
- var _b = _a, {
290
- className
291
- } = _b, props = __objRest(_b, [
292
- "className"
293
- ]);
294
- return /* @__PURE__ */ jsx2(
295
- DropdownMenuPrimitive.SubContent,
296
- __spreadValues({
297
- "data-slot": "dropdown-menu-sub-content",
298
- className: cn(
299
- "ui-menu ui-popup z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border border-border-strong p-1 shadow-lg",
300
- className
301
- )
302
- }, props)
303
- );
304
- }
305
-
306
- export {
307
- SiteFooter,
308
- DropdownMenu,
309
- DropdownMenuPortal,
310
- DropdownMenuTrigger,
311
- DropdownMenuContent,
312
- DropdownMenuGroup,
313
- DropdownMenuItem,
314
- DropdownMenuCheckboxItem,
315
- DropdownMenuRadioGroup,
316
- DropdownMenuRadioItem,
317
- DropdownMenuLabel,
318
- DropdownMenuSeparator,
319
- DropdownMenuShortcut,
320
- DropdownMenuSub,
321
- DropdownMenuSubTrigger,
322
- DropdownMenuSubContent
323
- };