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.
Files changed (77) hide show
  1. package/README.md +127 -25
  2. package/dist/rapidoc-flow-min.js +580 -0
  3. package/dist/rapidoc-flow-min.js.LICENSE.txt +171 -0
  4. package/dist/rapidoc-min.js +541 -1309
  5. package/dist/rapidoc-min.js.LICENSE.txt +381 -0
  6. package/package.json +21 -18
  7. package/src/README.md +150 -0
  8. package/src/components/README.md +91 -0
  9. package/src/components/api-request.ts +43 -0
  10. package/src/components/api-response.ts +9 -4
  11. package/src/components/schema-base-element.ts +18 -4
  12. package/src/components/schema-table.ts +58 -13
  13. package/src/components/schema-tree.ts +7 -0
  14. package/src/components/tag-input.ts +5 -9
  15. package/src/flow.js +3 -0
  16. package/src/rapi-flow.ts +1178 -0
  17. package/src/rapidoc-mini.ts +27 -6
  18. package/src/rapidoc.ts +108 -62
  19. package/src/styles/README.md +96 -0
  20. package/src/styles/css-bundle.js +2 -2
  21. package/src/styles/endpoint-styles.js +6 -1
  22. package/src/styles/flow-styles.js +248 -0
  23. package/src/styles/nav-styles.js +39 -15
  24. package/src/styles/schema-styles.js +13 -5
  25. package/src/styles/theme-tokens.js +2 -2
  26. package/src/templates/README.md +89 -0
  27. package/src/templates/api-response-template.js +14 -10
  28. package/src/templates/callback-template.js +1 -1
  29. package/src/templates/downloads-template.js +12 -27
  30. package/src/templates/endpoint-template.js +15 -24
  31. package/src/templates/expanded-endpoint-template.js +8 -4
  32. package/src/templates/focused-endpoint-template.js +4 -3
  33. package/src/templates/navbar-template.js +20 -11
  34. package/src/templates/request-body-template.js +29 -7
  35. package/src/templates/request-params-template.js +15 -4
  36. package/src/templates/security-scheme-template.js +11 -2
  37. package/src/templates/server-template.js +3 -2
  38. package/src/utils/README.md +146 -0
  39. package/src/utils/arazzo/README.md +119 -0
  40. package/src/utils/arazzo/components.js +15 -0
  41. package/src/utils/arazzo/graph.js +269 -0
  42. package/src/utils/arazzo/index.js +52 -0
  43. package/src/utils/arazzo/operations.js +106 -0
  44. package/src/utils/arazzo/parse.js +196 -0
  45. package/src/utils/arazzo/refs.js +40 -0
  46. package/src/utils/arazzo/svg.js +707 -0
  47. package/src/utils/curl-utils.js +5 -2
  48. package/src/utils/highlighter.js +3 -0
  49. package/src/utils/mcp/README.md +155 -0
  50. package/src/utils/mcp/common.js +45 -346
  51. package/src/utils/mcp/manifest.js +382 -0
  52. package/src/utils/mcp/project/server.ts +53 -0
  53. package/src/utils/mcp/project.js +88 -0
  54. package/src/utils/mcp/runtime.js +289 -0
  55. package/src/utils/mcp/schema.js +98 -0
  56. package/src/utils/mcp/workflow-runtime.js +374 -0
  57. package/src/utils/mcp/workflows.js +240 -0
  58. package/src/utils/mcp/zip.js +102 -0
  59. package/src/utils/mcp-generator.js +29 -125
  60. package/src/utils/mock-interceptor.js +134 -85
  61. package/src/utils/request-utils.js +61 -0
  62. package/src/utils/schema-utils.js +33 -6
  63. package/src/utils/slugger.js +5 -7
  64. package/src/utils/spec-parser.js +190 -164
  65. package/src/utils/url-utils.js +36 -1
  66. package/dist/rapidoc-static-min.js +0 -13542
  67. package/src/static/index.d.ts +0 -149
  68. package/src/static/index.js +0 -62
  69. package/src/static/navigation.js +0 -497
  70. package/src/static/render.js +0 -490
  71. package/src/static/serializer.js +0 -216
  72. package/src/utils/mcp/generators/csharp.js +0 -242
  73. package/src/utils/mcp/generators/go.js +0 -298
  74. package/src/utils/mcp/generators/java.js +0 -330
  75. package/src/utils/mcp/generators/python.js +0 -248
  76. package/src/utils/mcp/generators/rust.js +0 -281
  77. 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 aria-label="response examples" class="select-sm example-select" @change="${(e) => this.onSelectExample(e)}">
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.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary}
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
- if (this.config.schemaHideReadOnly !== undefined)
80
- this.schemaHideReadOnly = this.config.schemaHideReadOnly;
81
- if (this.config.schemaHideWriteOnly !== undefined)
82
- this.schemaHideWriteOnly = this.config.schemaHideWriteOnly;
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
- (opt, i) => html`
395
- <div class="tr expanded xxx-of-option" data-obj="OPTION ${opt.optionIndex || i + 1}">
396
- <div class="td key" style="padding-left:${leftPadding + 16}px">
397
- <span class="xxx-of-key">OPT ${opt.optionIndex || i + 1}</span>
398
- ${opt.optionTitle ? html`<span class="xxx-of-descr">${opt.optionTitle}</span>` : ""}
399
- </div>
400
- <div class="td key-type"></div>
401
- <div class="td key-descr"></div>
402
- </div>
403
- <div class="object-body">${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}</div>
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
- ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${node.name}">${node.name}</span>` : ""}
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
@@ -0,0 +1,3 @@
1
+ import RapiFlow from "~/rapi-flow";
2
+
3
+ export default RapiFlow;