@cisri/json-schema-ui-editor 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 +60 -0
- package/dist/field-config-panel.d.ts +9 -0
- package/dist/field-config-panel.d.ts.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +266 -0
- package/dist/json-schema-ui-editor.d.ts +12 -0
- package/dist/json-schema-ui-editor.d.ts.map +1 -0
- package/dist/ui-editor-utils.d.ts +6 -0
- package/dist/ui-editor-utils.d.ts.map +1 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @cisri/json-schema-ui-editor
|
|
2
|
+
|
|
3
|
+
A visual uiSchema editor for `@cisri/json-schema-form`. Renders a live form preview and lets the user configure each field's `ui:*` properties inline (RJSF-style uiSchema). Emits the updated `uiSchema` via `onChange`; never modifies the `schema`.
|
|
4
|
+
|
|
5
|
+
> 与 `@cisri/json-schema-form`(编辑 JSON 数据)不同,本组件的输入是 schema + uiSchema,产出是 **uiSchema**。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @cisri/json-schema-ui-editor
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Peer Dependencies
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react react-dom lucide-react @dnd-kit/core @dnd-kit/sortable @dnd-kit/modifiers @dnd-kit/utilities
|
|
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 { JsonSchemaUiEditor } from '@cisri/json-schema-ui-editor';
|
|
26
|
+
import { generateDefaultUiSchema } from '@cisri/json-schema-ui-core';
|
|
27
|
+
|
|
28
|
+
const schema = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: { name: { type: 'string', title: 'Name' }, age: { type: 'integer', title: 'Age' } },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default function App() {
|
|
34
|
+
const [uiSchema, setUiSchema] = useState(() => generateDefaultUiSchema(schema));
|
|
35
|
+
return (
|
|
36
|
+
<JsonSchemaUiEditor
|
|
37
|
+
schema={schema}
|
|
38
|
+
uiSchema={uiSchema}
|
|
39
|
+
onChange={setUiSchema}
|
|
40
|
+
formData={{ name: '', age: 0 }}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 交互
|
|
47
|
+
|
|
48
|
+
每个字段右侧出现「配置」按钮,点击弹出面板:widget 下拉、label(含隐藏)、help、placeholder、hidden/disabled/readonly 开关、classNames。对象字段额外可拖曳调整字段顺序(`ui:order`)。改动实时反映在预览表单,并经 `onChange` 产出最新 `uiSchema`。
|
|
49
|
+
|
|
50
|
+
## Tailwind 配置
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
content: [
|
|
54
|
+
'./node_modules/@cisri/*/dist/**/*.{js,cjs}',
|
|
55
|
+
];
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CSS 变量
|
|
59
|
+
|
|
60
|
+
使用 shadcn/ui 标准 CSS 变量,可通过覆盖 `:root` 自定义主题。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JsonSchema } from '@cisri/json-schema-core';
|
|
2
|
+
import type { UiSchema } from '@cisri/json-schema-ui-core';
|
|
3
|
+
export interface FieldConfigPanelProps {
|
|
4
|
+
schema: JsonSchema;
|
|
5
|
+
uiField: UiSchema | undefined;
|
|
6
|
+
onPatch: (patch: Partial<UiSchema>) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function FieldConfigPanel({ schema, uiField, onPatch }: FieldConfigPanelProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=field-config-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-config-panel.d.ts","sourceRoot":"","sources":["../src/field-config-panel.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,4BAA4B,CAAC;AAkErE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;CAC7C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,qBAAqB,+BAsHnF"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("react"),t=require("@cisri/shadcn"),j=require("lucide-react"),v=require("@cisri/core"),C=require("@cisri/json-schema-form"),N=require("@cisri/json-schema-ui-core"),d=require("@dnd-kit/core"),p=require("@dnd-kit/sortable"),w=require("@dnd-kit/modifiers"),L=require("@dnd-kit/utilities");function k(s){return s.enum?!0:s.type==="string"||s.type==="number"||s.type==="integer"||s.type==="boolean"}function q(s){if(s.enum)return["select","radio","hidden"];switch(s.type){case"string":return["text","textarea","password","color","date","hidden"];case"number":case"integer":return["updown","range","hidden"];case"boolean":return["checkbox","radio","hidden"];default:return[]}}function O(s,r,a){const l=s.indexOf(r),c=s.indexOf(a);if(l===-1||c===-1||l===c)return s;const o=[...s],[i]=o.splice(l,1);return o.splice(c,0,i),o}function V({name:s}){const{attributes:r,listeners:a,setNodeRef:l,transform:c,transition:o,isDragging:i}=p.useSortable({id:s});return e.jsxs("div",{ref:l,style:{transform:L.CSS.Transform.toString(c),transition:o},className:v.cn("flex items-center gap-2 rounded border border-border px-2 py-1",i&&"opacity-50"),children:[e.jsx("button",{type:"button",className:"flex h-6 w-6 cursor-grab items-center justify-center text-muted-foreground active:cursor-grabbing","aria-label":`拖动字段 ${s}`,...r,...a,children:e.jsx(j.GripVertical,{className:"pointer-events-none h-4 w-4"})}),e.jsx("span",{className:"text-sm",children:s})]})}function I({order:s,onReorder:r}){const a=d.useSensors(d.useSensor(d.PointerSensor,{activationConstraint:{distance:5}}),d.useSensor(d.KeyboardSensor,{coordinateGetter:p.sortableKeyboardCoordinates})),l=c=>{const{active:o,over:i}=c;!i||o.id===i.id||r(O(s,String(o.id),String(i.id)))};return e.jsx(d.DndContext,{sensors:a,collisionDetection:d.closestCenter,modifiers:[w.restrictToVerticalAxis],onDragEnd:l,children:e.jsx(p.SortableContext,{items:s,strategy:p.verticalListSortingStrategy,children:e.jsx("div",{className:"space-y-1",children:s.map(c=>e.jsx(V,{name:c},c))})})})}function D({schema:s,uiField:r,onPatch:a}){const l=k(s),c=s.type==="object"&&!!s.properties,o=q(s),i=typeof(r==null?void 0:r["ui:label"])=="string"?r["ui:label"]:"",g=(r==null?void 0:r["ui:label"])===!1;return e.jsxs("div",{className:"space-y-3",children:[o.length>0&&e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{htmlFor:"ui-cfg-widget",children:"Widget"}),e.jsxs(t.Select,{value:(r==null?void 0:r["ui:widget"])??o[0],onValueChange:n=>a({"ui:widget":n}),children:[e.jsx(t.SelectTrigger,{id:"ui-cfg-widget","aria-label":"widget",children:e.jsx(t.SelectValue,{})}),e.jsx(t.SelectContent,{children:o.map(n=>e.jsx(t.SelectItem,{value:n,children:n},n))})]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{htmlFor:"ui-cfg-label",children:"Label"}),e.jsx(t.Input,{id:"ui-cfg-label",autoComplete:"off",value:i,onChange:n=>a({"ui:label":n.target.value||void 0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(t.Switch,{id:"ui-cfg-hide-label","aria-label":"隐藏 label",checked:g,onCheckedChange:n=>a({"ui:label":n?!1:void 0})}),e.jsx(t.Label,{htmlFor:"ui-cfg-hide-label",children:"隐藏 label"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{htmlFor:"ui-cfg-help",children:"Help"}),e.jsx(t.Input,{id:"ui-cfg-help",autoComplete:"off",value:(r==null?void 0:r["ui:help"])??"",onChange:n=>a({"ui:help":n.target.value||void 0})})]}),l&&e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{htmlFor:"ui-cfg-placeholder",children:"Placeholder"}),e.jsx(t.Input,{id:"ui-cfg-placeholder",autoComplete:"off",value:(r==null?void 0:r["ui:placeholder"])??"",onChange:n=>a({"ui:placeholder":n.target.value||void 0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(t.Switch,{id:"ui-cfg-hidden","aria-label":"隐藏字段",checked:(r==null?void 0:r["ui:hidden"])===!0,onCheckedChange:n=>a({"ui:hidden":n})}),e.jsx(t.Label,{htmlFor:"ui-cfg-hidden",children:"隐藏字段"})]}),l&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(t.Switch,{id:"ui-cfg-disabled","aria-label":"禁用",checked:(r==null?void 0:r["ui:disabled"])===!0,onCheckedChange:n=>a({"ui:disabled":n})}),e.jsx(t.Label,{htmlFor:"ui-cfg-disabled",children:"禁用"})]}),l&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(t.Switch,{id:"ui-cfg-readonly","aria-label":"只读",checked:(r==null?void 0:r["ui:readonly"])===!0,onCheckedChange:n=>a({"ui:readonly":n})}),e.jsx(t.Label,{htmlFor:"ui-cfg-readonly",children:"只读"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{htmlFor:"ui-cfg-classnames",children:"Class names"}),e.jsx(t.Input,{id:"ui-cfg-classnames",autoComplete:"off",value:(r==null?void 0:r["ui:classNames"])??"",onChange:n=>a({"ui:classNames":n.target.value||void 0})})]}),c&&e.jsxs("div",{className:"space-y-1",children:[e.jsx(t.Label,{children:"字段顺序"}),e.jsx(I,{order:(r==null?void 0:r["ui:order"])??Object.keys(s.properties),onReorder:n=>a({"ui:order":n})})]})]})}function T({schema:s,uiSchema:r,onChange:a,formData:l,readOnly:c,className:o}){const[i,g]=x.useState(null),[n,y]=x.useState(l),f=x.useCallback((h,u)=>{a(N.setUiField(r,h,u))},[a,r]),S=x.useCallback(h=>{if(c)return null;const u=h.path.join(".");return e.jsxs(t.Popover,{open:i===u,onOpenChange:b=>g(b?u:m=>m===u?null:m),children:[e.jsx(t.PopoverTrigger,{asChild:!0,children:e.jsx("button",{type:"button",className:"inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-muted","aria-label":`配置字段 ${u}`,children:e.jsx(j.Settings2,{className:"h-4 w-4"})})}),e.jsx(t.PopoverContent,{align:"start",children:e.jsx(D,{schema:h.schema,uiField:h.uiSchema,onPatch:b=>f(h.path,b)})})]})},[c,i,f]);return e.jsx("div",{className:v.cn("space-y-3",o),children:e.jsx(C.JsonSchemaForm,{schema:s,uiSchema:r,value:n,onChange:y,renderFieldActions:S})})}exports.JsonSchemaUiEditor=T;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { JsonSchemaUiEditor } from './json-schema-ui-editor';
|
|
2
|
+
export type { JsonSchemaUiEditorProps } from './json-schema-ui-editor';
|
|
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,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,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,266 @@
|
|
|
1
|
+
import { jsxs as c, jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { useState as y, useCallback as C } from "react";
|
|
3
|
+
import { Label as d, Select as k, SelectTrigger as O, SelectValue as j, SelectContent as V, SelectItem as D, Input as p, Switch as f, Popover as L, PopoverTrigger as E, PopoverContent as K } from "@cisri/shadcn";
|
|
4
|
+
import { GripVertical as T, Settings2 as I } from "lucide-react";
|
|
5
|
+
import { cn as N } from "@cisri/core";
|
|
6
|
+
import { JsonSchemaForm as A } from "@cisri/json-schema-form";
|
|
7
|
+
import { setUiField as G } from "@cisri/json-schema-ui-core";
|
|
8
|
+
import { useSensors as J, useSensor as S, PointerSensor as R, KeyboardSensor as U, DndContext as $, closestCenter as H } from "@dnd-kit/core";
|
|
9
|
+
import { sortableKeyboardCoordinates as P, SortableContext as W, verticalListSortingStrategy as q, useSortable as z } from "@dnd-kit/sortable";
|
|
10
|
+
import { restrictToVerticalAxis as B } from "@dnd-kit/modifiers";
|
|
11
|
+
import { CSS as M } from "@dnd-kit/utilities";
|
|
12
|
+
function Q(t) {
|
|
13
|
+
return t.enum ? !0 : t.type === "string" || t.type === "number" || t.type === "integer" || t.type === "boolean";
|
|
14
|
+
}
|
|
15
|
+
function X(t) {
|
|
16
|
+
if (t.enum) return ["select", "radio", "hidden"];
|
|
17
|
+
switch (t.type) {
|
|
18
|
+
case "string":
|
|
19
|
+
return ["text", "textarea", "password", "color", "date", "hidden"];
|
|
20
|
+
case "number":
|
|
21
|
+
case "integer":
|
|
22
|
+
return ["updown", "range", "hidden"];
|
|
23
|
+
case "boolean":
|
|
24
|
+
return ["checkbox", "radio", "hidden"];
|
|
25
|
+
default:
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function Y(t, e, a) {
|
|
30
|
+
const l = t.indexOf(e), o = t.indexOf(a);
|
|
31
|
+
if (l === -1 || o === -1 || l === o) return t;
|
|
32
|
+
const s = [...t], [i] = s.splice(l, 1);
|
|
33
|
+
return s.splice(o, 0, i), s;
|
|
34
|
+
}
|
|
35
|
+
function Z({ name: t }) {
|
|
36
|
+
const { attributes: e, listeners: a, setNodeRef: l, transform: o, transition: s, isDragging: i } = z({ id: t });
|
|
37
|
+
return /* @__PURE__ */ c(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
ref: l,
|
|
41
|
+
style: { transform: M.Transform.toString(o), transition: s },
|
|
42
|
+
className: N(
|
|
43
|
+
"flex items-center gap-2 rounded border border-border px-2 py-1",
|
|
44
|
+
i && "opacity-50"
|
|
45
|
+
),
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ r(
|
|
48
|
+
"button",
|
|
49
|
+
{
|
|
50
|
+
type: "button",
|
|
51
|
+
className: "flex h-6 w-6 cursor-grab items-center justify-center text-muted-foreground active:cursor-grabbing",
|
|
52
|
+
"aria-label": `拖动字段 ${t}`,
|
|
53
|
+
...e,
|
|
54
|
+
...a,
|
|
55
|
+
children: /* @__PURE__ */ r(T, { className: "pointer-events-none h-4 w-4" })
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
/* @__PURE__ */ r("span", { className: "text-sm", children: t })
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function _({ order: t, onReorder: e }) {
|
|
64
|
+
const a = J(
|
|
65
|
+
S(R, { activationConstraint: { distance: 5 } }),
|
|
66
|
+
S(U, { coordinateGetter: P })
|
|
67
|
+
);
|
|
68
|
+
return /* @__PURE__ */ r(
|
|
69
|
+
$,
|
|
70
|
+
{
|
|
71
|
+
sensors: a,
|
|
72
|
+
collisionDetection: H,
|
|
73
|
+
modifiers: [B],
|
|
74
|
+
onDragEnd: (o) => {
|
|
75
|
+
const { active: s, over: i } = o;
|
|
76
|
+
!i || s.id === i.id || e(Y(t, String(s.id), String(i.id)));
|
|
77
|
+
},
|
|
78
|
+
children: /* @__PURE__ */ r(W, { items: t, strategy: q, children: /* @__PURE__ */ r("div", { className: "space-y-1", children: t.map((o) => /* @__PURE__ */ r(Z, { name: o }, o)) }) })
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function F({ schema: t, uiField: e, onPatch: a }) {
|
|
83
|
+
const l = Q(t), o = t.type === "object" && !!t.properties, s = X(t), i = typeof (e == null ? void 0 : e["ui:label"]) == "string" ? e["ui:label"] : "", g = (e == null ? void 0 : e["ui:label"]) === !1;
|
|
84
|
+
return /* @__PURE__ */ c("div", { className: "space-y-3", children: [
|
|
85
|
+
s.length > 0 && /* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
86
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-widget", children: "Widget" }),
|
|
87
|
+
/* @__PURE__ */ c(
|
|
88
|
+
k,
|
|
89
|
+
{
|
|
90
|
+
value: (e == null ? void 0 : e["ui:widget"]) ?? s[0],
|
|
91
|
+
onValueChange: (n) => a({ "ui:widget": n }),
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ r(O, { id: "ui-cfg-widget", "aria-label": "widget", children: /* @__PURE__ */ r(j, {}) }),
|
|
94
|
+
/* @__PURE__ */ r(V, { children: s.map((n) => /* @__PURE__ */ r(D, { value: n, children: n }, n)) })
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
100
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-label", children: "Label" }),
|
|
101
|
+
/* @__PURE__ */ r(
|
|
102
|
+
p,
|
|
103
|
+
{
|
|
104
|
+
id: "ui-cfg-label",
|
|
105
|
+
autoComplete: "off",
|
|
106
|
+
value: i,
|
|
107
|
+
onChange: (n) => a({ "ui:label": n.target.value || void 0 })
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] }),
|
|
111
|
+
/* @__PURE__ */ c("div", { className: "flex items-center gap-2", children: [
|
|
112
|
+
/* @__PURE__ */ r(
|
|
113
|
+
f,
|
|
114
|
+
{
|
|
115
|
+
id: "ui-cfg-hide-label",
|
|
116
|
+
"aria-label": "隐藏 label",
|
|
117
|
+
checked: g,
|
|
118
|
+
onCheckedChange: (n) => a({ "ui:label": n ? !1 : void 0 })
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-hide-label", children: "隐藏 label" })
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
124
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-help", children: "Help" }),
|
|
125
|
+
/* @__PURE__ */ r(
|
|
126
|
+
p,
|
|
127
|
+
{
|
|
128
|
+
id: "ui-cfg-help",
|
|
129
|
+
autoComplete: "off",
|
|
130
|
+
value: (e == null ? void 0 : e["ui:help"]) ?? "",
|
|
131
|
+
onChange: (n) => a({ "ui:help": n.target.value || void 0 })
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }),
|
|
135
|
+
l && /* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
136
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-placeholder", children: "Placeholder" }),
|
|
137
|
+
/* @__PURE__ */ r(
|
|
138
|
+
p,
|
|
139
|
+
{
|
|
140
|
+
id: "ui-cfg-placeholder",
|
|
141
|
+
autoComplete: "off",
|
|
142
|
+
value: (e == null ? void 0 : e["ui:placeholder"]) ?? "",
|
|
143
|
+
onChange: (n) => a({ "ui:placeholder": n.target.value || void 0 })
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] }),
|
|
147
|
+
/* @__PURE__ */ c("div", { className: "flex items-center gap-2", children: [
|
|
148
|
+
/* @__PURE__ */ r(
|
|
149
|
+
f,
|
|
150
|
+
{
|
|
151
|
+
id: "ui-cfg-hidden",
|
|
152
|
+
"aria-label": "隐藏字段",
|
|
153
|
+
checked: (e == null ? void 0 : e["ui:hidden"]) === !0,
|
|
154
|
+
onCheckedChange: (n) => a({ "ui:hidden": n })
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-hidden", children: "隐藏字段" })
|
|
158
|
+
] }),
|
|
159
|
+
l && /* @__PURE__ */ c("div", { className: "flex items-center gap-2", children: [
|
|
160
|
+
/* @__PURE__ */ r(
|
|
161
|
+
f,
|
|
162
|
+
{
|
|
163
|
+
id: "ui-cfg-disabled",
|
|
164
|
+
"aria-label": "禁用",
|
|
165
|
+
checked: (e == null ? void 0 : e["ui:disabled"]) === !0,
|
|
166
|
+
onCheckedChange: (n) => a({ "ui:disabled": n })
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-disabled", children: "禁用" })
|
|
170
|
+
] }),
|
|
171
|
+
l && /* @__PURE__ */ c("div", { className: "flex items-center gap-2", children: [
|
|
172
|
+
/* @__PURE__ */ r(
|
|
173
|
+
f,
|
|
174
|
+
{
|
|
175
|
+
id: "ui-cfg-readonly",
|
|
176
|
+
"aria-label": "只读",
|
|
177
|
+
checked: (e == null ? void 0 : e["ui:readonly"]) === !0,
|
|
178
|
+
onCheckedChange: (n) => a({ "ui:readonly": n })
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-readonly", children: "只读" })
|
|
182
|
+
] }),
|
|
183
|
+
/* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
184
|
+
/* @__PURE__ */ r(d, { htmlFor: "ui-cfg-classnames", children: "Class names" }),
|
|
185
|
+
/* @__PURE__ */ r(
|
|
186
|
+
p,
|
|
187
|
+
{
|
|
188
|
+
id: "ui-cfg-classnames",
|
|
189
|
+
autoComplete: "off",
|
|
190
|
+
value: (e == null ? void 0 : e["ui:classNames"]) ?? "",
|
|
191
|
+
onChange: (n) => a({ "ui:classNames": n.target.value || void 0 })
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
] }),
|
|
195
|
+
o && /* @__PURE__ */ c("div", { className: "space-y-1", children: [
|
|
196
|
+
/* @__PURE__ */ r(d, { children: "字段顺序" }),
|
|
197
|
+
/* @__PURE__ */ r(
|
|
198
|
+
_,
|
|
199
|
+
{
|
|
200
|
+
order: (e == null ? void 0 : e["ui:order"]) ?? Object.keys(t.properties),
|
|
201
|
+
onReorder: (n) => a({ "ui:order": n })
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
] })
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
function he({
|
|
208
|
+
schema: t,
|
|
209
|
+
uiSchema: e,
|
|
210
|
+
onChange: a,
|
|
211
|
+
formData: l,
|
|
212
|
+
readOnly: o,
|
|
213
|
+
className: s
|
|
214
|
+
}) {
|
|
215
|
+
const [i, g] = y(null), [n, x] = y(l), b = C(
|
|
216
|
+
(h, m) => {
|
|
217
|
+
a(G(e, h, m));
|
|
218
|
+
},
|
|
219
|
+
[a, e]
|
|
220
|
+
), w = C(
|
|
221
|
+
(h) => {
|
|
222
|
+
if (o) return null;
|
|
223
|
+
const m = h.path.join(".");
|
|
224
|
+
return /* @__PURE__ */ c(
|
|
225
|
+
L,
|
|
226
|
+
{
|
|
227
|
+
open: i === m,
|
|
228
|
+
onOpenChange: (u) => g(u ? m : (v) => v === m ? null : v),
|
|
229
|
+
children: [
|
|
230
|
+
/* @__PURE__ */ r(E, { asChild: !0, children: /* @__PURE__ */ r(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
type: "button",
|
|
234
|
+
className: "inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-muted",
|
|
235
|
+
"aria-label": `配置字段 ${m}`,
|
|
236
|
+
children: /* @__PURE__ */ r(I, { className: "h-4 w-4" })
|
|
237
|
+
}
|
|
238
|
+
) }),
|
|
239
|
+
/* @__PURE__ */ r(K, { align: "start", children: /* @__PURE__ */ r(
|
|
240
|
+
F,
|
|
241
|
+
{
|
|
242
|
+
schema: h.schema,
|
|
243
|
+
uiField: h.uiSchema,
|
|
244
|
+
onPatch: (u) => b(h.path, u)
|
|
245
|
+
}
|
|
246
|
+
) })
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
},
|
|
251
|
+
[o, i, b]
|
|
252
|
+
);
|
|
253
|
+
return /* @__PURE__ */ r("div", { className: N("space-y-3", s), children: /* @__PURE__ */ r(
|
|
254
|
+
A,
|
|
255
|
+
{
|
|
256
|
+
schema: t,
|
|
257
|
+
uiSchema: e,
|
|
258
|
+
value: n,
|
|
259
|
+
onChange: x,
|
|
260
|
+
renderFieldActions: w
|
|
261
|
+
}
|
|
262
|
+
) });
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
he as JsonSchemaUiEditor
|
|
266
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JsonSchema } from '@cisri/json-schema-core';
|
|
2
|
+
import { type UiSchema } from '@cisri/json-schema-ui-core';
|
|
3
|
+
export interface JsonSchemaUiEditorProps {
|
|
4
|
+
schema: JsonSchema;
|
|
5
|
+
uiSchema: UiSchema;
|
|
6
|
+
onChange: (uiSchema: UiSchema) => void;
|
|
7
|
+
formData?: unknown;
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function JsonSchemaUiEditor({ schema, uiSchema, onChange, formData, readOnly, className, }: JsonSchemaUiEditorProps): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=json-schema-ui-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-schema-ui-editor.d.ts","sourceRoot":"","sources":["../src/json-schema-ui-editor.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAGvE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,uBAAuB,+BAuDzB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JsonSchema } from '@cisri/json-schema-core';
|
|
2
|
+
import type { UiWidget } from '@cisri/json-schema-ui-core';
|
|
3
|
+
export declare function isPrimitiveField(schema: JsonSchema): boolean;
|
|
4
|
+
export declare function widgetsForSchema(schema: JsonSchema): UiWidget[];
|
|
5
|
+
export declare function reorderOrder(order: string[], activeId: string, overId: string): string[];
|
|
6
|
+
//# sourceMappingURL=ui-editor-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-editor-utils.d.ts","sourceRoot":"","sources":["../src/ui-editor-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAQ5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,EAAE,CAa/D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAQxF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cisri/json-schema-ui-editor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Visual uiSchema editor for @cisri/json-schema-form (RJSF-style per-field UI config)",
|
|
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/json-schema-form": "1.0.0",
|
|
28
|
+
"@cisri/shadcn": "1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
32
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"lucide-react": "^0.300.0",
|
|
34
|
+
"@dnd-kit/core": "^6.0.0",
|
|
35
|
+
"@dnd-kit/sortable": "^8.0.0",
|
|
36
|
+
"@dnd-kit/modifiers": "^7.0.0",
|
|
37
|
+
"@dnd-kit/utilities": "^3.2.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"lucide-react": {
|
|
41
|
+
"optional": false
|
|
42
|
+
},
|
|
43
|
+
"@dnd-kit/core": {
|
|
44
|
+
"optional": false
|
|
45
|
+
},
|
|
46
|
+
"@dnd-kit/sortable": {
|
|
47
|
+
"optional": false
|
|
48
|
+
},
|
|
49
|
+
"@dnd-kit/modifiers": {
|
|
50
|
+
"optional": false
|
|
51
|
+
},
|
|
52
|
+
"@dnd-kit/utilities": {
|
|
53
|
+
"optional": false
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@testing-library/jest-dom": "^6.4.0",
|
|
58
|
+
"@testing-library/react": "^15.0.0",
|
|
59
|
+
"@testing-library/user-event": "^14.5.0",
|
|
60
|
+
"@types/react": "^18.3.0",
|
|
61
|
+
"@types/react-dom": "^18.3.0",
|
|
62
|
+
"jsdom": "^24.0.0",
|
|
63
|
+
"react": "^18.3.1",
|
|
64
|
+
"react-dom": "^18.3.1",
|
|
65
|
+
"typescript": "^5.4.5",
|
|
66
|
+
"vite": "^5.2.11",
|
|
67
|
+
"vitest": "^1.6.0",
|
|
68
|
+
"@vitejs/plugin-react": "^4.2.0"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsc && vite build",
|
|
72
|
+
"test": "vitest run",
|
|
73
|
+
"test:watch": "vitest"
|
|
74
|
+
}
|
|
75
|
+
}
|