@scalar/api-client 3.13.7 → 3.14.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 +32 -0
- package/README.md +2 -1
- package/dist/style.css +98 -83
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts +6 -6
- package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.js +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js +72 -11
- package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.d.ts +19 -0
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.d.ts.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.js +7 -0
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.js.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.script.js +81 -0
- package/dist/v2/blocks/request-block/components/RequestBodyStructured.vue.script.js.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.d.ts +13 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.d.ts.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.js +7 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.js.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.script.js +34 -0
- package/dist/v2/blocks/request-block/components/RequestBodyViewToggle.vue.script.js.map +1 -0
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js +8 -2
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/helpers/fold-structured-body-rows.d.ts +17 -0
- package/dist/v2/blocks/request-block/helpers/fold-structured-body-rows.d.ts.map +1 -0
- package/dist/v2/blocks/request-block/helpers/fold-structured-body-rows.js +33 -0
- package/dist/v2/blocks/request-block/helpers/fold-structured-body-rows.js.map +1 -0
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts +28 -0
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js +2 -2
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-structured-body-rows.d.ts +13 -0
- package/dist/v2/blocks/request-block/helpers/get-structured-body-rows.d.ts.map +1 -0
- package/dist/v2/blocks/request-block/helpers/get-structured-body-rows.js +39 -0
- package/dist/v2/blocks/request-block/helpers/get-structured-body-rows.js.map +1 -0
- package/dist/v2/blocks/request-block/helpers/structured-body-codec.d.ts +15 -0
- package/dist/v2/blocks/request-block/helpers/structured-body-codec.d.ts.map +1 -0
- package/dist/v2/blocks/request-block/helpers/structured-body-codec.js +33 -0
- package/dist/v2/blocks/request-block/helpers/structured-body-codec.js.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.d.ts +18 -2
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js +18 -13
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.d.ts +6 -2
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.script.js +16 -8
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.d.ts +7 -3
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.script.js +338 -25
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js +7 -11
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/run-oauth2-authorize.d.ts +42 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/run-oauth2-authorize.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/run-oauth2-authorize.js +60 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/run-oauth2-authorize.js.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.d.ts +17 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.js +33 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.js.map +1 -1
- package/dist/v2/components/data-table/DataTableCheckbox.vue.d.ts +2 -0
- package/dist/v2/components/data-table/DataTableCheckbox.vue.d.ts.map +1 -1
- package/dist/v2/components/data-table/DataTableCheckbox.vue.js.map +1 -1
- package/dist/v2/components/data-table/DataTableCheckbox.vue.script.js +8 -2
- package/dist/v2/components/data-table/DataTableCheckbox.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/vue-styles.css +1 -1
- package/package.json +10 -10
|
@@ -5,8 +5,11 @@ import DataTableHeader_default from "../../../components/data-table/DataTableHea
|
|
|
5
5
|
import DataTableRow_default from "../../../components/data-table/DataTableRow.vue.js";
|
|
6
6
|
import { getFileName } from "../helpers/files.js";
|
|
7
7
|
import RequestBodyForm_default from "./RequestBodyForm.vue.js";
|
|
8
|
+
import { getStructuredBodyCodec } from "../helpers/structured-body-codec.js";
|
|
9
|
+
import RequestBodyStructured_default from "./RequestBodyStructured.vue.js";
|
|
10
|
+
import RequestBodyViewToggle_default from "./RequestBodyViewToggle.vue.js";
|
|
8
11
|
import CollapsibleSection_default from "../../../components/layout/CollapsibleSection.vue.js";
|
|
9
|
-
import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, toDisplayString, unref, withCtx } from "vue";
|
|
12
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, watch, withCtx } from "vue";
|
|
10
13
|
import { ScalarButton } from "@scalar/components/button";
|
|
11
14
|
import { ScalarListbox } from "@scalar/components/listbox";
|
|
12
15
|
import { objectEntries } from "@scalar/helpers/object/object-entries";
|
|
@@ -15,7 +18,9 @@ import { getExampleFromBody, getSelectedBodyContentType } from "@scalar/workspac
|
|
|
15
18
|
import { parseMimeType } from "@scalar/helpers/http/mime-type";
|
|
16
19
|
import { unpackProxyObject } from "@scalar/workspace-store/helpers/unpack-proxy";
|
|
17
20
|
import { CONTENT_TYPES } from "@scalar/helpers/http/content-types";
|
|
21
|
+
import { isObject } from "@scalar/helpers/object/is-object";
|
|
18
22
|
import { resolve } from "@scalar/workspace-store/resolve";
|
|
23
|
+
import { isObjectSchema } from "@scalar/workspace-store/schemas/v3.1/strict/type-guards";
|
|
19
24
|
//#region src/v2/blocks/request-block/components/RequestBody.vue?vue&type=script&setup=true&lang.ts
|
|
20
25
|
var _hoisted_1 = {
|
|
21
26
|
key: 0,
|
|
@@ -118,6 +123,42 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
118
123
|
const bodySchema = computed(() => {
|
|
119
124
|
return resolve.schema(__props.requestBody?.content?.[selectedContentType.value]?.schema);
|
|
120
125
|
});
|
|
126
|
+
/** Codec for structured (JSON/YAML) bodies, undefined for everything else */
|
|
127
|
+
const structuredCodec = computed(() => selectedContentType.value === "none" ? void 0 : getStructuredBodyCodec(selectedContentType.value));
|
|
128
|
+
/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */
|
|
129
|
+
const parsedBody = computed(() => {
|
|
130
|
+
const codec = structuredCodec.value;
|
|
131
|
+
if (!codec) return { ok: false };
|
|
132
|
+
const raw = example.value?.value;
|
|
133
|
+
if (raw === void 0 || raw === null || raw === "") return {
|
|
134
|
+
ok: true,
|
|
135
|
+
value: {}
|
|
136
|
+
};
|
|
137
|
+
if (typeof raw === "string") try {
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
value: codec.parse(raw)
|
|
141
|
+
};
|
|
142
|
+
} catch {
|
|
143
|
+
return { ok: false };
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
ok: true,
|
|
147
|
+
value: raw
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
/** The form view only works on an object-shaped body root */
|
|
151
|
+
const isFormViewAvailable = computed(() => parsedBody.value.ok && isObject(parsedBody.value.value));
|
|
152
|
+
/**
|
|
153
|
+
* Show the Form/Raw toggle for structured bodies that are (or per schema should be)
|
|
154
|
+
* an object at the root. Non-object roots (arrays, primitives) stay raw-only.
|
|
155
|
+
*/
|
|
156
|
+
const showBodyViewToggle = computed(() => Boolean(structuredCodec.value) && (isFormViewAvailable.value || Boolean(bodySchema.value && isObjectSchema(bodySchema.value))));
|
|
157
|
+
/** Selected body view, raw by default so existing behavior is unchanged */
|
|
158
|
+
const bodyView = ref("raw");
|
|
159
|
+
watch(isFormViewAvailable, (ok) => {
|
|
160
|
+
if (!ok) bodyView.value = "raw";
|
|
161
|
+
});
|
|
121
162
|
return (_ctx, _cache) => {
|
|
122
163
|
return openBlock(), createBlock(unref(CollapsibleSection_default), null, {
|
|
123
164
|
title: withCtx(() => [createTextVNode(toDisplayString(__props.title), 1)]),
|
|
@@ -144,31 +185,36 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
144
185
|
_: 1
|
|
145
186
|
})]),
|
|
146
187
|
_: 1
|
|
147
|
-
}, 8, ["modelValue", "options"])
|
|
188
|
+
}, 8, ["modelValue", "options"]), showBodyViewToggle.value ? (openBlock(), createBlock(RequestBodyViewToggle_default, {
|
|
189
|
+
key: 0,
|
|
190
|
+
disabled: !isFormViewAvailable.value,
|
|
191
|
+
modelValue: bodyView.value,
|
|
192
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => bodyView.value = v)
|
|
193
|
+
}, null, 8, ["disabled", "modelValue"])) : createCommentVNode("", true)]),
|
|
148
194
|
_: 1
|
|
149
195
|
}), createVNode(unref(DataTableRow_default), null, {
|
|
150
|
-
default: withCtx(() => [selectedContentType.value === "none" ? (openBlock(), createElementBlock("div", _hoisted_1, [..._cache[
|
|
196
|
+
default: withCtx(() => [selectedContentType.value === "none" ? (openBlock(), createElementBlock("div", _hoisted_1, [..._cache[7] || (_cache[7] = [createElementVNode("span", null, "No Body", -1)])])) : selectedContentType.value === "application/octet-stream" ? (openBlock(), createElementBlock("div", _hoisted_2, [unref(getFileName)(unref(unpackProxyObject)(example.value?.value)) !== void 0 ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("span", _hoisted_3, toDisplayString(unref(getFileName)(unref(unpackProxyObject)(example.value?.value))), 1), createVNode(unref(ScalarButton), {
|
|
151
197
|
class: "bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none",
|
|
152
198
|
size: "sm",
|
|
153
199
|
variant: "outlined",
|
|
154
|
-
onClick: _cache[
|
|
200
|
+
onClick: _cache[2] || (_cache[2] = ($event) => emits("update:value", {
|
|
155
201
|
payload: void 0,
|
|
156
202
|
contentType: selectedContentType.value
|
|
157
203
|
}))
|
|
158
204
|
}, {
|
|
159
|
-
default: withCtx(() => [..._cache[
|
|
205
|
+
default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" Delete ", -1)])]),
|
|
160
206
|
_: 1
|
|
161
207
|
})], 64)) : (openBlock(), createBlock(unref(ScalarButton), {
|
|
162
208
|
key: 1,
|
|
163
209
|
class: "bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none",
|
|
164
210
|
size: "sm",
|
|
165
211
|
variant: "outlined",
|
|
166
|
-
onClick: _cache[
|
|
212
|
+
onClick: _cache[3] || (_cache[3] = () => handleFileUpload((file) => emits("update:value", {
|
|
167
213
|
payload: file,
|
|
168
214
|
contentType: selectedContentType.value
|
|
169
215
|
})))
|
|
170
216
|
}, {
|
|
171
|
-
default: withCtx(() => [_cache[
|
|
217
|
+
default: withCtx(() => [_cache[9] || (_cache[9] = createElementVNode("span", null, "Select File", -1)), createVNode(unref(ScalarIcon), {
|
|
172
218
|
class: "ml-1",
|
|
173
219
|
icon: "Upload",
|
|
174
220
|
size: "xs",
|
|
@@ -181,7 +227,7 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
181
227
|
environment: __props.environment,
|
|
182
228
|
example: example.value,
|
|
183
229
|
selectedContentType: selectedContentType.value,
|
|
184
|
-
"onUpdate:formValue": _cache[
|
|
230
|
+
"onUpdate:formValue": _cache[4] || (_cache[4] = (payload) => emits("update:formValue", {
|
|
185
231
|
payload,
|
|
186
232
|
contentType: selectedContentType.value
|
|
187
233
|
}))
|
|
@@ -190,8 +236,23 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
190
236
|
"environment",
|
|
191
237
|
"example",
|
|
192
238
|
"selectedContentType"
|
|
239
|
+
])) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [bodyView.value === "form" && isFormViewAvailable.value ? (openBlock(), createBlock(RequestBodyStructured_default, {
|
|
240
|
+
key: 0,
|
|
241
|
+
bodySchema: bodySchema.value,
|
|
242
|
+
contentType: selectedContentType.value,
|
|
243
|
+
environment: __props.environment,
|
|
244
|
+
parsedValue: parsedBody.value.value,
|
|
245
|
+
"onUpdate:value": _cache[5] || (_cache[5] = (value) => emits("update:value", {
|
|
246
|
+
payload: value,
|
|
247
|
+
contentType: selectedContentType.value
|
|
248
|
+
}))
|
|
249
|
+
}, null, 8, [
|
|
250
|
+
"bodySchema",
|
|
251
|
+
"contentType",
|
|
252
|
+
"environment",
|
|
253
|
+
"parsedValue"
|
|
193
254
|
])) : (openBlock(), createBlock(unref(CodeInput_default), {
|
|
194
|
-
key:
|
|
255
|
+
key: 1,
|
|
195
256
|
class: "border-t px-3",
|
|
196
257
|
content: "",
|
|
197
258
|
environment: __props.environment,
|
|
@@ -200,7 +261,7 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
200
261
|
lint: "",
|
|
201
262
|
modelValue: bodyValue.value,
|
|
202
263
|
withFakeData: "",
|
|
203
|
-
"onUpdate:modelValue": _cache[
|
|
264
|
+
"onUpdate:modelValue": _cache[6] || (_cache[6] = (value) => emits("update:value", {
|
|
204
265
|
payload: value,
|
|
205
266
|
contentType: selectedContentType.value
|
|
206
267
|
}))
|
|
@@ -208,7 +269,7 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ de
|
|
|
208
269
|
"environment",
|
|
209
270
|
"language",
|
|
210
271
|
"modelValue"
|
|
211
|
-
]))]),
|
|
272
|
+
]))], 64))]),
|
|
212
273
|
_: 1
|
|
213
274
|
})]),
|
|
214
275
|
_: 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <CodeInput\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const raw = example.value?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, raw by default so existing behavior is unchanged */\nconst bodyView = ref<'form' | 'raw'>('raw')\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <CodeInput\n v-else\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments';
|
|
2
|
+
import type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
/** The parsed (object) value of the structured body */
|
|
5
|
+
parsedValue: unknown;
|
|
6
|
+
/** Resolved schema for the body so the table can show enums and validation per field */
|
|
7
|
+
bodySchema?: SchemaObject;
|
|
8
|
+
/** The selected content type, decides how the folded body is serialized (JSON/YAML) */
|
|
9
|
+
contentType: string;
|
|
10
|
+
environment: XScalarEnvironment;
|
|
11
|
+
};
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
13
|
+
"update:value": (payload: string) => any;
|
|
14
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
+
"onUpdate:value"?: ((payload: string) => any) | undefined;
|
|
16
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=RequestBodyStructured.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyStructured.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyStructured.vue"],"names":[],"mappings":"AA8GA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAShG,KAAK,WAAW,GAAG;IACjB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAA;IACpB,wFAAwF;IACxF,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,uFAAuF;IACvF,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,kBAAkB,CAAA;CAChC,CAAC;AAqIF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import RequestBodyStructured_vue_vue_type_script_setup_true_lang_default from "./RequestBodyStructured.vue.script.js";
|
|
2
|
+
//#region src/v2/blocks/request-block/components/RequestBodyStructured.vue
|
|
3
|
+
var RequestBodyStructured_default = RequestBodyStructured_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { RequestBodyStructured_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=RequestBodyStructured.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyStructured.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyStructured.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { foldStructuredBodyRows } from '@/v2/blocks/request-block/helpers/fold-structured-body-rows'\nimport { getStructuredBodyRows } from '@/v2/blocks/request-block/helpers/get-structured-body-rows'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\n\nconst { parsedValue, bodySchema, contentType, environment } = defineProps<{\n /** The parsed (object) value of the structured body */\n parsedValue: unknown\n /** Resolved schema for the body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n /** The selected content type, decides how the folded body is serialized (JSON/YAML) */\n contentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n /** Serialized body text for the current content type */\n (e: 'update:value', payload: string): void\n}>()\n\nconst codec = computed(() => getStructuredBodyCodec(contentType))\n\n/** Local state for the body rows */\nconst localRows = ref<TableRow[]>([])\n\n/**\n * The serialized string we last emitted. Store updates triggered by our own emit come\n * back through the `parsedValue` prop — skip the row rebuild for those so typing does\n * not churn focus and a just-cleared row does not vanish mid-edit.\n */\nlet lastEmitted: string | undefined\n\nwatch(\n () => [parsedValue, bodySchema, contentType] as const,\n ([newValue, schema]) => {\n if (lastEmitted !== undefined && codec.value) {\n try {\n if (codec.value.stringify(newValue) === lastEmitted) {\n return\n }\n } catch {\n // Not serializable — treat as an external change and rebuild.\n }\n }\n lastEmitted = undefined\n localRows.value = getStructuredBodyRows(newValue, schema)\n },\n { immediate: true },\n)\n\nconst emitRows = () => {\n if (!codec.value) {\n return\n }\n const serialized = codec.value.stringify(\n foldStructuredBodyRows(localRows.value, bodySchema),\n )\n lastEmitted = serialized\n emit('update:value', serialized)\n}\n\n/** Update a row in the table, combines with the previous data so we emit the whole body */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localRows.value.length) {\n localRows.value = [\n ...localRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n emitRows()\n return\n }\n\n localRows.value = localRows.value.map((row, i) =>\n i === index ? { ...row, ...payload } : row,\n )\n emitRows()\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localRows.value = localRows.value.filter((_, i) => i !== index)\n emitRows()\n}\n</script>\n\n<template>\n <RequestTable\n :data=\"localRows\"\n :environment=\"environment\"\n label=\"Body\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import RequestTable_default from "./RequestTable.vue.js";
|
|
2
|
+
import { foldStructuredBodyRows } from "../helpers/fold-structured-body-rows.js";
|
|
3
|
+
import { getStructuredBodyRows } from "../helpers/get-structured-body-rows.js";
|
|
4
|
+
import { getStructuredBodyCodec } from "../helpers/structured-body-codec.js";
|
|
5
|
+
import { computed, createBlock, defineComponent, openBlock, ref, watch } from "vue";
|
|
6
|
+
//#region src/v2/blocks/request-block/components/RequestBodyStructured.vue?vue&type=script&setup=true&lang.ts
|
|
7
|
+
var RequestBodyStructured_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
8
|
+
__name: "RequestBodyStructured",
|
|
9
|
+
props: {
|
|
10
|
+
parsedValue: {},
|
|
11
|
+
bodySchema: {},
|
|
12
|
+
contentType: {},
|
|
13
|
+
environment: {}
|
|
14
|
+
},
|
|
15
|
+
emits: ["update:value"],
|
|
16
|
+
setup(__props, { emit: __emit }) {
|
|
17
|
+
const emit = __emit;
|
|
18
|
+
const codec = computed(() => getStructuredBodyCodec(__props.contentType));
|
|
19
|
+
/** Local state for the body rows */
|
|
20
|
+
const localRows = ref([]);
|
|
21
|
+
/**
|
|
22
|
+
* The serialized string we last emitted. Store updates triggered by our own emit come
|
|
23
|
+
* back through the `parsedValue` prop — skip the row rebuild for those so typing does
|
|
24
|
+
* not churn focus and a just-cleared row does not vanish mid-edit.
|
|
25
|
+
*/
|
|
26
|
+
let lastEmitted;
|
|
27
|
+
watch(() => [
|
|
28
|
+
__props.parsedValue,
|
|
29
|
+
__props.bodySchema,
|
|
30
|
+
__props.contentType
|
|
31
|
+
], ([newValue, schema]) => {
|
|
32
|
+
if (lastEmitted !== void 0 && codec.value) try {
|
|
33
|
+
if (codec.value.stringify(newValue) === lastEmitted) return;
|
|
34
|
+
} catch {}
|
|
35
|
+
lastEmitted = void 0;
|
|
36
|
+
localRows.value = getStructuredBodyRows(newValue, schema);
|
|
37
|
+
}, { immediate: true });
|
|
38
|
+
const emitRows = () => {
|
|
39
|
+
if (!codec.value) return;
|
|
40
|
+
const serialized = codec.value.stringify(foldStructuredBodyRows(localRows.value, __props.bodySchema));
|
|
41
|
+
lastEmitted = serialized;
|
|
42
|
+
emit("update:value", serialized);
|
|
43
|
+
};
|
|
44
|
+
/** Update a row in the table, combines with the previous data so we emit the whole body */
|
|
45
|
+
const handleUpsertRow = (index, payload) => {
|
|
46
|
+
if (index >= localRows.value.length) {
|
|
47
|
+
localRows.value = [...localRows.value, {
|
|
48
|
+
name: "",
|
|
49
|
+
value: "",
|
|
50
|
+
...payload,
|
|
51
|
+
isDisabled: false
|
|
52
|
+
}];
|
|
53
|
+
emitRows();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
localRows.value = localRows.value.map((row, i) => i === index ? {
|
|
57
|
+
...row,
|
|
58
|
+
...payload
|
|
59
|
+
} : row);
|
|
60
|
+
emitRows();
|
|
61
|
+
};
|
|
62
|
+
/** Delete a row from the table */
|
|
63
|
+
const handleDeleteRow = (index) => {
|
|
64
|
+
localRows.value = localRows.value.filter((_, i) => i !== index);
|
|
65
|
+
emitRows();
|
|
66
|
+
};
|
|
67
|
+
return (_ctx, _cache) => {
|
|
68
|
+
return openBlock(), createBlock(RequestTable_default, {
|
|
69
|
+
data: localRows.value,
|
|
70
|
+
environment: __props.environment,
|
|
71
|
+
label: "Body",
|
|
72
|
+
onDeleteRow: handleDeleteRow,
|
|
73
|
+
onUpsertRow: handleUpsertRow
|
|
74
|
+
}, null, 8, ["data", "environment"]);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
//#endregion
|
|
79
|
+
export { RequestBodyStructured_vue_vue_type_script_setup_true_lang_default as default };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=RequestBodyStructured.vue.script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyStructured.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyStructured.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { foldStructuredBodyRows } from '@/v2/blocks/request-block/helpers/fold-structured-body-rows'\nimport { getStructuredBodyRows } from '@/v2/blocks/request-block/helpers/get-structured-body-rows'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\n\nconst { parsedValue, bodySchema, contentType, environment } = defineProps<{\n /** The parsed (object) value of the structured body */\n parsedValue: unknown\n /** Resolved schema for the body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n /** The selected content type, decides how the folded body is serialized (JSON/YAML) */\n contentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n /** Serialized body text for the current content type */\n (e: 'update:value', payload: string): void\n}>()\n\nconst codec = computed(() => getStructuredBodyCodec(contentType))\n\n/** Local state for the body rows */\nconst localRows = ref<TableRow[]>([])\n\n/**\n * The serialized string we last emitted. Store updates triggered by our own emit come\n * back through the `parsedValue` prop — skip the row rebuild for those so typing does\n * not churn focus and a just-cleared row does not vanish mid-edit.\n */\nlet lastEmitted: string | undefined\n\nwatch(\n () => [parsedValue, bodySchema, contentType] as const,\n ([newValue, schema]) => {\n if (lastEmitted !== undefined && codec.value) {\n try {\n if (codec.value.stringify(newValue) === lastEmitted) {\n return\n }\n } catch {\n // Not serializable — treat as an external change and rebuild.\n }\n }\n lastEmitted = undefined\n localRows.value = getStructuredBodyRows(newValue, schema)\n },\n { immediate: true },\n)\n\nconst emitRows = () => {\n if (!codec.value) {\n return\n }\n const serialized = codec.value.stringify(\n foldStructuredBodyRows(localRows.value, bodySchema),\n )\n lastEmitted = serialized\n emit('update:value', serialized)\n}\n\n/** Update a row in the table, combines with the previous data so we emit the whole body */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localRows.value.length) {\n localRows.value = [\n ...localRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n emitRows()\n return\n }\n\n localRows.value = localRows.value.map((row, i) =>\n i === index ? { ...row, ...payload } : row,\n )\n emitRows()\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localRows.value = localRows.value.filter((_, i) => i !== index)\n emitRows()\n}\n</script>\n\n<template>\n <RequestTable\n :data=\"localRows\"\n :environment=\"environment\"\n label=\"Body\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAqBA,MAAM,OAAO;EAKb,MAAM,QAAQ,eAAe,uBAAuB,QAAA,YAAY,CAAA;;EAGhE,MAAM,YAAY,IAAgB,EAAE,CAAA;;;;;;EAOpC,IAAI;AAEJ,cACQ;GAAC,QAAA;GAAa,QAAA;GAAY,QAAA;GAAY,GAC3C,CAAC,UAAU,YAAY;AACtB,OAAI,gBAAgB,KAAA,KAAa,MAAM,MACrC,KAAI;AACF,QAAI,MAAM,MAAM,UAAU,SAAS,KAAK,YACtC;WAEI;AAIV,iBAAc,KAAA;AACd,aAAU,QAAQ,sBAAsB,UAAU,OAAM;KAE1D,EAAE,WAAW,MAAM,CACrB;EAEA,MAAM,iBAAiB;AACrB,OAAI,CAAC,MAAM,MACT;GAEF,MAAM,aAAa,MAAM,MAAM,UAC7B,uBAAuB,UAAU,OAAO,QAAA,WAAW,CACrD;AACA,iBAAc;AACd,QAAK,gBAAgB,WAAU;;;EAIjC,MAAM,mBACJ,OACA,YAKG;AAEH,OAAI,SAAS,UAAU,MAAM,QAAQ;AACnC,cAAU,QAAQ,CAChB,GAAG,UAAU,OACb;KAAE,MAAM;KAAI,OAAO;KAAI,GAAG;KAAS,YAAY;KAAO,CACxD;AACA,cAAS;AACT;;AAGF,aAAU,QAAQ,UAAU,MAAM,KAAK,KAAK,MAC1C,MAAM,QAAQ;IAAE,GAAG;IAAK,GAAG;IAAS,GAAG,IACzC;AACA,aAAS;;;EAIX,MAAM,mBAAmB,UAAkB;AACzC,aAAU,QAAQ,UAAU,MAAM,QAAQ,GAAG,MAAM,MAAM,MAAK;AAC9D,aAAS;;;uBAKT,YAKiC,sBAAA;IAJ9B,MAAM,UAAA;IACN,aAAa,QAAA;IACd,OAAM;IACL,aAAW;IACX,aAAW"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
modelValue: 'form' | 'raw';
|
|
3
|
+
/** Disable switching to the form view (e.g. while the raw body is not parseable) */
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
7
|
+
"update:modelValue": (v: "form" | "raw") => any;
|
|
8
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
9
|
+
"onUpdate:modelValue"?: ((v: "form" | "raw") => any) | undefined;
|
|
10
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=RequestBodyViewToggle.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyViewToggle.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyViewToggle.vue"],"names":[],"mappings":"AAAA,KAyCK,WAAW,GAAG;IACjB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAA;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAC;AA2EF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import RequestBodyViewToggle_vue_vue_type_script_setup_true_lang_default from "./RequestBodyViewToggle.vue.script.js";
|
|
2
|
+
//#region src/v2/blocks/request-block/components/RequestBodyViewToggle.vue
|
|
3
|
+
var RequestBodyViewToggle_default = RequestBodyViewToggle_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { RequestBodyViewToggle_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=RequestBodyViewToggle.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyViewToggle.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyViewToggle.vue"],"sourcesContent":["<script lang=\"ts\" setup>\ndefineProps<{\n modelValue: 'form' | 'raw'\n /** Disable switching to the form view (e.g. while the raw body is not parseable) */\n disabled?: boolean\n}>()\ndefineEmits<{\n (e: 'update:modelValue', v: 'form' | 'raw'): void\n}>()\n</script>\n<template>\n <div\n class=\"text-c-3 text-xxs -my-1 mr-2 flex justify-center gap-0.5 rounded p-0.5\">\n <button\n class=\"rounded px-1\"\n :class=\"\n disabled\n ? 'cursor-not-allowed opacity-50'\n : {\n 'bg-b-3 text-c-1 cursor-default': modelValue === 'form',\n 'hover:bg-b-3': modelValue !== 'form',\n }\n \"\n :disabled=\"disabled\"\n :title=\"disabled ? 'Fix the body to switch to the form view' : undefined\"\n type=\"button\"\n @click.stop=\"$emit('update:modelValue', 'form')\">\n Form\n </button>\n <button\n class=\"hover:bg-b-3 rounded px-1\"\n :class=\"{ 'bg-b-3 text-c-1 cursor-default': modelValue === 'raw' }\"\n type=\"button\"\n @click.stop=\"$emit('update:modelValue', 'raw')\">\n Raw\n </button>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, withModifiers } from "vue";
|
|
2
|
+
//#region src/v2/blocks/request-block/components/RequestBodyViewToggle.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
+
var _hoisted_1 = { class: "text-c-3 text-xxs -my-1 mr-2 flex justify-center gap-0.5 rounded p-0.5" };
|
|
4
|
+
var _hoisted_2 = ["disabled", "title"];
|
|
5
|
+
var RequestBodyViewToggle_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
6
|
+
__name: "RequestBodyViewToggle",
|
|
7
|
+
props: {
|
|
8
|
+
modelValue: {},
|
|
9
|
+
disabled: { type: Boolean }
|
|
10
|
+
},
|
|
11
|
+
emits: ["update:modelValue"],
|
|
12
|
+
setup(__props) {
|
|
13
|
+
return (_ctx, _cache) => {
|
|
14
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("button", {
|
|
15
|
+
class: normalizeClass(["rounded px-1", __props.disabled ? "cursor-not-allowed opacity-50" : {
|
|
16
|
+
"bg-b-3 text-c-1 cursor-default": __props.modelValue === "form",
|
|
17
|
+
"hover:bg-b-3": __props.modelValue !== "form"
|
|
18
|
+
}]),
|
|
19
|
+
disabled: __props.disabled,
|
|
20
|
+
title: __props.disabled ? "Fix the body to switch to the form view" : void 0,
|
|
21
|
+
type: "button",
|
|
22
|
+
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => _ctx.$emit("update:modelValue", "form"), ["stop"]))
|
|
23
|
+
}, " Form ", 10, _hoisted_2), createElementVNode("button", {
|
|
24
|
+
class: normalizeClass(["hover:bg-b-3 rounded px-1", { "bg-b-3 text-c-1 cursor-default": __props.modelValue === "raw" }]),
|
|
25
|
+
type: "button",
|
|
26
|
+
onClick: _cache[1] || (_cache[1] = withModifiers(($event) => _ctx.$emit("update:modelValue", "raw"), ["stop"]))
|
|
27
|
+
}, " Raw ", 2)]);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
//#endregion
|
|
32
|
+
export { RequestBodyViewToggle_vue_vue_type_script_setup_true_lang_default as default };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=RequestBodyViewToggle.vue.script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestBodyViewToggle.vue.script.js","names":["$emit"],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyViewToggle.vue"],"sourcesContent":["<script lang=\"ts\" setup>\ndefineProps<{\n modelValue: 'form' | 'raw'\n /** Disable switching to the form view (e.g. while the raw body is not parseable) */\n disabled?: boolean\n}>()\ndefineEmits<{\n (e: 'update:modelValue', v: 'form' | 'raw'): void\n}>()\n</script>\n<template>\n <div\n class=\"text-c-3 text-xxs -my-1 mr-2 flex justify-center gap-0.5 rounded p-0.5\">\n <button\n class=\"rounded px-1\"\n :class=\"\n disabled\n ? 'cursor-not-allowed opacity-50'\n : {\n 'bg-b-3 text-c-1 cursor-default': modelValue === 'form',\n 'hover:bg-b-3': modelValue !== 'form',\n }\n \"\n :disabled=\"disabled\"\n :title=\"disabled ? 'Fix the body to switch to the form view' : undefined\"\n type=\"button\"\n @click.stop=\"$emit('update:modelValue', 'form')\">\n Form\n </button>\n <button\n class=\"hover:bg-b-3 rounded px-1\"\n :class=\"{ 'bg-b-3 text-c-1 cursor-default': modelValue === 'raw' }\"\n type=\"button\"\n @click.stop=\"$emit('update:modelValue', 'raw')\">\n Raw\n </button>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;uBAWE,mBAyBM,OAzBN,YAyBM,CAvBJ,mBAeS,UAAA;IAdP,OAAK,eAAA,CAAC,gBACW,QAAA,WAAA,kCAAA;uCAAmH,QAAA,eAAU;qBAA2C,QAAA,eAAU;;IAQlM,UAAU,QAAA;IACV,OAAO,QAAA,WAAQ,4CAA+C,KAAA;IAC/D,MAAK;IACJ,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAOA,KAAAA,MAAK,qBAAA,OAAA,EAAA,CAAA,OAAA,CAAA;MAA+B,UAEnD,IAAA,WAAA,EACA,mBAMS,UAAA;IALP,OAAK,eAAA,CAAC,6BAA2B,EAAA,kCACW,QAAA,eAAU,OAAA,CAAA,CAAA;IACtD,MAAK;IACJ,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAOA,KAAAA,MAAK,qBAAA,MAAA,EAAA,CAAA,OAAA,CAAA;MAA8B,SAElD,EAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestTableRow.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RequestTableRow.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"names":[],"mappings":"AA6UA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAGxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACb,MAAM,8DAA8D,CAAA;AAcrE,MAAM,MAAM,QAAQ,GAAG;IACrB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAA;IAC/C,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wEAAwE;IACxE,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,eAAe,CAAA;IACnC,2FAA2F;IAC3F,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAA;CACpC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,QAAQ,CAAA;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,kBAAkB,CAAA;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAC;AAumBF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestTableRow.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarIconGlobe, ScalarIconTrash } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ParameterObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\nimport { CodeInputLite } from '@/v2/components/code-input'\nimport {\n DataTableCell,\n DataTableCheckbox,\n DataTableRow,\n} from '@/v2/components/data-table'\n\nimport RequestTableTooltip from './RequestTableTooltip.vue'\n\nexport type TableRow = {\n /** The parameter or field name/key */\n name: string\n /** The parameter value, can be a string, file, or null */\n value: string | File | null\n /** Optional description for the parameter */\n description?: string\n /** Optional route for global parameters (e.g., cookies shared across workspace) */\n globalRoute?: ApiReferenceEvents['ui:navigate']\n /** Whether the parameter is disabled/inactive */\n isDisabled?: boolean\n /** OpenAPI schema object with type, validation rules, examples, etc. */\n schema?: SchemaObject\n /** Whether the parameter is required */\n isRequired?: boolean\n /**\n * Whether the parameter is readonly and can not be modifies directly\n * User can still override the parameter which is going to show up with the linethrough style\n */\n isReadonly?: boolean\n /** Whether the parameter is overridden later on */\n isOverridden?: boolean\n /** Track the original parameter so we can update it */\n originalParameter?: ParameterObject\n /** Path to a value inside the original parameter example for expanded object parameters */\n sourceParameterValuePath?: string[]\n}\n\nexport type TableRowUpsertPayload = {\n name: string\n value: string | File\n isDisabled: boolean\n shouldRenameExpandedRow?: boolean\n}\n\nconst {\n data,\n environment,\n hasCheckboxDisabled,\n invalidParams,\n showUploadButton,\n} = defineProps<{\n data: TableRow\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n environment: XScalarEnvironment\n showUploadButton?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'upsertRow', payload: TableRowUpsertPayload): void\n (e: 'deleteRow'): void\n (e: 'uploadFile'): void\n (e: 'removeFile'): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n}>()\n\n/**\n * Track local state for the row\n *\n * Now this is required because of the way we get default values from the schema.\n * If we have a default value in data.value, then we update isDisabled to true. We lose the default value since now\n * we have an example where value: ''. This is why we need to track the local state and update all of the params at the\n * same time.\n */\nconst name = ref<string>(data.name ?? '')\nconst value = ref<string | File>(unpackProxyObject(data.value) ?? '')\nconst isDisabled = ref<boolean>(data.isDisabled ?? false)\n\n// Keep the above state synced with the data prop\nwatch(\n () => data.name,\n (newName) => (name.value = newName ?? ''),\n)\nwatch(\n () => data.value,\n (newValue) => (value.value = unpackProxyObject(newValue) ?? ''),\n)\nwatch(\n () => data.isDisabled,\n (newIsDisabled) => (isDisabled.value = newIsDisabled ?? false),\n)\n\n/** Check if the value is a File instance */\nconst isFile = computed(() => value.value instanceof File)\n\n/** Display value handles File instances and shows filename instead */\nconst displayValue = computed(\n () =>\n (isFile.value\n ? getFileName(value.value as File)\n : (value.value as string)) ?? '',\n)\n\nconst defaultValue = computed(() => data.schema?.default as string)\n\n/** See if we can extract enum values from the schema */\nconst enumValue = computed<string[]>(() => {\n if (!data.schema) {\n return []\n }\n\n // Grab the enum from the schema\n if (data.schema.enum) {\n return data.schema.enum.map((item) => String(item))\n }\n\n // Grab the enum from the items schema\n if ('items' in data.schema) {\n const resolved = resolve.schema(data.schema.items)\n if (resolved?.enum) {\n return resolved.enum.map((item) => String(item))\n }\n }\n\n return []\n})\n\nconst typeValue = computed(() =>\n data.schema && 'type' in data.schema ? data.schema.type : undefined,\n)\n\nconst validationResult = computed(() =>\n validateParameter(data.schema, value.value),\n)\n\n/** Handle row updates while preserving existing properties */\nconst handleUpdateRow = (\n payload: Partial<{ name: string; value: string; isDisabled: boolean }>,\n options: { shouldRenameExpandedRow?: boolean } = {},\n): void => {\n // Update our local state\n if (payload.name !== undefined) {\n name.value = payload.name\n }\n if (payload.value !== undefined) {\n value.value = payload.value\n }\n\n // Is disabled should always be false unless you explicitly set it to true\n isDisabled.value = payload.isDisabled ?? false\n\n if (\n payload.name !== undefined &&\n data.sourceParameterValuePath &&\n !options.shouldRenameExpandedRow\n ) {\n return\n }\n\n // Emit all of the local state\n emit('upsertRow', {\n name: name.value,\n value: value.value,\n isDisabled: isDisabled.value,\n ...(options.shouldRenameExpandedRow\n ? { shouldRenameExpandedRow: true }\n : {}),\n })\n}\n\n/**\n * Commit a key edit when the input loses focus. Expanded-object rows defer their rename to blur (see\n * handleUpdateRow), so we only emit when the key actually changed — focusing and blurring the field\n * without typing should not re-emit the row or silently reset its disabled state. The current\n * disabled state is passed through so a renamed row keeps it.\n */\nconst handleKeyBlur = (newName: string): void => {\n if (newName === data.name) {\n return\n }\n\n handleUpdateRow(\n { name: newName, isDisabled: isDisabled.value },\n { shouldRenameExpandedRow: Boolean(data.sourceParameterValuePath) },\n )\n}\n</script>\n\n<template>\n <DataTableRow\n :id=\"data.name\"\n :class=\"{\n alert: validationResult.ok === false,\n error: validationResult.ok === false && invalidParams?.has(data.name),\n }\">\n <DataTableCheckbox\n class=\"!border-r\"\n :disabled=\"hasCheckboxDisabled ?? false\"\n :modelValue=\"!isDisabled\"\n @update:modelValue=\"(v) => handleUpdateRow({ isDisabled: !v })\" />\n\n <!-- Name -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"`${label} Key`\"\n :disabled=\"data.isReadonly\"\n :environment=\"environment\"\n :modelValue=\"name\"\n placeholder=\"Key\"\n :required=\"Boolean(data.isRequired)\"\n @blur=\"(v) => handleKeyBlur(v)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ name: v })\" />\n </DataTableCell>\n\n <!-- Value -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"`${label} Value`\"\n class=\"pr-6 group-hover:pr-10 group-has-[.code-input-lite__editor:focus]:pr-10\"\n :default=\"defaultValue\"\n :disabled=\"data.isReadonly\"\n :enum=\"enumValue\"\n :environment=\"environment\"\n :examples=\"\n data.schema?.examples?.map((example) => String(example)) ?? []\n \"\n :linethrough=\"data.isOverridden\"\n :modelValue=\"displayValue\"\n placeholder=\"Value\"\n :type=\"typeValue\"\n withFakeData\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ value: v })\">\n <template #icon>\n <ScalarButton\n v-if=\"\n Boolean(data.name || value) &&\n !data.isRequired &&\n data.isReadonly !== true\n \"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 hidden h-fit rounded p-1 group-hover:flex group-has-[.code-input-lite__editor:focus]:flex\"\n size=\"sm\"\n variant=\"ghost\"\n @click=\"emit('deleteRow')\">\n <ScalarIconTrash class=\"size-3.5\" />\n </ScalarButton>\n\n <ScalarIconButton\n v-if=\"data.globalRoute !== undefined\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 h-fit\"\n :icon=\"ScalarIconGlobe\"\n label=\"Global cookies are shared across the whole workspace. Click to navigate.\"\n size=\"xs\"\n tooltip=\"top\"\n variant=\"ghost\"\n @click=\"emit('navigate', data.globalRoute)\" />\n\n <RequestTableTooltip\n v-if=\"data.isReadonly\"\n description=\"This is a readonly property and you can not modify it! If you want to change it you have to override it or disable it using the checkbox\"\n :value=\"null\" />\n <RequestTableTooltip\n v-else-if=\"data.schema\"\n :description=\"data.description\"\n :schema=\"data.schema\"\n :value />\n </template>\n </CodeInputLite>\n </DataTableCell>\n\n <!-- File upload -->\n <DataTableCell\n v-if=\"showUploadButton\"\n class=\"group/upload flex items-center justify-center whitespace-nowrap\">\n <template v-if=\"isFile\">\n <div\n class=\"text-c-2 filemask flex w-full max-w-[100%] items-center justify-center overflow-hidden p-1\">\n <span>{{ displayValue }}</span>\n </div>\n <button\n class=\"bg-b-2 mt-1 block rounded p-0.5 text-center text-xs font-medium md:pointer-events-none md:absolute md:inset-x-1 md:top-1/2 md:mt-0 md:-translate-y-1/2 md:opacity-0 md:group-hover/upload:pointer-events-auto md:group-hover/upload:opacity-100\"\n type=\"button\"\n @click=\"emit('removeFile')\">\n Delete\n </button>\n </template>\n <template v-else>\n <div class=\"p-0.5\">\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 h-fit border-0 py-px shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"emit('uploadFile')\">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </div>\n </template>\n </DataTableCell>\n </DataTableRow>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RequestTableRow.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarIconGlobe, ScalarIconTrash } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ParameterObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\nimport { CodeInputLite } from '@/v2/components/code-input'\nimport {\n DataTableCell,\n DataTableCheckbox,\n DataTableRow,\n} from '@/v2/components/data-table'\n\nimport RequestTableTooltip from './RequestTableTooltip.vue'\n\nexport type TableRow = {\n /** The parameter or field name/key */\n name: string\n /** The parameter value, can be a string, file, or null */\n value: string | File | null\n /** Optional description for the parameter */\n description?: string\n /** Optional route for global parameters (e.g., cookies shared across workspace) */\n globalRoute?: ApiReferenceEvents['ui:navigate']\n /** Whether the parameter is disabled/inactive */\n isDisabled?: boolean\n /** OpenAPI schema object with type, validation rules, examples, etc. */\n schema?: SchemaObject\n /** Whether the parameter is required */\n isRequired?: boolean\n /**\n * Whether the parameter is readonly and can not be modifies directly\n * User can still override the parameter which is going to show up with the linethrough style\n */\n isReadonly?: boolean\n /** Whether the parameter is overridden later on */\n isOverridden?: boolean\n /** Track the original parameter so we can update it */\n originalParameter?: ParameterObject\n /** Path to a value inside the original parameter example for expanded object parameters */\n sourceParameterValuePath?: string[]\n}\n\nexport type TableRowUpsertPayload = {\n name: string\n value: string | File\n isDisabled: boolean\n shouldRenameExpandedRow?: boolean\n}\n\nconst {\n data,\n environment,\n hasCheckboxDisabled,\n invalidParams,\n showUploadButton,\n} = defineProps<{\n data: TableRow\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n environment: XScalarEnvironment\n showUploadButton?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'upsertRow', payload: TableRowUpsertPayload): void\n (e: 'deleteRow'): void\n (e: 'uploadFile'): void\n (e: 'removeFile'): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n}>()\n\n/**\n * Track local state for the row\n *\n * Now this is required because of the way we get default values from the schema.\n * If we have a default value in data.value, then we update isDisabled to true. We lose the default value since now\n * we have an example where value: ''. This is why we need to track the local state and update all of the params at the\n * same time.\n */\nconst name = ref<string>(data.name ?? '')\nconst value = ref<string | File>(unpackProxyObject(data.value) ?? '')\nconst isDisabled = ref<boolean>(data.isDisabled ?? false)\n\n// Keep the above state synced with the data prop\nwatch(\n () => data.name,\n (newName) => (name.value = newName ?? ''),\n)\nwatch(\n () => data.value,\n (newValue) => (value.value = unpackProxyObject(newValue) ?? ''),\n)\nwatch(\n () => data.isDisabled,\n (newIsDisabled) => (isDisabled.value = newIsDisabled ?? false),\n)\n\n/** Check if the value is a File instance */\nconst isFile = computed(() => value.value instanceof File)\n\n/** Display value handles File instances and shows filename instead */\nconst displayValue = computed(\n () =>\n (isFile.value\n ? getFileName(value.value as File)\n : (value.value as string)) ?? '',\n)\n\nconst defaultValue = computed(() => data.schema?.default as string)\n\n/** See if we can extract enum values from the schema */\nconst enumValue = computed<string[]>(() => {\n if (!data.schema) {\n return []\n }\n\n // Grab the enum from the schema\n if (data.schema.enum) {\n return data.schema.enum.map((item) => String(item))\n }\n\n // Grab the enum from the items schema\n if ('items' in data.schema) {\n const resolved = resolve.schema(data.schema.items)\n if (resolved?.enum) {\n return resolved.enum.map((item) => String(item))\n }\n }\n\n return []\n})\n\nconst typeValue = computed(() =>\n data.schema && 'type' in data.schema ? data.schema.type : undefined,\n)\n\nconst validationResult = computed(() =>\n validateParameter(data.schema, value.value),\n)\n\n/** Handle row updates while preserving existing properties */\nconst handleUpdateRow = (\n payload: Partial<{ name: string; value: string; isDisabled: boolean }>,\n options: { shouldRenameExpandedRow?: boolean } = {},\n): void => {\n // Update our local state\n if (payload.name !== undefined) {\n name.value = payload.name\n }\n if (payload.value !== undefined) {\n value.value = payload.value\n }\n\n // Is disabled should always be false unless you explicitly set it to true\n isDisabled.value = payload.isDisabled ?? false\n\n if (\n payload.name !== undefined &&\n data.sourceParameterValuePath &&\n !options.shouldRenameExpandedRow\n ) {\n return\n }\n\n // Emit all of the local state\n emit('upsertRow', {\n name: name.value,\n value: value.value,\n isDisabled: isDisabled.value,\n ...(options.shouldRenameExpandedRow\n ? { shouldRenameExpandedRow: true }\n : {}),\n })\n}\n\n/**\n * Commit a key edit when the input loses focus. Expanded-object rows defer their rename to blur (see\n * handleUpdateRow), so we only emit when the key actually changed — focusing and blurring the field\n * without typing should not re-emit the row or silently reset its disabled state. The current\n * disabled state is passed through so a renamed row keeps it.\n */\nconst handleKeyBlur = (newName: string): void => {\n if (newName === data.name) {\n return\n }\n\n handleUpdateRow(\n { name: newName, isDisabled: isDisabled.value },\n { shouldRenameExpandedRow: Boolean(data.sourceParameterValuePath) },\n )\n}\n</script>\n\n<template>\n <DataTableRow\n :id=\"data.name\"\n :class=\"{\n alert: validationResult.ok === false,\n error: validationResult.ok === false && invalidParams?.has(data.name),\n }\">\n <DataTableCheckbox\n :ariaLabel=\"`Include ${data.name || 'row'} in request`\"\n class=\"!border-r\"\n :disabled=\"hasCheckboxDisabled ?? false\"\n :modelValue=\"!isDisabled\"\n @update:modelValue=\"(v) => handleUpdateRow({ isDisabled: !v })\" />\n\n <!-- Name -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"`${label} Key`\"\n :disabled=\"data.isReadonly\"\n :environment=\"environment\"\n :modelValue=\"name\"\n placeholder=\"Key\"\n :required=\"Boolean(data.isRequired)\"\n @blur=\"(v) => handleKeyBlur(v)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ name: v })\" />\n </DataTableCell>\n\n <!-- Value -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"`${label} Value`\"\n class=\"pr-6 group-hover:pr-10 group-has-[.code-input-lite__editor:focus]:pr-10\"\n :default=\"defaultValue\"\n :disabled=\"data.isReadonly\"\n :enum=\"enumValue\"\n :environment=\"environment\"\n :examples=\"\n data.schema?.examples?.map((example) => String(example)) ?? []\n \"\n :linethrough=\"data.isOverridden\"\n :modelValue=\"displayValue\"\n placeholder=\"Value\"\n :type=\"typeValue\"\n withFakeData\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ value: v })\">\n <template #icon>\n <ScalarButton\n v-if=\"\n Boolean(data.name || value) &&\n !data.isRequired &&\n data.isReadonly !== true\n \"\n :aria-label=\"`Delete ${data.name || 'row'}`\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 hidden h-fit rounded p-1 group-hover:flex group-has-[.code-input-lite__editor:focus]:flex\"\n size=\"sm\"\n variant=\"ghost\"\n @click=\"emit('deleteRow')\">\n <ScalarIconTrash class=\"size-3.5\" />\n </ScalarButton>\n\n <ScalarIconButton\n v-if=\"data.globalRoute !== undefined\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 h-fit\"\n :icon=\"ScalarIconGlobe\"\n label=\"Global cookies are shared across the whole workspace. Click to navigate.\"\n size=\"xs\"\n tooltip=\"top\"\n variant=\"ghost\"\n @click=\"emit('navigate', data.globalRoute)\" />\n\n <RequestTableTooltip\n v-if=\"data.isReadonly\"\n description=\"This is a readonly property and you can not modify it! If you want to change it you have to override it or disable it using the checkbox\"\n :value=\"null\" />\n <RequestTableTooltip\n v-else-if=\"data.schema\"\n :description=\"data.description\"\n :schema=\"data.schema\"\n :value />\n </template>\n </CodeInputLite>\n </DataTableCell>\n\n <!-- File upload -->\n <DataTableCell\n v-if=\"showUploadButton\"\n class=\"group/upload flex items-center justify-center whitespace-nowrap\">\n <template v-if=\"isFile\">\n <div\n class=\"text-c-2 filemask flex w-full max-w-[100%] items-center justify-center overflow-hidden p-1\">\n <span>{{ displayValue }}</span>\n </div>\n <button\n class=\"bg-b-2 mt-1 block rounded p-0.5 text-center text-xs font-medium md:pointer-events-none md:absolute md:inset-x-1 md:top-1/2 md:mt-0 md:-translate-y-1/2 md:opacity-0 md:group-hover/upload:pointer-events-auto md:group-hover/upload:opacity-100\"\n type=\"button\"\n @click=\"emit('removeFile')\">\n Delete\n </button>\n </template>\n <template v-else>\n <div class=\"p-0.5\">\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 h-fit border-0 py-px shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"emit('uploadFile')\">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </div>\n </template>\n </DataTableCell>\n </DataTableRow>\n</template>\n"],"mappings":""}
|
|
@@ -104,11 +104,16 @@ var RequestTableRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ *
|
|
|
104
104
|
}, {
|
|
105
105
|
default: withCtx(() => [
|
|
106
106
|
createVNode(unref(DataTableCheckbox_default), {
|
|
107
|
+
ariaLabel: `Include ${__props.data.name || "row"} in request`,
|
|
107
108
|
class: "!border-r",
|
|
108
109
|
disabled: __props.hasCheckboxDisabled ?? false,
|
|
109
110
|
modelValue: !isDisabled.value,
|
|
110
111
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => handleUpdateRow({ isDisabled: !v }))
|
|
111
|
-
}, null, 8, [
|
|
112
|
+
}, null, 8, [
|
|
113
|
+
"ariaLabel",
|
|
114
|
+
"disabled",
|
|
115
|
+
"modelValue"
|
|
116
|
+
]),
|
|
112
117
|
createVNode(unref(DataTableCell_default), null, {
|
|
113
118
|
default: withCtx(() => [createVNode(unref(CodeInputLite_default), {
|
|
114
119
|
"aria-label": `${__props.label} Key`,
|
|
@@ -149,6 +154,7 @@ var RequestTableRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ *
|
|
|
149
154
|
icon: withCtx(() => [
|
|
150
155
|
Boolean(__props.data.name || value.value) && !__props.data.isRequired && __props.data.isReadonly !== true ? (openBlock(), createBlock(unref(ScalarButton), {
|
|
151
156
|
key: 0,
|
|
157
|
+
"aria-label": `Delete ${__props.data.name || "row"}`,
|
|
152
158
|
class: "text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 hidden h-fit rounded p-1 group-hover:flex group-has-[.code-input-lite__editor:focus]:flex",
|
|
153
159
|
size: "sm",
|
|
154
160
|
variant: "ghost",
|
|
@@ -156,7 +162,7 @@ var RequestTableRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ *
|
|
|
156
162
|
}, {
|
|
157
163
|
default: withCtx(() => [createVNode(unref(ScalarIconTrash), { class: "size-3.5" })]),
|
|
158
164
|
_: 1
|
|
159
|
-
})) : createCommentVNode("", true),
|
|
165
|
+
}, 8, ["aria-label"])) : createCommentVNode("", true),
|
|
160
166
|
__props.data.globalRoute !== void 0 ? (openBlock(), createBlock(unref(ScalarIconButton), {
|
|
161
167
|
key: 1,
|
|
162
168
|
class: "text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 h-fit",
|