nfx-ui 0.28.0 → 0.29.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/README.en.md ADDED
@@ -0,0 +1,12 @@
1
+ # NFX-UI
2
+
3
+ [中文](README.md)
4
+
5
+ Shared React library for NebulaForgeX (theme, i18n, hooks). **Not** a standalone site. Hosts: Identity, Vault, News, Storages, Documentation.
6
+
7
+ Exports and host wiring: [NFX-Documentation chapter 7](https://github.com/NebulaForgeX/NFX-Documentation/blob/main/books/en/chapter-07-nfx-ui.md). Current **0.29.0**; hosts pin exactly `"nfx-ui": "0.29.0"`. No `file:` dual path.
8
+
9
+ ```bash
10
+ npm install
11
+ npm run build
12
+ ```
package/README.md CHANGED
@@ -1,119 +1,12 @@
1
- # NFX-UI — Unified Frontend UI Library
1
+ # NFX-UI
2
2
 
3
- **NFX-UI 1.0** is the shared frontend UI library of the NebulaForgeX ecosystem. Chrome follows **CityPulso PulsoLink-Package** (Radix Themes + `react-pro-sidebar`). Hosts compose Radix primitives themselves; NFX-UI ships theme, language, layout, modal, and preference chrome.
3
+ [English](README.en.md)
4
4
 
5
- **NFX-UI 1.0** 是 NebulaForgeX 的共享前端 UI 库。壳层对齐 **CityPulso PulsoLink-Package**(Radix Themes + `react-pro-sidebar`)。宿主自己组合 Radix 原语;NFX-UI 提供主题、语言、布局、模态框与偏好设置壳。
5
+ NebulaForgeX 共享 React 库(主题、i18n、hooks)。**不是**独立站点。宿主:Identity、Vault、News、Storages、Documentation。
6
6
 
7
- Hosts: Identity, Vault, Documentation, Storages. Not LSR / PQTTEC / SJGZ.
8
-
9
- 宿主:Identity、Vault、Documentation、Storages。不含 LSR / PQTTEC / SJGZ。
10
-
11
- ---
12
-
13
- ## Install / 安装
14
-
15
- Host apps use the local package:
16
-
17
- 宿主使用本地包:
18
-
19
- ```json
20
- "nfx-ui": "file:../../NFX-UI"
21
- ```
22
-
23
- Peer versions follow PulsoLink-WEB (do not `ncu` past them independently):
24
-
25
- 对等依赖版本对齐 PulsoLink-WEB(不要单独 `ncu` 越过):
26
-
27
- - `react` / `react-dom` `^19.2.8`
28
- - `react-router` `^8.3.1` (not `react-router-dom`)
29
- - `@radix-ui/themes` `^3.3.0`, `radix-ui` `^1.6.7`, `@radix-ui/react-icons` `^1.3.2`
30
- - `lucide-react` `^1.41.0`
31
- - `vite` `^8.2.2`, `typescript` `~6.0.3`
32
-
33
- ---
34
-
35
- ## Host bootstrap / 宿主接入
36
-
37
- ```tsx
38
- import { LanguageEnum } from "nfx-ui/enums";
39
- import { LanguageProvider, ThemeProvider, ModalProvider } from "nfx-ui/providers";
40
- import { LayoutFrame, LayoutProvider, Sidebar, Header, Main, PageFrame } from "nfx-ui/layouts";
41
- import { PreferencesPopover, ThemeSettings, PageHeader, EmptyState } from "nfx-ui/components";
42
- import { usePreferenceStore } from "nfx-ui/stores";
43
- import { configurePreferenceSync, useSyncPreference, useApplyPreferenceOnLoad } from "nfx-ui/hooks";
44
- import { showInfo, showError, showSuccess, showConfirm } from "nfx-ui/stores";
45
- import "@radix-ui/themes/styles.css";
46
- import "nfx-ui/themes/fonts";
47
- import "nfx-ui/themes/styles.css";
48
-
49
- export function App() {
50
- return (
51
- <LanguageProvider getBuiltinBundles={getBuiltinI18nBundles}>
52
- <ThemeProvider>
53
- <LayoutProvider>
54
- <ModalProvider>
55
- <LayoutFrame items={menuItems}>{/* pages */}</LayoutFrame>
56
- </ModalProvider>
57
- </LayoutProvider>
58
- </ThemeProvider>
59
- </LanguageProvider>
60
- );
61
- }
62
- ```
63
-
64
- - `ThemeProvider` does **not** take `defaultTheme`. Theme lives on preference: `{ accent, gray, appearance, radius, scaling, panelBackground, fontFamily }`.
65
- - `ThemeProvider` **不再**接收 `defaultTheme`。主题在 preference 的 theme 对象上。
66
- - Fonts: import `"nfx-ui/themes/fonts"` (source, not bundled into every lib entry).
67
- - 字体:`"nfx-ui/themes/fonts"`(源码入口,不要打进每个 lib chunk)。
68
- - Page chrome: `PageFrame` + `PageHeader` (`icon` is required) + Radix `Card` / `Flex` / `Button` / `TextField`.
69
- - 页面壳:`PageFrame` + `PageHeader`(必须有 `icon`)+ Radix 原语。
70
- - Auth screens copy PulsoLink `AuthShell`. Theme/language UI is `PreferencesPopover` / `ThemeSettings`.
71
- - 登录页拷 PulsoLink `AuthShell`。主题/语言用 `PreferencesPopover` / `ThemeSettings`。
72
- - Queries: `useUnifiedQuery` / `useUnifiedInfiniteQuery` from `nfx-ui/hooks` (or `nfx-ui/utils`). Pages never call axios.
73
-
74
- ```tsx
75
- import { Button, TextField, Flex, Card } from "@radix-ui/themes";
76
- ```
77
-
78
- ---
79
-
80
- ## Modules / 模块
81
-
82
- | Module | Contents |
83
- |--------|----------|
84
- | **enums** | Language, appearance, accent, radius, dashboard background |
85
- | **providers** | ThemeProvider, LanguageProvider, ModalProvider |
86
- | **themes** | Radix mappings, fonts, CSS |
87
- | **languages** | i18n init, createI18nResources, labels |
88
- | **layouts** | LayoutFrame, Sidebar (`react-pro-sidebar`), Header, Main, PageFrame, UserTopBar |
89
- | **components** | PreferencesPopover, ThemeSettings, PageHeader, EmptyState, LucideIcon, Logo, VirtualList + Radix re-exports |
90
- | **stores** | preference + modal (`showInfo` / `showSuccess` / `showError` / `showConfirm` / `showLoading`) |
91
- | **hooks** | useUnifiedQuery, useSyncPreference, useApplyPreferenceOnLoad, configurePreferenceSync |
92
- | **animations** | WaveBackground, ECGLoading, TruckLoading, … |
93
- | **types / utils / constants** | Shared types and helpers |
94
-
95
- `useTheme().setTheme` takes `Partial<ResolvedThemePreference>`. Preference persist key is `nfx-preference`.
96
-
97
- ---
98
-
99
- ## Development / 开发
100
-
101
- This repo is library-only. Build with `npm run build`. Output in `dist/`. Font CSS must not be injected into every lib entry.
102
-
103
- 本仓库仅库模式。`npm run build` 输出到 `dist/`。字体 CSS 不得注入每个入口。
7
+ 接入与真实导出:[NFX-Documentation 第七章](https://github.com/NebulaForgeX/NFX-Documentation/blob/main/books/zh/chapter-07-nfx-ui.md)。当前 **0.29.0**,宿主精确钉 `"nfx-ui": "0.29.0"`。不要 `file:` 双路径。
104
8
 
105
9
  ```bash
106
10
  npm install
107
11
  npm run build
108
- npm run lint
109
12
  ```
110
-
111
- Test via host `file:../../NFX-UI`. Do not modify `Example/` (CityPulso PulsoLink is source of truth only).
112
-
113
- 用宿主 `file:../../NFX-UI` 验证。不要改 `Example/`(CityPulso PulsoLink 只作对照源)。
114
-
115
- ---
116
-
117
- ## License
118
-
119
- [MIT](LICENSE)
@@ -1 +1 @@
1
- *,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;scroll-behavior:smooth;scrollbar-gutter:stable;width:100%;min-height:100%}body{text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:100%;min-height:100vh;margin:0}#root{min-height:100vh}main,section,article,aside,header,footer,nav{display:block}main{min-height:100%}h1,h2,h3,h4,h5,h6,p{margin:0}em,i{font-style:italic}abbr[title]{cursor:help;text-underline-offset:.18em;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}address{font-style:normal}blockquote{margin:0}sub,sup{vertical-align:baseline;position:relative}sub{bottom:-.25em}sup{top:-.5em}code,kbd,samp{padding:.12em .32em}pre{margin:0;overflow:auto}pre code{color:inherit;background:0 0;padding:0}a{text-decoration:none;transition:color .12s cubic-bezier(.2,0,0,1),opacity .12s cubic-bezier(.2,0,0,1)}a:focus-visible{outline:none}ul,ol,li,dl,dd{margin:0}img,picture,video,canvas,svg{max-width:100%;display:block}img{-webkit-user-select:none;user-select:none}svg{flex-shrink:0}audio{width:100%}iframe{border:0;max-width:100%;display:block}figure{margin:0}button,input,textarea,select{font:inherit;color:inherit}dialog:not(.rt-DialogContent){border:none}table{border-collapse:collapse;border-spacing:0;width:100%}caption,th,td{text-align:left}hr{border:none;height:1px;margin:0}fieldset{border:0;min-width:0;margin:0;padding:0}legend{padding:0}summary{cursor:pointer}progress,meter{width:100%}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{border-radius:999px}[hidden]{display:none!important}:focus-visible{outline:none}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.radix-themes{--heading-font-family:var(--default-font-family);--strong-font-family:var(--default-font-family);--em-font-family:var(--default-font-family);--quote-font-family:var(--default-font-family);--radius-chip:max(var(--radius-3), var(--radius-full));--radius-icon:max(var(--radius-4), var(--radius-full))}.radix-themes .rt-BaseTabListTriggerInner,.radix-themes .rt-BaseTabListTriggerInnerHidden,.radix-themes .rt-TabsTriggerInner{border-radius:max(var(--tab-inner-border-radius), var(--radius-full))!important}.radix-themes[data-radius=full] .rt-Card{--card-border-radius:var(--radius-6)}.radix-themes,.radix-themes[data-font-family=system]{--default-font-family:system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif}.radix-themes[data-font-family=ibm-plex]{--default-font-family:"IBM Plex Sans", "IBM Plex Sans SC", "Noto Sans SC", "Noto Sans TC", system-ui, sans-serif}.radix-themes[data-font-family=noto]{--default-font-family:"Noto Sans", "Noto Sans SC", "Noto Sans TC", sans-serif}.radix-themes[data-font-family=source-sans]{--default-font-family:"Source Sans 3", "Source Han Sans SC", "Noto Sans SC", "Noto Sans TC", sans-serif}.radix-themes{--color-primary:var(--accent-9);--color-primary-hover:var(--accent-10);--color-primary-light:var(--accent-4);--color-primary-bg:var(--accent-3);--color-primary-fg:var(--accent-contrast);--color-success:var(--green-9);--color-success-light:var(--green-4);--color-info:var(--blue-9);--color-info-light:var(--blue-4);--color-warning:var(--amber-9);--color-warning-light:var(--amber-4);--color-danger:var(--red-9);--color-error:var(--red-9);--color-danger-light:var(--red-4);--color-bg:var(--color-background);--color-bg-1:var(--color-background);--color-bg-2:var(--color-panel-solid);--color-bg-3:var(--gray-3);--color-bg-4:var(--gray-4);--color-bg-secondary:var(--gray-2);--color-border:var(--gray-6);--color-border-2:var(--gray-5);--color-border-3:var(--gray-6);--color-border-4:var(--gray-7);--color-border-5:var(--gray-8);--color-border-hover:var(--gray-8);--color-fg:var(--gray-11);--color-fg-text:var(--gray-12);--color-fg-heading:var(--gray-12);--color-fg-highlight:var(--accent-11);--color-fg-muted:var(--gray-10);--color-fg-on-primary:var(--accent-contrast);--color-separator:var(--gray-6);--color-overlay:var(--black-a6);--color-shadow:var(--gray-a4);--color-ring:var(--accent-8)}
1
+ *,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;scroll-behavior:smooth;scrollbar-gutter:stable;width:100%;min-height:100%}body{text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:100%;min-height:100vh;margin:0}#root{min-height:100vh}main,section,article,aside,header,footer,nav{display:block}main{min-height:100%}h1,h2,h3,h4,h5,h6,p{margin:0}em,i{font-style:italic}abbr[title]{cursor:help;text-underline-offset:.18em;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}address{font-style:normal}blockquote{margin:0}sub,sup{vertical-align:baseline;position:relative}sub{bottom:-.25em}sup{top:-.5em}code,kbd,samp{padding:.12em .32em}pre{margin:0;overflow:auto}pre code{color:inherit;background:0 0;padding:0}a{text-decoration:none;transition:color .12s cubic-bezier(.2,0,0,1),opacity .12s cubic-bezier(.2,0,0,1)}a:focus-visible{outline:none}ul,ol,li,dl,dd{margin:0}img,picture,video,canvas,svg{max-width:100%;display:block}img{-webkit-user-select:none;user-select:none}svg{flex-shrink:0}audio{width:100%}iframe{border:0;max-width:100%;display:block}figure{margin:0}button,input,textarea,select{font:inherit;color:inherit}dialog:not(.rt-DialogContent){border:none}table{border-collapse:collapse;border-spacing:0;width:100%}caption,th,td{text-align:left}hr{border:none;height:1px;margin:0}fieldset{border:0;min-width:0;margin:0;padding:0}legend{padding:0}summary{cursor:pointer}progress,meter{width:100%}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{border-radius:999px}[hidden]{display:none!important}:focus-visible{outline:none}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.radix-themes{--heading-font-family:var(--default-font-family);--strong-font-family:var(--default-font-family);--em-font-family:var(--default-font-family);--quote-font-family:var(--default-font-family);--radius-chip:max(var(--radius-3), var(--radius-full));--radius-icon:max(var(--radius-4), var(--radius-full))}.radix-themes .rt-BaseTabListTriggerInner,.radix-themes .rt-BaseTabListTriggerInnerHidden,.radix-themes .rt-TabsTriggerInner{border-radius:max(var(--tab-inner-border-radius), var(--radius-full))!important}.radix-themes .rt-BaseTabListTrigger:before{content:"";background-color:var(--accent-indicator);transform-origin:50%;pointer-events:none;height:2px;transition:transform .22s cubic-bezier(.22,1,.36,1);position:absolute;bottom:0;left:0;right:0;transform:scaleX(0)}.radix-themes .rt-BaseTabListTrigger[data-state=active]:before,.radix-themes .rt-BaseTabListTrigger[data-active]:before{transform:scaleX(1)}.radix-themes .rt-BaseTabListTrigger[data-state=active] .rt-BaseTabListTriggerInner,.radix-themes .rt-BaseTabListTrigger[data-active] .rt-BaseTabListTriggerInner{font-weight:var(--font-weight-bold);background-color:#0000}@media (hover:hover){.radix-themes .rt-BaseTabListTrigger:hover:before{transform:scaleX(1)}.radix-themes :where(.rt-BaseTabListTrigger:hover:not([data-state=active]):not([data-active])) :where(.rt-BaseTabListTriggerInner){background-color:var(--accent-a3);color:var(--accent-11)}.radix-themes .rt-BaseTabListTrigger[data-state=active]:hover .rt-BaseTabListTriggerInner,.radix-themes .rt-BaseTabListTrigger[data-active]:hover .rt-BaseTabListTriggerInner{background-color:#0000}}@media (prefers-reduced-motion:reduce){.radix-themes .rt-BaseTabListTrigger:before{transition:none}}.radix-themes[data-radius=full] .rt-Card{--card-border-radius:var(--radius-6)}.radix-themes,.radix-themes[data-font-family=system]{--default-font-family:system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif}.radix-themes[data-font-family=ibm-plex]{--default-font-family:"IBM Plex Sans", "IBM Plex Sans SC", "Noto Sans SC", "Noto Sans TC", system-ui, sans-serif}.radix-themes[data-font-family=noto]{--default-font-family:"Noto Sans", "Noto Sans SC", "Noto Sans TC", sans-serif}.radix-themes[data-font-family=source-sans]{--default-font-family:"Source Sans 3", "Source Han Sans SC", "Noto Sans SC", "Noto Sans TC", sans-serif}.radix-themes{--color-primary:var(--accent-9);--color-primary-hover:var(--accent-10);--color-primary-light:var(--accent-4);--color-primary-bg:var(--accent-3);--color-primary-fg:var(--accent-contrast);--color-success:var(--green-9);--color-success-light:var(--green-4);--color-info:var(--blue-9);--color-info-light:var(--blue-4);--color-warning:var(--amber-9);--color-warning-light:var(--amber-4);--color-danger:var(--red-9);--color-error:var(--red-9);--color-danger-light:var(--red-4);--color-bg:var(--color-background);--color-bg-1:var(--color-background);--color-bg-2:var(--color-panel-solid);--color-bg-3:var(--gray-3);--color-bg-4:var(--gray-4);--color-bg-secondary:var(--gray-2);--color-border:var(--gray-6);--color-border-2:var(--gray-5);--color-border-3:var(--gray-6);--color-border-4:var(--gray-7);--color-border-5:var(--gray-8);--color-border-hover:var(--gray-8);--color-fg:var(--gray-11);--color-fg-text:var(--gray-12);--color-fg-heading:var(--gray-12);--color-fg-highlight:var(--accent-11);--color-fg-muted:var(--gray-10);--color-fg-on-primary:var(--accent-contrast);--color-separator:var(--gray-6);--color-overlay:var(--black-a6);--color-shadow:var(--gray-a4);--color-ring:var(--accent-8)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nfx-ui",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -319,6 +319,58 @@ meter {
319
319
  border-radius: max(var(--tab-inner-border-radius), var(--radius-full)) !important;
320
320
  }
321
321
 
322
+ /*
323
+ * Tabs hover: PulsoLink-WEB pattern. Radix paints both the active underline
324
+ * (::before) and TabsTriggerInner, which reads as two color layers. Keep the
325
+ * bar; only fill the inner on inactive hover. Active hover stays underline-only.
326
+ */
327
+ .radix-themes .rt-BaseTabListTrigger::before {
328
+ content: "";
329
+ height: 2px;
330
+ position: absolute;
331
+ bottom: 0;
332
+ left: 0;
333
+ right: 0;
334
+ background-color: var(--accent-indicator);
335
+ transform: scaleX(0);
336
+ transform-origin: center;
337
+ transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
338
+ pointer-events: none;
339
+ }
340
+
341
+ .radix-themes .rt-BaseTabListTrigger[data-state="active"]::before,
342
+ .radix-themes .rt-BaseTabListTrigger[data-active]::before {
343
+ transform: scaleX(1);
344
+ }
345
+
346
+ .radix-themes .rt-BaseTabListTrigger[data-state="active"] .rt-BaseTabListTriggerInner,
347
+ .radix-themes .rt-BaseTabListTrigger[data-active] .rt-BaseTabListTriggerInner {
348
+ font-weight: var(--font-weight-bold);
349
+ background-color: transparent;
350
+ }
351
+
352
+ @media (hover: hover) {
353
+ .radix-themes .rt-BaseTabListTrigger:hover::before {
354
+ transform: scaleX(1);
355
+ }
356
+
357
+ .radix-themes :where(.rt-BaseTabListTrigger:hover:not([data-state="active"]):not([data-active])) :where(.rt-BaseTabListTriggerInner) {
358
+ background-color: var(--accent-a3);
359
+ color: var(--accent-11);
360
+ }
361
+
362
+ .radix-themes .rt-BaseTabListTrigger[data-state="active"]:hover .rt-BaseTabListTriggerInner,
363
+ .radix-themes .rt-BaseTabListTrigger[data-active]:hover .rt-BaseTabListTriggerInner {
364
+ background-color: transparent;
365
+ }
366
+ }
367
+
368
+ @media (prefers-reduced-motion: reduce) {
369
+ .radix-themes .rt-BaseTabListTrigger::before {
370
+ transition: none;
371
+ }
372
+ }
373
+
322
374
  .radix-themes[data-radius="full"] .rt-Card {
323
375
  --card-border-radius: var(--radius-6);
324
376
  }