@vanstack/solid-ark 0.0.4 → 0.0.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 ADDED
@@ -0,0 +1,59 @@
1
+ # @vanstack/solid-ark
2
+
3
+ 基于 [Ark UI](https://ark-ui.com) + TanStack 生态([TanStack Form](https://tanstack.com/form)、[TanStack Table](https://tanstack.com/table))的 Solid 组件库。样式统一消费 Tailwind 语义 token,支持 CSS 变量主题化。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @vanstack/solid-ark
9
+ ```
10
+
11
+ 按使用的子入口安装对应 peer 依赖:`solid-js`、`@ark-ui/solid`、`@tanstack/solid-form`、`@tanstack/solid-table`、`zod`。
12
+
13
+ ## 快速开始
14
+
15
+ ```tsx
16
+ import { Button } from "@vanstack/solid-ark/button";
17
+
18
+ function Demo() {
19
+ return (
20
+ <Button variant="success" loading={false} onClick={() => console.log("clicked")}>
21
+ 确认
22
+ </Button>
23
+ );
24
+ }
25
+ ```
26
+
27
+ ## 子入口
28
+
29
+ 包按功能拆分为独立子入口,按需引入:
30
+
31
+ | 入口 | 说明 |
32
+ |------|------|
33
+ | `@vanstack/solid-ark/button` | 原生 `<button>` 样式封装,支持 `loading`、`variant` |
34
+ | `@vanstack/solid-ark/form` | 受控表单:TanStack Form + Ark UI + Zod,支持外部 `values` 双向绑定、变更集(patch)、跨字段校验、防抖输入、datePicker 等控件 |
35
+ | `@vanstack/solid-ark/modal` | 基于 Ark UI Dialog 的命令式弹窗管理器,Promise 驱动 |
36
+ | `@vanstack/solid-ark/drawer` | 基于 Ark UI Drawer 的命令式抽屉管理器,支持四方向滑入 |
37
+ | `@vanstack/solid-ark/toast` | 基于 Ark UI Toast 的通知组件,命令式调用 |
38
+ | `@vanstack/solid-ark/table` | 基于 TanStack Table 的非受控表格,内置排序和行选择 |
39
+ | `@vanstack/solid-ark/data-table` | 搜索表单 + 表格 + 分页的组合组件,支持内部/外部取数两种模式 |
40
+
41
+ ## 详细文档
42
+
43
+ 各子入口的完整使用文档位于源码对应目录的 `README.md`,可在线查看:
44
+
45
+ - [packages/solid/ark/src/form](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/form)
46
+ - [packages/solid/ark/src/button](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/button)
47
+ - [packages/solid/ark/src/modal](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/modal)
48
+ - [packages/solid/ark/src/drawer](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/drawer)
49
+ - [packages/solid/ark/src/toast](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/toast)
50
+ - [packages/solid/ark/src/table](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/table)
51
+ - [packages/solid/ark/src/data-table](https://gitee.com/janzho/vanstack/tree/master/packages/solid/ark/src/data-table)
52
+
53
+ ## 主题化
54
+
55
+ 组件样式基于 Tailwind 语义 token,覆盖 `:root` CSS 变量即可自定义(各子入口文档中有对应的 token 示例)。
56
+
57
+ ## License
58
+
59
+ MIT
@@ -1,6 +1,6 @@
1
1
  import "../solid-ark.css";
2
2
  import { t as e } from "../button-C9xYlZbo.js";
3
- import { t } from "../form-TmnHhNkx.js";
3
+ import { t } from "../form-DmSwMQlR.js";
4
4
  import { t as n } from "../table-B88tYpbb.js";
5
5
  import { className as r, createComponent as i, effect as a, insert as o, memo as s, setAttribute as c, template as l } from "solid-js/web";
6
6
  import { For as u, Show as d, createEffect as f, createMemo as p, createSignal as m, mergeProps as h, on as g, onMount as _ } from "solid-js";
@@ -1,2 +1,2 @@
1
1
  export { Form } from './Form';
2
- export type { DatePickerStoredValue, FieldConfig, FieldControlProps, FieldRule, FieldRuleContext, FormInvalidSubmitProps, FormProps, } from './type';
2
+ export type { DatePickerStoredValue, FieldConfig, FieldControlProps, FieldRule, FieldRuleContext, FormInvalidSubmitProps, FormProps, FormSubmitProps, } from './type';
@@ -1,3 +1,3 @@
1
1
  import "../solid-ark.css";
2
- import { t as e } from "../form-TmnHhNkx.js";
2
+ import { t as e } from "../form-DmSwMQlR.js";
3
3
  export { e as Form };
@@ -0,0 +1,16 @@
1
+ import { FieldConfig } from './type';
2
+ export interface BuildPatchResult<TData extends Record<string, unknown>> {
3
+ /** 与初始值不同的字段集合;无变化时为空对象 */
4
+ patch: Partial<TData>;
5
+ /** patch 是否非空 */
6
+ changed: boolean;
7
+ }
8
+ /**
9
+ * 对比初始快照与当前值,返回发生变化的字段集合。
10
+ * - 字符串/布尔:空值归一(undefined/null → "")后严格比较;
11
+ * - valueType: "number":比较前 Number() 归一(1 与 "1" 视为相同),patch 里放数字(清空保存 0);
12
+ * - 数组:空值归一 [],排序后深度比较([1,2] 与 [2,1] 视为相同);
13
+ * - 对象:空值归一 {},深度比较;
14
+ * - 用户改后又改回原值 → 无变化,不进 patch。
15
+ */
16
+ export declare function buildPatch<TData extends Record<string, unknown>>(initial: TData, current: TData, fields: FieldConfig[]): BuildPatchResult<TData>;
@@ -25,6 +25,8 @@ interface BaseFieldConfig {
25
25
  renderLabel?: (label: string) => JSX.Element;
26
26
  /** 自定义 error */
27
27
  renderError?: (errors: string[]) => JSX.Element;
28
+ /** 提交/比较前转换为数字(select/radio 值是字符串、目标字段是数字时使用) */
29
+ valueType?: "number";
28
30
  }
29
31
  export interface InputFieldConfig extends BaseFieldConfig {
30
32
  fieldType: "input";
@@ -75,7 +77,7 @@ export interface CheckboxGroupFieldConfig extends BaseFieldConfig {
75
77
  value: string;
76
78
  }[];
77
79
  }
78
- export interface DatePickerFieldConfig extends BaseFieldConfig {
80
+ export interface DatePickerFieldConfig extends Omit<BaseFieldConfig, "valueType"> {
79
81
  fieldType: "datePicker";
80
82
  /** 选择粒度:year | month | week | date | datetime,默认 date */
81
83
  mode?: "date" | "datetime" | "month" | "week" | "year";
@@ -108,6 +110,15 @@ export interface FormInvalidSubmitProps<TData extends Record<string, unknown>> {
108
110
  /** 字段名 → 错误消息列表,只包含有错误的字段 */
109
111
  errors: Record<string, string[]>;
110
112
  }
113
+ /** 校验通过后的提交载荷:patch=true 时额外携带 patch / changed */
114
+ export interface FormSubmitProps<TData extends Record<string, unknown>> {
115
+ /** 当前表单值 */
116
+ value: TData;
117
+ /** patch=true 时提供:与初始值不同的字段集合(含变化的数组/对象) */
118
+ patch?: Partial<TData>;
119
+ /** patch=true 时提供:patch 是否非空 */
120
+ changed?: boolean;
121
+ }
111
122
  export interface FormProps<TData extends Record<string, unknown>> {
112
123
  /** 字段配置 */
113
124
  fields: FieldConfig[];
@@ -115,10 +126,10 @@ export interface FormProps<TData extends Record<string, unknown>> {
115
126
  values?: Partial<TData>;
116
127
  /** 表单值变化通知:内部值变化(用户输入、外部 values 同步)时触发,配合 values 使用可实现双向绑定 */
117
128
  onValuesChange?: (values: TData) => void;
129
+ /** 是否计算变更集:默认 false;开启后 onSubmit 额外收到 { patch, changed }(与初始值比较,提交时计算) */
130
+ patch?: boolean;
118
131
  /** 提交回调(仅在校验通过时触发) */
119
- onSubmit: (props: {
120
- value: TData;
121
- }) => Promise<void> | void;
132
+ onSubmit: (props: FormSubmitProps<TData>) => Promise<void> | void;
122
133
  /** 提交被校验拦截时的回调 */
123
134
  onSubmitInvalid?: (props: FormInvalidSubmitProps<TData>) => void;
124
135
  /** 表单布局 class */
@@ -6,15 +6,16 @@ import { createForm as x } from "@tanstack/solid-form";
6
6
  import { Checkbox as S } from "@ark-ui/solid/checkbox";
7
7
  import { RadioGroup as C } from "@ark-ui/solid/radio-group";
8
8
  import { Switch as w } from "@ark-ui/solid/switch";
9
+ import { deepEqual as T } from "fast-equals";
9
10
  //#region src/form/components/FieldSkeleton.tsx
10
- var T = /*#__PURE__*/ c("<div aria-hidden=true class=van-form-field-skeleton>");
11
- function E() {
12
- return T();
11
+ var E = /*#__PURE__*/ c("<div aria-hidden=true class=van-form-field-skeleton>");
12
+ function D() {
13
+ return E();
13
14
  }
14
15
  //#endregion
15
16
  //#region src/form/components/FormCheckbox.tsx
16
- var D = /*#__PURE__*/ c("<svg class=van-form-check-icon fill=none stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=3 viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5l10 -10\">");
17
- function O(e) {
17
+ var O = /*#__PURE__*/ c("<svg class=van-form-check-icon fill=none stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=3 viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5l10 -10\">");
18
+ function k(e) {
18
19
  return n(S.Root, {
19
20
  get checked() {
20
21
  return e.value ?? !1;
@@ -32,7 +33,7 @@ function O(e) {
32
33
  return n(S.Indicator, {
33
34
  class: "van-form-checkbox-indicator",
34
35
  get children() {
35
- return D();
36
+ return O();
36
37
  }
37
38
  });
38
39
  }
@@ -50,8 +51,8 @@ function O(e) {
50
51
  }
51
52
  //#endregion
52
53
  //#region src/form/components/FormCheckboxGroup.tsx
53
- var k = /*#__PURE__*/ c("<svg class=van-form-check-icon fill=none stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=3 viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5l10 -10\">");
54
- function A(t) {
54
+ var A = /*#__PURE__*/ c("<svg class=van-form-check-icon fill=none stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=3 viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5l10 -10\">");
55
+ function j(t) {
55
56
  return n(S.Group, {
56
57
  class: "van-form-checkbox-group",
57
58
  get id() {
@@ -86,7 +87,7 @@ function A(t) {
86
87
  return n(S.Indicator, {
87
88
  class: "van-form-checkbox-indicator",
88
89
  get children() {
89
- return k();
90
+ return A();
90
91
  }
91
92
  });
92
93
  }
@@ -107,7 +108,7 @@ function A(t) {
107
108
  }
108
109
  //#endregion
109
110
  //#region src/form/components/useDebouncedValue.ts
110
- function j(e) {
111
+ function M(e) {
111
112
  let [t, n] = m(e.value() ?? ""), r, i = (t) => {
112
113
  if (n(t), (e.debounce ?? 0) > 0) {
113
114
  clearTimeout(r), r = setTimeout(() => {
@@ -132,12 +133,12 @@ function j(e) {
132
133
  }
133
134
  //#endregion
134
135
  //#region src/form/components/FormInput.tsx
135
- var M = /*#__PURE__*/ c("<svg fill=none height=14 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 viewBox=\"0 0 24 24\"width=14 xmlns=http://www.w3.org/2000/svg><path d=\"M18 6 6 18\"></path><path d=\"m6 6 12 12\">"), N = /*#__PURE__*/ c("<button aria-label=清空 type=button>"), P = /*#__PURE__*/ c("<div class=van-form-input-group>"), F = /*#__PURE__*/ c("<span class=\"van-form-input-addon van-form-input-addon-prefix\">"), I = /*#__PURE__*/ c("<span class=\"van-form-input-addon van-form-input-addon-suffix\">");
136
+ var ee = /*#__PURE__*/ c("<svg fill=none height=14 stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 viewBox=\"0 0 24 24\"width=14 xmlns=http://www.w3.org/2000/svg><path d=\"M18 6 6 18\"></path><path d=\"m6 6 12 12\">"), N = /*#__PURE__*/ c("<button aria-label=清空 type=button>"), P = /*#__PURE__*/ c("<div class=van-form-input-group>"), F = /*#__PURE__*/ c("<span class=\"van-form-input-addon van-form-input-addon-prefix\">"), I = /*#__PURE__*/ c("<span class=\"van-form-input-addon van-form-input-addon-suffix\">");
136
137
  function L() {
137
- return M();
138
+ return ee();
138
139
  }
139
140
  function R(e) {
140
- let { local: r, handleInput: s, handleBlur: c, handleClear: l } = j({
141
+ let { local: r, handleInput: s, handleBlur: c, handleClear: l } = M({
141
142
  value: () => e.value,
142
143
  debounce: e.field.debounce,
143
144
  onChange: e.onChange,
@@ -268,7 +269,7 @@ function B(e) {
268
269
  //#endregion
269
270
  //#region src/form/components/FormTextarea.tsx
270
271
  function V(e) {
271
- let { local: t, handleInput: r, handleBlur: i } = j({
272
+ let { local: t, handleInput: r, handleBlur: i } = M({
272
273
  value: () => e.value,
273
274
  debounce: e.field.debounce,
274
275
  onChange: e.onChange,
@@ -325,7 +326,7 @@ function K(e) {
325
326
  }
326
327
  }) : H(e.field, "select") ? n(d, {
327
328
  get fallback() {
328
- return n(E, {});
329
+ return n(D, {});
329
330
  },
330
331
  get children() {
331
332
  return n(G, {
@@ -346,7 +347,7 @@ function K(e) {
346
347
  }
347
348
  }) : H(e.field, "datePicker") ? n(d, {
348
349
  get fallback() {
349
- return n(E, {});
350
+ return n(D, {});
350
351
  },
351
352
  get children() {
352
353
  return n(W, {
@@ -365,7 +366,7 @@ function K(e) {
365
366
  }
366
367
  });
367
368
  }
368
- }) : H(e.field, "checkbox") ? n(O, {
369
+ }) : H(e.field, "checkbox") ? n(k, {
369
370
  get controlId() {
370
371
  return e.controlId;
371
372
  },
@@ -398,7 +399,7 @@ function K(e) {
398
399
  get value() {
399
400
  return e.value;
400
401
  }
401
- }) : H(e.field, "checkbox-group") ? n(A, {
402
+ }) : H(e.field, "checkbox-group") ? n(j, {
402
403
  get controlId() {
403
404
  return e.controlId;
404
405
  },
@@ -412,12 +413,43 @@ function K(e) {
412
413
  }) : null;
413
414
  }
414
415
  //#endregion
415
- //#region src/form/Form.tsx
416
- var q = /*#__PURE__*/ c("<form>"), J = /*#__PURE__*/ c("<div class=van-form-field>");
416
+ //#region src/form/patch.ts
417
+ function q(e) {
418
+ return Y(e) || e === "" ? void 0 : Number(e);
419
+ }
420
+ function J(e) {
421
+ return Y(e) || e === "" ? 0 : Number(e);
422
+ }
417
423
  function Y(e) {
418
- return typeof e == "string" ? e : e && typeof e == "object" && "message" in e ? String(e.message) : String(e);
424
+ return e == null;
419
425
  }
420
426
  function X(e) {
427
+ return Array.isArray(e) ? [...e].sort() : [];
428
+ }
429
+ function Z(e) {
430
+ return e && typeof e == "object" && !Array.isArray(e) ? e : {};
431
+ }
432
+ function Q(e) {
433
+ return typeof e == "object" && !!e && !Array.isArray(e);
434
+ }
435
+ function te(e, t, n) {
436
+ let r = {};
437
+ for (let i of n) {
438
+ let { name: n, valueType: a } = i, o = e[n], s = t[n], c;
439
+ c = a === "number" ? T(q(o), q(s)) : Array.isArray(o) || Array.isArray(s) ? T(X(o), X(s)) : Q(o) || Q(s) ? T(Z(o), Z(s)) : T(Y(o) ? "" : o, Y(s) ? "" : s), c || (r[n] = a === "number" ? J(s) : s);
440
+ }
441
+ return {
442
+ patch: r,
443
+ changed: Object.keys(r).length > 0
444
+ };
445
+ }
446
+ //#endregion
447
+ //#region src/form/Form.tsx
448
+ var ne = /*#__PURE__*/ c("<form>"), re = /*#__PURE__*/ c("<div class=van-form-field>");
449
+ function $(e) {
450
+ return typeof e == "string" ? e : e && typeof e == "object" && "message" in e ? String(e.message) : String(e);
451
+ }
452
+ function ie(e) {
421
453
  let t = e.rule;
422
454
  if (t) return {
423
455
  onChange: typeof t == "function" ? (e) => t({
@@ -427,17 +459,27 @@ function X(e) {
427
459
  ...e.validateListenTo?.length ? { onChangeListenTo: e.validateListenTo } : {}
428
460
  };
429
461
  }
430
- function Z(e) {
462
+ function ae(e) {
431
463
  let r = _({ values: {} }, e), c = r.values, d = h(), m = (e) => `${d}-${e}`, g = x(() => ({
432
464
  defaultValues: c,
433
465
  onSubmit: async ({ value: e }) => {
434
- await r.onSubmit({ value: e });
466
+ let t = e;
467
+ if (r.patch) {
468
+ let { patch: e, changed: n } = te(c, t, r.fields);
469
+ await r.onSubmit({
470
+ value: t,
471
+ patch: e,
472
+ changed: n
473
+ });
474
+ return;
475
+ }
476
+ await r.onSubmit({ value: t });
435
477
  },
436
478
  onSubmitInvalid: ({ value: e, formApi: t }) => {
437
479
  if (!r.onSubmitInvalid) return;
438
480
  let n = {}, i = t.state.fieldMeta;
439
481
  for (let [e, t] of Object.entries(i)) {
440
- let r = (t?.errors ?? []).map(Y).filter(Boolean);
482
+ let r = (t?.errors ?? []).map($).filter(Boolean);
441
483
  r.length > 0 && (n[e] = r);
442
484
  }
443
485
  r.onSubmitInvalid({
@@ -449,7 +491,7 @@ function Z(e) {
449
491
  return f(v(() => r.values, (e) => {
450
492
  for (let [t, n] of Object.entries(e ?? {})) g.state.values[t] !== n && C(t, n);
451
493
  }, { defer: !0 })), f(v(S, (e) => r.onValuesChange?.(e), { defer: !0 })), (() => {
452
- var e = q();
494
+ var e = ne();
453
495
  return e.addEventListener("submit", (e) => {
454
496
  e.preventDefault(), e.stopPropagation(), g.handleSubmit();
455
497
  }), a(e, n(l, {
@@ -461,12 +503,12 @@ function Z(e) {
461
503
  return e.name;
462
504
  },
463
505
  get validators() {
464
- return X(e);
506
+ return ie(e);
465
507
  },
466
508
  children: (t) => {
467
- let r = t(), c = p(() => (t().state.meta.errors ?? []).map(Y).filter(Boolean)), d = p(() => t().state.value), f = U(e.fieldWidth), h = f ? { width: f } : void 0;
509
+ let r = t(), c = p(() => (t().state.meta.errors ?? []).map($).filter(Boolean)), d = p(() => t().state.value), f = U(e.fieldWidth), h = f ? { width: f } : void 0;
468
510
  return e.control ? (() => {
469
- var t = J();
511
+ var t = re();
470
512
  return a(t, () => e.control({
471
513
  value: d(),
472
514
  onChange: (e) => r.handleChange(e),
@@ -557,4 +599,4 @@ function Z(e) {
557
599
  })();
558
600
  }
559
601
  //#endregion
560
- export { Z as t };
602
+ export { ae as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vanstack/solid-ark",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -52,7 +52,8 @@
52
52
  "zod": "^4.4.0"
53
53
  },
54
54
  "dependencies": {
55
- "@internationalized/date": "^3.12.2"
55
+ "@internationalized/date": "^3.12.2",
56
+ "fast-equals": "^6.0.2"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@ark-ui/solid": "^5.0.0",
@@ -63,15 +64,17 @@
63
64
  "solid-js": "^1.9.0",
64
65
  "vite": "^8.0.0",
65
66
  "vite-plugin-solid": "^2.11.0",
67
+ "vitest": "^4.1.10",
66
68
  "zod": "^4.4.0",
67
- "@vanstack/eslint-config": "0.0.1",
69
+ "@vanstack/vite-config": "0.0.2",
70
+ "@vanstack/tsconfig": "0.0.1",
68
71
  "@vanstack/prettier-config": "0.0.1",
69
- "@vanstack/vite-config": "0.0.1",
70
- "@vanstack/tsconfig": "0.0.1"
72
+ "@vanstack/eslint-config": "0.0.1"
71
73
  },
72
74
  "scripts": {
73
75
  "build": "tsc --noEmit && vite build",
74
76
  "tsc": "tsc --noEmit",
75
- "lint": "eslint . --fix"
77
+ "lint": "eslint . --fix",
78
+ "test": "vitest run"
76
79
  }
77
80
  }