rapidoc 11.0.0 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +46 -31
  2. package/dist/rapidoc-min.js +1424 -213
  3. package/dist/rapidoc-static-min.js +13542 -0
  4. package/package.json +48 -34
  5. package/src/components/api-request.ts +869 -0
  6. package/src/components/api-response.ts +509 -0
  7. package/src/components/json-tree.ts +171 -0
  8. package/src/components/schema-base-element.ts +315 -0
  9. package/src/components/schema-table.ts +430 -0
  10. package/src/components/schema-tree.ts +386 -0
  11. package/src/components/tag-input.ts +184 -0
  12. package/src/index.js +6 -0
  13. package/src/oauth-receiver.ts +29 -0
  14. package/src/rapidoc-mini.ts +582 -0
  15. package/src/rapidoc.ts +1808 -0
  16. package/src/static/index.d.ts +149 -0
  17. package/src/static/index.js +62 -0
  18. package/src/static/navigation.js +497 -0
  19. package/src/static/render.js +490 -0
  20. package/src/static/serializer.js +216 -0
  21. package/src/styles/auth-styles.js +318 -0
  22. package/src/styles/border-styles.js +24 -0
  23. package/src/styles/css-bundle.js +129 -0
  24. package/src/styles/custom-styles.js +8 -0
  25. package/src/styles/dialog-box-styles.js +102 -0
  26. package/src/styles/endpoint-styles.js +559 -0
  27. package/src/styles/flex-styles.js +16 -0
  28. package/src/styles/font-styles.js +432 -0
  29. package/src/styles/info-styles.js +36 -0
  30. package/src/styles/input-styles.js +555 -0
  31. package/src/styles/layout-styles.js +396 -0
  32. package/src/styles/microlighter-styles.js +99 -0
  33. package/src/styles/nav-styles.js +609 -0
  34. package/src/styles/param-list-styles.js +261 -0
  35. package/src/styles/schema-styles.js +288 -0
  36. package/src/styles/tab-styles.js +106 -0
  37. package/src/styles/theme-tokens.js +582 -0
  38. package/src/templates/advanced-search-dialog-template.js +94 -0
  39. package/src/templates/api-response-template.js +219 -0
  40. package/src/templates/callback-template.js +86 -0
  41. package/src/templates/code-samples-template.js +52 -0
  42. package/src/templates/components-template.js +86 -0
  43. package/src/templates/downloads-template.js +85 -0
  44. package/src/templates/endpoint-template.js +386 -0
  45. package/src/templates/expanded-endpoint-template.js +266 -0
  46. package/src/templates/focused-endpoint-template.js +145 -0
  47. package/src/templates/header-template.js +92 -0
  48. package/src/templates/logo-template.js +21 -0
  49. package/src/templates/main-body-template.js +102 -0
  50. package/src/templates/navbar-template.js +489 -0
  51. package/src/templates/overview-template.js +104 -0
  52. package/src/templates/request-body-template.js +651 -0
  53. package/src/templates/request-params-template.js +474 -0
  54. package/src/templates/security-scheme-template.js +1263 -0
  55. package/src/templates/server-template.js +152 -0
  56. package/src/utils/auto-color-scheme.js +99 -0
  57. package/src/utils/bind-navbar.js +175 -0
  58. package/src/utils/color-utils.js +59 -0
  59. package/src/utils/common-utils.js +191 -0
  60. package/src/utils/curl-utils.js +123 -0
  61. package/src/utils/highlighter.js +416 -0
  62. package/src/utils/markdown-utils.js +110 -0
  63. package/src/utils/mcp/common.js +467 -0
  64. package/src/utils/mcp/generators/csharp.js +242 -0
  65. package/src/utils/mcp/generators/go.js +298 -0
  66. package/src/utils/mcp/generators/java.js +330 -0
  67. package/src/utils/mcp/generators/python.js +248 -0
  68. package/src/utils/mcp/generators/rust.js +281 -0
  69. package/src/utils/mcp/generators/typescript.js +290 -0
  70. package/src/utils/mcp-generator.js +143 -0
  71. package/src/utils/mock-interceptor.js +508 -0
  72. package/src/utils/oauth-utils.js +87 -0
  73. package/src/utils/request-utils.js +203 -0
  74. package/src/utils/response-utils.js +203 -0
  75. package/src/utils/sanitize.js +57 -0
  76. package/src/utils/schema-utils.js +1927 -0
  77. package/src/utils/slugger.js +189 -0
  78. package/src/utils/spec-parser.js +915 -0
  79. package/src/utils/url-utils.js +211 -0
  80. package/src/utils/xml-utils.js +61 -0
@@ -0,0 +1,386 @@
1
+ import { html, css, nothing } from "lit";
2
+ import { customElement } from "lit/decorators.js";
3
+ import FontStyles from "../styles/font-styles.js";
4
+ import SchemaStyles from "../styles/schema-styles.js";
5
+ import BorderStyles from "../styles/border-styles.js";
6
+ import CustomStyles from "../styles/custom-styles.js";
7
+ import SchemaBaseElement from "./schema-base-element";
8
+
9
+ @customElement("schema-tree")
10
+ export default class SchemaTree extends SchemaBaseElement {
11
+ static get styles() {
12
+ return [
13
+ FontStyles,
14
+ SchemaStyles,
15
+ BorderStyles,
16
+ css`
17
+ .tree {
18
+ font-size: var(--font-size-small);
19
+ text-align: left;
20
+ direction: ltr;
21
+ line-height: calc(var(--font-size-small) + 6px);
22
+
23
+ & .tr:hover {
24
+ background-color: rgba(0, 0, 0, 0.15);
25
+ }
26
+ & .tr.expanded-descr {
27
+ max-height: none;
28
+ }
29
+ /* faint separators where descriptions wrap: every row in multiline mode, or around a row expanded by click */
30
+ &.expanded-all-descr .tr,
31
+ &.collapsed-all-descr .tr.expanded-descr {
32
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
33
+ }
34
+ &.collapsed-all-descr .tr.expanded-descr {
35
+ box-shadow: 0 -1px 0 color-mix(in srgb, var(--border) 60%, transparent);
36
+ }
37
+ & .key {
38
+ max-width: 300px;
39
+ }
40
+ &.collapsed-all-descr .tr:not(.expanded-descr) {
41
+ overflow: hidden;
42
+ max-height: calc(var(--font-size-small) + 8px);
43
+
44
+ & .key {
45
+ white-space: nowrap;
46
+ text-overflow: ellipsis;
47
+ overflow: hidden;
48
+ }
49
+ }
50
+ }
51
+
52
+ .tr {
53
+ &.expanded {
54
+ &:hover {
55
+ & > .td.key > .open-bracket,
56
+ & + .inside-bracket + .close-bracket {
57
+ color: var(--primary);
58
+ }
59
+ & + .inside-bracket {
60
+ border-left: 1px solid var(--muted-foreground);
61
+ }
62
+ }
63
+ & .open-bracket .open-bracket-collapsed {
64
+ display: none;
65
+ }
66
+ }
67
+ &.collapsed {
68
+ & + .inside-bracket,
69
+ & + .inside-bracket + .close-bracket {
70
+ overflow: hidden;
71
+ display: none;
72
+ }
73
+ & .open-bracket .open-bracket-expanded {
74
+ display: none;
75
+ }
76
+ }
77
+ }
78
+
79
+ .inside-bracket {
80
+ &.xxx-of-option {
81
+ border-left: 1px solid transparent;
82
+ }
83
+ &.object,
84
+ &.array {
85
+ border-left: 1px dotted var(--border);
86
+ }
87
+ }
88
+
89
+ .open-bracket {
90
+ display: inline-block;
91
+ padding: 0 20px 0 0;
92
+ cursor: pointer;
93
+ border: 1px solid transparent;
94
+ border-radius: min(var(--radius), 0.5rem);
95
+
96
+ &:hover {
97
+ color: var(--primary);
98
+ border: 1px solid var(--border);
99
+ }
100
+ &:focus-visible {
101
+ outline: 2px solid var(--primary);
102
+ border-color: var(--primary);
103
+ }
104
+ }
105
+
106
+ .close-bracket {
107
+ display: inline-block;
108
+ font-family: var(--font-mono);
109
+ }
110
+ `,
111
+ CustomStyles,
112
+ ];
113
+ }
114
+
115
+ render() {
116
+ if (!this.data) {
117
+ return html`
118
+ <div class="tree">
119
+ <span class="mono-font" style="color:var(--destructive)"> Schema not found </span>
120
+ </div>
121
+ `;
122
+ }
123
+
124
+ const rootType = this.data.kind === "primitive" ? this.data.type : this.data.kind || "";
125
+ const rootDescription = this.data.description || "";
126
+
127
+ return html` <div
128
+ class="tree ${this.schemaDescriptionExpanded === "true" ? "expanded-all-descr" : "collapsed-all-descr"}"
129
+ @click="${(e) => this.handleAllEvents(e)}"
130
+ @keydown="${(e) => this.handleKeyDown(e)}"
131
+ >
132
+ ${this.renderToolbar(rootType)}
133
+ <span part="schema-description" class="m-markdown"> ${this.renderDescriptionHtml(rootDescription)}</span>
134
+ ${this.renderAST(this.data)}
135
+ </div>`;
136
+ }
137
+
138
+ renderAST(node, parentType = "", schemaLevel = 0, indentLevel = 0) {
139
+ if (this.isFieldHidden(node)) {
140
+ return "";
141
+ }
142
+
143
+ const leftPadding = 12;
144
+ const minFieldColWidth = 400 - indentLevel * leftPadding;
145
+ const isExpanded = schemaLevel < this.schemaExpandLevel;
146
+ const readWriteBadge = this.getReadWriteBadge(node);
147
+ const readWriteTip = this.getReadWriteTip(node);
148
+ const nullMarker = node.nullable ? "null┃" : "";
149
+ const nullClass = node.nullable ? "nullable" : "";
150
+ const deprecatedIcon = this.renderDeprecatedIcon(node);
151
+
152
+ // 1. Array kind
153
+ if (node.kind === "array") {
154
+ const items = node.items;
155
+ if (!items) {
156
+ const fieldTitle = node.name
157
+ ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}`
158
+ : "";
159
+ return html`
160
+ <div class="tr ${isExpanded ? "expanded" : "collapsed"} array ${nullClass}" title="${node.deprecated ? "Deprecated" : nothing}">
161
+ <div class="td key ${node.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
162
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}${readWriteBadge}:</span>` : ""}
163
+ <span class="open-bracket array">${nullMarker}[ ]</span>
164
+ </div>
165
+ ${this.renderDescrCell(node.description)}
166
+ </div>
167
+ `;
168
+ }
169
+
170
+ // If array of primitives
171
+ if (items.kind === "primitive") {
172
+ const itemType = items.format || items.contentMediaType || items.type || "string";
173
+ const dataTypeCss = this.getDataTypeCss(itemType);
174
+ const fieldTitle = node.name
175
+ ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}`
176
+ : "";
177
+
178
+ return html`
179
+ <div class="tr primitive ${nullClass}" title="${node.deprecated || items.deprecated ? "Deprecated" : nothing}">
180
+ <div class="td key ${node.deprecated || items.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
181
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}:</span>` : ""}
182
+ <span class="${dataTypeCss}" title="${readWriteTip || nothing}">${nullMarker}[${itemType}]${readWriteBadge}</span>
183
+ </div>
184
+ ${this.renderDescrCell(node.description || items.description, items)}
185
+ </div>
186
+ `;
187
+ }
188
+
189
+ // If array of objects
190
+ if (items.kind === "object") {
191
+ const openBracket = html`<span
192
+ class="open-bracket array-of-object"
193
+ role="button"
194
+ tabindex="0"
195
+ aria-label="Toggle ${node.name || "object"}"
196
+ aria-expanded="${isExpanded ? "true" : "false"}"
197
+ >${nullMarker}<span class="open-bracket-expanded">[{</span><span class="open-bracket-collapsed">[{...}]</span></span
198
+ >`;
199
+ const closeBracket = "}]";
200
+ const fieldTitle = node.name
201
+ ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}`
202
+ : "";
203
+
204
+ return html`
205
+ <div
206
+ class="tr ${isExpanded ? "expanded" : "collapsed"} object ${nullClass}"
207
+ title="${node.deprecated || items.deprecated ? "Deprecated" : nothing}"
208
+ >
209
+ <div class="td key ${node.deprecated || items.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
210
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}${readWriteBadge}:</span>` : ""}
211
+ ${openBracket}
212
+ </div>
213
+ ${this.renderDescrCell(node.description || items.description)}
214
+ </div>
215
+ <div class="inside-bracket object" style="padding-left:${leftPadding}px;">
216
+ ${items.properties?.map((p) => this.renderAST(p, "object", schemaLevel + 1, indentLevel + 1))}
217
+ ${items.patternProperties?.map((p) => this.renderAST(p, "object", schemaLevel + 1, indentLevel + 1))}
218
+ ${items.additionalProperties ? this.renderAST(items.additionalProperties, "object", schemaLevel + 1, indentLevel + 1) : ""}
219
+ ${items.unions?.map((u) => this.renderAST(u, "object", schemaLevel + 1, indentLevel + 1))}
220
+ </div>
221
+ <div class="close-bracket">${closeBracket}</div>
222
+ `;
223
+ }
224
+
225
+ // If array of arrays
226
+ if (items.kind === "array") {
227
+ const arrType = node.arrayType !== "object" ? node.arrayType : "";
228
+ const openBracket = html`<span
229
+ class="open-bracket array-of-array"
230
+ data-array-type="${arrType}"
231
+ role="button"
232
+ tabindex="0"
233
+ aria-label="Toggle ${node.name || "array"}"
234
+ aria-expanded="${isExpanded ? "true" : "false"}"
235
+ >${nullMarker}<span class="open-bracket-expanded">[[ ${arrType} </span><span class="open-bracket-collapsed">[[...]]</span></span
236
+ >`;
237
+ const closeBracket = "]]";
238
+ const fieldTitle = node.name
239
+ ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}`
240
+ : "";
241
+
242
+ return html`
243
+ <div class="tr ${isExpanded ? "expanded" : "collapsed"} array ${nullClass}" title="${node.deprecated ? "Deprecated" : nothing}">
244
+ <div class="td key ${node.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
245
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}${readWriteBadge}:</span>` : ""}
246
+ ${openBracket}
247
+ </div>
248
+ ${this.renderDescrCell(node.description)}
249
+ </div>
250
+ <div class="inside-bracket array" style="padding-left:${leftPadding}px;">
251
+ ${this.renderAST(items, "array", schemaLevel + 1, indentLevel + 1)}
252
+ </div>
253
+ <div class="close-bracket">${closeBracket}</div>
254
+ `;
255
+ }
256
+
257
+ // If array of unions or others
258
+ const fieldTitle = node.name ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}` : "";
259
+ return html`
260
+ <div class="tr ${isExpanded ? "expanded" : "collapsed"} array ${nullClass}" title="${node.deprecated ? "Deprecated" : nothing}">
261
+ <div class="td key ${node.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
262
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}${readWriteBadge}:</span>` : ""}
263
+ <span
264
+ class="open-bracket array"
265
+ role="button"
266
+ tabindex="0"
267
+ aria-label="Toggle ${node.name || "array"}"
268
+ aria-expanded="${isExpanded ? "true" : "false"}"
269
+ >${nullMarker}<span class="open-bracket-expanded">[</span><span class="open-bracket-collapsed">[...]</span></span
270
+ >
271
+ </div>
272
+ ${this.renderDescrCell(node.description)}
273
+ </div>
274
+ <div class="inside-bracket array" style="padding-left:${leftPadding}px;">
275
+ ${this.renderAST(items, "array", schemaLevel + 1, indentLevel + 1)}
276
+ </div>
277
+ <div class="close-bracket">]</div>
278
+ `;
279
+ }
280
+
281
+ // 2. Object kind
282
+ if (node.kind === "object") {
283
+ const openBracket = html`<span
284
+ class="open-bracket object"
285
+ role="button"
286
+ tabindex="0"
287
+ aria-label="Toggle ${node.name || "object"}"
288
+ aria-expanded="${isExpanded ? "true" : "false"}"
289
+ >${nullMarker}<span class="open-bracket-expanded">{</span><span class="open-bracket-collapsed">{...}</span></span
290
+ >`;
291
+ const closeBracket = "}";
292
+ const fieldTitle = node.name ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}` : "";
293
+
294
+ return html`
295
+ <div class="tr ${isExpanded ? "expanded" : "collapsed"} object ${nullClass}" title="${node.deprecated ? "Deprecated" : nothing}">
296
+ <div class="td key ${node.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
297
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}${readWriteBadge}:</span>` : ""}
298
+ ${openBracket}
299
+ </div>
300
+ ${this.renderDescrCell(node.description)}
301
+ </div>
302
+ <div class="inside-bracket object" style="padding-left:${leftPadding}px;">
303
+ ${node.properties?.map((p) => this.renderAST(p, "object", schemaLevel + 1, indentLevel + 1))}
304
+ ${node.patternProperties?.map((p) => this.renderAST(p, "object", schemaLevel + 1, indentLevel + 1))}
305
+ ${node.additionalProperties ? this.renderAST(node.additionalProperties, "object", schemaLevel + 1, indentLevel + 1) : ""}
306
+ ${node.unions?.map((u) => this.renderAST(u, "object", schemaLevel + 1, indentLevel + 1))}
307
+ </div>
308
+ <div class="close-bracket">${closeBracket}</div>
309
+ `;
310
+ }
311
+
312
+ // 3. Union kind (oneOf / anyOf)
313
+ if (node.kind === "union") {
314
+ const opLabel =
315
+ (node.operator === "anyOf" ? "ANY OF" : "ONE OF") + (node.suffix ? ` ${node.suffix}` : "");
316
+ const newIndent = parentType === "xxx-of-option" ? indentLevel : indentLevel + 1;
317
+ // Options are already labelled by their OPT row, so only property names are shown
318
+ const showName = node.name && parentType !== "xxx-of-option";
319
+ // A named union puts its label and options one level inside the property row
320
+ const bodyFieldColWidth = showName ? minFieldColWidth - leftPadding : minFieldColWidth;
321
+
322
+ const unionBody = html`
323
+ <div class="tr expanded xxx-of-option">
324
+ <div class="td key" style="min-width:${bodyFieldColWidth}px">
325
+ <span class="key-label xxx-of-key">${opLabel}</span>
326
+ </div>
327
+ ${showName ? "" : this.renderDescrCell(node.description)}
328
+ </div>
329
+ <div class="inside-bracket xxx-of-option" style="padding-left:0px;">
330
+ ${node.options?.map(
331
+ (opt, i) => html`
332
+ <div>
333
+ <div class="tr expanded xxx-of-option">
334
+ <div class="td key" style="min-width:${bodyFieldColWidth}px">
335
+ <span class="key-label xxx-of-key">OPT ${opt.optionIndex || i + 1}</span>
336
+ ${opt.optionTitle ? html`<span class="xxx-of-descr">${opt.optionTitle}</span>` : ""}
337
+ </div>
338
+ </div>
339
+ ${this.renderAST(opt, "xxx-of-option", schemaLevel, newIndent)}
340
+ </div>
341
+ `,
342
+ )}
343
+ </div>
344
+ `;
345
+
346
+ return html`
347
+ ${node.properties?.map((p) => this.renderAST(p, "object", schemaLevel, indentLevel))}
348
+ ${
349
+ showName
350
+ ? html`
351
+ <div class="tr expanded xxx-of-prop">
352
+ <div class="td key" style="min-width:${minFieldColWidth}px">
353
+ <span class="key-label" title="${node.name}"
354
+ >${node.name}${node.required ? html`<span style="color:var(--destructive)">*</span>` : ""}:</span
355
+ >
356
+ </div>
357
+ ${this.renderDescrCell(node.description)}
358
+ </div>
359
+ <div class="inside-bracket xxx-of-option" style="padding-left:${leftPadding}px;">${unionBody}</div>
360
+ `
361
+ : unionBody
362
+ }
363
+ `;
364
+ }
365
+
366
+ // 4. Primitive kind
367
+ if (node.kind === "primitive") {
368
+ const dataTypeCss = this.getDataTypeCss(node.type);
369
+ const fieldTitle = node.name ? `${node.name}${readWriteTip ? ` (${readWriteTip})` : ""}` : "";
370
+
371
+ return html`
372
+ <div class="tr primitive" title="${node.deprecated ? "Deprecated" : nothing}">
373
+ <div class="td key ${node.deprecated ? "deprecated" : ""}" style="min-width:${minFieldColWidth}px">
374
+ ${deprecatedIcon}${node.name ? html`<span class="key-label" title="${fieldTitle}">${node.name}${node.required ? html`<span style="color:var(--destructive);">*</span>` : ""}:</span>` : ""}
375
+ <span class="${dataTypeCss}" title="${readWriteTip || nothing}"
376
+ >${parentType === "array" ? `[${node.type}]` : node.type}${readWriteBadge}</span
377
+ >
378
+ </div>
379
+ ${this.renderDescrCell(node.description, node, node.title)}
380
+ </div>
381
+ `;
382
+ }
383
+
384
+ return "";
385
+ }
386
+ }
@@ -0,0 +1,184 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { customElement, property } from "lit/decorators.js";
3
+
4
+ @customElement("tag-input")
5
+ export default class TagInput extends LitElement {
6
+ @property({ type: String }) placeholder = "";
7
+
8
+ @property({
9
+ type: Array,
10
+ attribute: "value",
11
+ hasChanged(newVal, oldVal) {
12
+ if (newVal === oldVal) return false;
13
+ if (!Array.isArray(newVal) || !Array.isArray(oldVal)) return true;
14
+ if (newVal.length !== oldVal.length) return true;
15
+ return newVal.some((v, i) => v !== oldVal[i]);
16
+ },
17
+ })
18
+ value = [];
19
+
20
+ // For backwards compatibility with static properties inspection in tests
21
+ static get properties() {
22
+ return Object.fromEntries(this.elementProperties || []);
23
+ }
24
+
25
+ render() {
26
+ let tagItemTmpl = "";
27
+ if (Array.isArray(this.value)) {
28
+ tagItemTmpl = html`${this.value
29
+ .filter((v) => v !== "" && v !== null && v !== undefined && String(v).trim() !== "")
30
+ .map((v) => html`<span class="tag">${v}</span>`)}`;
31
+ }
32
+ return html`<div class="tags" @click="${(e) => this.focusEditor(e)}">
33
+ ${tagItemTmpl}
34
+ <input
35
+ type="text"
36
+ class="editor"
37
+ @paste="${(e) => this.afterPaste(e)}"
38
+ @keydown="${(e) => this.afterKeyDown(e)}"
39
+ @blur="${(e) => this.onBlur(e)}"
40
+ placeholder="${this.placeholder || ""}"
41
+ />
42
+ </div>`;
43
+ }
44
+
45
+ focusEditor(e) {
46
+ if (e.target.classList.contains("tags")) {
47
+ this.shadowRoot?.querySelector(".editor")?.focus();
48
+ }
49
+ }
50
+
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
+ dispatchEvents() {
61
+ this.dispatchEvent(
62
+ new CustomEvent("change", { bubbles: true, composed: true, detail: { value: this.value } }),
63
+ );
64
+ this.dispatchEvent(
65
+ new CustomEvent("input", { bubbles: true, composed: true, detail: { value: this.value } }),
66
+ );
67
+ }
68
+
69
+ afterPaste(e) {
70
+ const clipboardData = e.clipboardData || window.clipboardData;
71
+ const pastedData = clipboardData.getData("Text");
72
+ const pastedArray = pastedData ? pastedData.split(",").filter((v) => v.trim() !== "") : "";
73
+ if (pastedArray) {
74
+ if (Array.isArray(this.value)) {
75
+ this.value = [...this.value, ...pastedArray];
76
+ } else {
77
+ this.value = pastedArray;
78
+ }
79
+ this.dispatchEvents();
80
+ }
81
+ e.preventDefault();
82
+ }
83
+
84
+ afterKeyDown(e) {
85
+ if (e.key === "Enter" || e.keyCode === 13) {
86
+ e.stopPropagation();
87
+ e.preventDefault();
88
+ const val = e.target.value ? e.target.value.trim() : "";
89
+ if (val) {
90
+ if (Array.isArray(this.value)) {
91
+ this.value = [...this.value, val];
92
+ } else {
93
+ this.value = [val];
94
+ }
95
+ e.target.value = "";
96
+ this.dispatchEvents();
97
+ }
98
+ } else if (e.key === "Backspace" || e.key === "Delete" || e.keyCode === 8 || e.keyCode === 46) {
99
+ if (!e.target.value || e.target.value.length === 0) {
100
+ if (Array.isArray(this.value) && this.value.length > 0) {
101
+ e.preventDefault();
102
+ this.value = this.value.slice(0, -1);
103
+ this.dispatchEvents();
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ onBlur(e) {
110
+ const val = e.target.value ? e.target.value.trim() : "";
111
+ if (val) {
112
+ if (Array.isArray(this.value)) {
113
+ this.value = [...this.value, val];
114
+ } else {
115
+ this.value = [val];
116
+ }
117
+ e.target.value = "";
118
+ this.dispatchEvents();
119
+ }
120
+ }
121
+
122
+ static get styles() {
123
+ return [
124
+ css`
125
+ .tags {
126
+ display: flex;
127
+ flex-wrap: wrap;
128
+ align-items: center;
129
+ box-sizing: border-box;
130
+ /* Same height as the text inputs in a parameter row (see param-list-styles.js) */
131
+ min-height: calc(2.75 * var(--font-size-small) + 2px);
132
+ outline: none;
133
+ padding: 0;
134
+ border-radius: var(--radius);
135
+ border: 1px solid var(--input);
136
+ cursor: text;
137
+ overflow: hidden;
138
+ background: var(--input-background);
139
+ transition:
140
+ border-color 0.2s ease,
141
+ box-shadow 0.2s ease;
142
+
143
+ /* Same focus ring as the text inputs */
144
+ &:focus-within {
145
+ border-color: var(--primary);
146
+ box-shadow: var(--focus-shadow);
147
+ }
148
+ }
149
+ .tag,
150
+ .editor {
151
+ padding: 3px;
152
+ margin: 2px;
153
+ }
154
+ /* Chips: a muted fill with the form-field border, so each one stands apart on a white or dark field */
155
+ .tag {
156
+ border: 1px solid var(--input);
157
+ background: var(--muted);
158
+ color: var(--foreground);
159
+ border-radius: var(--radius);
160
+ word-break: break-all;
161
+ font-size: var(--font-size-small);
162
+ }
163
+ .tag:hover ~ #cursor {
164
+ display: block;
165
+ }
166
+ .editor {
167
+ flex: 1;
168
+ border: 1px solid transparent;
169
+ color: var(--foreground);
170
+ min-width: 60px;
171
+ outline: none;
172
+ line-height: inherit;
173
+ font-family: inherit;
174
+ background: transparent;
175
+ font-size: calc(var(--font-size-small) + 1px);
176
+ }
177
+ .editor::placeholder {
178
+ color: var(--muted-foreground);
179
+ opacity: 1;
180
+ }
181
+ `,
182
+ ];
183
+ }
184
+ }
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import RapiDoc from "~/rapidoc";
2
+ import RapiDocMini from "~/rapidoc-mini";
3
+ import OAuthReceiver from "~/oauth-receiver";
4
+
5
+ export default { RapiDoc };
6
+ export { RapiDocMini, OAuthReceiver };
@@ -0,0 +1,29 @@
1
+ import { customElement } from "lit/decorators.js";
2
+ import { OAUTH_CHANNEL, readOAuthReply } from "~/utils/oauth-utils";
3
+
4
+ /**
5
+ * The OAuth return page's element: the provider sends the sign-in popup back here with a code (or, in the legacy
6
+ * implicit flow, a token). It hands the reply to the page that opened the popup, which checks it (origin, popup
7
+ * window, state) before using it, then removes it from the address bar and history.
8
+ *
9
+ * Two ways to reach the opener, both limited to this site's origin: `window.opener.postMessage`, and a
10
+ * BroadcastChannel for when the provider's page cut the link to the opener (Cross-Origin-Opener-Policy).
11
+ */
12
+ @customElement("oauth-receiver")
13
+ export default class OauthReceiver extends HTMLElement {
14
+ connectedCallback() {
15
+ const reply = readOAuthReply(window.location);
16
+ if (!reply) {
17
+ return;
18
+ }
19
+ window.history.replaceState(null, "", window.location.pathname);
20
+ if (window.opener) {
21
+ window.opener.postMessage(reply, window.location.origin);
22
+ }
23
+ if (typeof BroadcastChannel === "function") {
24
+ const channel = new BroadcastChannel(OAUTH_CHANNEL);
25
+ channel.postMessage(reply);
26
+ channel.close();
27
+ }
28
+ }
29
+ }