@acellera/pm-rjsf 0.0.1
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 +58 -0
- package/dist/pm-rjsf.js +284 -0
- package/index.d.ts +36 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @acellera/pm-rjsf
|
|
2
|
+
|
|
3
|
+
The shared RJSF (Material-UI) renderer for **PlayMolecule v2 manifests** — the
|
|
4
|
+
JSON Schema (`inputSchema`) + `uiSchema` produced by
|
|
5
|
+
[`pmmanifest`](https://github.com/Acellera/pmmanifest). One implementation,
|
|
6
|
+
consumed by both **pmview** and the **pmmanifest dev preview**, so they render
|
|
7
|
+
identically.
|
|
8
|
+
|
|
9
|
+
## Why a separate package
|
|
10
|
+
|
|
11
|
+
The canonical `inputSchema` stays a faithful Pydantic JSON Schema (it's what the
|
|
12
|
+
backend stages files against, what the SDK reconstructs signatures from, and
|
|
13
|
+
what input validation uses). The RJSF-specific *presentation* lives here:
|
|
14
|
+
|
|
15
|
+
- **AJV 2020 validator** (Pydantic emits 2020-12; RJSF's default is draft-07).
|
|
16
|
+
- **Render-time transforms** (`prepareForRjsf`): collapse `Optional` (`anyOf[X, null]`)
|
|
17
|
+
to a clean field, label discriminated-`oneOf` options from the discriminator,
|
|
18
|
+
and strip platform-managed dirs (`outdir`/`scratchdir`/`execdir`).
|
|
19
|
+
- **MUI templates**: grouped accordions (`ui:options.group`), flat arrays,
|
|
20
|
+
`additionalProperties` maps with custom key labels (`ui:options.keyLabel`),
|
|
21
|
+
`?`-tooltip descriptions, small field titles, hidden discriminators.
|
|
22
|
+
|
|
23
|
+
These are **presentation only** — never mutate the canonical schema with them.
|
|
24
|
+
|
|
25
|
+
## Use in pmview (Vite)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @acellera/pm-rjsf
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { PMForm } from "@acellera/pm-rjsf";
|
|
33
|
+
|
|
34
|
+
// For a v2 manifest function entry { inputSchema, uiSchema }:
|
|
35
|
+
<PMForm schema={fn.inputSchema} uiSchema={fn.uiSchema} onSubmit={({ formData }) => run(formData)} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
(pmview keeps its existing bespoke renderer for v1 manifests; this is used only
|
|
39
|
+
when `manifestVersion === 2` / an `inputSchema` is present.)
|
|
40
|
+
|
|
41
|
+
## Use in the pmmanifest preview (no build step)
|
|
42
|
+
|
|
43
|
+
Once published, the preview imports it from a CDN like its other deps:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { PMForm } from "https://esm.sh/@acellera/pm-rjsf";
|
|
47
|
+
root.render(React.createElement(PMForm, { schema, uiSchema, onSubmit }));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Build
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install
|
|
54
|
+
npm run build # -> dist/pm-rjsf.js (ESM)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
React, react-dom, MUI and emotion are **peer dependencies** (the consumer
|
|
58
|
+
provides them); `@rjsf/*` and `ajv` are bundled-as-dependencies.
|
package/dist/pm-rjsf.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import p from "react";
|
|
2
|
+
import T from "@rjsf/mui";
|
|
3
|
+
import { customizeValidator as v } from "@rjsf/validator-ajv8";
|
|
4
|
+
import A from "ajv/dist/2020";
|
|
5
|
+
import { canExpand as F, ADDITIONAL_PROPERTY_FLAG as j } from "@rjsf/utils";
|
|
6
|
+
function O() {
|
|
7
|
+
return v({
|
|
8
|
+
AjvClass: A,
|
|
9
|
+
customFormats: { path: () => !0 },
|
|
10
|
+
ajvOptionsOverrides: { strict: !1 }
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const h = ["outdir", "scratchdir", "execdir"];
|
|
14
|
+
function C(e) {
|
|
15
|
+
if (!e || !e.properties) return e;
|
|
16
|
+
const r = { ...e.properties };
|
|
17
|
+
for (const t of h) delete r[t];
|
|
18
|
+
const n = (e.required || []).filter((t) => !h.includes(t));
|
|
19
|
+
return { ...e, properties: r, required: n };
|
|
20
|
+
}
|
|
21
|
+
function s(e) {
|
|
22
|
+
if (Array.isArray(e)) return e.map(s);
|
|
23
|
+
if (e && typeof e == "object") {
|
|
24
|
+
if (Array.isArray(e.anyOf)) {
|
|
25
|
+
const n = e.anyOf.filter((t) => !(t && t.type === "null"));
|
|
26
|
+
if (n.length === 1) {
|
|
27
|
+
const { anyOf: t, ...a } = e;
|
|
28
|
+
return s({ ...n[0], ...a });
|
|
29
|
+
}
|
|
30
|
+
e = { ...e, anyOf: n };
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(e.oneOf) && e.discriminator && e.discriminator.mapping) {
|
|
33
|
+
const n = {};
|
|
34
|
+
for (const [t, a] of Object.entries(e.discriminator.mapping))
|
|
35
|
+
n[a.split("/").pop()] = t;
|
|
36
|
+
e = {
|
|
37
|
+
...e,
|
|
38
|
+
oneOf: e.oneOf.map((t) => {
|
|
39
|
+
const a = t && t.$ref ? t.$ref.split("/").pop() : null;
|
|
40
|
+
return a && n[a] && !t.title ? { ...t, title: n[a] } : t;
|
|
41
|
+
})
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const r = {};
|
|
45
|
+
for (const [n, t] of Object.entries(e)) r[n] = s(t);
|
|
46
|
+
return r;
|
|
47
|
+
}
|
|
48
|
+
return e;
|
|
49
|
+
}
|
|
50
|
+
function B(e) {
|
|
51
|
+
return s(C(e));
|
|
52
|
+
}
|
|
53
|
+
const i = p.createElement;
|
|
54
|
+
function I(e) {
|
|
55
|
+
const r = e.uiSchema || {}, n = e.registry.templates, t = [], a = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const l of e.properties) {
|
|
57
|
+
const c = r[l.name] && r[l.name]["ui:options"] && r[l.name]["ui:options"].group;
|
|
58
|
+
c ? (a.get(c) || a.set(c, []).get(c)).push(l) : t.push(l);
|
|
59
|
+
}
|
|
60
|
+
const d = [];
|
|
61
|
+
e.title && d.push(
|
|
62
|
+
i(n.TitleFieldTemplate, {
|
|
63
|
+
key: "__title",
|
|
64
|
+
id: e.idSchema.$id + "__title",
|
|
65
|
+
title: e.title,
|
|
66
|
+
required: e.required,
|
|
67
|
+
schema: e.schema,
|
|
68
|
+
uiSchema: e.uiSchema,
|
|
69
|
+
registry: e.registry
|
|
70
|
+
})
|
|
71
|
+
), e.description && d.push(
|
|
72
|
+
i(n.DescriptionFieldTemplate, {
|
|
73
|
+
key: "__desc",
|
|
74
|
+
id: e.idSchema.$id + "__description",
|
|
75
|
+
description: e.description,
|
|
76
|
+
schema: e.schema,
|
|
77
|
+
uiSchema: e.uiSchema,
|
|
78
|
+
registry: e.registry
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
for (const l of t)
|
|
82
|
+
d.push(i("div", { key: l.name, className: "pm-field" }, l.content));
|
|
83
|
+
for (const [l, c] of a)
|
|
84
|
+
d.push(
|
|
85
|
+
i(
|
|
86
|
+
"details",
|
|
87
|
+
{ key: l, className: "pm-group" },
|
|
88
|
+
i("summary", null, l),
|
|
89
|
+
i(
|
|
90
|
+
"div",
|
|
91
|
+
{ className: "pm-body" },
|
|
92
|
+
c.map((m) => i("div", { key: m.name, className: "pm-field" }, m.content))
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
return F(e.schema, e.uiSchema, e.formData) && d.push(
|
|
97
|
+
i(n.ButtonTemplates.AddButton, {
|
|
98
|
+
key: "__add",
|
|
99
|
+
className: "object-property-expand",
|
|
100
|
+
onClick: e.onAddClick(e.schema),
|
|
101
|
+
disabled: e.disabled || e.readonly,
|
|
102
|
+
uiSchema: e.uiSchema,
|
|
103
|
+
registry: e.registry
|
|
104
|
+
})
|
|
105
|
+
), i("div", null, d);
|
|
106
|
+
}
|
|
107
|
+
function R(e) {
|
|
108
|
+
const {
|
|
109
|
+
children: r,
|
|
110
|
+
classNames: n,
|
|
111
|
+
style: t,
|
|
112
|
+
disabled: a,
|
|
113
|
+
id: d,
|
|
114
|
+
label: l,
|
|
115
|
+
onKeyChange: c,
|
|
116
|
+
onDropPropertyClick: m,
|
|
117
|
+
readonly: u,
|
|
118
|
+
schema: b,
|
|
119
|
+
uiSchema: o,
|
|
120
|
+
registry: f
|
|
121
|
+
} = e;
|
|
122
|
+
if (!(j in b))
|
|
123
|
+
return i("div", { className: n, style: t }, r);
|
|
124
|
+
const S = f.templates.ButtonTemplates.RemoveButton, k = o && o["ui:options"] && o["ui:options"].keyLabel || l + " Key", y = d + "-key", _ = {
|
|
125
|
+
width: "100%",
|
|
126
|
+
padding: ".5rem",
|
|
127
|
+
border: "1px solid #ccc",
|
|
128
|
+
borderRadius: "4px",
|
|
129
|
+
font: "inherit",
|
|
130
|
+
boxSizing: "border-box"
|
|
131
|
+
};
|
|
132
|
+
return i(
|
|
133
|
+
"div",
|
|
134
|
+
{ style: { display: "flex", gap: "1rem", alignItems: "flex-start", marginBottom: ".75rem" } },
|
|
135
|
+
i(
|
|
136
|
+
"div",
|
|
137
|
+
{ style: { flex: "0 0 30%" } },
|
|
138
|
+
i("label", { htmlFor: y, style: { display: "block", marginBottom: ".25rem" } }, k),
|
|
139
|
+
i("input", {
|
|
140
|
+
id: y,
|
|
141
|
+
type: "text",
|
|
142
|
+
defaultValue: l,
|
|
143
|
+
disabled: a || u,
|
|
144
|
+
style: _,
|
|
145
|
+
onBlur: (x) => c(x.target.value)
|
|
146
|
+
})
|
|
147
|
+
),
|
|
148
|
+
i("div", { style: { flex: "1 1 auto" } }, r),
|
|
149
|
+
i(
|
|
150
|
+
"div",
|
|
151
|
+
{ style: { flex: "0 0 auto", paddingTop: "1.6rem" } },
|
|
152
|
+
i(S, {
|
|
153
|
+
disabled: a || u,
|
|
154
|
+
onClick: m(l),
|
|
155
|
+
uiSchema: o,
|
|
156
|
+
registry: f
|
|
157
|
+
})
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function D(e) {
|
|
162
|
+
const r = e.schema && e.schema.description || (typeof e.description == "string" ? e.description : "");
|
|
163
|
+
if (!r) return null;
|
|
164
|
+
const n = String(r);
|
|
165
|
+
return i(
|
|
166
|
+
"span",
|
|
167
|
+
{
|
|
168
|
+
title: n,
|
|
169
|
+
"aria-label": n,
|
|
170
|
+
style: {
|
|
171
|
+
display: "inline-flex",
|
|
172
|
+
alignItems: "center",
|
|
173
|
+
justifyContent: "center",
|
|
174
|
+
width: "18px",
|
|
175
|
+
height: "18px",
|
|
176
|
+
borderRadius: "50%",
|
|
177
|
+
border: "1px solid #999",
|
|
178
|
+
color: "#666",
|
|
179
|
+
fontSize: "12px",
|
|
180
|
+
lineHeight: 1,
|
|
181
|
+
cursor: "help",
|
|
182
|
+
marginLeft: "6px",
|
|
183
|
+
verticalAlign: "middle"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"?"
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function N(e) {
|
|
190
|
+
return e.title ? i(
|
|
191
|
+
"div",
|
|
192
|
+
{ style: { fontSize: ".9rem", fontWeight: 600, margin: ".4rem 0 .3rem" } },
|
|
193
|
+
e.title,
|
|
194
|
+
e.required ? i("span", { style: { color: "#b00" } }, " *") : null
|
|
195
|
+
) : null;
|
|
196
|
+
}
|
|
197
|
+
function w(e) {
|
|
198
|
+
const r = e.registry.templates, n = r.ArrayFieldItemTemplate, t = e.uiSchema && e.uiSchema["ui:title"] || e.title, a = [];
|
|
199
|
+
t && a.push(
|
|
200
|
+
i(r.TitleFieldTemplate, {
|
|
201
|
+
key: "__t",
|
|
202
|
+
id: e.idSchema.$id + "__title",
|
|
203
|
+
title: t,
|
|
204
|
+
required: e.required,
|
|
205
|
+
schema: e.schema,
|
|
206
|
+
uiSchema: e.uiSchema,
|
|
207
|
+
registry: e.registry
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
const d = e.schema && e.schema.description;
|
|
211
|
+
return d && a.push(
|
|
212
|
+
i(r.DescriptionFieldTemplate, {
|
|
213
|
+
key: "__d",
|
|
214
|
+
id: e.idSchema.$id + "__description",
|
|
215
|
+
description: d,
|
|
216
|
+
schema: e.schema,
|
|
217
|
+
uiSchema: e.uiSchema,
|
|
218
|
+
registry: e.registry
|
|
219
|
+
})
|
|
220
|
+
), (e.items || []).forEach(
|
|
221
|
+
({ key: l, ...c }) => a.push(i(n, { key: l, ...c }))
|
|
222
|
+
), e.canAdd && a.push(
|
|
223
|
+
i(r.ButtonTemplates.AddButton, {
|
|
224
|
+
key: "__add",
|
|
225
|
+
onClick: e.onAddClick,
|
|
226
|
+
disabled: e.disabled || e.readonly,
|
|
227
|
+
uiSchema: e.uiSchema,
|
|
228
|
+
registry: e.registry
|
|
229
|
+
})
|
|
230
|
+
), i("div", null, a);
|
|
231
|
+
}
|
|
232
|
+
const q = {
|
|
233
|
+
ObjectFieldTemplate: I,
|
|
234
|
+
WrapIfAdditionalTemplate: R,
|
|
235
|
+
DescriptionFieldTemplate: D,
|
|
236
|
+
TitleFieldTemplate: N,
|
|
237
|
+
ArrayFieldTemplate: w
|
|
238
|
+
}, E = `
|
|
239
|
+
.pm-field { margin: .5rem 0; }
|
|
240
|
+
details.pm-group { border: 1px solid #e0e0e0; border-radius: 4px; margin: .75rem 0; }
|
|
241
|
+
details.pm-group > summary {
|
|
242
|
+
list-style: none; cursor: pointer; padding: .6rem .9rem; font-size: .9rem;
|
|
243
|
+
font-weight: 600; display: flex; justify-content: space-between; align-items: center;
|
|
244
|
+
}
|
|
245
|
+
details.pm-group > summary::-webkit-details-marker { display: none; }
|
|
246
|
+
details.pm-group > summary::after { content: "\\25be"; color: #888; font-weight: 400; }
|
|
247
|
+
details.pm-group[open] > summary::after { content: "\\25b4"; }
|
|
248
|
+
details.pm-group > .pm-body { padding: .25rem .9rem .6rem; }
|
|
249
|
+
`;
|
|
250
|
+
let g = !1;
|
|
251
|
+
function L() {
|
|
252
|
+
if (g || typeof document > "u") return;
|
|
253
|
+
g = !0;
|
|
254
|
+
const e = document.createElement("style");
|
|
255
|
+
e.setAttribute("data-pm-rjsf", ""), e.textContent = E, document.head.appendChild(e);
|
|
256
|
+
}
|
|
257
|
+
const P = O();
|
|
258
|
+
function K({ schema: e, uiSchema: r, ...n }) {
|
|
259
|
+
L();
|
|
260
|
+
const t = B(e);
|
|
261
|
+
return p.createElement(T, {
|
|
262
|
+
schema: t,
|
|
263
|
+
uiSchema: r,
|
|
264
|
+
validator: P,
|
|
265
|
+
templates: q,
|
|
266
|
+
...n
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
export {
|
|
270
|
+
w as ArrayFieldTemplate,
|
|
271
|
+
D as DescriptionFieldTemplate,
|
|
272
|
+
I as GroupedObjectFieldTemplate,
|
|
273
|
+
K as PMForm,
|
|
274
|
+
E as PM_RJSF_CSS,
|
|
275
|
+
h as SPECIAL_DIRS,
|
|
276
|
+
N as TitleFieldTemplate,
|
|
277
|
+
R as WrapIfAdditionalTemplate,
|
|
278
|
+
L as injectStyles,
|
|
279
|
+
O as makeValidator,
|
|
280
|
+
B as prepareForRjsf,
|
|
281
|
+
s as simplify,
|
|
282
|
+
C as stripSpecialDirs,
|
|
283
|
+
q as templates
|
|
284
|
+
};
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
|
|
3
|
+
export interface PMFormProps {
|
|
4
|
+
/** Canonical inputSchema (JSON Schema 2020-12, as emitted by pmmanifest). */
|
|
5
|
+
schema: Record<string, unknown>;
|
|
6
|
+
/** uiSchema (RJSF), as emitted by pmmanifest. */
|
|
7
|
+
uiSchema?: Record<string, unknown>;
|
|
8
|
+
/** Called with { formData } on submit. */
|
|
9
|
+
onSubmit?: (data: { formData: unknown }) => void;
|
|
10
|
+
formData?: unknown;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
readonly?: boolean;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Drop-in RJSF (MUI) form for a PlayMolecule v2 manifest function entry. */
|
|
17
|
+
export const PMForm: ComponentType<PMFormProps>;
|
|
18
|
+
|
|
19
|
+
/** Build the AJV-2020 validator used by PMForm (Pydantic emits 2020-12). */
|
|
20
|
+
export function makeValidator(): unknown;
|
|
21
|
+
|
|
22
|
+
/** Render-time schema prep: strip platform dirs + collapse Optional anyOf + title oneOf options. */
|
|
23
|
+
export function prepareForRjsf<T>(schema: T): T;
|
|
24
|
+
export function simplify<T>(schema: T): T;
|
|
25
|
+
export function stripSpecialDirs<T>(schema: T): T;
|
|
26
|
+
export const SPECIAL_DIRS: string[];
|
|
27
|
+
|
|
28
|
+
export const templates: Record<string, ComponentType<any>>;
|
|
29
|
+
export const GroupedObjectFieldTemplate: ComponentType<any>;
|
|
30
|
+
export const WrapIfAdditionalTemplate: ComponentType<any>;
|
|
31
|
+
export const DescriptionFieldTemplate: ComponentType<any>;
|
|
32
|
+
export const TitleFieldTemplate: ComponentType<any>;
|
|
33
|
+
export const ArrayFieldTemplate: ComponentType<any>;
|
|
34
|
+
|
|
35
|
+
export const PM_RJSF_CSS: string;
|
|
36
|
+
export function injectStyles(): void;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@acellera/pm-rjsf",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Shared RJSF renderer for PlayMolecule v2 (JSON Schema) manifests — used by pmview and the pmmanifest preview.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Acellera/pm-rjsf.git"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"index.d.ts"
|
|
14
|
+
],
|
|
15
|
+
"main": "./dist/pm-rjsf.js",
|
|
16
|
+
"module": "./dist/pm-rjsf.js",
|
|
17
|
+
"types": "./index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"import": "./dist/pm-rjsf.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"dev": "vite build --watch"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@rjsf/core": "^5",
|
|
30
|
+
"@rjsf/mui": "^5",
|
|
31
|
+
"@rjsf/utils": "^5",
|
|
32
|
+
"@rjsf/validator-ajv8": "^5",
|
|
33
|
+
"ajv": "^8.12.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@emotion/react": "^11",
|
|
37
|
+
"@emotion/styled": "^11",
|
|
38
|
+
"@mui/icons-material": "^5",
|
|
39
|
+
"@mui/material": "^5",
|
|
40
|
+
"react": "^18",
|
|
41
|
+
"react-dom": "^18"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"vite": "^7"
|
|
45
|
+
}
|
|
46
|
+
}
|