@cisri/json-schema-form 1.0.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 ADDED
@@ -0,0 +1,58 @@
1
+ # @cisri/json-schema-form
2
+
3
+ A schema-driven JSON data form based on shadcn/ui (RJSF-style uiSchema). Given a `JsonSchema` (+ optional `uiSchema`), renders an editable form and emits JSON data via `onChange`.
4
+
5
+ > 与 `@cisri/json-schema-editor`(编辑 schema 结构)不同,本组件的输入是 schema,产出是 **JSON 数据**。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @cisri/json-schema-form
11
+ ```
12
+
13
+ ## Peer Dependencies
14
+
15
+ ```bash
16
+ npm install react react-dom lucide-react
17
+ ```
18
+
19
+ Radix/cva/clsx/tailwind-merge 由 `@cisri/shadcn` 与 `@cisri/core` 间接 peer(按其说明安装)。
20
+
21
+ ## 使用
22
+
23
+ ```tsx
24
+ import { useState } from 'react';
25
+ import { JsonSchemaForm } from '@cisri/json-schema-form';
26
+
27
+ export default function App() {
28
+ const [data, setData] = useState({ name: '', age: 0 });
29
+ const schema = {
30
+ type: 'object',
31
+ properties: { name: { type: 'string', title: 'Name' }, age: { type: 'integer', title: 'Age' } },
32
+ required: ['name'],
33
+ };
34
+ return <JsonSchemaForm schema={schema} value={data} onChange={setData} />;
35
+ }
36
+ ```
37
+
38
+ ## uiSchema
39
+
40
+ 可选 `uiSchema` prop(RJSF 风格 `ui:*` 键,来自 `@cisri/json-schema-ui-core`)控制每个字段的 widget/label/help/placeholder/order/hidden/disabled/readonly/classNames 等。缺省时用 `generateDefaultUiSchema(schema)`。
41
+
42
+ ## Tailwind 配置
43
+
44
+ ```ts
45
+ content: [
46
+ './node_modules/@cisri/*/dist/**/*.{js,cjs}',
47
+ ];
48
+ ```
49
+
50
+ ## CSS 变量
51
+
52
+ 使用 shadcn/ui 标准 CSS 变量,可通过覆盖 `:root` 自定义主题。
53
+
54
+ ## 自定义样式
55
+
56
+ ```tsx
57
+ <JsonSchemaForm schema={schema} value={data} onChange={setData} className="w-full max-w-2xl" />
58
+ ```
@@ -0,0 +1,3 @@
1
+ import type { JsonSchema } from '@cisri/json-schema-core';
2
+ export declare function validateForm(schema: JsonSchema, value: unknown): string[];
3
+ //# sourceMappingURL=form-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-utils.d.ts","sourceRoot":"","sources":["../src/form-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAYzE"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),O=require("react"),u=require("@cisri/shadcn"),N=require("@cisri/core"),P=require("@cisri/json-schema-ui-core");function R(s,t){const r=[];if(s.type==="object"&&s.properties){const x=new Set(s.required??[]),n=t??{};for(const d of Object.keys(s.properties))x.has(d)&&(n[d]==null||n[d]==="")&&r.push(`${d} is required`)}return r}function V(s){if(s.enum)return"select";switch(s.type){case"number":case"integer":return"updown";case"boolean":return"checkbox";case"string":default:return"text"}}function C({schema:s,uiSchema:t,path:r,value:x,onChange:n,readOnly:d,required:f,renderFieldActions:l}){const a=(t==null?void 0:t["ui:widget"])??V(s);if((t==null?void 0:t["ui:hidden"])===!0||a==="hidden")return null;const p=(t==null?void 0:t["ui:label"])===!1?null:(t==null?void 0:t["ui:label"])??s.title??r[r.length-1],o=t==null?void 0:t["ui:help"],c=t==null?void 0:t["ui:placeholder"],v=d===!0||(t==null?void 0:t["ui:disabled"])===!0,q=d===!0||(t==null?void 0:t["ui:readonly"])===!0,k=t==null?void 0:t["ui:classNames"],$=(t==null?void 0:t["ui:autofocus"])===!0,T=`jsf-${r.join(".")}`;if(s.type==="object"&&s.properties){const g=(t==null?void 0:t["ui:order"])??Object.keys(s.properties),w=new Set(s.required??[]),b=x??{},i=l==null?void 0:l({path:r,uiSchema:t,schema:s});return e.jsxs("fieldset",{className:N.cn("space-y-3",k),children:[p&&e.jsx("legend",{className:"text-sm font-medium",children:p}),i&&e.jsx("div",{className:"mt-1",children:i}),g.map(j=>e.jsx(C,{schema:s.properties[j],uiSchema:t==null?void 0:t[j],path:[...r,j],value:b[j],onChange:y=>n({...b,[j]:y}),readOnly:d,required:w.has(j),renderFieldActions:l},j))]})}if(s.type==="array"&&s.items){const g=Array.isArray(x)?x:[],w=t==null?void 0:t.items,b=l==null?void 0:l({path:r,uiSchema:t,schema:s});return e.jsxs("fieldset",{className:N.cn("space-y-2",k),children:[p&&e.jsx("legend",{className:"text-sm font-medium",children:p}),b&&e.jsx("div",{className:"mt-1",children:b}),g.map((i,j)=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(C,{schema:s.items,uiSchema:w,path:[...r,String(j)],value:i,onChange:y=>n(g.map((I,J)=>J===j?y:I)),readOnly:d,renderFieldActions:l}),!d&&e.jsx("button",{type:"button",onClick:()=>n(g.filter((y,I)=>I!==j)),"aria-label":`删除第 ${j+1} 项`,children:"×"})]},j)),!d&&e.jsx("button",{type:"button",onClick:()=>n([...g,void 0]),children:"添加一项"})]})}const U=l==null?void 0:l({path:r,uiSchema:t,schema:s});return e.jsxs("div",{className:N.cn("space-y-1",k),children:[p&&e.jsxs(u.Label,{htmlFor:T,children:[p,f&&e.jsx("span",{className:"text-destructive",children:" *"})]}),D(a,s,T,x,n,{placeholder:c,disabled:v,readonly:q,autoFocus:$}),o&&e.jsx("p",{className:"text-xs text-muted-foreground",children:o}),U&&e.jsx("div",{className:"mt-1",children:U})]})}function D(s,t,r,x,n,d){const{placeholder:f,disabled:l,readonly:a,autoFocus:p}=d;switch(s){case"textarea":return e.jsx(u.Textarea,{id:r,value:x??"",placeholder:f,disabled:l,readOnly:a,autoFocus:p,onChange:o=>n(o.target.value)});case"password":case"color":case"date":case"text":return e.jsx(u.Input,{id:r,type:s,value:x??"",placeholder:f,disabled:l,readOnly:a,autoFocus:p,onChange:o=>n(o.target.value)});case"updown":return e.jsx(u.Input,{id:r,type:"number",value:x??"",placeholder:f,disabled:l,readOnly:a,autoFocus:p,onChange:o=>n(o.target.value===""?void 0:Number(o.target.value))});case"range":return e.jsx(u.Input,{id:r,type:"range",value:x??0,disabled:l,readOnly:a,autoFocus:p,onChange:o=>n(Number(o.target.value))});case"checkbox":return e.jsx(u.Checkbox,{id:r,checked:x===!0,disabled:l,autoFocus:p,onCheckedChange:o=>n(o===!0)});case"radio":{const o=t.enum?t.enum:[!1,!0];return e.jsx("div",{className:"space-y-1",children:o.map(c=>e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"radio",name:r,checked:x===c,disabled:l,onChange:()=>n(c)}),t.enum?String(c):c?"true":"false"]},String(c)))})}case"select":{const o=t.enum??[];return e.jsxs(u.Select,{value:x!=null?String(x):void 0,disabled:l,onValueChange:c=>{const v=o.find(q=>String(q)===c);n(v)},children:[e.jsx(u.SelectTrigger,{id:r,children:e.jsx(u.SelectValue,{placeholder:f})}),e.jsx(u.SelectContent,{children:o.map(c=>e.jsx(u.SelectItem,{value:String(c),children:String(c)},String(c)))})]})}default:return e.jsx(u.Input,{id:r,value:x??"",placeholder:f,disabled:l,onChange:o=>n(o.target.value)})}}function L({schema:s,uiSchema:t,value:r,onChange:x,onError:n,readOnly:d,className:f,renderFieldActions:l}){const a=t??P.generateDefaultUiSchema(s);return O.useEffect(()=>{n==null||n(R(s,r))},[s,r,n]),e.jsx("div",{className:N.cn("space-y-3",f),children:e.jsx(C,{schema:s,uiSchema:a,path:[],value:r,onChange:x,readOnly:d,renderFieldActions:l})})}exports.JsonSchemaForm=L;
@@ -0,0 +1,5 @@
1
+ export { JsonSchemaForm } from './json-schema-form';
2
+ export type { JsonSchemaFormProps } from './json-schema-form';
3
+ export type { UiSchema, UiWidget } from '@cisri/json-schema-ui-core';
4
+ export type { JsonSchema } from '@cisri/json-schema-core';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,255 @@
1
+ import { jsx as r, jsxs as b } from "react/jsx-runtime";
2
+ import { useEffect as J } from "react";
3
+ import { Label as L, Input as v, Select as O, SelectTrigger as P, SelectValue as R, SelectContent as W, SelectItem as z, Checkbox as B, Textarea as G } from "@cisri/shadcn";
4
+ import { cn as j } from "@cisri/core";
5
+ import { generateDefaultUiSchema as H } from "@cisri/json-schema-ui-core";
6
+ function K(e, t) {
7
+ const s = [];
8
+ if (e.type === "object" && e.properties) {
9
+ const d = new Set(e.required ?? []), n = t ?? {};
10
+ for (const p of Object.keys(e.properties))
11
+ d.has(p) && (n[p] == null || n[p] === "") && s.push(`${p} is required`);
12
+ }
13
+ return s;
14
+ }
15
+ function M(e) {
16
+ if (e.enum) return "select";
17
+ switch (e.type) {
18
+ case "number":
19
+ case "integer":
20
+ return "updown";
21
+ case "boolean":
22
+ return "checkbox";
23
+ case "string":
24
+ default:
25
+ return "text";
26
+ }
27
+ }
28
+ function T({
29
+ schema: e,
30
+ uiSchema: t,
31
+ path: s,
32
+ value: d,
33
+ onChange: n,
34
+ readOnly: p,
35
+ required: g,
36
+ renderFieldActions: l
37
+ }) {
38
+ const u = (t == null ? void 0 : t["ui:widget"]) ?? M(e);
39
+ if ((t == null ? void 0 : t["ui:hidden"]) === !0 || u === "hidden") return null;
40
+ const c = (t == null ? void 0 : t["ui:label"]) === !1 ? null : (t == null ? void 0 : t["ui:label"]) ?? e.title ?? s[s.length - 1], o = t == null ? void 0 : t["ui:help"], f = t == null ? void 0 : t["ui:placeholder"], k = p === !0 || (t == null ? void 0 : t["ui:disabled"]) === !0, w = p === !0 || (t == null ? void 0 : t["ui:readonly"]) === !0, q = t == null ? void 0 : t["ui:classNames"], V = (t == null ? void 0 : t["ui:autofocus"]) === !0, U = `jsf-${s.join(".")}`;
41
+ if (e.type === "object" && e.properties) {
42
+ const y = (t == null ? void 0 : t["ui:order"]) ?? Object.keys(e.properties), C = new Set(e.required ?? []), i = d ?? {}, x = l == null ? void 0 : l({ path: s, uiSchema: t, schema: e });
43
+ return /* @__PURE__ */ b("fieldset", { className: j("space-y-3", q), children: [
44
+ c && /* @__PURE__ */ r("legend", { className: "text-sm font-medium", children: c }),
45
+ x && /* @__PURE__ */ r("div", { className: "mt-1", children: x }),
46
+ y.map((a) => /* @__PURE__ */ r(
47
+ T,
48
+ {
49
+ schema: e.properties[a],
50
+ uiSchema: t == null ? void 0 : t[a],
51
+ path: [...s, a],
52
+ value: i[a],
53
+ onChange: (N) => n({ ...i, [a]: N }),
54
+ readOnly: p,
55
+ required: C.has(a),
56
+ renderFieldActions: l
57
+ },
58
+ a
59
+ ))
60
+ ] });
61
+ }
62
+ if (e.type === "array" && e.items) {
63
+ const y = Array.isArray(d) ? d : [], C = t == null ? void 0 : t.items, i = l == null ? void 0 : l({ path: s, uiSchema: t, schema: e });
64
+ return /* @__PURE__ */ b("fieldset", { className: j("space-y-2", q), children: [
65
+ c && /* @__PURE__ */ r("legend", { className: "text-sm font-medium", children: c }),
66
+ i && /* @__PURE__ */ r("div", { className: "mt-1", children: i }),
67
+ y.map((x, a) => /* @__PURE__ */ b("div", { className: "flex items-start gap-2", children: [
68
+ /* @__PURE__ */ r(
69
+ T,
70
+ {
71
+ schema: e.items,
72
+ uiSchema: C,
73
+ path: [...s, String(a)],
74
+ value: x,
75
+ onChange: (N) => n(y.map((I, D) => D === a ? N : I)),
76
+ readOnly: p,
77
+ renderFieldActions: l
78
+ }
79
+ ),
80
+ !p && /* @__PURE__ */ r(
81
+ "button",
82
+ {
83
+ type: "button",
84
+ onClick: () => n(y.filter((N, I) => I !== a)),
85
+ "aria-label": `删除第 ${a + 1} 项`,
86
+ children: "×"
87
+ }
88
+ )
89
+ ] }, a)),
90
+ !p && /* @__PURE__ */ r("button", { type: "button", onClick: () => n([...y, void 0]), children: "添加一项" })
91
+ ] });
92
+ }
93
+ const $ = l == null ? void 0 : l({ path: s, uiSchema: t, schema: e });
94
+ return /* @__PURE__ */ b("div", { className: j("space-y-1", q), children: [
95
+ c && /* @__PURE__ */ b(L, { htmlFor: U, children: [
96
+ c,
97
+ g && /* @__PURE__ */ r("span", { className: "text-destructive", children: " *" })
98
+ ] }),
99
+ Q(u, e, U, d, n, {
100
+ placeholder: f,
101
+ disabled: k,
102
+ readonly: w,
103
+ autoFocus: V
104
+ }),
105
+ o && /* @__PURE__ */ r("p", { className: "text-xs text-muted-foreground", children: o }),
106
+ $ && /* @__PURE__ */ r("div", { className: "mt-1", children: $ })
107
+ ] });
108
+ }
109
+ function Q(e, t, s, d, n, p) {
110
+ const { placeholder: g, disabled: l, readonly: u, autoFocus: c } = p;
111
+ switch (e) {
112
+ case "textarea":
113
+ return /* @__PURE__ */ r(
114
+ G,
115
+ {
116
+ id: s,
117
+ value: d ?? "",
118
+ placeholder: g,
119
+ disabled: l,
120
+ readOnly: u,
121
+ autoFocus: c,
122
+ onChange: (o) => n(o.target.value)
123
+ }
124
+ );
125
+ case "password":
126
+ case "color":
127
+ case "date":
128
+ case "text":
129
+ return /* @__PURE__ */ r(
130
+ v,
131
+ {
132
+ id: s,
133
+ type: e,
134
+ value: d ?? "",
135
+ placeholder: g,
136
+ disabled: l,
137
+ readOnly: u,
138
+ autoFocus: c,
139
+ onChange: (o) => n(o.target.value)
140
+ }
141
+ );
142
+ case "updown":
143
+ return /* @__PURE__ */ r(
144
+ v,
145
+ {
146
+ id: s,
147
+ type: "number",
148
+ value: d ?? "",
149
+ placeholder: g,
150
+ disabled: l,
151
+ readOnly: u,
152
+ autoFocus: c,
153
+ onChange: (o) => n(o.target.value === "" ? void 0 : Number(o.target.value))
154
+ }
155
+ );
156
+ case "range":
157
+ return /* @__PURE__ */ r(
158
+ v,
159
+ {
160
+ id: s,
161
+ type: "range",
162
+ value: d ?? 0,
163
+ disabled: l,
164
+ readOnly: u,
165
+ autoFocus: c,
166
+ onChange: (o) => n(Number(o.target.value))
167
+ }
168
+ );
169
+ case "checkbox":
170
+ return /* @__PURE__ */ r(
171
+ B,
172
+ {
173
+ id: s,
174
+ checked: d === !0,
175
+ disabled: l,
176
+ autoFocus: c,
177
+ onCheckedChange: (o) => n(o === !0)
178
+ }
179
+ );
180
+ case "radio": {
181
+ const o = t.enum ? t.enum : [!1, !0];
182
+ return /* @__PURE__ */ r("div", { className: "space-y-1", children: o.map((f) => /* @__PURE__ */ b("label", { className: "flex items-center gap-2 text-sm", children: [
183
+ /* @__PURE__ */ r(
184
+ "input",
185
+ {
186
+ type: "radio",
187
+ name: s,
188
+ checked: d === f,
189
+ disabled: l,
190
+ onChange: () => n(f)
191
+ }
192
+ ),
193
+ t.enum ? String(f) : f ? "true" : "false"
194
+ ] }, String(f))) });
195
+ }
196
+ case "select": {
197
+ const o = t.enum ?? [];
198
+ return /* @__PURE__ */ b(
199
+ O,
200
+ {
201
+ value: d != null ? String(d) : void 0,
202
+ disabled: l,
203
+ onValueChange: (f) => {
204
+ const k = o.find((w) => String(w) === f);
205
+ n(k);
206
+ },
207
+ children: [
208
+ /* @__PURE__ */ r(P, { id: s, children: /* @__PURE__ */ r(R, { placeholder: g }) }),
209
+ /* @__PURE__ */ r(W, { children: o.map((f) => /* @__PURE__ */ r(z, { value: String(f), children: String(f) }, String(f))) })
210
+ ]
211
+ }
212
+ );
213
+ }
214
+ default:
215
+ return /* @__PURE__ */ r(
216
+ v,
217
+ {
218
+ id: s,
219
+ value: d ?? "",
220
+ placeholder: g,
221
+ disabled: l,
222
+ onChange: (o) => n(o.target.value)
223
+ }
224
+ );
225
+ }
226
+ }
227
+ function F({
228
+ schema: e,
229
+ uiSchema: t,
230
+ value: s,
231
+ onChange: d,
232
+ onError: n,
233
+ readOnly: p,
234
+ className: g,
235
+ renderFieldActions: l
236
+ }) {
237
+ const u = t ?? H(e);
238
+ return J(() => {
239
+ n == null || n(K(e, s));
240
+ }, [e, s, n]), /* @__PURE__ */ r("div", { className: j("space-y-3", g), children: /* @__PURE__ */ r(
241
+ T,
242
+ {
243
+ schema: e,
244
+ uiSchema: u,
245
+ path: [],
246
+ value: s,
247
+ onChange: d,
248
+ readOnly: p,
249
+ renderFieldActions: l
250
+ }
251
+ ) });
252
+ }
253
+ export {
254
+ F as JsonSchemaForm
255
+ };
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { JsonSchema } from '@cisri/json-schema-core';
3
+ import { type UiSchema } from '@cisri/json-schema-ui-core';
4
+ export interface JsonSchemaFormProps {
5
+ schema: JsonSchema;
6
+ uiSchema?: UiSchema;
7
+ value: unknown;
8
+ onChange: (value: unknown) => void;
9
+ onError?: (errors: string[]) => void;
10
+ readOnly?: boolean;
11
+ className?: string;
12
+ renderFieldActions?: (ctx: {
13
+ path: string[];
14
+ uiSchema: UiSchema | undefined;
15
+ schema: JsonSchema;
16
+ }) => ReactNode;
17
+ }
18
+ export declare function JsonSchemaForm({ schema, uiSchema, value, onChange, onError, readOnly, className, renderFieldActions, }: JsonSchemaFormProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=json-schema-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-schema-form.d.ts","sourceRoot":"","sources":["../src/json-schema-form.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAalD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,4BAA4B,CAAC;AAGpC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE;QACzB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;QAC/B,MAAM,EAAE,UAAU,CAAC;KACpB,KAAK,SAAS,CAAC;CACjB;AAuRD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,kBAAkB,GACnB,EAAE,mBAAmB,+BAkBrB"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@cisri/json-schema-form",
3
+ "version": "1.0.0",
4
+ "description": "Schema-driven JSON data form editor based on shadcn/ui (RJSF-style uiSchema)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "dependencies": {
24
+ "@cisri/core": "1.0.0",
25
+ "@cisri/json-schema-core": "1.0.0",
26
+ "@cisri/json-schema-ui-core": "1.0.0",
27
+ "@cisri/shadcn": "1.0.0"
28
+ },
29
+ "peerDependencies": {
30
+ "react": "^18.0.0 || ^19.0.0",
31
+ "react-dom": "^18.0.0 || ^19.0.0",
32
+ "lucide-react": "^0.300.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "lucide-react": {
36
+ "optional": false
37
+ }
38
+ },
39
+ "devDependencies": {
40
+ "@testing-library/jest-dom": "^6.4.0",
41
+ "@testing-library/react": "^15.0.0",
42
+ "@testing-library/user-event": "^14.5.0",
43
+ "@types/react": "^18.3.0",
44
+ "@types/react-dom": "^18.3.0",
45
+ "jsdom": "^24.0.0",
46
+ "react": "^18.3.1",
47
+ "react-dom": "^18.3.1",
48
+ "typescript": "^5.4.5",
49
+ "vite": "^5.2.11",
50
+ "vitest": "^1.6.0",
51
+ "@vitejs/plugin-react": "^4.2.0"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc && vite build",
55
+ "test": "vitest run",
56
+ "test:watch": "vitest"
57
+ }
58
+ }