@golemui/gui-mcp 1.0.3 → 1.1.1-rc.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/CHANGELOG.md +20 -0
- package/README.md +16 -0
- package/cli-commands.d.ts +18 -0
- package/cli.js +87 -2
- package/dx/dx-specs.d.ts +12 -0
- package/{get-concept-Py3iLVmb.js → get-concept-DRFSt-XX.js} +574 -263
- package/json.js +1 -1
- package/lib.js +2 -2
- package/{list-dx-factories-C-Z5Sc5E.js → list-dx-factories-Dwy_BYZi.js} +396 -9
- package/package.json +5 -5
- package/shared/lint/reactive-expressions.d.ts +8 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { h as checkReactiveExpression } from "./get-concept-
|
|
1
|
+
import { h as checkReactiveExpression } from "./get-concept-DRFSt-XX.js";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { resolve, join, dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { Decoder, err, ok, object, array, string, oneOf, boolean, optional, succeed, number, literal, lazy, discriminatedUnion, record } from "ts.data.json";
|
|
7
|
+
import "subscript/justin";
|
|
8
|
+
import { pipe, map, distinctUntilChanged, filter } from "rxjs";
|
|
6
9
|
const CONFIG_FIELDS = ["include", "exclude", "disabled", "readonly"];
|
|
7
10
|
function lintDxSnippet(ts, sourceText, lineOffset) {
|
|
8
11
|
const sf = ts.createSourceFile("__dx_lint__.ts", sourceText, ts.ScriptTarget.ES2020, true);
|
|
@@ -23,7 +26,7 @@ function lintDxSnippet(ts, sourceText, lineOffset) {
|
|
|
23
26
|
while (ts.isPropertyAccessExpression(e)) e = e.expression;
|
|
24
27
|
return ts.isIdentifier(e) && e.text === "gui";
|
|
25
28
|
};
|
|
26
|
-
const visit = (node) => {
|
|
29
|
+
const visit = (node, inTemplate) => {
|
|
27
30
|
if (ts.isObjectLiteralExpression(node)) {
|
|
28
31
|
const spreadsGui = node.properties.some(
|
|
29
32
|
(p) => ts.isSpreadAssignment(p) && isGuiCall(p.expression)
|
|
@@ -46,13 +49,17 @@ function lintDxSnippet(ts, sourceText, lineOffset) {
|
|
|
46
49
|
}
|
|
47
50
|
if (ts.isPropertyAssignment(node) && nameOf(node.name) === "when" && ts.isStringLiteralLike(node.initializer)) {
|
|
48
51
|
const { line, column } = posOf(node.initializer);
|
|
49
|
-
|
|
52
|
+
const findings = checkReactiveExpression(node.initializer.text, `when@${line}:${column}`, {
|
|
53
|
+
inRepeaterTemplate: inTemplate
|
|
54
|
+
});
|
|
55
|
+
for (const f of findings) {
|
|
50
56
|
expressionWarnings.push(f);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
|
-
ts.
|
|
59
|
+
const childInTemplate = inTemplate || ts.isPropertyAssignment(node) && nameOf(node.name) === "template";
|
|
60
|
+
ts.forEachChild(node, (child) => visit(child, childInTemplate));
|
|
54
61
|
};
|
|
55
|
-
visit(sf);
|
|
62
|
+
visit(sf, false);
|
|
56
63
|
return { diagnostics, expressionWarnings };
|
|
57
64
|
}
|
|
58
65
|
let cachedRequire;
|
|
@@ -63,8 +70,7 @@ const GOLEMUI_TYPE_PACKAGES = [
|
|
|
63
70
|
{ spec: "@golemui/gui-shared", rel: "gui/shared/index.d.ts" },
|
|
64
71
|
{ spec: "@golemui/gui-shared/internals", rel: "gui/shared/internals.d.ts" },
|
|
65
72
|
{ spec: "@golemui/gui-validators", rel: "gui/validators/index.d.ts" },
|
|
66
|
-
{ spec: "@golemui/core", rel: "core/index.d.ts" }
|
|
67
|
-
{ spec: "@golemui/core/internals", rel: "core/internals.d.ts" }
|
|
73
|
+
{ spec: "@golemui/core", rel: "core/index.d.ts" }
|
|
68
74
|
];
|
|
69
75
|
const DIST_SEARCH_DEPTH = 8;
|
|
70
76
|
const SNIPPET_FILE = "__dx_snippet__.ts";
|
|
@@ -213,6 +219,249 @@ const DX_CHECK_CODE_TOOL = {
|
|
|
213
219
|
required: ["code"]
|
|
214
220
|
}
|
|
215
221
|
};
|
|
222
|
+
function objectWithSuffix(specs) {
|
|
223
|
+
return new Decoder((json) => {
|
|
224
|
+
if (typeof json !== "object" || json === null) {
|
|
225
|
+
return err([{ message: `Expected object literal, got "${typeof json}"`, path: [] }]);
|
|
226
|
+
}
|
|
227
|
+
const out = {};
|
|
228
|
+
for (const [specKey, spec] of Object.entries(specs)) {
|
|
229
|
+
const res = spec.decoder.decode(json[specKey]);
|
|
230
|
+
if (!res.isOk()) {
|
|
231
|
+
return err(
|
|
232
|
+
res.issues.map((issue) => ({
|
|
233
|
+
message: issue.message,
|
|
234
|
+
path: [specKey, ...issue.path]
|
|
235
|
+
}))
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
out[specKey] = res.value;
|
|
239
|
+
if (spec.suffixed) {
|
|
240
|
+
for (const [jsonKey, jsonValue] of Object.entries(json)) {
|
|
241
|
+
if (jsonKey.startsWith(specKey + ".")) {
|
|
242
|
+
const res2 = spec.decoder.decode(jsonValue);
|
|
243
|
+
if (!res2.isOk()) {
|
|
244
|
+
return err(
|
|
245
|
+
res2.issues.map((issue) => ({
|
|
246
|
+
message: issue.message,
|
|
247
|
+
path: [jsonKey, ...issue.path]
|
|
248
|
+
}))
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
out[jsonKey] = res2.value;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return ok(out);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const shortUUID = () => crypto.randomUUID().slice(0, 8);
|
|
260
|
+
const isInputWidget = (widget) => typeof widget !== "function" && widget.kind === "input";
|
|
261
|
+
const isLayoutWidget = (widget) => typeof widget !== "function" && widget.kind === "layout";
|
|
262
|
+
const isFunctionWidget = (widget) => typeof widget === "function";
|
|
263
|
+
const inDecoder = object({ in: array(string()) });
|
|
264
|
+
const whenDecoder = object({ when: string() });
|
|
265
|
+
const includeDecoder = oneOf([inDecoder, whenDecoder]);
|
|
266
|
+
const fromDecoder = object({ from: array(string()) });
|
|
267
|
+
const excludeDecoder = oneOf([fromDecoder, whenDecoder]);
|
|
268
|
+
const boolWhenDecoder = oneOf([boolean(), whenDecoder]);
|
|
269
|
+
const widgetPropFnDecoder = new Decoder((json) => {
|
|
270
|
+
const jsonTypeof = typeof json;
|
|
271
|
+
if (jsonTypeof === "function") {
|
|
272
|
+
return ok(json);
|
|
273
|
+
} else {
|
|
274
|
+
return err([{ message: `Expected a function, got '${jsonTypeof}'`, path: [] }]);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
const decodeWidgetPropOrWidgetPropFn = (decoder) => oneOf([decoder, widgetPropFnDecoder]);
|
|
278
|
+
const onDecoder = objectWithSuffix({
|
|
279
|
+
load: { suffixed: true, decoder: decodeWidgetPropOrWidgetPropFn(optional(string())) },
|
|
280
|
+
click: { suffixed: true, decoder: decodeWidgetPropOrWidgetPropFn(optional(string())) },
|
|
281
|
+
change: { suffixed: true, decoder: decodeWidgetPropOrWidgetPropFn(optional(string())) },
|
|
282
|
+
filter: { suffixed: true, decoder: decodeWidgetPropOrWidgetPropFn(optional(string())) },
|
|
283
|
+
blur: { suffixed: true, decoder: decodeWidgetPropOrWidgetPropFn(optional(string())) }
|
|
284
|
+
});
|
|
285
|
+
const translationConfigDecoder = object({
|
|
286
|
+
key: string(),
|
|
287
|
+
default: optional(string()),
|
|
288
|
+
params: optional(succeed())
|
|
289
|
+
});
|
|
290
|
+
const localizableDecoder = oneOf([string(), translationConfigDecoder]);
|
|
291
|
+
const uidDecoder = optional(string());
|
|
292
|
+
const displayWidgetDecoder = objectWithSuffix({
|
|
293
|
+
kind: { decoder: literal("display") },
|
|
294
|
+
uid: { decoder: uidDecoder },
|
|
295
|
+
type: { decoder: string() },
|
|
296
|
+
size: { suffixed: true, decoder: optional(number()) },
|
|
297
|
+
include: { decoder: optional(includeDecoder) },
|
|
298
|
+
exclude: { decoder: optional(excludeDecoder) },
|
|
299
|
+
props: { decoder: optional(succeed()) }
|
|
300
|
+
});
|
|
301
|
+
const actionWidgetDecoder = objectWithSuffix({
|
|
302
|
+
kind: { decoder: literal("action") },
|
|
303
|
+
uid: { decoder: uidDecoder },
|
|
304
|
+
type: { decoder: string() },
|
|
305
|
+
actionType: {
|
|
306
|
+
decoder: optional(oneOf([literal("button"), literal("submit")]))
|
|
307
|
+
},
|
|
308
|
+
size: { suffixed: true, decoder: optional(number()) },
|
|
309
|
+
include: { decoder: optional(includeDecoder) },
|
|
310
|
+
exclude: { decoder: optional(excludeDecoder) },
|
|
311
|
+
label: {
|
|
312
|
+
suffixed: true,
|
|
313
|
+
decoder: decodeWidgetPropOrWidgetPropFn(optional(localizableDecoder))
|
|
314
|
+
},
|
|
315
|
+
disabled: { suffixed: true, decoder: optional(boolWhenDecoder) },
|
|
316
|
+
on: { decoder: optional(onDecoder) },
|
|
317
|
+
props: { decoder: optional(succeed()) }
|
|
318
|
+
});
|
|
319
|
+
const functionWidgetDecoder = new Decoder((json) => {
|
|
320
|
+
const jsonTypeof = typeof json;
|
|
321
|
+
if (jsonTypeof === "function") {
|
|
322
|
+
const fnWidget = json;
|
|
323
|
+
const widget = fnWidget({
|
|
324
|
+
$form: {},
|
|
325
|
+
errors: void 0,
|
|
326
|
+
touched: void 0,
|
|
327
|
+
translate: void 0
|
|
328
|
+
});
|
|
329
|
+
fnWidget.uid = fnWidget.uid || widget.uid || shortUUID();
|
|
330
|
+
fnWidget.type = widget.type;
|
|
331
|
+
fnWidget.path = widget.path;
|
|
332
|
+
return ok(fnWidget);
|
|
333
|
+
} else {
|
|
334
|
+
return err([{ message: `Expected a function, got '${jsonTypeof}'`, path: [] }]);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
const inputWidgetDecoder = objectWithSuffix({
|
|
338
|
+
kind: { decoder: literal("input") },
|
|
339
|
+
uid: { decoder: optional(string()) },
|
|
340
|
+
type: { decoder: string() },
|
|
341
|
+
size: { suffixed: true, decoder: optional(number()) },
|
|
342
|
+
include: { decoder: optional(includeDecoder) },
|
|
343
|
+
exclude: { decoder: optional(excludeDecoder) },
|
|
344
|
+
disabled: { suffixed: true, decoder: optional(boolWhenDecoder) },
|
|
345
|
+
readonly: { suffixed: true, decoder: optional(boolWhenDecoder) },
|
|
346
|
+
on: { decoder: optional(onDecoder) },
|
|
347
|
+
props: { decoder: optional(succeed()) },
|
|
348
|
+
label: {
|
|
349
|
+
suffixed: true,
|
|
350
|
+
decoder: decodeWidgetPropOrWidgetPropFn(optional(localizableDecoder))
|
|
351
|
+
},
|
|
352
|
+
path: { decoder: string() },
|
|
353
|
+
defaultValue: { decoder: optional(succeed()) },
|
|
354
|
+
validator: {
|
|
355
|
+
suffixed: true,
|
|
356
|
+
decoder: decodeWidgetPropOrWidgetPropFn(optional(succeed()))
|
|
357
|
+
}
|
|
358
|
+
}).map((ctrl) => {
|
|
359
|
+
const transformed = { ...ctrl };
|
|
360
|
+
if (!ctrl.uid) {
|
|
361
|
+
transformed.uid = `${ctrl.path}-${ctrl.type}`;
|
|
362
|
+
}
|
|
363
|
+
if (ctrl.type === "repeater") {
|
|
364
|
+
const props = ctrl["props"];
|
|
365
|
+
transformed.props = { ...props, template: layoutWidgetDecoder.parse(props["template"]) };
|
|
366
|
+
}
|
|
367
|
+
return transformed;
|
|
368
|
+
});
|
|
369
|
+
const formWidgetDecoder = lazy(() => {
|
|
370
|
+
const objectWidgetDecoder = discriminatedUnion("kind", {
|
|
371
|
+
input: inputWidgetDecoder,
|
|
372
|
+
layout: layoutWidgetDecoder,
|
|
373
|
+
display: displayWidgetDecoder,
|
|
374
|
+
action: actionWidgetDecoder
|
|
375
|
+
});
|
|
376
|
+
return new Decoder(
|
|
377
|
+
(json) => typeof json === "function" ? functionWidgetDecoder.decode(json) : objectWidgetDecoder.decode(json)
|
|
378
|
+
);
|
|
379
|
+
});
|
|
380
|
+
const layoutWidgetDecoder = objectWithSuffix({
|
|
381
|
+
kind: { decoder: literal("layout") },
|
|
382
|
+
uid: { decoder: uidDecoder },
|
|
383
|
+
type: { decoder: string() },
|
|
384
|
+
size: { suffixed: true, decoder: optional(number()) },
|
|
385
|
+
include: { decoder: optional(includeDecoder) },
|
|
386
|
+
exclude: { decoder: optional(excludeDecoder) },
|
|
387
|
+
props: { decoder: optional(succeed()) },
|
|
388
|
+
on: { decoder: optional(onDecoder) },
|
|
389
|
+
children: { decoder: array(formWidgetDecoder) }
|
|
390
|
+
});
|
|
391
|
+
function assignDeterministicUids(root) {
|
|
392
|
+
assignUidByPosition(root, "#0");
|
|
393
|
+
}
|
|
394
|
+
function assignUidByPosition(widget, positionUid) {
|
|
395
|
+
if (isFunctionWidget(widget)) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (!widget.uid) {
|
|
399
|
+
widget.uid = positionUid;
|
|
400
|
+
}
|
|
401
|
+
if (isLayoutWidget(widget)) {
|
|
402
|
+
widget.children.forEach((child, index) => {
|
|
403
|
+
assignUidByPosition(child, `${positionUid}.${index}`);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
if (isInputWidget(widget) && widget.type === "repeater") {
|
|
407
|
+
const template = widget.props?.["template"];
|
|
408
|
+
if (template) {
|
|
409
|
+
assignUidByPosition(template, `${positionUid}.t`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
object({
|
|
414
|
+
states: optional(record(string())),
|
|
415
|
+
form: lazy(() => layoutWidgetDecoder)
|
|
416
|
+
}).map((formDef) => {
|
|
417
|
+
assignDeterministicUids(formDef.form);
|
|
418
|
+
return formDef;
|
|
419
|
+
});
|
|
420
|
+
const formEventNames = {
|
|
421
|
+
submit: "formSubmit"
|
|
422
|
+
};
|
|
423
|
+
pipe(
|
|
424
|
+
map((store) => store.data),
|
|
425
|
+
distinctUntilChanged()
|
|
426
|
+
);
|
|
427
|
+
pipe(
|
|
428
|
+
filter((store) => store.touched === true),
|
|
429
|
+
map((store) => store.validations),
|
|
430
|
+
distinctUntilChanged()
|
|
431
|
+
);
|
|
432
|
+
pipe(
|
|
433
|
+
filter((store) => store.touched === true),
|
|
434
|
+
map((store) => store.injectedValidations),
|
|
435
|
+
distinctUntilChanged()
|
|
436
|
+
);
|
|
437
|
+
pipe(
|
|
438
|
+
map((store) => store.lang),
|
|
439
|
+
distinctUntilChanged()
|
|
440
|
+
);
|
|
441
|
+
pipe(
|
|
442
|
+
map((store) => store.calculatedWidgets),
|
|
443
|
+
distinctUntilChanged()
|
|
444
|
+
);
|
|
445
|
+
pipe(
|
|
446
|
+
map((store) => store.widgetFlags),
|
|
447
|
+
distinctUntilChanged()
|
|
448
|
+
);
|
|
449
|
+
pipe(
|
|
450
|
+
map((store) => store.touchedControls),
|
|
451
|
+
distinctUntilChanged()
|
|
452
|
+
);
|
|
453
|
+
pipe(
|
|
454
|
+
map((store) => store.formHealth),
|
|
455
|
+
distinctUntilChanged((prev, current) => {
|
|
456
|
+
if (prev.status !== current.status) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
if (prev.status === "errored" && current.status === "errored") {
|
|
460
|
+
return prev.message === current.message && prev.code === current.code;
|
|
461
|
+
}
|
|
462
|
+
return true;
|
|
463
|
+
})
|
|
464
|
+
);
|
|
216
465
|
const DX_FRAMEWORKS = ["react", "angular", "vue", "lit", "vanilla"];
|
|
217
466
|
function resolveDxFramework() {
|
|
218
467
|
const raw = typeof process !== "undefined" ? process.env?.["GOLEMUI_FRAMEWORK"]?.toLowerCase() : void 0;
|
|
@@ -222,8 +471,8 @@ const FRAMEWORK_SETUP = {
|
|
|
222
471
|
react: "RENDER (React) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-react'; import type { FormSubmitEvent } from '@golemui/core';`, then render `<GuiForm config={{ formDef: form }} formSubmit={(e: FormSubmitEvent) => { /* e.data is the form data */ }} />`. A `gui.displays.display(() => <h2>…</h2>)` returns React JSX.",
|
|
223
472
|
angular: "RENDER (Angular) — `import { gui } from '@golemui/gui-shared'; import { FormComponent } from '@golemui/gui-angular';`, add `FormComponent` to the standalone component's `imports`, then in the template `<gui-form [config]=\"{ formDef: form }\" (formSubmit)=\"onSubmit($event)\"></gui-form>` — `$event` is a `FormSubmitEvent` (type from `@golemui/core`), `$event.data` is the form data.",
|
|
224
473
|
vue: "RENDER (Vue) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-vue';`, then `<GuiForm :config=\"{ formDef: form }\" @form-submit=\"onSubmit\" />` — the handler receives a `FormSubmitEvent` (`.data` is the form data). The event is `form-submit` (kebab-case), not `formSubmit`.",
|
|
225
|
-
lit: "RENDER (Lit) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers the `<gui-form>` custom element), then `<gui-form .config=${{ formDef: form }} @
|
|
226
|
-
vanilla: "RENDER (vanilla JS) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers `<gui-form>`), then `const el = document.querySelector('gui-form'); el.config = { formDef: form }; el.addEventListener('
|
|
474
|
+
lit: "RENDER (Lit) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers the `<gui-form>` custom element), then `<gui-form .config=${{ formDef: form }} @" + formEventNames.submit + "=${(e: CustomEvent) => { /* e.detail is the FormSubmitEvent; e.detail.data */ }}></gui-form>`. The event name is `" + formEventNames.submit + "` (camelCase) — Lit dispatches a raw CustomEvent, so there is no kebab-case alias.",
|
|
475
|
+
vanilla: "RENDER (vanilla JS) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers `<gui-form>`), then `const el = document.querySelector('gui-form'); el.config = { formDef: form }; el.addEventListener('" + formEventNames.submit + "', (e) => { /* e.detail.data */ });`. In TypeScript, type the element — `import type { FormElement } from '@golemui/gui-lit'; const el = document.querySelector<FormElement>('gui-form');` — the published types do not register `gui-form` in `HTMLElementTagNameMap`, so an untyped `querySelector` yields `Element` and `el.config` fails to compile."
|
|
227
476
|
};
|
|
228
477
|
function commonNote(fw = "react") {
|
|
229
478
|
return "GolemUI builds FORMS — data collection and validation. It is NOT a general-purpose UI toolkit: it never renders documents, page content, or markdown for display. A form is just an array of these items: `export const form = [ /* items */ ];`. " + FRAMEWORK_SETUP[fw] + " Import the component stylesheet ONCE — `@golemui/gui-components/index.css` — or the form renders unstyled. To RECEIVE A SUBMIT: add a `gui.actions.button({ label, actionType: 'submit' })` to the form and listen for the submit on the host component (the RENDER line above shows how for your framework) — the handler gets a `FormSubmitEvent` whose `.data` is the collected form data. To DISABLE submit until the form is valid, add `disabled: { when: '$formIsInvalid' }` to that button (`$formIsInvalid` is a built-in validity flag) — see the conditional-and-state-props pattern. The SAME `formDef` renders in every framework (React/Angular/Vue/Lit/vanilla) — only the host wrapper changes. FORM-LEVEL CONFIG — `formDef` is ALWAYS the bare array. Anything form-wide (named `states`, `validateOn`) goes in a sibling `formConfig` on the config (`config={{ formDef: form, formConfig: { states, validateOn } }}`), NEVER inside `formDef`. Do NOT wrap the array as `{ states, form: [...] }` and pass THAT as `formDef` — `formDef` is typed `Record<string, any>` so it COMPILES, but the `gui.*` items are never resolved and the form renders BLANK with no error. See the form-level-states pattern. Common fields like `include`/`exclude` (conditional visibility) go INSIDE a factory’s config argument — never spread them onto the result (`{ ...gui.inputs.x(...), include }` compiles but silently does nothing). See the conditional-visibility pattern. STATIC CONTENT — a section heading or any non-input text/block is the HOST’s job, not GolemUI’s: use `gui.displays.display(() => <h2>…</h2>)` returning your framework’s own node (React JSX, Vue/Angular/Lit node) — it needs no dependency and always renders. MARKDOWN has exactly ONE use: `gui.inputs.markdown`, an INPUT where the user EDITS markdown (its value is their markdown string). There is NO markdown-for-display widget — never use markdown to render a heading or content; use `display` for that. VALIDATOR `type` — one rule, three cases (so you never have to guess): (1) choice widgets (`dropdown`, `radiogroup`, `select`) REQUIRE an explicit `type`: `validator: { type: 'string', required: true }`. (2) `repeater` (array) validators auto-supply `type: 'array'` — supply only the rules, e.g. `validator: { required: true, minItems: 1 }`, never `type`. (3) everything else (text, number, date) takes the loose validator with NO `type`: `validator: { required: true }`. EVENT HANDLERS — `onChange`/`onLoad`/`onFilter`/`onBlur` (inputs/layouts) and `onClick` (actions) are FUNCTIONS, never bare strings: return a string to dispatch a host event by that name (`onChange: () => 'languageChanged'`), or take the event to push live changes (`onChange: (event) => event.update({ path: 'city', options: [...] })`).";
|
|
@@ -264,6 +513,7 @@ const INPUTS = [
|
|
|
264
513
|
{
|
|
265
514
|
factory: "textInput",
|
|
266
515
|
namespace: "inputs",
|
|
516
|
+
docSlug: "textinput",
|
|
267
517
|
call: "gui.inputs.textInput(path, { label, placeholder?, defaultValue?, validator? })",
|
|
268
518
|
example: "gui.inputs.textInput('fullName', { label: 'Full name', validator: { required: true, minLength: 2 } })",
|
|
269
519
|
notes: [
|
|
@@ -274,6 +524,7 @@ const INPUTS = [
|
|
|
274
524
|
{
|
|
275
525
|
factory: "numberInput",
|
|
276
526
|
namespace: "inputs",
|
|
527
|
+
docSlug: "number",
|
|
277
528
|
call: "gui.inputs.numberInput(path, { label, defaultValue?, validator? })",
|
|
278
529
|
example: "gui.inputs.numberInput('age', { label: 'Age', validator: { required: true, minimum: 0, maximum: 120 } })",
|
|
279
530
|
notes: [
|
|
@@ -283,6 +534,7 @@ const INPUTS = [
|
|
|
283
534
|
{
|
|
284
535
|
factory: "booleanInput",
|
|
285
536
|
namespace: "inputs",
|
|
537
|
+
docSlug: "toggle",
|
|
286
538
|
call: "gui.inputs.booleanInput(path, { label, defaultValue? })",
|
|
287
539
|
example: "gui.inputs.booleanInput('newsletter', { label: 'Subscribe to newsletter', defaultValue: false })",
|
|
288
540
|
notes: ["The on/off toggle for a single boolean. Use `checkbox` for a checkbox presentation."]
|
|
@@ -290,6 +542,7 @@ const INPUTS = [
|
|
|
290
542
|
{
|
|
291
543
|
factory: "checkbox",
|
|
292
544
|
namespace: "inputs",
|
|
545
|
+
docSlug: "checkbox",
|
|
293
546
|
call: "gui.inputs.checkbox(path, { label, defaultValue? })",
|
|
294
547
|
example: "gui.inputs.checkbox('terms', { label: 'I accept the terms', defaultValue: false })",
|
|
295
548
|
notes: ["A single boolean rendered as a checkbox."]
|
|
@@ -297,6 +550,7 @@ const INPUTS = [
|
|
|
297
550
|
{
|
|
298
551
|
factory: "textarea",
|
|
299
552
|
namespace: "inputs",
|
|
553
|
+
docSlug: "textarea",
|
|
300
554
|
call: "gui.inputs.textarea(path, { label, placeholder?, validator? })",
|
|
301
555
|
example: "gui.inputs.textarea('bio', { label: 'Bio', validator: { maxLength: 500 } })",
|
|
302
556
|
notes: ["Multi-line text; same loose string validator as `textInput`."]
|
|
@@ -304,6 +558,7 @@ const INPUTS = [
|
|
|
304
558
|
{
|
|
305
559
|
factory: "password",
|
|
306
560
|
namespace: "inputs",
|
|
561
|
+
docSlug: "password",
|
|
307
562
|
call: "gui.inputs.password(path, { label, validator? })",
|
|
308
563
|
example: "gui.inputs.password('password', { label: 'Password', validator: { required: true, minLength: 8 } })",
|
|
309
564
|
notes: ["Masked text input; loose string validator."]
|
|
@@ -311,6 +566,7 @@ const INPUTS = [
|
|
|
311
566
|
{
|
|
312
567
|
factory: "dropdown",
|
|
313
568
|
namespace: "inputs",
|
|
569
|
+
docSlug: "dropdown",
|
|
314
570
|
call: "gui.inputs.dropdown(path, { label, items, validator? })",
|
|
315
571
|
example: "gui.inputs.dropdown('country', { label: 'Country', items: [{ value: 'us', label: 'United States' }, { value: 'ca', label: 'Canada' }], validator: { type: 'string', required: true } })",
|
|
316
572
|
notes: [
|
|
@@ -321,6 +577,7 @@ const INPUTS = [
|
|
|
321
577
|
{
|
|
322
578
|
factory: "radiogroup",
|
|
323
579
|
namespace: "inputs",
|
|
580
|
+
docSlug: "radiogroup",
|
|
324
581
|
call: "gui.inputs.radiogroup(path, { label, options, defaultValue?, validator? })",
|
|
325
582
|
example: "gui.inputs.radiogroup('accountType', { label: 'Account type', defaultValue: 'personal', options: [{ value: 'personal', label: 'Personal' }, { value: 'business', label: 'Business' }] })",
|
|
326
583
|
notes: [
|
|
@@ -331,6 +588,7 @@ const INPUTS = [
|
|
|
331
588
|
{
|
|
332
589
|
factory: "datePicker",
|
|
333
590
|
namespace: "inputs",
|
|
591
|
+
docSlug: "date-picker",
|
|
334
592
|
call: "gui.inputs.datePicker(path, { label, minDate?, maxDate?, validator? })",
|
|
335
593
|
example: "gui.inputs.datePicker('startDate', { label: 'Coverage start', minDate: '2025-01-01', validator: { required: true } })",
|
|
336
594
|
notes: [
|
|
@@ -341,6 +599,7 @@ const INPUTS = [
|
|
|
341
599
|
{
|
|
342
600
|
factory: "currency",
|
|
343
601
|
namespace: "inputs",
|
|
602
|
+
docSlug: "currency",
|
|
344
603
|
call: "gui.inputs.currency(path, { label, validator? })",
|
|
345
604
|
example: "gui.inputs.currency('price', { label: 'Price', validator: { required: true, minimum: 0 } })",
|
|
346
605
|
notes: ["Numeric money input; number-style validator."]
|
|
@@ -348,6 +607,7 @@ const INPUTS = [
|
|
|
348
607
|
{
|
|
349
608
|
factory: "select",
|
|
350
609
|
namespace: "inputs",
|
|
610
|
+
docSlug: "select",
|
|
351
611
|
call: "gui.inputs.select(path, { label, options, validator? })",
|
|
352
612
|
example: "gui.inputs.select('plan', { label: 'Plan', options: [{ value: 'free', label: 'Free' }, { value: 'pro', label: 'Pro' }], validator: { type: 'string', required: true } })",
|
|
353
613
|
notes: [
|
|
@@ -358,6 +618,7 @@ const INPUTS = [
|
|
|
358
618
|
{
|
|
359
619
|
factory: "dateInput",
|
|
360
620
|
namespace: "inputs",
|
|
621
|
+
docSlug: "dateinput",
|
|
361
622
|
call: "gui.inputs.dateInput(path, { label, minDate?, maxDate?, validator? })",
|
|
362
623
|
example: "gui.inputs.dateInput('startDate', { label: 'Start date', validator: { required: true } })",
|
|
363
624
|
notes: [
|
|
@@ -365,9 +626,40 @@ const INPUTS = [
|
|
|
365
626
|
"`minDate` / `maxDate` (ISO `YYYY-MM-DD` strings) constrain the accepted range — see `datePicker`."
|
|
366
627
|
]
|
|
367
628
|
},
|
|
629
|
+
{
|
|
630
|
+
factory: "timeInput",
|
|
631
|
+
namespace: "inputs",
|
|
632
|
+
docSlug: "timeinput",
|
|
633
|
+
call: "gui.inputs.timeInput(path, { label, hourFormat?, minuteStep?, validator? })",
|
|
634
|
+
example: "gui.inputs.timeInput('meetingTime', { label: 'Meeting time', minuteStep: 15 })",
|
|
635
|
+
notes: [
|
|
636
|
+
"Typed time entry (hh:mm segments). Emits an ISO time string (`HH:mm:ss`) — pair with the `{ format: 'time' }` validator. `hourFormat` forces '12'/'24' (default: locale); `minuteStep` sets the arrow-key minute increment."
|
|
637
|
+
]
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
factory: "timePicker",
|
|
641
|
+
namespace: "inputs",
|
|
642
|
+
docSlug: "timepicker",
|
|
643
|
+
call: "gui.inputs.timePicker(path, { label, minTime?, maxTime?, minuteStep?, disabledRanges?, allowCustomTime?, validator? })",
|
|
644
|
+
example: "gui.inputs.timePicker('meetingTime', { label: 'Meeting time', minTime: '09:00', maxTime: '18:00', minuteStep: 30 })",
|
|
645
|
+
notes: [
|
|
646
|
+
"Time field with a popover list of slots built from `minTime`..`maxTime` stepping `minuteStep` (default 30). `disabledRanges` (`{ start, end }[]`, inclusive) greys slots out. Typing is off unless `allowCustomTime: true`. Emits `HH:mm:ss` — pair with the `{ format: 'time' }` validator."
|
|
647
|
+
]
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
factory: "dateTimeInput",
|
|
651
|
+
namespace: "inputs",
|
|
652
|
+
docSlug: "datetimeinput",
|
|
653
|
+
call: "gui.inputs.dateTimeInput(path, { label, hourFormat?, minuteStep?, validator? })",
|
|
654
|
+
example: "gui.inputs.dateTimeInput('meetingAt', { label: 'Meeting at' })",
|
|
655
|
+
notes: [
|
|
656
|
+
"Typed date+time entry in one locale-ordered row. Emits a local ISO date-time (`YYYY-MM-DDTHH:mm:ss`) — pair with the `{ format: 'date-time' }` validator. `hourFormat`/`minuteStep` as in `timeInput`."
|
|
657
|
+
]
|
|
658
|
+
},
|
|
368
659
|
{
|
|
369
660
|
factory: "calendar",
|
|
370
661
|
namespace: "inputs",
|
|
662
|
+
docSlug: "calendar",
|
|
371
663
|
call: "gui.inputs.calendar(path, { label, minDate?, maxDate? })",
|
|
372
664
|
example: "gui.inputs.calendar('day', { label: 'Pick a day', minDate: '2025-01-01' })",
|
|
373
665
|
notes: [
|
|
@@ -375,9 +667,34 @@ const INPUTS = [
|
|
|
375
667
|
"`minDate` / `maxDate` (ISO `YYYY-MM-DD` strings) constrain the selectable range — see `datePicker`."
|
|
376
668
|
]
|
|
377
669
|
},
|
|
670
|
+
{
|
|
671
|
+
factory: "dateTimeCalendar",
|
|
672
|
+
namespace: "inputs",
|
|
673
|
+
docSlug: "datetimecalendar",
|
|
674
|
+
call: "gui.inputs.dateTimeCalendar(path, { label, minDate?, maxDate?, minTime?, maxTime?, minuteStep?, disabledTimeRanges?, allowCustomTime? })",
|
|
675
|
+
example: "gui.inputs.dateTimeCalendar('appointmentAt', { label: 'Appointment', minTime: '09:00', maxTime: '18:00' })",
|
|
676
|
+
notes: [
|
|
677
|
+
"An INLINE calendar with an embedded time picker: a segmented time input between the header and the days grid opens a time grid in place of the days (like the year selector).",
|
|
678
|
+
"Emits a local ISO date-time (`YYYY-MM-DDTHH:mm:ss`) only when BOTH day and time are selected — pair with a `{ type: 'string', format: 'date-time' }` validator. Picking a different day clears the time and resets the value to null.",
|
|
679
|
+
"`disabledTimeRanges` entries take `start`/`end` ISO times plus optional `date` (ISO date) and/or `weekdays` (getDay() numbering: 0=Sunday … 6=Saturday) to scope the range to specific days."
|
|
680
|
+
]
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
factory: "dateTimePicker",
|
|
684
|
+
namespace: "inputs",
|
|
685
|
+
docSlug: "datetimepicker",
|
|
686
|
+
call: "gui.inputs.dateTimePicker(path, { label, minDate?, maxDate?, minTime?, maxTime?, minuteStep?, disabledTimeRanges?, allowCustomTime? })",
|
|
687
|
+
example: "gui.inputs.dateTimePicker('appointmentAt', { label: 'Appointment', minTime: '09:00', maxTime: '18:00' })",
|
|
688
|
+
notes: [
|
|
689
|
+
"A compact date-time FIELD that opens a `dateTimeCalendar` POPOVER on focus — the space-saving counterpart to the inline `dateTimeCalendar`, like `datePicker` is to `calendar`.",
|
|
690
|
+
"Emits a local ISO date-time (`YYYY-MM-DDTHH:mm:ss`); the popover closes only when BOTH day and time are selected. Pair with a `{ type: 'string', format: 'date-time' }` validator.",
|
|
691
|
+
"Takes the same time props as `dateTimeCalendar` (`minTime`/`maxTime`/`minuteStep`/`disabledTimeRanges` with per-date/weekday scoping/`allowCustomTime`) plus `icon` and `invalidDateMessage` for the typed input."
|
|
692
|
+
]
|
|
693
|
+
},
|
|
378
694
|
{
|
|
379
695
|
factory: "markdown",
|
|
380
696
|
namespace: "inputs",
|
|
697
|
+
docSlug: "markdown",
|
|
381
698
|
call: "gui.inputs.markdown(path, { label })",
|
|
382
699
|
example: "gui.inputs.markdown('notes', { label: 'Notes (markdown)' })",
|
|
383
700
|
notes: [
|
|
@@ -387,6 +704,7 @@ const INPUTS = [
|
|
|
387
704
|
{
|
|
388
705
|
factory: "tags",
|
|
389
706
|
namespace: "inputs",
|
|
707
|
+
docSlug: "tags",
|
|
390
708
|
call: "gui.inputs.tags(path, { label })",
|
|
391
709
|
example: "gui.inputs.tags('skills', { label: 'Skills' })",
|
|
392
710
|
notes: ["Free-form multi-value tag input; the value is a string array."]
|
|
@@ -394,6 +712,7 @@ const INPUTS = [
|
|
|
394
712
|
{
|
|
395
713
|
factory: "repeater",
|
|
396
714
|
namespace: "inputs",
|
|
715
|
+
docSlug: "repeater",
|
|
397
716
|
call: "gui.inputs.repeater(path, { label?, addLabel?, removeLabel?, limit?, template })",
|
|
398
717
|
example: "gui.inputs.repeater('attendees', { label: 'Attendees', addLabel: 'Add attendee', template: [ gui.inputs.textInput('attendees.items.name', { label: 'Name' }) ] })",
|
|
399
718
|
notes: [
|
|
@@ -405,6 +724,7 @@ const INPUTS = [
|
|
|
405
724
|
{
|
|
406
725
|
factory: "list",
|
|
407
726
|
namespace: "inputs",
|
|
727
|
+
docSlug: "list",
|
|
408
728
|
call: "gui.inputs.list(path, { label, items, height?, itemHeight? })",
|
|
409
729
|
example: "gui.inputs.list('selection', { label: 'Pick an option', items: ['Option 1', 'Option 2', 'Option 3'], height: 200, itemHeight: 40 })",
|
|
410
730
|
notes: [
|
|
@@ -415,6 +735,7 @@ const INPUTS = [
|
|
|
415
735
|
{
|
|
416
736
|
factory: "rangeCalendar",
|
|
417
737
|
namespace: "inputs",
|
|
738
|
+
docSlug: "range-calendar",
|
|
418
739
|
call: "gui.inputs.rangeCalendar(path, { label? })",
|
|
419
740
|
example: "gui.inputs.rangeCalendar('stayDates', { label: 'Stay dates' })",
|
|
420
741
|
notes: [
|
|
@@ -424,22 +745,78 @@ const INPUTS = [
|
|
|
424
745
|
{
|
|
425
746
|
factory: "rangeDateInput",
|
|
426
747
|
namespace: "inputs",
|
|
748
|
+
docSlug: "range-date-input",
|
|
427
749
|
call: "gui.inputs.rangeDateInput(path, { label? })",
|
|
428
750
|
example: "gui.inputs.rangeDateInput('stayDates', { label: 'Stay dates' })",
|
|
429
751
|
notes: ["Typed start–end date **range** entry (the range sibling of `dateInput`)."]
|
|
430
752
|
},
|
|
753
|
+
{
|
|
754
|
+
factory: "rangeTimeInput",
|
|
755
|
+
namespace: "inputs",
|
|
756
|
+
docSlug: "range-time-input",
|
|
757
|
+
call: "gui.inputs.rangeTimeInput(path, { label?, minTime?, maxTime? })",
|
|
758
|
+
example: "gui.inputs.rangeTimeInput('shift', { label: 'Shift', minTime: '06:00:00', maxTime: '22:00:00' })",
|
|
759
|
+
notes: [
|
|
760
|
+
"Typed start–end time **range** entry (the range sibling of `timeInput`); value is `TimeRange[]`. End time must be after start time."
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
factory: "rangeDateTimeInput",
|
|
765
|
+
namespace: "inputs",
|
|
766
|
+
docSlug: "range-datetime-input",
|
|
767
|
+
call: "gui.inputs.rangeDateTimeInput(path, { label?, minDateTime?, maxDateTime? })",
|
|
768
|
+
example: "gui.inputs.rangeDateTimeInput('window', { label: 'Window', minDateTime: '2026-03-01T06:00:00', maxDateTime: '2026-03-31T22:00:00' })",
|
|
769
|
+
notes: [
|
|
770
|
+
"Typed start–end date-time **range** entry (the range sibling of `dateTimeInput`); value is `DateTimeRange[]`. A backward selection reorders (swaps) instead of erroring.",
|
|
771
|
+
"Each endpoint is an instant, so it is bounded by instants: use **`minDateTime`** / **`maxDateTime`** (ISO `YYYY-MM-DDTHH:mm:ss`), not `minDate`/`maxDate`. There is no `minTime`/`maxTime` here — a per-day time window is a different constraint from an instant bound."
|
|
772
|
+
]
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
factory: "rangeDateTimeCalendar",
|
|
776
|
+
namespace: "inputs",
|
|
777
|
+
docSlug: "range-datetime-calendar",
|
|
778
|
+
call: "gui.inputs.rangeDateTimeCalendar(path, { label?, minDateTime?, maxDateTime?, disabledRanges?, startTimeLabel?, endTimeLabel? })",
|
|
779
|
+
example: "gui.inputs.rangeDateTimeCalendar('stay', { label: 'Stay', startTimeLabel: 'Check-in', endTimeLabel: 'Check-out' })",
|
|
780
|
+
notes: [
|
|
781
|
+
"INLINE range calendar with TWO embedded time pickers (start/end); value is `DateTimeRange[]`, rendered as pills. Pick a date range, then a start time (enables the end time), then an end time to commit a pill. A day holding more than one range shows a count badge.",
|
|
782
|
+
"Everything is in instant-space: bounds are **`minDateTime`** / **`maxDateTime`** and **`disabledRanges`** are `DateTimeRange[]` instant spans (block a whole day with `00:00:00`–`23:59:59`). There is no `minDate`/`maxDate`/`minTime`/`maxTime`/`disabledTimeRanges` — a time-of-day constraint cannot bound a multi-day span."
|
|
783
|
+
]
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
factory: "rangeDateTimePicker",
|
|
787
|
+
namespace: "inputs",
|
|
788
|
+
docSlug: "range-datetime-picker",
|
|
789
|
+
call: "gui.inputs.rangeDateTimePicker(path, { label?, minDateTime?, maxDateTime?, disabledRanges?, startTimeLabel?, endTimeLabel? })",
|
|
790
|
+
example: "gui.inputs.rangeDateTimePicker('stay', { label: 'Stay', startTimeLabel: 'Check-in', endTimeLabel: 'Check-out' })",
|
|
791
|
+
notes: [
|
|
792
|
+
"POPOVER date-time range picker: the typed `rangeDateTimeInput` as the trigger (pills live there) with the `rangeDateTimeCalendar` in a dropdown. Value is `DateTimeRange[]`. Committing a pill keeps the popover open so several ranges can be added; it closes on outside-click, blur or Escape.",
|
|
793
|
+
"Everything is in instant-space: bounds are **`minDateTime`** / **`maxDateTime`** and **`disabledRanges`** are `DateTimeRange[]` instant spans (block a whole day with `00:00:00`–`23:59:59`). There is no `minDate`/`maxDate`/`minTime`/`maxTime`/`disabledTimeRanges`."
|
|
794
|
+
]
|
|
795
|
+
},
|
|
431
796
|
{
|
|
432
797
|
factory: "rangeDatePicker",
|
|
433
798
|
namespace: "inputs",
|
|
799
|
+
docSlug: "range-date-picker",
|
|
434
800
|
call: "gui.inputs.rangeDatePicker(path, { label? })",
|
|
435
801
|
example: "gui.inputs.rangeDatePicker('stayDates', { label: 'Stay dates' })",
|
|
436
802
|
notes: ["Popover calendar for a start–end date **range** (the range sibling of `datePicker`)."]
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
factory: "rangeTimePicker",
|
|
806
|
+
namespace: "inputs",
|
|
807
|
+
docSlug: "range-time-picker",
|
|
808
|
+
call: "gui.inputs.rangeTimePicker(path, { label?, minTime?, maxTime? })",
|
|
809
|
+
example: "gui.inputs.rangeTimePicker('shift', { label: 'Shift', minTime: '06:00:00', maxTime: '22:00:00' })",
|
|
810
|
+
notes: [
|
|
811
|
+
"Two-list popover for a start–end time **range** (the range sibling of `timePicker`); value is `TimeRange[]`. The out list floors one slot after the chosen in so end is strictly after start."
|
|
812
|
+
]
|
|
437
813
|
}
|
|
438
814
|
];
|
|
439
815
|
const ACTIONS = [
|
|
440
816
|
{
|
|
441
817
|
factory: "button",
|
|
442
818
|
namespace: "actions",
|
|
819
|
+
docSlug: "button",
|
|
443
820
|
call: "gui.actions.button({ label, actionType?: 'submit', onClick? })",
|
|
444
821
|
example: "gui.actions.button({ label: 'Sign up', actionType: 'submit' })",
|
|
445
822
|
notes: [
|
|
@@ -453,6 +830,7 @@ const DISPLAYS = [
|
|
|
453
830
|
{
|
|
454
831
|
factory: "alert",
|
|
455
832
|
namespace: "displays",
|
|
833
|
+
docSlug: "alert",
|
|
456
834
|
call: "gui.displays.alert({ text })",
|
|
457
835
|
example: "gui.displays.alert({ text: 'Please review your details before submitting.' })",
|
|
458
836
|
notes: [
|
|
@@ -462,6 +840,7 @@ const DISPLAYS = [
|
|
|
462
840
|
{
|
|
463
841
|
factory: "display",
|
|
464
842
|
namespace: "displays",
|
|
843
|
+
docSlug: "renderer",
|
|
465
844
|
call: "gui.displays.display(render)",
|
|
466
845
|
example: "gui.displays.display(() => 'Order summary')",
|
|
467
846
|
notes: [
|
|
@@ -474,6 +853,7 @@ const LAYOUTS = [
|
|
|
474
853
|
{
|
|
475
854
|
factory: "flex",
|
|
476
855
|
namespace: "layouts",
|
|
856
|
+
docSlug: "flex",
|
|
477
857
|
call: "gui.layouts.flex(children, props?)",
|
|
478
858
|
example: "gui.layouts.flex([ gui.inputs.textInput('firstName', { label: 'First name' }), gui.inputs.textInput('lastName', { label: 'Last name' }) ])",
|
|
479
859
|
notes: [
|
|
@@ -484,6 +864,7 @@ const LAYOUTS = [
|
|
|
484
864
|
{
|
|
485
865
|
factory: "verticalFlex",
|
|
486
866
|
namespace: "layouts",
|
|
867
|
+
docSlug: "flex",
|
|
487
868
|
call: "gui.layouts.verticalFlex(children, props?)",
|
|
488
869
|
example: "gui.layouts.verticalFlex([ gui.inputs.textInput('a', { label: 'A' }), gui.inputs.textInput('b', { label: 'B' }) ])",
|
|
489
870
|
notes: ["A `flex` with direction fixed to vertical."]
|
|
@@ -491,6 +872,7 @@ const LAYOUTS = [
|
|
|
491
872
|
{
|
|
492
873
|
factory: "horizontalFlex",
|
|
493
874
|
namespace: "layouts",
|
|
875
|
+
docSlug: "flex",
|
|
494
876
|
call: "gui.layouts.horizontalFlex(children, props?)",
|
|
495
877
|
example: "gui.layouts.horizontalFlex([ gui.inputs.textInput('a', { label: 'A' }), gui.inputs.textInput('b', { label: 'B' }) ])",
|
|
496
878
|
notes: ["A `flex` with direction fixed to horizontal."]
|
|
@@ -498,6 +880,7 @@ const LAYOUTS = [
|
|
|
498
880
|
{
|
|
499
881
|
factory: "grid",
|
|
500
882
|
namespace: "layouts",
|
|
883
|
+
docSlug: "grid",
|
|
501
884
|
call: "gui.layouts.grid(children, props?)",
|
|
502
885
|
example: "gui.layouts.grid([ gui.inputs.textInput('a', { label: 'A' }), gui.inputs.textInput('b', { label: 'B' }) ])",
|
|
503
886
|
notes: ["Grid layout; `horizontalGrid` / `verticalGrid` lock the direction."]
|
|
@@ -505,6 +888,7 @@ const LAYOUTS = [
|
|
|
505
888
|
{
|
|
506
889
|
factory: "verticalGrid",
|
|
507
890
|
namespace: "layouts",
|
|
891
|
+
docSlug: "grid",
|
|
508
892
|
call: "gui.layouts.verticalGrid(children, props?)",
|
|
509
893
|
example: "gui.layouts.verticalGrid([ gui.inputs.textInput('a', { label: 'A' }), gui.inputs.textInput('b', { label: 'B' }) ])",
|
|
510
894
|
notes: ["A `grid` with direction fixed to vertical."]
|
|
@@ -512,6 +896,7 @@ const LAYOUTS = [
|
|
|
512
896
|
{
|
|
513
897
|
factory: "horizontalGrid",
|
|
514
898
|
namespace: "layouts",
|
|
899
|
+
docSlug: "grid",
|
|
515
900
|
call: "gui.layouts.horizontalGrid(children, props?)",
|
|
516
901
|
example: "gui.layouts.horizontalGrid([ gui.inputs.textInput('a', { label: 'A' }), gui.inputs.textInput('b', { label: 'B' }) ])",
|
|
517
902
|
notes: ["A `grid` with direction fixed to horizontal."]
|
|
@@ -519,6 +904,7 @@ const LAYOUTS = [
|
|
|
519
904
|
{
|
|
520
905
|
factory: "tabs",
|
|
521
906
|
namespace: "layouts",
|
|
907
|
+
docSlug: "tabs",
|
|
522
908
|
call: "gui.layouts.tabs(sections)",
|
|
523
909
|
example: "gui.layouts.tabs([ { label: 'Account', children: [ gui.inputs.textInput('email', { label: 'Email' }) ] }, { label: 'Profile', children: [ gui.inputs.textInput('name', { label: 'Name' }) ] } ])",
|
|
524
910
|
notes: [
|
|
@@ -528,6 +914,7 @@ const LAYOUTS = [
|
|
|
528
914
|
{
|
|
529
915
|
factory: "accordion",
|
|
530
916
|
namespace: "layouts",
|
|
917
|
+
docSlug: "accordion",
|
|
531
918
|
call: "gui.layouts.accordion(sections)",
|
|
532
919
|
example: "gui.layouts.accordion([ { label: 'Billing', children: [ gui.inputs.textInput('card', { label: 'Card' }) ] } ])",
|
|
533
920
|
notes: [
|