@svp-chain-sdk/ui 0.1.4 → 0.1.5

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
@@ -2,90 +2,9 @@
2
2
 
3
3
  ## 1. 适用范围
4
4
 
5
- `@svp-chain-sdk/ui` 提供 Tailwind provider,也提供 React 钱包公共能力。钱包支持链、钱包分组、RainbowKit 主题、SVP Wallet 检测与安装地址统一维护在 `src/wallet.tsx` `walletDefaults` 及相关导出中。
5
+ `@svp-chain-sdk/ui` 当前的主要接入方式是 Tailwind provider:消费方把 `@svp-chain-sdk/ui/tailwind` 注册为 Tailwind 插件,然后直接在 HTML、React、Vue 或其他模板中组合 `.ui-button` CSS class。
6
6
 
7
- React 项目可直接复用 Provider 和连接按钮:
8
-
9
- ```tsx
10
- import {
11
- ConnectWalletButton,
12
- SvpWalletProvider,
13
- } from '@svp-chain-sdk/ui'
14
- import '@rainbow-me/rainbowkit/styles.css'
15
-
16
- export function App() {
17
- return (
18
- <SvpWalletProvider appName="SVP Bridge" projectId={walletConnectProjectId}>
19
- <ConnectWalletButton
20
- labels={{ connect: '连接钱包', wrongNetwork: '网络错误' }}
21
- />
22
- </SvpWalletProvider>
23
- )
24
- }
25
- ```
26
-
27
- 消费项目需要安装本包声明的 React、RainbowKit、TanStack Query、Wagmi 和 Viem peer dependencies。业务层只传应用名、WalletConnect Project ID 和本地化文案;默认链、钱包顺序、弹窗主题与 SVP Wallet 行为由 SDK 统一提供。
28
-
29
- ### 在现有项目中接入(以 swap-v2 为例)
30
-
31
- 1. 安装公共包及 peer dependencies:
32
-
33
- ```sh
34
- pnpm add @svp-chain-sdk/ui @rainbow-me/rainbowkit @tanstack/react-query wagmi viem
35
- ```
36
-
37
- 本地联调尚未发布的 SDK 时,可以把依赖临时指向仓库中的 UI 包:
38
-
39
- ```json
40
- {
41
- "dependencies": {
42
- "@svp-chain-sdk/ui": "file:../svp-chain-sdk-monorepo/packages/ui"
43
- }
44
- }
45
- ```
46
-
47
- 2. 在应用入口导入 SDK 与 RainbowKit 样式,并用 `SvpWalletProvider` 包裹应用。`projectId` 应读取项目自己的 `VITE_WALLETCONNECT_PROJECT_ID`,不要提交真实凭据:
48
-
49
- ```tsx
50
- import { SvpWalletProvider } from '@svp-chain-sdk/ui'
51
- import '@svp-chain-sdk/ui/styles.css'
52
- import '@rainbow-me/rainbowkit/styles.css'
53
-
54
- createRoot(document.getElementById('root')!).render(
55
- <SvpWalletProvider
56
- appName="NovaSwap"
57
- projectId={import.meta.env.VITE_WALLETCONNECT_PROJECT_ID}
58
- locale="zh-CN"
59
- >
60
- <App />
61
- </SvpWalletProvider>,
62
- )
63
- ```
64
-
65
- 如果项目已经自行声明了 `WagmiProvider`、`QueryClientProvider` 和 `RainbowKitProvider`,应删除重复 Provider,避免出现两个 Wagmi/Query Client 上下文。默认网络、钱包排序、SVP Wallet 图标、安装跳转和弹窗主题由 SDK 提供。
66
-
67
- 3. 在 Header 或其他业务组件中直接使用连接按钮:
68
-
69
- ```tsx
70
- import { ConnectWalletButton } from '@svp-chain-sdk/ui'
71
-
72
- export function HeaderWallet() {
73
- return (
74
- <ConnectWalletButton
75
- labels={{
76
- connect: '连接钱包',
77
- wrongNetwork: '网络错误',
78
- }}
79
- />
80
- )
81
- }
82
- ```
83
-
84
- 按钮会自动处理未连接、错误网络和已连接三种状态。`showChain={false}` 可以在已连接时隐藏独立网络按钮;`className` 只应用到钱包操作按钮。SVP Wallet 图标已经随 npm 包构建,不需要消费项目把图片复制到 `public`。
85
-
86
- 4. 升级 SDK 后执行项目自身的测试与生产构建,至少确认连接弹窗、SVP Wallet 图标、未安装跳转、网络切换和账户弹窗正常。
87
-
88
- 这是**框架无关、非 React 组件优先**的模式。包根入口仍提供可选的 React `Button`、`buttonVariants` 和 `cn`,也导出 `@svp-chain-sdk/ui/styles.css` 样式入口,但它们不是本文的主要接入方式。目前 Tailwind provider 只注册按钮相关 class,不应假定它包含表单、弹窗或其他尚未实现的组件。
7
+ 这是**框架无关样式优先**的模式。包根入口同时提供可选的 React `Button`、零配置 `NoticeMarquee`、`FeedbackWidget` 和 `cn`。Tailwind provider 当前注册按钮与通知走马灯样式;不应假定它包含其他尚未实现的组件。
89
8
 
90
9
  ## 2. 从公共 npm registry 安装
91
10
 
@@ -250,7 +169,8 @@ function submit() {
250
169
  `@svp-chain-sdk/ui/tailwind` 提供两个具名导出:
251
170
 
252
171
  - `uiButtonComponents`:当前按钮组件配置对象。
253
- - `uiComponents`:默认插件注册的完整组件配置;当前它与 `uiButtonComponents` 是同一个对象。未来若增加其他组件,应优先使用 `uiComponents` 来继承完整配置。
172
+ - `uiNoticeMarqueeComponents`:通知走马灯的完整样式配置对象。
173
+ - `uiComponents`:默认插件注册的完整组件配置,包含按钮和通知走马灯配置。
254
174
 
255
175
  如需在同一个插件中加入应用级 class,可组合 `uiComponents`:
256
176
 
@@ -344,14 +264,132 @@ npm run release:minor -- --dry-run
344
264
 
345
265
  `prepublishOnly` 保留为 npm 的独立发布保护,会再次运行质量检查;`release` 脚本不依赖 Git 提交或 tag。
346
266
 
347
- ## 11. 当前公开入口速查
267
+ ## 11. React FeedbackWidget
268
+
269
+ `FeedbackWidget` 是一个零配置 React 公共组件。其结构和样式对齐 `svp-website-frontend` 的 `.svp-contact-fab.js-contact-us`:桌面端为视口右侧垂直居中的 54px 渐变方形 FAB,560px 以下缩为 48px;点击后在新的浏览器标签页打开 Google Form。
270
+
271
+ 建议在应用最外层 Layout 或 AppShell 中只挂载一次:
272
+
273
+ ```tsx
274
+ import { FeedbackWidget } from '@svp-chain-sdk/ui'
275
+ import '@svp-chain-sdk/ui/styles.css'
276
+
277
+ export function AppShell() {
278
+ return (
279
+ <>
280
+ {/* 页面与路由内容 */}
281
+ <FeedbackWidget />
282
+ </>
283
+ )
284
+ }
285
+ ```
286
+
287
+ 表单短链接由组件内部的 `GOOGLE_FORM_URL` 统一维护,消费页面无需传入地址或了解表单字段。当前源码已内置项目提供的 Google Form 地址。
288
+
289
+ 组件使用带有 `target="_blank"` 和 `rel="noopener noreferrer"` 的链接打开表单,不读取、存储或转发表单内容,也不依赖 iframe 或消费站点的 `frame-src` 配置。
290
+
291
+ 可选 `className` 仅用于宿主页面协调多个悬浮控件。组件使用参考项目相同的 `--svp-primary` 和 `--svp-accent` 变量,并分别回退到 `#2563eb` 与 `#38bdf8`。
292
+
293
+ `FeedbackWidget` 属于包根入口的 React API,不属于 `@svp-chain-sdk/ui/tailwind` 提供的框架无关 class。
294
+
295
+ ## 12. 当前公开入口速查
348
296
 
349
297
  以 `packages/ui/package.json` 当前的 `exports` 为准:
350
298
 
351
299
  | 导入路径 | 当前内容 |
352
300
  | --- | --- |
353
- | `@svp-chain-sdk/ui/tailwind` | 默认 Tailwind plugin;具名 `uiButtonComponents`、`uiComponents` |
354
- | `@svp-chain-sdk/ui` | 可选 React `Button`、`buttonVariants`、`ButtonProps` 和 `cn` |
301
+ | `@svp-chain-sdk/ui/tailwind` | 默认 Tailwind plugin;具名 `uiButtonComponents`、`uiNoticeMarqueeComponents`、`uiComponents` |
302
+ | `@svp-chain-sdk/ui` | React `Button`、零配置 `NoticeMarquee` 与 `FeedbackWidget`、相关类型和常量、`buttonVariants` 和 `cn` |
355
303
  | `@svp-chain-sdk/ui/styles.css` | 包构建产出的 Tailwind v4 CSS 样式入口;当前不包含 provider 的 `.ui-button*` 规则 |
356
304
 
357
305
  所有 JavaScript 入口均按 ESM 使用。公开 API 之外的源码路径不属于 package exports,不应从消费项目直接导入。
306
+
307
+ ## 13. React NoticeMarquee 通知走马灯
308
+
309
+ `NoticeMarquee` 是零配置公共组件。业务页面只需要渲染组件,无需传入通知内容或文件地址:
310
+
311
+ ```tsx
312
+ import { NoticeMarquee } from '@svp-chain-sdk/ui'
313
+
314
+ export function Layout() {
315
+ return (
316
+ <>
317
+ <NoticeMarquee />
318
+ {/* 页面内容 */}
319
+ </>
320
+ )
321
+ }
322
+ ```
323
+
324
+ 组件默认读取 `DEFAULT_NOTICE_URL` 指向的公共 IPFS Gateway JSON。地址集中在 SDK 常量中,业务页面无需传入。组件首次加载后默认每 5 分钟静默重新请求一次,不显示 loading,也不会因一次网络失败导致页面报错。
325
+
326
+ 服务端 JSON 格式:
327
+
328
+ ```json
329
+ {
330
+ "en": "English notice",
331
+ "zh": "中文通知",
332
+ "fr": "Notification française"
333
+ }
334
+ ```
335
+
336
+ 组件根据 `navigator.language` 选择 `zh`、`fr` 或 `en`。找不到对应语言时先回退到 `en`,再回退到第一个非空字符串。内容始终按纯文本展示,不使用 `innerHTML`。
337
+
338
+ 浏览器会在 `localStorage` 的 `DEFAULT_NOTICE_STORAGE_KEY` 下保存:
339
+
340
+ ```json
341
+ {
342
+ "seenAt": 1789445000000,
343
+ "content": "{\"en\":\"English notice\",\"fr\":\"Notification française\",\"zh\":\"中文通知\"}"
344
+ }
345
+ ```
346
+
347
+ `content` 不是当前语言的展示文案,而是完整远程公告对象的 canonical JSON 快照:对象 key 会递归排序,因此属性顺序不同但内容相同的 JSON 会得到相同值。展示文本仍独立地按 `navigator.language` 选择。
348
+
349
+ 规则如下:
350
+
351
+ 1. 浏览器没有记录时,记录当前时间与完整公告快照,并播放当前语言文案。
352
+ 2. 完整远程公告与本地快照相同时,不播放;切换浏览器语言也不会把同一公告当作新公告。
353
+ 3. 任一语言或其他公告字段变化时,用当前时间和新的完整快照覆盖旧记录,并播放当前语言文案。
354
+ 4. 同一页面即使意外挂载多个实例(包括 React StrictMode),同一首次公告也最多由一个实例播放。
355
+ 5. 清除浏览器数据、使用无痕窗口或更换设备后,该通知会被视为首次看到。
356
+ 6. `localStorage` 不可用时组件不会报错,并在本次页面中播放获取到的内容。
357
+
358
+ 使用 `NoticeMarquee` 时,消费项目可以按第 3 节注册并构建 `@svp-chain-sdk/ui/tailwind` provider,也可以直接导入 `@svp-chain-sdk/ui/styles.css`。两条样式入口都提供相同的固定宽度、走马灯动画、hover 暂停和 `prefers-reduced-motion` 规则,不要同时重复引入。
359
+
360
+ 样式与 `dex-website-frontend` Footer 的 InfiniteMarquee 保持一致:可视区宽度为 `16rem`,两份相同内容首尾衔接,轨道从 `0` 平移至 `-50%`,默认周期为 `32s`,仅在 hover 时原地暂停。可通过 CSS 变量调整滚动周期:
361
+
362
+ ```css
363
+ :root {
364
+ --ui-notice-marquee-duration: 30s;
365
+ }
366
+ ```
367
+
368
+ 高级场景可以覆盖可选参数,但普通业务页面不需要传参:
369
+
370
+ ```tsx
371
+ <NoticeMarquee
372
+ url="https://example.com/notice.json"
373
+ storageKey="my-app:notice"
374
+ refreshIntervalMs={5 * 60 * 1000}
375
+ />
376
+ ```
377
+
378
+ 公开常量和类型:
379
+
380
+ ```ts
381
+ import {
382
+ DEFAULT_NOTICE_REFRESH_INTERVAL_MS,
383
+ DEFAULT_NOTICE_STORAGE_KEY,
384
+ DEFAULT_NOTICE_URL,
385
+ NoticeMarquee,
386
+ type NoticeMarqueeProps,
387
+ } from '@svp-chain-sdk/ui'
388
+ ```
389
+
390
+ 服务器配置注意事项:
391
+
392
+ - 跨域地址必须正确配置 CORS。
393
+ - 响应必须是 JSON 对象,语言字段的值应为非空字符串。
394
+ - SDK 不携带 cookie、token 或其他认证信息。
395
+ - IPFS CID 对应不可变内容;发布新通知时需要更新 CID 或改用能指向最新 JSON 的稳定地址。
@@ -0,0 +1,2 @@
1
+ export { Button, buttonVariants } from './button';
2
+ export type { ButtonProps } from './button';
@@ -0,0 +1,4 @@
1
+ export interface FeedbackWidgetProps {
2
+ className?: string;
3
+ }
4
+ export declare function FeedbackWidget({ className }?: FeedbackWidgetProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export { FeedbackWidget } from './feedback-widget';
2
+ export type { FeedbackWidgetProps } from './feedback-widget';
@@ -0,0 +1,6 @@
1
+ export { Button, buttonVariants } from './button';
2
+ export type { ButtonProps } from './button';
3
+ export { FeedbackWidget } from './feedback-widget';
4
+ export type { FeedbackWidgetProps } from './feedback-widget';
5
+ export { DEFAULT_NOTICE_REFRESH_INTERVAL_MS, DEFAULT_NOTICE_STORAGE_KEY, DEFAULT_NOTICE_URL, NoticeMarquee, } from './notice-marquee';
6
+ export type { NoticeMarqueeProps } from './notice-marquee';
@@ -0,0 +1,2 @@
1
+ export { DEFAULT_NOTICE_REFRESH_INTERVAL_MS, DEFAULT_NOTICE_STORAGE_KEY, DEFAULT_NOTICE_URL, NoticeMarquee, } from './notice-marquee';
2
+ export type { NoticeMarqueeProps } from './notice-marquee';
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ export declare const DEFAULT_NOTICE_URL = "https://moccasin-glad-tyrannosaurus-244.mypinata.cloud/ipfs/bafkreiavwd7xwacprggfzpdcicluvv4tzdxdfzj6brlzayxsk64upezdwa";
3
+ export declare const DEFAULT_NOTICE_STORAGE_KEY = "@svp-chain-sdk/ui:notice-marquee";
4
+ export declare const DEFAULT_NOTICE_REFRESH_INTERVAL_MS: number;
5
+ export type NoticeMarqueeProps = {
6
+ /** Optional override for the SDK default multilingual JSON URL. */
7
+ url?: string;
8
+ /** Optional override for the localStorage key used to remember seen content. */
9
+ storageKey?: string;
10
+ /** Optional override for the default five-minute silent refresh interval. */
11
+ refreshIntervalMs?: number;
12
+ className?: string;
13
+ };
14
+ /**
15
+ * Zero-config remote notice marquee. It shows unseen localized content once per
16
+ * browser and remembers the content in localStorage.
17
+ */
18
+ export declare function NoticeMarquee({ url, storageKey, refreshIntervalMs, className, }?: NoticeMarqueeProps): React.JSX.Element | null;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- export { Button, buttonVariants } from './button';
2
- export type { ButtonProps } from './button';
3
- export { cn } from './utils';
4
- export { ConnectWalletButton, createSvpWalletConfig, resolveSvpProvider, resolveSvpProviderForConnection, SVP_WALLET_DOWNLOAD_URL, svpTestnet, svpWallet, SvpWalletProvider, walletDefaults, walletTheme, } from './wallet';
5
- export type { ConnectWalletButtonProps, ConnectWalletLabels, CreateSvpWalletConfigOptions, SvpWalletProviderProps, SvpWalletDefaults, } from './wallet';
1
+ export { Button, buttonVariants, DEFAULT_NOTICE_REFRESH_INTERVAL_MS, DEFAULT_NOTICE_STORAGE_KEY, DEFAULT_NOTICE_URL, FeedbackWidget, NoticeMarquee, } from './components';
2
+ export type { ButtonProps, FeedbackWidgetProps, NoticeMarqueeProps, } from './components';
3
+ export { cn } from './lib/utils';