@jike-js/ui 0.1.1 → 0.2.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.md CHANGED
@@ -5,13 +5,21 @@
5
5
  ## 安装
6
6
 
7
7
  ```powershell
8
- pnpm add @jike-js/ui
8
+ pnpm add @jike-js/ui@0.1.1
9
9
  ```
10
10
 
11
- 当前公开版本:`0.1.0`;行高隔离修订的源码版本为 `0.1.1`,尚未发布。业务项目安装后锁定明确版本,不使用 Git、本地 `file:` 或未发布版本。
11
+ 当前公开版本:`0.1.1`,已包含登录行高隔离修订;品牌配置 API 的本地实现版本为 `0.2.0`,尚未发布。业务项目安装后锁定明确版本,不使用 Git、本地 `file:` 或未发布版本。
12
12
 
13
13
  业务项目只需引入 `@jike-js/ui/styles.css` 一个样式入口;该入口在构建时包含 Token、基础层和组件层,避免漏引或形成第二份 reset。Token 的唯一源文件仍是 `frontend/tokens/design-tokens.json`,CSS 是其运行时输出。
14
14
 
15
15
  当前提供:`LoginShell`、`PageLayout`、`PageHeader`、`Notice`、`EmptyState`、`LoadingState`。包构建会同时检查 TypeScript,并输出单一 `styles.css`。
16
16
 
17
17
  本包不包含业务请求、路由、权限和项目 Logo。业务项目只能通过组件参数接入这些能力。登录文案使用 `brandTitle`、`brandSubtitle`、`formTitle`、`formSubtitle` 四个明确层级。
18
+
19
+ 品牌差异通过 `LoginBrandConfig` 传入:
20
+
21
+ ```tsx
22
+ <LoginShell brand={{ primary: "#3566ec", primaryHover: "#2853d4" }} {...props} />
23
+ ```
24
+
25
+ 不得通过全局 CSS 或任意 Token 覆盖登录结构。
@@ -4,14 +4,21 @@ export interface LoginCredentials {
4
4
  username: string;
5
5
  password: string;
6
6
  }
7
+ export interface LoginBrandConfig {
8
+ primary?: string;
9
+ primaryHover?: string;
10
+ focusRing?: string;
11
+ fontFamily?: string;
12
+ }
7
13
  export interface LoginShellProps {
8
14
  brandTitle: string;
9
15
  logo: string | ReactNode;
10
16
  brandSubtitle?: string;
11
17
  formTitle?: string;
12
18
  formSubtitle?: string;
19
+ brand?: LoginBrandConfig;
13
20
  onSubmit: (credentials: LoginCredentials) => void | Promise<void>;
14
21
  loading?: boolean;
15
22
  error?: string;
16
23
  }
17
- export declare function LoginShell({ brandTitle, logo, brandSubtitle, formTitle, formSubtitle, onSubmit, loading, error, }: LoginShellProps): import("react").JSX.Element;
24
+ export declare function LoginShell({ brandTitle, logo, brandSubtitle, formTitle, formSubtitle, brand, onSubmit, loading, error, }: LoginShellProps): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { LockKeyhole, LoaderCircle, LogIn, User } from "lucide-react";
3
3
  import { useState } from "react";
4
4
  import "./styles.css";
5
- export function LoginShell({ brandTitle, logo, brandSubtitle = "提升工作效率,简化操作流程", formTitle = "欢迎回来", formSubtitle = "登录以继续使用", onSubmit, loading = false, error, }) {
5
+ export function LoginShell({ brandTitle, logo, brandSubtitle = "提升工作效率,简化操作流程", formTitle = "欢迎回来", formSubtitle = "登录以继续使用", brand, onSubmit, loading = false, error, }) {
6
6
  const [username, setUsername] = useState("");
7
7
  const [password, setPassword] = useState("");
8
8
  async function handleSubmit(event) {
@@ -11,5 +11,11 @@ export function LoginShell({ brandTitle, logo, brandSubtitle = "提升工作效
11
11
  return;
12
12
  await onSubmit({ username: username.trim(), password: password.trim() });
13
13
  }
14
- return (_jsx("main", { className: "jike-login-shell", children: _jsxs("section", { className: "jike-login-shell__card", "aria-label": `${brandTitle}登录`, children: [_jsxs("div", { className: "jike-login-shell__brand", children: [_jsx("div", { className: "jike-login-shell__logo", children: typeof logo === "string" ? _jsx("img", { src: logo, alt: brandTitle }) : logo }), _jsx("h1", { children: brandTitle }), _jsx("p", { children: brandSubtitle })] }), _jsxs("div", { className: "jike-login-shell__form-panel", children: [_jsx("h2", { children: formTitle }), _jsx("p", { className: "jike-login-shell__hint", children: formSubtitle }), _jsxs("form", { onSubmit: handleSubmit, autoComplete: "on", noValidate: true, "aria-busy": loading, children: [_jsxs("label", { className: "jike-login-shell__field", htmlFor: "jike-login-username", children: [_jsx("span", { children: "\u7528\u6237\u540D" }), _jsxs("span", { className: "jike-login-shell__control", children: [_jsx(User, { "aria-hidden": "true" }), _jsx("input", { id: "jike-login-username", name: "username", value: username, onChange: (event) => setUsername(event.target.value), placeholder: "\u8BF7\u8F93\u5165\u7528\u6237\u540D", autoComplete: "username", disabled: loading, required: true, "aria-invalid": Boolean(error) })] })] }), _jsxs("label", { className: "jike-login-shell__field", htmlFor: "jike-login-password", children: [_jsx("span", { children: "\u5BC6\u7801" }), _jsxs("span", { className: "jike-login-shell__control", children: [_jsx(LockKeyhole, { "aria-hidden": "true" }), _jsx("input", { id: "jike-login-password", name: "password", type: "password", value: password, onChange: (event) => setPassword(event.target.value), placeholder: "\u8BF7\u8F93\u5165\u5BC6\u7801", autoComplete: "current-password", disabled: loading, required: true, "aria-invalid": Boolean(error) })] })] }), error ? _jsx("p", { className: "jike-login-shell__error", role: "alert", "aria-live": "polite", children: error }) : null, _jsxs("button", { className: "jike-login-shell__submit", type: "submit", disabled: loading, children: [loading ? "登录中..." : "登录", loading ? _jsx(LoaderCircle, { "aria-hidden": "true", className: "jike-login-shell__spinner" }) : _jsx(LogIn, { "aria-hidden": "true" })] })] })] })] }) }));
14
+ const brandStyle = {
15
+ ...(brand?.primary ? { "--jike-login-brand-primary": brand.primary } : {}),
16
+ ...(brand?.primaryHover ? { "--jike-login-brand-primary-hover": brand.primaryHover } : {}),
17
+ ...(brand?.focusRing ? { "--jike-login-brand-focus-ring": brand.focusRing } : {}),
18
+ ...(brand?.fontFamily ? { "--jike-login-font-family": brand.fontFamily } : {}),
19
+ };
20
+ return (_jsx("main", { className: "jike-login-shell", style: brandStyle, children: _jsxs("section", { className: "jike-login-shell__card", "aria-label": `${brandTitle}登录`, children: [_jsxs("div", { className: "jike-login-shell__brand", children: [_jsx("div", { className: "jike-login-shell__logo", children: typeof logo === "string" ? _jsx("img", { src: logo, alt: brandTitle }) : logo }), _jsx("h1", { children: brandTitle }), _jsx("p", { children: brandSubtitle })] }), _jsxs("div", { className: "jike-login-shell__form-panel", children: [_jsx("h2", { children: formTitle }), _jsx("p", { className: "jike-login-shell__hint", children: formSubtitle }), _jsxs("form", { onSubmit: handleSubmit, autoComplete: "on", noValidate: true, "aria-busy": loading, children: [_jsxs("label", { className: "jike-login-shell__field", htmlFor: "jike-login-username", children: [_jsx("span", { children: "\u7528\u6237\u540D" }), _jsxs("span", { className: "jike-login-shell__control", children: [_jsx(User, { "aria-hidden": "true" }), _jsx("input", { id: "jike-login-username", name: "username", value: username, onChange: (event) => setUsername(event.target.value), placeholder: "\u8BF7\u8F93\u5165\u7528\u6237\u540D", autoComplete: "username", disabled: loading, required: true, "aria-invalid": Boolean(error) })] })] }), _jsxs("label", { className: "jike-login-shell__field", htmlFor: "jike-login-password", children: [_jsx("span", { children: "\u5BC6\u7801" }), _jsxs("span", { className: "jike-login-shell__control", children: [_jsx(LockKeyhole, { "aria-hidden": "true" }), _jsx("input", { id: "jike-login-password", name: "password", type: "password", value: password, onChange: (event) => setPassword(event.target.value), placeholder: "\u8BF7\u8F93\u5165\u5BC6\u7801", autoComplete: "current-password", disabled: loading, required: true, "aria-invalid": Boolean(error) })] })] }), error ? _jsx("p", { className: "jike-login-shell__error", role: "alert", "aria-live": "polite", children: error }) : null, _jsxs("button", { className: "jike-login-shell__submit", type: "submit", disabled: loading, children: [loading ? "登录中..." : "登录", loading ? _jsx(LoaderCircle, { "aria-hidden": "true", className: "jike-login-shell__spinner" }) : _jsx(LogIn, { "aria-hidden": "true" })] })] })] })] }) }));
15
21
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { LoginShell } from "./LoginShell";
2
- export type { LoginCredentials, LoginShellProps } from "./LoginShell";
2
+ export type { LoginBrandConfig, LoginCredentials, LoginShellProps } from "./LoginShell";
3
3
  export { PageLayout } from "./PageLayout";
4
4
  export type { PageLayoutProps } from "./PageLayout";
5
5
  export { PageHeader } from "./PageHeader";
package/dist/styles.css CHANGED
@@ -131,9 +131,9 @@ svg {
131
131
  display: block;
132
132
  }
133
133
 
134
- .jike-login-shell { box-sizing: border-box; min-height: 100vh; display: grid; place-items: center; padding: var(--space-6); background: var(--color-bg-page); color: var(--color-text-primary); font-family: var(--font-family-sans); line-height: var(--login-form-line-height); }
134
+ .jike-login-shell { box-sizing: border-box; min-height: 100vh; display: grid; place-items: center; padding: var(--space-6); background: var(--color-bg-page); color: var(--color-text-primary); font-family: var(--jike-login-font-family, var(--font-family-sans)); line-height: var(--login-form-line-height); }
135
135
  .jike-login-shell__card { box-sizing: border-box; display: grid; grid-template-columns: var(--login-column-ratio); width: var(--login-container-width); max-width: var(--login-container-max-width); min-height: var(--login-container-min-height); overflow: hidden; background: var(--color-bg-surface); border: var(--border-width-default) solid var(--color-border); border-radius: var(--radius-container); box-shadow: var(--shadow-login); }
136
- .jike-login-shell__brand { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--login-brand-padding-y) var(--login-brand-padding-x); color: var(--color-bg-surface); text-align: center; background: linear-gradient(135deg, var(--color-brand), var(--color-brand-hover)); }
136
+ .jike-login-shell__brand { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--login-brand-padding-y) var(--login-brand-padding-x); color: var(--color-bg-surface); text-align: center; background: linear-gradient(135deg, var(--jike-login-brand-primary, var(--color-brand)), var(--jike-login-brand-primary-hover, var(--color-brand-hover))); }
137
137
  .jike-login-shell__logo { display: grid; place-items: center; width: var(--login-logo-size); height: var(--login-logo-size); margin-bottom: var(--space-6); }
138
138
  .jike-login-shell__logo img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
139
139
  .jike-login-shell__brand h1, .jike-login-shell__form-panel h2 { margin: 0; font-size: var(--font-size-2xl); font-weight: 700; line-height: var(--line-height-heading); }
@@ -146,14 +146,14 @@ svg {
146
146
  .jike-login-shell__control svg { position: absolute; top: 50%; left: var(--space-4); width: var(--icon-size-sm); height: var(--icon-size-sm); color: var(--color-text-muted); transform: translateY(-50%); pointer-events: none; }
147
147
  .jike-login-shell__control input { box-sizing: border-box; width: 100%; height: var(--login-control-height); padding: 0 var(--control-input-padding-x) 0 var(--control-input-icon-padding-left); color: var(--color-text-primary); font: inherit; font-size: var(--font-size-md); line-height: var(--login-form-line-height); background: var(--color-bg-muted); border: var(--border-width-default) solid var(--color-border); border-radius: var(--radius-control); transition: border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease; }
148
148
  .jike-login-shell__control input:hover:not(:disabled) { border-color: var(--color-text-muted); background: var(--color-bg-surface); }
149
- .jike-login-shell__control input:focus { outline: none; border-color: var(--color-primary); background: var(--color-bg-surface); box-shadow: var(--focus-ring); }
149
+ .jike-login-shell__control input:focus { outline: none; border-color: var(--jike-login-brand-primary, var(--color-primary)); background: var(--color-bg-surface); box-shadow: var(--jike-login-brand-focus-ring, var(--focus-ring)); }
150
150
  .jike-login-shell__control input[aria-invalid="true"] { border-color: var(--color-danger); }
151
151
  .jike-login-shell__control input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(180, 35, 24, .18); }
152
152
  .jike-login-shell__control input:disabled { cursor: not-allowed; background: var(--color-bg-muted); }
153
153
  .jike-login-shell__error { margin: calc(var(--space-2) * -1) 0 var(--space-4); color: var(--color-danger); font-size: var(--font-size-sm); line-height: var(--login-form-line-height); }
154
- .jike-login-shell__submit { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); width: 100%; height: var(--login-control-height); margin-top: var(--login-button-gap); color: var(--color-bg-surface); font: inherit; font-size: var(--font-size-lg); font-weight: 600; line-height: var(--login-form-line-height); background: var(--color-brand); border: var(--border-width-default) solid var(--color-brand); border-radius: var(--radius-control); transition: background-color 160ms ease, border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; }
155
- .jike-login-shell__submit:hover:not(:disabled) { background: var(--color-brand-hover); border-color: var(--color-brand-hover); }
156
- .jike-login-shell__submit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
154
+ .jike-login-shell__submit { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); width: 100%; height: var(--login-control-height); margin-top: var(--login-button-gap); color: var(--color-bg-surface); font: inherit; font-size: var(--font-size-lg); font-weight: 600; line-height: var(--login-form-line-height); background: var(--jike-login-brand-primary, var(--color-brand)); border: var(--border-width-default) solid var(--jike-login-brand-primary, var(--color-brand)); border-radius: var(--radius-control); transition: background-color 160ms ease, border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; }
155
+ .jike-login-shell__submit:hover:not(:disabled) { background: var(--jike-login-brand-primary-hover, var(--color-brand-hover)); border-color: var(--jike-login-brand-primary-hover, var(--color-brand-hover)); }
156
+ .jike-login-shell__submit:focus-visible { outline: none; box-shadow: var(--jike-login-brand-focus-ring, var(--focus-ring)); }
157
157
  .jike-login-shell__submit:active:not(:disabled) { transform: translateY(1px); }
158
158
  .jike-login-shell__submit:disabled { cursor: wait; opacity: var(--state-disabled-opacity); }
159
159
  .jike-login-shell__submit svg { width: var(--icon-size-md); height: var(--icon-size-md); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jike-js/ui",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "jike 统一前端组件实现",
5
5
  "license": "MIT",
6
6
  "repository": {