rapidoc 12.0.0 → 12.1.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 +127 -25
- package/dist/rapidoc-flow-min.js +580 -0
- package/dist/rapidoc-flow-min.js.LICENSE.txt +171 -0
- package/dist/rapidoc-min.js +541 -1309
- package/dist/rapidoc-min.js.LICENSE.txt +381 -0
- package/package.json +21 -18
- package/src/README.md +150 -0
- package/src/components/README.md +91 -0
- package/src/components/api-request.ts +43 -0
- package/src/components/api-response.ts +9 -4
- package/src/components/schema-base-element.ts +18 -4
- package/src/components/schema-table.ts +58 -13
- package/src/components/schema-tree.ts +7 -0
- package/src/components/tag-input.ts +5 -9
- package/src/flow.js +3 -0
- package/src/rapi-flow.ts +1178 -0
- package/src/rapidoc-mini.ts +27 -6
- package/src/rapidoc.ts +108 -62
- package/src/styles/README.md +96 -0
- package/src/styles/css-bundle.js +2 -2
- package/src/styles/endpoint-styles.js +6 -1
- package/src/styles/flow-styles.js +248 -0
- package/src/styles/nav-styles.js +39 -15
- package/src/styles/schema-styles.js +13 -5
- package/src/styles/theme-tokens.js +2 -2
- package/src/templates/README.md +89 -0
- package/src/templates/api-response-template.js +14 -10
- package/src/templates/callback-template.js +1 -1
- package/src/templates/downloads-template.js +12 -27
- package/src/templates/endpoint-template.js +15 -24
- package/src/templates/expanded-endpoint-template.js +8 -4
- package/src/templates/focused-endpoint-template.js +4 -3
- package/src/templates/navbar-template.js +20 -11
- package/src/templates/request-body-template.js +29 -7
- package/src/templates/request-params-template.js +15 -4
- package/src/templates/security-scheme-template.js +11 -2
- package/src/templates/server-template.js +3 -2
- package/src/utils/README.md +146 -0
- package/src/utils/arazzo/README.md +119 -0
- package/src/utils/arazzo/components.js +15 -0
- package/src/utils/arazzo/graph.js +269 -0
- package/src/utils/arazzo/index.js +52 -0
- package/src/utils/arazzo/operations.js +106 -0
- package/src/utils/arazzo/parse.js +196 -0
- package/src/utils/arazzo/refs.js +40 -0
- package/src/utils/arazzo/svg.js +707 -0
- package/src/utils/curl-utils.js +5 -2
- package/src/utils/highlighter.js +3 -0
- package/src/utils/mcp/README.md +155 -0
- package/src/utils/mcp/common.js +45 -346
- package/src/utils/mcp/manifest.js +382 -0
- package/src/utils/mcp/project/server.ts +53 -0
- package/src/utils/mcp/project.js +88 -0
- package/src/utils/mcp/runtime.js +289 -0
- package/src/utils/mcp/schema.js +98 -0
- package/src/utils/mcp/workflow-runtime.js +374 -0
- package/src/utils/mcp/workflows.js +240 -0
- package/src/utils/mcp/zip.js +102 -0
- package/src/utils/mcp-generator.js +29 -125
- package/src/utils/mock-interceptor.js +134 -85
- package/src/utils/request-utils.js +61 -0
- package/src/utils/schema-utils.js +33 -6
- package/src/utils/slugger.js +5 -7
- package/src/utils/spec-parser.js +190 -164
- package/src/utils/url-utils.js +36 -1
- package/dist/rapidoc-static-min.js +0 -13542
- package/src/static/index.d.ts +0 -149
- package/src/static/index.js +0 -62
- package/src/static/navigation.js +0 -497
- package/src/static/render.js +0 -490
- package/src/static/serializer.js +0 -216
- package/src/utils/mcp/generators/csharp.js +0 -242
- package/src/utils/mcp/generators/go.js +0 -298
- package/src/utils/mcp/generators/java.js +0 -330
- package/src/utils/mcp/generators/python.js +0 -248
- package/src/utils/mcp/generators/rust.js +0 -281
- package/src/utils/mcp/generators/typescript.js +0 -290
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# RapiDoc Sub-Components
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Lit 3 elements that `<rapi-doc>` (and `@rapidoc/prerender`, which constructs the schema renderers
|
|
6
|
+
directly) composes into each operation's request and response panels. Each file is a TypeScript
|
|
7
|
+
class with `@customElement` / `@property` decorators and shadow DOM.
|
|
8
|
+
|
|
9
|
+
This folder owns the request panel (parameters, body, Try, cURL, live response), the response
|
|
10
|
+
panel, the schema tree/table renderers, the JSON example viewer and the tag editor. It does not own
|
|
11
|
+
the surrounding templates (`../templates/`), styles (`../styles/`), or the parsing, URL, cURL and
|
|
12
|
+
fetch helpers (`../utils/`). Callers: `../rapidoc.ts`, `../templates/*` and
|
|
13
|
+
`../static/render.js` (`renderSchema` uses `SchemaTable` / `SchemaTree`).
|
|
14
|
+
|
|
15
|
+
## Interface
|
|
16
|
+
|
|
17
|
+
- `<api-request>` [api-request.ts:ApiRequest] — request panel; the only component that calls
|
|
18
|
+
`fetch`. Dispatches `before-try`, `after-try` and `request-aborted`.
|
|
19
|
+
- `<api-response>` [api-response.ts:ApiResponse] — status buttons, MIME dropdown, Example/Schema
|
|
20
|
+
tabs, response headers. `resetSelection()` is called by `<rapi-doc>` when focused mode reuses one
|
|
21
|
+
instance. Dispatches no events.
|
|
22
|
+
- `SchemaBaseElement` [schema-base-element.ts:SchemaBaseElement] — unregistered base class for the
|
|
23
|
+
schema renderers: config sync, hide rules, description cells, expand/collapse.
|
|
24
|
+
- `<schema-tree>` [schema-tree.ts:SchemaTree] and `<schema-table>` [schema-table.ts:SchemaTable] —
|
|
25
|
+
render a schema AST (from `../utils/schema-utils.js`) as a bracketed tree or a
|
|
26
|
+
Field/Type/Description table via `renderAST(node, parentType, schemaLevel, indentLevel)`.
|
|
27
|
+
- `<json-tree>` [json-tree.ts:JsonTree] — collapsible JSON viewer with a Copy button.
|
|
28
|
+
- `<tag-input>` [tag-input.ts:TagInput] — tag editor for array-valued parameters; dispatches
|
|
29
|
+
`change` then `input` with `detail: { value }`.
|
|
30
|
+
|
|
31
|
+
The `before-try`, `after-try`, `request-aborted`, `change` and `input` events are all
|
|
32
|
+
`bubbles: true, composed: true`. Property lists, CSS parts and event detail fields are in the code.
|
|
33
|
+
|
|
34
|
+
## Invariants
|
|
35
|
+
|
|
36
|
+
- Flags passed from `<rapi-doc>` are strings, compared with `=== "true"` / `=== "false"`, not
|
|
37
|
+
booleans (`allowTry`, `showCurlBeforeTry`, `webhook`, `callback`, the `schema*` flags).
|
|
38
|
+
- `<api-request>` omits the Try bar only when `allowTry === "false"`, and sets
|
|
39
|
+
`allowTry = "false"` after every update while `webhook === "true"`
|
|
40
|
+
[api-request.ts:ApiRequest.updated].
|
|
41
|
+
- Config sync runs in `willUpdate` only when `config` is set and changed. `<api-request>` and
|
|
42
|
+
`<api-response>` map a defined `config.schemaHide*` of `"never"` to `"false"` and any other value
|
|
43
|
+
to the direction default (requests hide read-only, responses hide write-only; webhooks invert
|
|
44
|
+
both) [api-request.ts:ApiRequest.willUpdate; api-response.ts:ApiResponse.willUpdate].
|
|
45
|
+
`SchemaBaseElement` maps `"never"` to `"false"`, copies a literal `"true"` / `"false"`, and
|
|
46
|
+
otherwise keeps the value the template set [schema-base-element.ts:hideFlagFromConfig].
|
|
47
|
+
- `SchemaBaseElement.willUpdate` resets a falsy or `< 1` `schemaExpandLevel` to `99999`, and
|
|
48
|
+
resets `schemaDescriptionExpanded` to `"false"` and either hide flag to `"true"` when the value
|
|
49
|
+
is not `"true"` or `"false"`.
|
|
50
|
+
- `isFieldHidden(node)` hides read-only / write-only nodes per the hide flags, and `renderAST`
|
|
51
|
+
skips a hidden node's whole subtree [schema-base-element.ts:SchemaBaseElement.isFieldHidden].
|
|
52
|
+
- A node starts expanded iff `schemaLevel < schemaExpandLevel`; children render at
|
|
53
|
+
`schemaLevel + 1`. Union options stay at the parent's level in `<schema-tree>` but go one level
|
|
54
|
+
deeper in `<schema-table>`.
|
|
55
|
+
- Markdown descriptions are passed through `sanitizeHTML` before `unsafeHTML`; plain-text schema
|
|
56
|
+
descriptions skip markdown and rely on Lit escaping
|
|
57
|
+
[schema-base-element.ts:SchemaBaseElement.renderDescrCell; api-response.ts:ApiResponse].
|
|
58
|
+
- Each Try gets its own `AbortController`; `before-try` listeners may change the request (`url`,
|
|
59
|
+
`method`, `headers`, `credentials`, `body`) before the `Request` is built, and `loading` is reset
|
|
60
|
+
in `finally` [api-request.ts:ApiRequest.onTryClick].
|
|
61
|
+
- `<tag-input value="a,b">` is a comma-separated list: blank parts are dropped, values are not
|
|
62
|
+
trimmed, and removing the attribute sets `value` to `[]` [tag-input.ts:TagInput]. Events fire
|
|
63
|
+
only after user edits (paste, Enter, Backspace/Delete on an empty input, blur), never on
|
|
64
|
+
attribute or property sets.
|
|
65
|
+
|
|
66
|
+
## Side Effects
|
|
67
|
+
|
|
68
|
+
- Network: `ApiRequest.onTryClick` calls `fetch`; `disconnectedCallback` aborts an in-flight
|
|
69
|
+
request.
|
|
70
|
+
- Object URLs for blob responses are revoked before each Try, in `clearResponseData`, and in
|
|
71
|
+
`disconnectedCallback` (they are created in `../utils/response-utils.js`).
|
|
72
|
+
- `ApiRequest.updated` schedules `syncTryBarState` with `queueMicrotask` and calls
|
|
73
|
+
`scheduleHighlight` on the host's shadow root; example selection uses `window.setTimeout`.
|
|
74
|
+
- `<api-request>` reads outside its shadow root: `buildFetchHeaders` takes `Accept` from the
|
|
75
|
+
sibling `<api-response>`'s `selectedMimeType` via `this.closest(...)`.
|
|
76
|
+
- Direct DOM writes without reactive state: example fill/clear writes input values;
|
|
77
|
+
`<api-response>` toggles `.example` panel `display`; expand/collapse swaps classes.
|
|
78
|
+
- Clipboard: `<json-tree>` copies JSON; `<tag-input>` reads pasted text and prevents the default.
|
|
79
|
+
- `renderMarkdownDescription` keeps a module-level cache shared by all schema renderers, cleared
|
|
80
|
+
when it reaches 5000 entries [schema-base-element.ts:renderMarkdownDescription].
|
|
81
|
+
- `willUpdate` overwrites reactive properties from `config`, so a `config` change replaces values
|
|
82
|
+
set by attribute.
|
|
83
|
+
|
|
84
|
+
## Verification
|
|
85
|
+
|
|
86
|
+
- Run `npm run test:unit` from the repo root (`tests/unit/*.test.js`, Node test runner).
|
|
87
|
+
- Unit tests that render or load these components: `semantic-markup.test.js` (heading outline,
|
|
88
|
+
`exportparts`), `schema-modernization.test.js`, `schema-nullable.test.js`,
|
|
89
|
+
`multipart-form-curl.test.js` (`<tag-input>` converter), `static-renderer.test.js`;
|
|
90
|
+
`css-variables.test.js` checks every `var(--x)` read here is defined.
|
|
91
|
+
- Template rendering is also covered by `npm run test:component` and `npm run test:e2e`.
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
constructFetchHeaders,
|
|
17
17
|
buildUrlEncodedBody,
|
|
18
18
|
buildMultipartFormData,
|
|
19
|
+
serializeMultipartBody,
|
|
19
20
|
} from "~/utils/request-utils";
|
|
20
21
|
import { processFetchResponse } from "../utils/response-utils.js";
|
|
21
22
|
import { inputParametersTemplate } from "~/templates/request-params-template";
|
|
@@ -78,6 +79,10 @@ export default class ApiRequest extends LitElement {
|
|
|
78
79
|
@state() selectedRequestBodyExample = "";
|
|
79
80
|
@state() fileInputKeys: any = {};
|
|
80
81
|
@state() loading = false;
|
|
82
|
+
// Try-bar buttons follow the fields: Fill Example shows only when a field has an example,
|
|
83
|
+
// Clear is enabled only while a field holds a value
|
|
84
|
+
@state() hasExampleToFill = false;
|
|
85
|
+
@state() hasValueToClear = false;
|
|
81
86
|
|
|
82
87
|
activeAbortController: any = null;
|
|
83
88
|
|
|
@@ -342,6 +347,8 @@ export default class ApiRequest extends LitElement {
|
|
|
342
347
|
|
|
343
348
|
render() {
|
|
344
349
|
return html`<div
|
|
350
|
+
@input="${this.syncTryBarState}"
|
|
351
|
+
@change="${this.syncTryBarState}"
|
|
345
352
|
class="col regular-font request-panel ${
|
|
346
353
|
"read focused".includes(this.renderStyle) || this.callback === "true"
|
|
347
354
|
? "read-mode"
|
|
@@ -379,6 +386,8 @@ export default class ApiRequest extends LitElement {
|
|
|
379
386
|
if (this.webhook === "true") {
|
|
380
387
|
this.allowTry = "false";
|
|
381
388
|
}
|
|
389
|
+
// After the fields are in the DOM; deferred so the update is not requested mid-update
|
|
390
|
+
queueMicrotask(() => this.syncTryBarState());
|
|
382
391
|
}
|
|
383
392
|
|
|
384
393
|
// This method is called before navigation change in focused mode
|
|
@@ -452,6 +461,7 @@ export default class ApiRequest extends LitElement {
|
|
|
452
461
|
}
|
|
453
462
|
}
|
|
454
463
|
});
|
|
464
|
+
this.syncTryBarState();
|
|
455
465
|
}
|
|
456
466
|
|
|
457
467
|
async onClearRequestData(e) {
|
|
@@ -462,6 +472,20 @@ export default class ApiRequest extends LitElement {
|
|
|
462
472
|
requestPanelInputEls.forEach((el) => {
|
|
463
473
|
el.value = "";
|
|
464
474
|
});
|
|
475
|
+
this.syncTryBarState();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Reads the request fields (the same ones Fill Example and Clear act on) and updates the
|
|
479
|
+
// try-bar flags; setting a flag to its current value schedules no render
|
|
480
|
+
syncTryBarState() {
|
|
481
|
+
const requestPanelEl = this.shadowRoot?.querySelector(".request-panel");
|
|
482
|
+
const inputEls: any[] = requestPanelEl
|
|
483
|
+
? [...requestPanelEl.querySelectorAll("input, tag-input, textarea:not(.is-hidden)")]
|
|
484
|
+
: [];
|
|
485
|
+
this.hasExampleToFill = inputEls.some((el) => !!el.dataset.example);
|
|
486
|
+
this.hasValueToClear = inputEls.some((el) =>
|
|
487
|
+
Array.isArray(el.value) ? el.value.length > 0 : el.value !== "",
|
|
488
|
+
);
|
|
465
489
|
}
|
|
466
490
|
|
|
467
491
|
buildFetchURL(requestPanelEl) {
|
|
@@ -493,6 +517,7 @@ export default class ApiRequest extends LitElement {
|
|
|
493
517
|
explode,
|
|
494
518
|
allowReserved,
|
|
495
519
|
isArray,
|
|
520
|
+
itemType: el.dataset.itemType || "",
|
|
496
521
|
});
|
|
497
522
|
});
|
|
498
523
|
|
|
@@ -582,6 +607,15 @@ export default class ApiRequest extends LitElement {
|
|
|
582
607
|
isArray: el.dataset.array !== "false",
|
|
583
608
|
}));
|
|
584
609
|
fetchOptions.body = buildMultipartFormData(fields);
|
|
610
|
+
// Content-Type of the parts that need one (application/json for objects, or the spec's encoding)
|
|
611
|
+
const partContentTypes = Object.fromEntries(
|
|
612
|
+
formDataEls
|
|
613
|
+
.filter((el: any) => el.dataset.contentType)
|
|
614
|
+
.map((el: any) => [el.dataset.pname, el.dataset.contentType]),
|
|
615
|
+
);
|
|
616
|
+
if (Object.keys(partContentTypes).length > 0) {
|
|
617
|
+
fetchOptions.partContentTypes = partContentTypes;
|
|
618
|
+
}
|
|
585
619
|
} else if (
|
|
586
620
|
/^audio\/|^image\/|^video\/|^font\/|tar$|zip$|7z$|rtf$|msword$|excel$|\/pdf$|\/octet-stream$/.test(
|
|
587
621
|
requestBodyType,
|
|
@@ -651,6 +685,14 @@ export default class ApiRequest extends LitElement {
|
|
|
651
685
|
credentials: tempRequest.credentials,
|
|
652
686
|
body: tempRequest.body,
|
|
653
687
|
};
|
|
688
|
+
// FormData can't give a text part a Content-Type, so such bodies are serialized here
|
|
689
|
+
if (tempRequest.body instanceof FormData && tempRequest.partContentTypes) {
|
|
690
|
+
const multipart = serializeMultipartBody(tempRequest.body, tempRequest.partContentTypes);
|
|
691
|
+
const headers = new Headers(tempRequest.headers);
|
|
692
|
+
headers.set("Content-Type", multipart.contentType);
|
|
693
|
+
updatedFetchOptions.headers = headers;
|
|
694
|
+
updatedFetchOptions.body = multipart.body;
|
|
695
|
+
}
|
|
654
696
|
let fetchRequest;
|
|
655
697
|
let fetchResponse;
|
|
656
698
|
let responseClone;
|
|
@@ -776,6 +818,7 @@ export default class ApiRequest extends LitElement {
|
|
|
776
818
|
headers: fetchHeaders,
|
|
777
819
|
body,
|
|
778
820
|
contentType: requestBodyType,
|
|
821
|
+
partContentTypes: fetchOptions?.partContentTypes,
|
|
779
822
|
});
|
|
780
823
|
}
|
|
781
824
|
|
|
@@ -4,6 +4,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
|
4
4
|
import { sanitizeHTML } from "../utils/sanitize.js";
|
|
5
5
|
import { marked } from "marked";
|
|
6
6
|
import { prepareResponseDefinitions } from "../utils/response-utils.js";
|
|
7
|
+
import { exampleLabel } from "~/utils/schema-utils";
|
|
7
8
|
import FontStyles from "~/styles/font-styles";
|
|
8
9
|
import FlexStyles from "~/styles/flex-styles";
|
|
9
10
|
import ParamListStyles from "~/styles/param-list-styles";
|
|
@@ -435,11 +436,16 @@ export default class ApiResponse extends LitElement {
|
|
|
435
436
|
}`
|
|
436
437
|
: html`
|
|
437
438
|
<div class="example-panel">
|
|
438
|
-
<select
|
|
439
|
+
<select
|
|
440
|
+
aria-label="response examples"
|
|
441
|
+
class="select-sm example-select"
|
|
442
|
+
title="${exampleLabel(mimeRespDetails.examples.find((v) => v.exampleId === mimeRespDetails.selectedExample))}"
|
|
443
|
+
@change="${(e) => this.onSelectExample(e)}"
|
|
444
|
+
>
|
|
439
445
|
${mimeRespDetails.examples.map(
|
|
440
446
|
(v) =>
|
|
441
|
-
html`<option value="${v.exampleId}" ?selected=${v.exampleId === mimeRespDetails.selectedExample}>
|
|
442
|
-
${v
|
|
447
|
+
html`<option value="${v.exampleId}" title="${exampleLabel(v)}" ?selected=${v.exampleId === mimeRespDetails.selectedExample}>
|
|
448
|
+
${exampleLabel(v)}
|
|
443
449
|
</option>`,
|
|
444
450
|
)}
|
|
445
451
|
</select>
|
|
@@ -450,7 +456,6 @@ export default class ApiResponse extends LitElement {
|
|
|
450
456
|
data-example="${v.exampleId}"
|
|
451
457
|
style="display: ${v.exampleId === mimeRespDetails.selectedExample ? "block" : "none"}"
|
|
452
458
|
>
|
|
453
|
-
${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style="padding: 4px 0">${v.exampleSummary}</div>` : ""}
|
|
454
459
|
${
|
|
455
460
|
v.exampleDescription
|
|
456
461
|
? html`<div class="m-markdown-small" style="padding: 4px 0">
|
|
@@ -56,6 +56,16 @@ function renderMarkdownDescription(text, title) {
|
|
|
56
56
|
return rendered;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The host's `schema-hide-*` flags are `"default"` | `"never"`: `"never"` shows the fields, `"default"` keeps
|
|
61
|
+
* the `"true"` | `"false"` the template chose for this element (requests hide read-only, responses write-only).
|
|
62
|
+
*/
|
|
63
|
+
function hideFlagFromConfig(configValue, current) {
|
|
64
|
+
if (configValue === "never") return "false";
|
|
65
|
+
if (configValue === "true" || configValue === "false") return configValue;
|
|
66
|
+
return current;
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
export default class SchemaBaseElement extends LitElement {
|
|
60
70
|
@property({ type: Object }) config: any;
|
|
61
71
|
@property({ type: Object }) data: any;
|
|
@@ -76,10 +86,14 @@ export default class SchemaBaseElement extends LitElement {
|
|
|
76
86
|
this.schemaDescriptionExpanded = this.config.schemaDescriptionExpanded;
|
|
77
87
|
if (this.config.allowSchemaDescriptionExpandToggle !== undefined)
|
|
78
88
|
this.allowSchemaDescriptionExpandToggle = this.config.allowSchemaDescriptionExpandToggle;
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
this.schemaHideReadOnly = hideFlagFromConfig(
|
|
90
|
+
this.config.schemaHideReadOnly,
|
|
91
|
+
this.schemaHideReadOnly,
|
|
92
|
+
);
|
|
93
|
+
this.schemaHideWriteOnly = hideFlagFromConfig(
|
|
94
|
+
this.config.schemaHideWriteOnly,
|
|
95
|
+
this.schemaHideWriteOnly,
|
|
96
|
+
);
|
|
83
97
|
}
|
|
84
98
|
if (!this.schemaExpandLevel || this.schemaExpandLevel < 1) {
|
|
85
99
|
this.schemaExpandLevel = 99999;
|
|
@@ -150,6 +150,25 @@ export default class SchemaTable extends SchemaBaseElement {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
/* Narrow schemas: the description spans the row, under the field and type, with no column of its own (#1074) */
|
|
155
|
+
@container (width < 500px) {
|
|
156
|
+
.table {
|
|
157
|
+
/* no column for descriptions, and field and type share the width instead of scrolling sideways */
|
|
158
|
+
& .table-container {
|
|
159
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, max-content) 0;
|
|
160
|
+
}
|
|
161
|
+
& .tr > .key-descr {
|
|
162
|
+
grid-column: 1 / -1;
|
|
163
|
+
}
|
|
164
|
+
& .table-header > .key-descr {
|
|
165
|
+
display: none;
|
|
166
|
+
}
|
|
167
|
+
&.collapsed-all-descr .tr:not(.expanded-descr) {
|
|
168
|
+
max-height: calc(2 * (var(--font-size-small) + 16px));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
153
172
|
`,
|
|
154
173
|
CustomStyles,
|
|
155
174
|
];
|
|
@@ -390,18 +409,29 @@ export default class SchemaTable extends SchemaBaseElement {
|
|
|
390
409
|
${this.renderDescrCell(node.description)}
|
|
391
410
|
</div>
|
|
392
411
|
<div class="object-body">
|
|
393
|
-
${node.options?.map(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
412
|
+
${node.options?.map((opt, i) =>
|
|
413
|
+
opt.kind === "primitive"
|
|
414
|
+
? // A primitive option fits on a single row: the badge, then its type and description
|
|
415
|
+
html`<div class="object-body">
|
|
416
|
+
${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}
|
|
417
|
+
</div>`
|
|
418
|
+
: html`
|
|
419
|
+
<div class="tr expanded xxx-of-option" data-obj="OPTION ${opt.optionIndex || i + 1}">
|
|
420
|
+
<div class="td key" style="padding-left:${leftPadding + 16}px">
|
|
421
|
+
<span class="xxx-of-key">OPT ${opt.optionIndex || i + 1}</span>
|
|
422
|
+
${
|
|
423
|
+
opt.optionTitle
|
|
424
|
+
? html`<span class="xxx-of-descr">${opt.optionTitle}</span>`
|
|
425
|
+
: ""
|
|
426
|
+
}
|
|
427
|
+
</div>
|
|
428
|
+
<div class="td key-type"></div>
|
|
429
|
+
<div class="td key-descr"></div>
|
|
430
|
+
</div>
|
|
431
|
+
<div class="object-body">
|
|
432
|
+
${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}
|
|
433
|
+
</div>
|
|
434
|
+
`,
|
|
405
435
|
)}
|
|
406
436
|
</div>
|
|
407
437
|
`;
|
|
@@ -410,11 +440,26 @@ export default class SchemaTable extends SchemaBaseElement {
|
|
|
410
440
|
// 4. Primitive kind
|
|
411
441
|
if (node.kind === "primitive") {
|
|
412
442
|
const dataTypeCss = this.getDataTypeCss(node.type);
|
|
443
|
+
// Primitive option of a oneOf / anyOf: the OPT n badge (and the title) takes the place of the name
|
|
444
|
+
const isOption = parentType === "xxx-of-option" && !!node.optionIndex;
|
|
413
445
|
|
|
414
446
|
return html`
|
|
415
447
|
<div class="tr primitive ${nullClass}" title="${node.deprecated ? "Deprecated" : nothing}">
|
|
416
448
|
<div class="td key ${node.deprecated ? "deprecated" : ""}" style="padding-left:${leftPadding}px">
|
|
417
|
-
${
|
|
449
|
+
${
|
|
450
|
+
isOption
|
|
451
|
+
? html`<span class="xxx-of-key">OPT ${node.optionIndex}</span
|
|
452
|
+
>${
|
|
453
|
+
node.optionTitle
|
|
454
|
+
? html`<span class="xxx-of-descr">${node.optionTitle}</span>`
|
|
455
|
+
: ""
|
|
456
|
+
}`
|
|
457
|
+
: ""
|
|
458
|
+
}${deprecatedIcon}${
|
|
459
|
+
node.name && !isOption
|
|
460
|
+
? html`<span class="key-label" title="${node.name}">${node.name}</span>`
|
|
461
|
+
: ""
|
|
462
|
+
}
|
|
418
463
|
${node.required ? html`<span style="color:var(--destructive);">*</span>` : ""}
|
|
419
464
|
</div>
|
|
420
465
|
<div class="td key-type ${dataTypeCss}" title="${readWriteTip || nothing}">
|
|
@@ -76,6 +76,13 @@ export default class SchemaTree extends SchemaBaseElement {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/* Narrow schemas: a collapsed row holds its field line and the description's line under it (#1074) */
|
|
80
|
+
@container (width < 500px) {
|
|
81
|
+
.tree.collapsed-all-descr .tr:not(.expanded-descr) {
|
|
82
|
+
max-height: calc(2 * (var(--font-size-small) + 8px));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
.inside-bracket {
|
|
80
87
|
&.xxx-of-option {
|
|
81
88
|
border-left: 1px solid transparent;
|
|
@@ -8,6 +8,11 @@ export default class TagInput extends LitElement {
|
|
|
8
8
|
@property({
|
|
9
9
|
type: Array,
|
|
10
10
|
attribute: "value",
|
|
11
|
+
converter: {
|
|
12
|
+
// `value="a,b"` is a comma-separated list, not the JSON Lit's Array converter expects
|
|
13
|
+
fromAttribute: (value) => (value ? value.split(",").filter((v) => v.trim() !== "") : []),
|
|
14
|
+
toAttribute: (value) => (Array.isArray(value) ? value.join(",") : value),
|
|
15
|
+
},
|
|
11
16
|
hasChanged(newVal, oldVal) {
|
|
12
17
|
if (newVal === oldVal) return false;
|
|
13
18
|
if (!Array.isArray(newVal) || !Array.isArray(oldVal)) return true;
|
|
@@ -48,15 +53,6 @@ export default class TagInput extends LitElement {
|
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
attributeChangedCallback(name, oldVal, newVal) {
|
|
52
|
-
if (name === "value") {
|
|
53
|
-
if (newVal && oldVal !== newVal) {
|
|
54
|
-
this.value = newVal.split(",").filter((v) => v.trim() !== "");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
super.attributeChangedCallback(name, oldVal, newVal);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
56
|
dispatchEvents() {
|
|
61
57
|
this.dispatchEvent(
|
|
62
58
|
new CustomEvent("change", { bubbles: true, composed: true, detail: { value: this.value } }),
|
package/src/flow.js
ADDED