@scalar/components 0.30.3 → 0.30.5
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/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.d.ts +8 -0
- package/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.js.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.script.js +13 -10
- package/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.script.js.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.d.ts.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.js.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.script.js +5 -4
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.script.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOptions.vue.d.ts.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOptions.vue.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOptions.vue.script.js +2 -0
- package/dist/components/ScalarCombobox/ScalarComboboxOptions.vue.script.js.map +1 -1
- package/dist/components/ScalarMarkdown/ScalarMarkdownSummary.vue.js.map +1 -1
- package/dist/components/ScalarMarkdown/ScalarMarkdownSummary.vue.script.js.map +1 -1
- package/dist/components/ScalarSidebar/ScalarSidebarFooter.vue.d.ts.map +1 -1
- package/dist/components/ScalarSidebar/ScalarSidebarFooter.vue.js.map +1 -1
- package/dist/components/ScalarSidebar/ScalarSidebarFooter.vue.script.js +8 -3
- package/dist/components/ScalarSidebar/ScalarSidebarFooter.vue.script.js.map +1 -1
- package/dist/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue.d.ts +4 -0
- package/dist/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue.js.map +1 -1
- package/dist/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue.script.js +10 -3
- package/dist/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue.script.js.map +1 -1
- package/dist/style.css +19 -5
- package/dist/vue-styles.css +1 -1
- package/package.json +4 -4
|
@@ -11,6 +11,8 @@ import type { StandardLanguageKey } from './types';
|
|
|
11
11
|
declare const _default: typeof __VLS_export;
|
|
12
12
|
export default _default;
|
|
13
13
|
declare const __VLS_export: import("vue").DefineComponent<({
|
|
14
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
15
|
+
label?: string;
|
|
14
16
|
content?: string | object;
|
|
15
17
|
prettyPrintedContent?: string;
|
|
16
18
|
lang?: StandardLanguageKey | string;
|
|
@@ -21,6 +23,8 @@ declare const __VLS_export: import("vue").DefineComponent<({
|
|
|
21
23
|
/** Raw unformatted object or string content */
|
|
22
24
|
content: string | object;
|
|
23
25
|
}) | ({
|
|
26
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
27
|
+
label?: string;
|
|
24
28
|
content?: string | object;
|
|
25
29
|
prettyPrintedContent?: string;
|
|
26
30
|
lang?: StandardLanguageKey | string;
|
|
@@ -35,6 +39,8 @@ declare const __VLS_export: import("vue").DefineComponent<({
|
|
|
35
39
|
*/
|
|
36
40
|
prettyPrintedContent: string;
|
|
37
41
|
}), {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<({
|
|
42
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
43
|
+
label?: string;
|
|
38
44
|
content?: string | object;
|
|
39
45
|
prettyPrintedContent?: string;
|
|
40
46
|
lang?: StandardLanguageKey | string;
|
|
@@ -45,6 +51,8 @@ declare const __VLS_export: import("vue").DefineComponent<({
|
|
|
45
51
|
/** Raw unformatted object or string content */
|
|
46
52
|
content: string | object;
|
|
47
53
|
}) | ({
|
|
54
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
55
|
+
label?: string;
|
|
48
56
|
content?: string | object;
|
|
49
57
|
prettyPrintedContent?: string;
|
|
50
58
|
lang?: StandardLanguageKey | string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlock.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlock.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"names":[],"mappings":"AAwLA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAGlD;;;;;;;;GAQG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,8DAA8D;YACtD,MAAM;cACJ,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;;IAWzB,+CAA+C;aACtC,MAAM,GAAG,MAAM;;IAnBhC,8DAA8D;YACtD,MAAM;cACJ,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;;IAezB;;;;OAIG;0BACmB,MAAM;;IA3BpC,8DAA8D;YACtD,MAAM;cACJ,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;;IAWzB,+CAA+C;aACtC,MAAM,GAAG,MAAM;;IAnBhC,8DAA8D;YACtD,MAAM;cACJ,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;;IAezB;;;;OAIG;0BACmB,MAAM;kGA4LlC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlock.vue.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n}\n\n/**\n * Uses highlight.js for syntax highlighting\n *\n * Requires at least one of content or prettyPrintedContent\n */\nconst {\n lang = 'plaintext',\n lineNumbers = false,\n copy = 'hover',\n content,\n prettyPrintedContent,\n hideCredentials,\n} = defineProps<\n BaseProps &\n (\n | {\n /** Raw unformatted object or string content */\n content: string | object\n }\n | {\n /**\n * Pre-pretty printed content string for better performance\n *\n * Avoids unnecessary costly re-serialization of large content\n */\n prettyPrintedContent: string\n }\n )\n>()\n\n/** Base id for the code block */\nconst id = useId()\n\n/** Formatted the content into an indented json string */\nconst prettyContent = computed(\n () => prettyPrintedContent || prettyPrintJson(content ?? ''),\n)\n\nconst highlightedCode = computed(() => {\n const html = syntaxHighlight(prettyContent.value, {\n lang: lang.trim(),\n languages: standardLanguages,\n lineNumbers: lineNumbers,\n maskCredentials: hideCredentials,\n })\n\n // Need to remove the wrapping <pre> element so we can use v-html without another wrapper\n return html.slice(5, -6)\n})\n\n/** Determine if the content is a single line */\nconst isOneLine = computed(() => !prettyContent.value.includes('\\n'))\n\nconst isContentValid = computed(() => {\n return (\n prettyContent.value !== null &&\n prettyContent.value !== 'null' &&\n prettyContent.value !== '404 Not Found'\n )\n})\n\n/** Whether the copy button is rendered on top of the code */\nconst showCopy = computed(() => copy && isContentValid.value)\n\n/**\n * A one-liner has the copy button floating over the end of the line, so the line needs room to\n * scroll clear of the button. The padding goes on the `w-fit` <pre> because browsers disagree about\n * whether trailing padding on an `overflow-x` container is scrollable.\n */\nconst reserveCopySpace = computed(() => isOneLine.value && showCopy.value)\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0 focus-visible:outline',\n )\n \">\n <!-- Inherits the corners so the inset focus ring follows a rounded code block -->\n <div\n
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlock.vue.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n /** Accessible name of the keyboard-scrollable code region. */\n label?: string\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n}\n\n/**\n * Uses highlight.js for syntax highlighting\n *\n * Requires at least one of content or prettyPrintedContent\n */\nconst {\n label = 'Code sample',\n lang = 'plaintext',\n lineNumbers = false,\n copy = 'hover',\n content,\n prettyPrintedContent,\n hideCredentials,\n} = defineProps<\n BaseProps &\n (\n | {\n /** Raw unformatted object or string content */\n content: string | object\n }\n | {\n /**\n * Pre-pretty printed content string for better performance\n *\n * Avoids unnecessary costly re-serialization of large content\n */\n prettyPrintedContent: string\n }\n )\n>()\n\n/** Base id for the code block */\nconst id = useId()\n\n/** Formatted the content into an indented json string */\nconst prettyContent = computed(\n () => prettyPrintedContent || prettyPrintJson(content ?? ''),\n)\n\nconst highlightedCode = computed(() => {\n const html = syntaxHighlight(prettyContent.value, {\n lang: lang.trim(),\n languages: standardLanguages,\n lineNumbers: lineNumbers,\n maskCredentials: hideCredentials,\n })\n\n // Need to remove the wrapping <pre> element so we can use v-html without another wrapper\n return html.slice(5, -6)\n})\n\n/** Determine if the content is a single line */\nconst isOneLine = computed(() => !prettyContent.value.includes('\\n'))\n\nconst isContentValid = computed(() => {\n return (\n prettyContent.value !== null &&\n prettyContent.value !== 'null' &&\n prettyContent.value !== '404 Not Found'\n )\n})\n\n/** Whether the copy button is rendered on top of the code */\nconst showCopy = computed(() => copy && isContentValid.value)\n\n/**\n * A one-liner has the copy button floating over the end of the line, so the line needs room to\n * scroll clear of the button. The padding goes on the `w-fit` <pre> because browsers disagree about\n * whether trailing padding on an `overflow-x` container is scrollable.\n */\nconst reserveCopySpace = computed(() => isOneLine.value && showCopy.value)\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0 focus-visible:outline',\n )\n \">\n <!-- Inherits the corners so the inset focus ring follows a rounded code block -->\n <div\n :aria-label=\"label\"\n class=\"custom-scroll overflow-x-auto p-2 -outline-offset-2 rounded-[inherit] min-h-0 min-w-0 flex-1\"\n role=\"region\"\n tabindex=\"0\">\n <pre\n :id=\"id\"\n class=\"m-0 bg-transparent text-nowrap whitespace-pre w-fit\"\n :class=\"{ 'pr-6': reserveCopySpace }\"\n v-html=\"highlightedCode\" />\n </div>\n <ScalarCodeBlockCopy\n v-if=\"showCopy\"\n :aria-controls=\"id\"\n class=\"scalar-code-copy absolute\"\n :class=\"[\n isOneLine\n ? 'top-1/2 -translate-y-1/2 m-0 right-1'\n : 'top-2.5 right-2.5',\n { 'opacity-100': copy === 'always' },\n ]\"\n :content=\"prettyContent\"\n :lang=\"lang\"\n :showLang=\"!isOneLine\">\n <template #backdrop>\n <ScalarCopyBackdrop\n class=\"scalar-code-copy-backdrop\"\n :class=\"[\n isOneLine\n ? '-inset-y-0.75 -right-1 group-hocus-within/code-block:-left-0.5 left-3'\n : '-right-1.5 -top-1',\n ]\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n</template>\n<style>\n@reference '../../style.css';\n@import '@scalar/code-highlight/css/code.css';\n\n/** Disable font ligatures so code renders with literal characters */\n.scalar-code-block pre {\n font-variant-ligatures: none;\n}\n\n/** Make the copy button label backdrop match the background */\n.scalar-code-block.bg-b-1 .scalar-code-copy-backdrop {\n @apply bg-b-1;\n}\n.scalar-code-block.bg-b-2 .scalar-code-copy-backdrop {\n @apply bg-b-2;\n}\n/** Make the copy button one shade darker than the background */\n.scalar-code-block.bg-b-2 .scalar-code-copy {\n @apply bg-b-3;\n}\n</style>\n"],"mappings":""}
|
|
@@ -5,15 +5,13 @@ import { computed, createBlock, createCommentVNode, createElementBlock, createEl
|
|
|
5
5
|
import { standardLanguages, syntaxHighlight } from "@scalar/code-highlight";
|
|
6
6
|
import { prettyPrintJson } from "@scalar/helpers/json/pretty-print-json";
|
|
7
7
|
//#region src/components/ScalarCodeBlock/ScalarCodeBlock.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
-
var _hoisted_1 =
|
|
9
|
-
tabindex: "0",
|
|
10
|
-
class: "custom-scroll overflow-x-auto p-2 -outline-offset-2 rounded-[inherit] min-h-0 min-w-0 flex-1"
|
|
11
|
-
};
|
|
8
|
+
var _hoisted_1 = ["aria-label"];
|
|
12
9
|
var _hoisted_2 = ["id", "innerHTML"];
|
|
13
10
|
var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
14
11
|
inheritAttrs: false,
|
|
15
12
|
__name: "ScalarCodeBlock",
|
|
16
13
|
props: {
|
|
14
|
+
label: { default: "Code sample" },
|
|
17
15
|
content: {},
|
|
18
16
|
prettyPrintedContent: {},
|
|
19
17
|
lang: { default: "plaintext" },
|
|
@@ -55,26 +53,31 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
55
53
|
const reserveCopySpace = computed(() => isOneLine.value && showCopy.value);
|
|
56
54
|
const { cx } = useBindCx();
|
|
57
55
|
return (_ctx, _cache) => {
|
|
58
|
-
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("scalar-code-block group/code-block flex flex-col", "relative bg-b-1 min-h-0 min-w-0 focus-visible:outline"))), [createElementVNode("div",
|
|
56
|
+
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("scalar-code-block group/code-block flex flex-col", "relative bg-b-1 min-h-0 min-w-0 focus-visible:outline"))), [createElementVNode("div", {
|
|
57
|
+
"aria-label": __props.label,
|
|
58
|
+
class: "custom-scroll overflow-x-auto p-2 -outline-offset-2 rounded-[inherit] min-h-0 min-w-0 flex-1",
|
|
59
|
+
role: "region",
|
|
60
|
+
tabindex: "0"
|
|
61
|
+
}, [createElementVNode("pre", {
|
|
59
62
|
id: unref(id),
|
|
60
63
|
class: normalizeClass(["m-0 bg-transparent text-nowrap whitespace-pre w-fit", { "pr-6": reserveCopySpace.value }]),
|
|
61
64
|
innerHTML: highlightedCode.value
|
|
62
|
-
}, null, 10, _hoisted_2)]), showCopy.value ? (openBlock(), createBlock(unref(ScalarCodeBlockCopy_default), {
|
|
65
|
+
}, null, 10, _hoisted_2)], 8, _hoisted_1), showCopy.value ? (openBlock(), createBlock(unref(ScalarCodeBlockCopy_default), {
|
|
63
66
|
key: 0,
|
|
67
|
+
"aria-controls": unref(id),
|
|
64
68
|
class: normalizeClass(["scalar-code-copy absolute", [isOneLine.value ? "top-1/2 -translate-y-1/2 m-0 right-1" : "top-2.5 right-2.5", { "opacity-100": __props.copy === "always" }]]),
|
|
65
69
|
content: prettyContent.value,
|
|
66
|
-
showLang: !isOneLine.value,
|
|
67
70
|
lang: __props.lang,
|
|
68
|
-
|
|
71
|
+
showLang: !isOneLine.value
|
|
69
72
|
}, {
|
|
70
73
|
backdrop: withCtx(() => [createVNode(ScalarCopyBackdrop_default, { class: normalizeClass(["scalar-code-copy-backdrop", [isOneLine.value ? "-inset-y-0.75 -right-1 group-hocus-within/code-block:-left-0.5 left-3" : "-right-1.5 -top-1"]]) }, null, 8, ["class"])]),
|
|
71
74
|
_: 1
|
|
72
75
|
}, 8, [
|
|
76
|
+
"aria-controls",
|
|
73
77
|
"class",
|
|
74
78
|
"content",
|
|
75
|
-
"showLang",
|
|
76
79
|
"lang",
|
|
77
|
-
"
|
|
80
|
+
"showLang"
|
|
78
81
|
])) : createCommentVNode("", true)], 16);
|
|
79
82
|
};
|
|
80
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlock.vue.script.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n}\n\n/**\n * Uses highlight.js for syntax highlighting\n *\n * Requires at least one of content or prettyPrintedContent\n */\nconst {\n lang = 'plaintext',\n lineNumbers = false,\n copy = 'hover',\n content,\n prettyPrintedContent,\n hideCredentials,\n} = defineProps<\n BaseProps &\n (\n | {\n /** Raw unformatted object or string content */\n content: string | object\n }\n | {\n /**\n * Pre-pretty printed content string for better performance\n *\n * Avoids unnecessary costly re-serialization of large content\n */\n prettyPrintedContent: string\n }\n )\n>()\n\n/** Base id for the code block */\nconst id = useId()\n\n/** Formatted the content into an indented json string */\nconst prettyContent = computed(\n () => prettyPrintedContent || prettyPrintJson(content ?? ''),\n)\n\nconst highlightedCode = computed(() => {\n const html = syntaxHighlight(prettyContent.value, {\n lang: lang.trim(),\n languages: standardLanguages,\n lineNumbers: lineNumbers,\n maskCredentials: hideCredentials,\n })\n\n // Need to remove the wrapping <pre> element so we can use v-html without another wrapper\n return html.slice(5, -6)\n})\n\n/** Determine if the content is a single line */\nconst isOneLine = computed(() => !prettyContent.value.includes('\\n'))\n\nconst isContentValid = computed(() => {\n return (\n prettyContent.value !== null &&\n prettyContent.value !== 'null' &&\n prettyContent.value !== '404 Not Found'\n )\n})\n\n/** Whether the copy button is rendered on top of the code */\nconst showCopy = computed(() => copy && isContentValid.value)\n\n/**\n * A one-liner has the copy button floating over the end of the line, so the line needs room to\n * scroll clear of the button. The padding goes on the `w-fit` <pre> because browsers disagree about\n * whether trailing padding on an `overflow-x` container is scrollable.\n */\nconst reserveCopySpace = computed(() => isOneLine.value && showCopy.value)\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0 focus-visible:outline',\n )\n \">\n <!-- Inherits the corners so the inset focus ring follows a rounded code block -->\n <div\n
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlock.vue.script.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n /** Accessible name of the keyboard-scrollable code region. */\n label?: string\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n}\n\n/**\n * Uses highlight.js for syntax highlighting\n *\n * Requires at least one of content or prettyPrintedContent\n */\nconst {\n label = 'Code sample',\n lang = 'plaintext',\n lineNumbers = false,\n copy = 'hover',\n content,\n prettyPrintedContent,\n hideCredentials,\n} = defineProps<\n BaseProps &\n (\n | {\n /** Raw unformatted object or string content */\n content: string | object\n }\n | {\n /**\n * Pre-pretty printed content string for better performance\n *\n * Avoids unnecessary costly re-serialization of large content\n */\n prettyPrintedContent: string\n }\n )\n>()\n\n/** Base id for the code block */\nconst id = useId()\n\n/** Formatted the content into an indented json string */\nconst prettyContent = computed(\n () => prettyPrintedContent || prettyPrintJson(content ?? ''),\n)\n\nconst highlightedCode = computed(() => {\n const html = syntaxHighlight(prettyContent.value, {\n lang: lang.trim(),\n languages: standardLanguages,\n lineNumbers: lineNumbers,\n maskCredentials: hideCredentials,\n })\n\n // Need to remove the wrapping <pre> element so we can use v-html without another wrapper\n return html.slice(5, -6)\n})\n\n/** Determine if the content is a single line */\nconst isOneLine = computed(() => !prettyContent.value.includes('\\n'))\n\nconst isContentValid = computed(() => {\n return (\n prettyContent.value !== null &&\n prettyContent.value !== 'null' &&\n prettyContent.value !== '404 Not Found'\n )\n})\n\n/** Whether the copy button is rendered on top of the code */\nconst showCopy = computed(() => copy && isContentValid.value)\n\n/**\n * A one-liner has the copy button floating over the end of the line, so the line needs room to\n * scroll clear of the button. The padding goes on the `w-fit` <pre> because browsers disagree about\n * whether trailing padding on an `overflow-x` container is scrollable.\n */\nconst reserveCopySpace = computed(() => isOneLine.value && showCopy.value)\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0 focus-visible:outline',\n )\n \">\n <!-- Inherits the corners so the inset focus ring follows a rounded code block -->\n <div\n :aria-label=\"label\"\n class=\"custom-scroll overflow-x-auto p-2 -outline-offset-2 rounded-[inherit] min-h-0 min-w-0 flex-1\"\n role=\"region\"\n tabindex=\"0\">\n <pre\n :id=\"id\"\n class=\"m-0 bg-transparent text-nowrap whitespace-pre w-fit\"\n :class=\"{ 'pr-6': reserveCopySpace }\"\n v-html=\"highlightedCode\" />\n </div>\n <ScalarCodeBlockCopy\n v-if=\"showCopy\"\n :aria-controls=\"id\"\n class=\"scalar-code-copy absolute\"\n :class=\"[\n isOneLine\n ? 'top-1/2 -translate-y-1/2 m-0 right-1'\n : 'top-2.5 right-2.5',\n { 'opacity-100': copy === 'always' },\n ]\"\n :content=\"prettyContent\"\n :lang=\"lang\"\n :showLang=\"!isOneLine\">\n <template #backdrop>\n <ScalarCopyBackdrop\n class=\"scalar-code-copy-backdrop\"\n :class=\"[\n isOneLine\n ? '-inset-y-0.75 -right-1 group-hocus-within/code-block:-left-0.5 left-3'\n : '-right-1.5 -top-1',\n ]\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n</template>\n<style>\n@reference '../../style.css';\n@import '@scalar/code-highlight/css/code.css';\n\n/** Disable font ligatures so code renders with literal characters */\n.scalar-code-block pre {\n font-variant-ligatures: none;\n}\n\n/** Make the copy button label backdrop match the background */\n.scalar-code-block.bg-b-1 .scalar-code-copy-backdrop {\n @apply bg-b-1;\n}\n.scalar-code-block.bg-b-2 .scalar-code-copy-backdrop {\n @apply bg-b-2;\n}\n/** Make the copy button one shade darker than the background */\n.scalar-code-block.bg-b-2 .scalar-code-copy {\n @apply bg-b-3;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiEA,MAAM,KAAK,MAAM;;EAGjB,MAAM,gBAAgB,eACd,QAAA,wBAAwB,gBAAgB,QAAA,WAAW,EAAE,CAC7D;EAEA,MAAM,kBAAkB,eAAe;GASrC,OARa,gBAAgB,cAAc,OAAO;IAChD,MAAM,QAAA,KAAK,KAAK;IAChB,WAAW;IACX,aAAa,QAAA;IACb,iBAAiB,QAAA;GACnB,CAGO,CAAA,CAAK,MAAM,GAAG,EAAE;EACzB,CAAC;;EAGD,MAAM,YAAY,eAAe,CAAC,cAAc,MAAM,SAAS,IAAI,CAAC;EAEpE,MAAM,iBAAiB,eAAe;GACpC,OACE,cAAc,UAAU,QACxB,cAAc,UAAU,UACxB,cAAc,UAAU;EAE5B,CAAC;;EAGD,MAAM,WAAW,eAAe,QAAA,QAAQ,eAAe,KAAK;;;;;;EAO5D,MAAM,mBAAmB,eAAe,UAAU,SAAS,SAAS,KAAK;EAGzE,MAAM,EAAE,OAAO,UAAU;;uBAGvB,mBA0CM,OAAA,eAAA,mBAzCW,MAAA,EAAA,CAAE,CAAA,oDAAA,uDAAA,CAAA,CAAA,GAAA,CAOjB,mBAUM,OAAA;IATH,cAAY,QAAA;IACb,OAAM;IACN,MAAK;IACL,UAAS;OACT,mBAI6B,OAAA;IAH1B,IAAI,MAAA,EAAA;IACL,OAAK,eAAA,CAAC,uDAAqD,EAAA,QACzC,iBAAA,MAAgB,CAAA,CAAA;IAClC,WAAQ,gBAAA;8CAGJ,SAAA,SAAA,UAAA,GADR,YAsBsB,MAAA,2BAAA,GAAA;;IApBnB,iBAAe,MAAA,EAAA;IAChB,OAAK,eAAA,CAAC,6BAA2B,CACf,UAAA,QAAA,yCAAA,qBAAA,EAAA,eAAuH,QAAA,SAAI,SAAA,CAAA,CAAA,CAAA;IAM5I,SAAS,cAAA;IACT,MAAM,QAAA;IACN,UAAQ,CAAG,UAAA;;IACD,UAAQ,cAOV,CANP,YAMO,4BAAA,EALL,OAAK,eAAA,CAAC,6BAA2B,CACX,UAAA,QAAA,0EAAA,mBAAA,CAAA,CAAA,EAAA,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlockCopy.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlockCopy.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"names":[],"mappings":"AA2FA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAGlD;;;;;;;;GAQG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,yCAAyC;eAC9B,OAAO;IAClB,mCAAmC;aAC1B,MAAM,GAAG,MAAM;IACxB,iCAAiC;WAC1B,mBAAmB,GAAG,MAAM;;aA0B1B,OAAO;;;;IA/BhB,yCAAyC;eAC9B,OAAO;IAClB,mCAAmC;aAC1B,MAAM,GAAG,MAAM;IACxB,iCAAiC;WAC1B,mBAAmB,GAAG,MAAM;;aA0B1B,OAAO;;;;eAwGH,CAAC,KAAK,IAAiB,KAAK,GAAG;EAU1C,CAAC;AACL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlockCopy.vue.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block Copy button\n *\n * Displays a copy-to-clipboard button with an optional language label.\n * Used internally by ScalarCodeBlock.\n *\n * @example\n * <ScalarCodeBlockCopy content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarCopy } from '@/components/ScalarCopy'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport { LANGUAGE_LABELS } from './constants'\nimport type { StandardLanguageKey } from './types'\n\nconst { content } = defineProps<{\n /** Whether to show the language label */\n showLang?: boolean\n /** Content to copy to clipboard */\n content: string | object\n /** Language of the code block */\n lang?: StandardLanguageKey | string\n}>()\n\nconst copied = defineModel<boolean>('copied', { default: false })\n\n/** Handles the copy button click */\nconst contentToCopy = computed<string>(() => {\n return typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2)\n})\n\n/** Type guard to check if the language is a standard language */\nconst isStandardLanguage = (lang: string): lang is StandardLanguageKey =>\n lang in LANGUAGE_LABELS\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <ScalarCopy\n :
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlockCopy.vue.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block Copy button\n *\n * Displays a copy-to-clipboard button with an optional language label.\n * Used internally by ScalarCodeBlock.\n *\n * @example\n * <ScalarCodeBlockCopy content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarCopy } from '@/components/ScalarCopy'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport { LANGUAGE_LABELS } from './constants'\nimport type { StandardLanguageKey } from './types'\n\nconst { content } = defineProps<{\n /** Whether to show the language label */\n showLang?: boolean\n /** Content to copy to clipboard */\n content: string | object\n /** Language of the code block */\n lang?: StandardLanguageKey | string\n}>()\n\nconst copied = defineModel<boolean>('copied', { default: false })\n\n/** Handles the copy button click */\nconst contentToCopy = computed<string>(() => {\n return typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2)\n})\n\n/** Type guard to check if the language is a standard language */\nconst isStandardLanguage = (lang: string): lang is StandardLanguageKey =>\n lang in LANGUAGE_LABELS\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <ScalarCopy\n v-model:copied=\"copied\"\n aria-label=\"Copy code sample\"\n :content=\"contentToCopy\"\n placement=\"left\"\n showLabel\n v-bind=\"{\n ...cx(\n copied\n ? 'opacity-100'\n : 'opacity-0 group-hocus-within/code-block:opacity-100',\n ),\n }\">\n <template\n v-if=\"lang\"\n #copy>\n <span class=\"hidden group-hocus-within/code-block:inline\">\n <span\n v-if=\"showLang\"\n class=\"group-hocus/copy-button:sr-only\"\n :class=\"{ capitalize: !isStandardLanguage(lang) }\">\n {{ isStandardLanguage(lang) ? LANGUAGE_LABELS[lang] : lang }}\n </span>\n <span\n :class=\"{\n 'group-hocus/copy-button:not-sr-only sr-only': showLang,\n }\"\n >Copy</span\n >\n </span>\n </template>\n <template #backdrop>\n <slot name=\"backdrop\" />\n </template>\n </ScalarCopy>\n</template>\n"],"mappings":""}
|
|
@@ -30,11 +30,12 @@ var ScalarCodeBlockCopy_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
|
|
|
30
30
|
const { cx } = useBindCx();
|
|
31
31
|
return (_ctx, _cache) => {
|
|
32
32
|
return openBlock(), createBlock(unref(ScalarCopy_default), mergeProps({
|
|
33
|
-
content: contentToCopy.value,
|
|
34
|
-
showLabel: "",
|
|
35
33
|
copied: copied.value,
|
|
36
34
|
"onUpdate:copied": _cache[0] || (_cache[0] = ($event) => copied.value = $event),
|
|
37
|
-
|
|
35
|
+
"aria-label": "Copy code sample",
|
|
36
|
+
content: contentToCopy.value,
|
|
37
|
+
placement: "left",
|
|
38
|
+
showLabel: ""
|
|
38
39
|
}, { ...unref(cx)(copied.value ? "opacity-100" : "opacity-0 group-hocus-within/code-block:opacity-100") }), createSlots({
|
|
39
40
|
backdrop: withCtx(() => [renderSlot(_ctx.$slots, "backdrop")]),
|
|
40
41
|
_: 2
|
|
@@ -45,7 +46,7 @@ var ScalarCodeBlockCopy_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
|
|
|
45
46
|
class: normalizeClass(["group-hocus/copy-button:sr-only", { capitalize: !isStandardLanguage(__props.lang) }])
|
|
46
47
|
}, toDisplayString(isStandardLanguage(__props.lang) ? unref(LANGUAGE_LABELS)[__props.lang] : __props.lang), 3)) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass({ "group-hocus/copy-button:not-sr-only sr-only": __props.showLang }) }, "Copy", 2)])]),
|
|
47
48
|
key: "0"
|
|
48
|
-
} : void 0]), 1040, ["
|
|
49
|
+
} : void 0]), 1040, ["copied", "content"]);
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarCodeBlockCopy.vue.script.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block Copy button\n *\n * Displays a copy-to-clipboard button with an optional language label.\n * Used internally by ScalarCodeBlock.\n *\n * @example\n * <ScalarCodeBlockCopy content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarCopy } from '@/components/ScalarCopy'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport { LANGUAGE_LABELS } from './constants'\nimport type { StandardLanguageKey } from './types'\n\nconst { content } = defineProps<{\n /** Whether to show the language label */\n showLang?: boolean\n /** Content to copy to clipboard */\n content: string | object\n /** Language of the code block */\n lang?: StandardLanguageKey | string\n}>()\n\nconst copied = defineModel<boolean>('copied', { default: false })\n\n/** Handles the copy button click */\nconst contentToCopy = computed<string>(() => {\n return typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2)\n})\n\n/** Type guard to check if the language is a standard language */\nconst isStandardLanguage = (lang: string): lang is StandardLanguageKey =>\n lang in LANGUAGE_LABELS\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <ScalarCopy\n :
|
|
1
|
+
{"version":3,"file":"ScalarCodeBlockCopy.vue.script.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block Copy button\n *\n * Displays a copy-to-clipboard button with an optional language label.\n * Used internally by ScalarCodeBlock.\n *\n * @example\n * <ScalarCodeBlockCopy content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarCopy } from '@/components/ScalarCopy'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport { LANGUAGE_LABELS } from './constants'\nimport type { StandardLanguageKey } from './types'\n\nconst { content } = defineProps<{\n /** Whether to show the language label */\n showLang?: boolean\n /** Content to copy to clipboard */\n content: string | object\n /** Language of the code block */\n lang?: StandardLanguageKey | string\n}>()\n\nconst copied = defineModel<boolean>('copied', { default: false })\n\n/** Handles the copy button click */\nconst contentToCopy = computed<string>(() => {\n return typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2)\n})\n\n/** Type guard to check if the language is a standard language */\nconst isStandardLanguage = (lang: string): lang is StandardLanguageKey =>\n lang in LANGUAGE_LABELS\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <ScalarCopy\n v-model:copied=\"copied\"\n aria-label=\"Copy code sample\"\n :content=\"contentToCopy\"\n placement=\"left\"\n showLabel\n v-bind=\"{\n ...cx(\n copied\n ? 'opacity-100'\n : 'opacity-0 group-hocus-within/code-block:opacity-100',\n ),\n }\">\n <template\n v-if=\"lang\"\n #copy>\n <span class=\"hidden group-hocus-within/code-block:inline\">\n <span\n v-if=\"showLang\"\n class=\"group-hocus/copy-button:sr-only\"\n :class=\"{ capitalize: !isStandardLanguage(lang) }\">\n {{ isStandardLanguage(lang) ? LANGUAGE_LABELS[lang] : lang }}\n </span>\n <span\n :class=\"{\n 'group-hocus/copy-button:not-sr-only sr-only': showLang,\n }\"\n >Copy</span\n >\n </span>\n </template>\n <template #backdrop>\n <slot name=\"backdrop\" />\n </template>\n </ScalarCopy>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EA6BA,MAAM,SAAS,SAAoB,SAAC,QAA4B;;EAGhE,MAAM,gBAAgB,eAAuB;GAC3C,OAAO,OAAO,QAAA,YAAY,WACtB,QAAA,UACA,KAAK,UAAU,QAAA,SAAS,MAAM,CAAC;EACrC,CAAC;;EAGD,MAAM,sBAAsB,SAC1B,QAAQ;EAGV,MAAM,EAAE,OAAO,UAAU;;uBAIvB,YAkCa,MAAA,kBAAA,GAlCb,WAkCa;IAjCH,QAAQ,OAAA;oEAAM,QAAA;IACtB,cAAW;IACV,SAAS,cAAA;IACV,WAAU;IACV,WAAA;WACmB,MAAA,EAAA,CAAE,CAAU,OAAA,QAAA,gBAAA,qDAAA,EAAA,CAAA,GAAA,YAAA;IAyBpB,UAAQ,cACO,CAAxB,WAAwB,KAAA,QAAA,UAAA,CAAA,CAAA;;OAlBlB,QAAA,OAAA;UACL;sBAcM,CAbP,mBAaO,QAbP,YAaO,CAXG,QAAA,YAAA,UAAA,GADR,mBAKO,QAAA;;KAHL,OAAK,eAAA,CAAC,mCAAiC,EAAA,YAAA,CAChB,mBAAmB,QAAA,IAAI,EAAA,CAAA,CAAA;uBAC3C,mBAAmB,QAAA,IAAI,IAAI,MAAA,eAAA,CAAe,CAAC,QAAA,QAAQ,QAAA,IAAI,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAE5D,mBAKC,QAAA,EAJE,OAAK,eAAA,EAAA,+CAA+D,QAAA,SAAA,CAAA,EAAA,GAGpE,QAAI,CAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarComboboxOptions.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarComboboxOptions.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"names":[],"mappings":"AAkUA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,eAAe,EAErB,MAAM,SAAS,CAAA;AAGhB;;;;;;;;GAQG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY,GAAK,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,EAC1F,aAAa,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAC9D,YAAY,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAC3G,gBAAgB,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAClE;WAigBO,OAAO,KAAK,EAAE,WAAW,GAAG,mBAAmB,CAAC;QA/ftD,6CAA6C;iBACpC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9B,sDAAsD;sBACxC,MAAM;QACpB,+CAA+C;qBAClC,MAAM;QACnB,+DAA+D;oBACnD,MAAM;QAClB,+DAA+D;gBACvD,MAAM,IAAI;QAClB;;;;;WAKG;mBACQ,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/B,qEAAqE;sBACvD,OAAO;;qBAwKV,CAAC,EAAE;;;;KAqU2E,CAAC,GAAG,CAAC,OAAO,UAAU,SAAS;QAAE,oBAAoB,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAC5J,CAAC,OAAO,EAAE,EAAE,KAAK,IAAI;WACtB,GAAG;;UAEJ,2EAAoC;EAEvC,KACQ,OAAO,KAAK,EAAE,KAAK,GAAG;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAA;CAAI,CAAC;AACzF,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,CAAC,GAAG,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarComboboxOptions.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Options component\n *\n * Renders the searchable option list for a combobox, including\n * filtering, keyboard navigation, and an optional \"add new\" action.\n *\n * @example\n * <ScalarComboboxOptions v-model=\"selected\" :options=\"options\" />\n */\nexport default {}\n</script>\n\n<!-- prettier-ignore-attribute generic -->\n<script\n setup\n lang=\"ts\"\n generic=\"O extends Option = Option, G extends OptionGroup<O> = OptionGroup<O>\">\nimport { filterByOptionLabel } from '@/components/ScalarCombobox/filter-by-option-label'\nimport { ScalarIconMagnifyingGlass, ScalarIconPlus } from '@scalar/icons'\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport { ScalarListboxCheckbox } from '../ScalarListbox'\nimport ComboboxOption from './ScalarComboboxOption.vue'\nimport ComboboxOptionGroup from './ScalarComboboxOptionGroup.vue'\nimport {\n type ComboboxEmits,\n type ComboboxSlots,\n type FilterFunction,\n type Option,\n type OptionGroup,\n type OptionsOrGroups,\n isGroups,\n} from './types'\n\nconst {\n options: optionsOrGroups,\n placeholder,\n inputLabel,\n noResults,\n close,\n filterFn = filterByOptionLabel,\n multiselect,\n} = defineProps<{\n /** The options to display in the combobox */\n options: OptionsOrGroups<O, G>\n /** The placeholder text to display in the combobox */\n placeholder?: string\n /** An accessible label for the search input */\n inputLabel?: string\n /** The message to display when filtering returns no options */\n noResults?: string\n /** Close the popover without changing the current selection */\n close?: () => void\n /**\n * A function to filter the options based on a query,\n * if not provided, the options will be filtered by option label\n *\n * @see {@link FilterFunction} for more information\n */\n filterFn?: FilterFunction<O, G>\n /** Whether the combobox is in multiselect mode, defaults to false */\n multiselect?: boolean\n}>()\n\nconst emit = defineEmits<ComboboxEmits>()\n\nconst model = defineModel<O[]>({ default: () => [] })\n\nconst slots = defineSlots<Omit<ComboboxSlots<O, G>, 'default'>>()\n\ndefineOptions({ inheritAttrs: false })\n\n/** A unique ID for the combobox */\nconst id = `scalar-combobox-items-${useId()}`\n\n/** A static option entry for the \"Add a new option\" option */\nconst addOption: Option = { id: `${useId()}-add`, label: 'Add a new option' }\n\n/** Generate a unique ID for an option */\nfunction getOptionId(option: Option) {\n return `${id}-${option.id}`\n}\n\n/** A flat list of all options */\nconst options = computed<O[]>(() =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups.flatMap((group) => group.options)\n : optionsOrGroups,\n)\n\n/** An list of all groups */\nconst groups = computed<G[]>(\n () =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups // G extends OptionGroup<O>\n : /*\n We know G is an unextended OptionGroup<O> here because of the\n structure of the component props so we can cast it to G\n */\n [{ label: '', options: optionsOrGroups } as G], // G is OptionGroup<O>\n)\n\nconst query = ref<string>('')\nconst activeRef = ref<Option | undefined>(model.value?.[0] ?? options.value[0])\n\n// Clear the query on open and close\nonMounted(() => {\n // Clear the query\n query.value = ''\n\n // Set the active option to the selected option or the first option\n activeRef.value = model.value?.[0] ?? options.value[0]\n\n // Scroll to the selected option\n const selected = model.value[0]\n if (selected) {\n setTimeout(() => {\n const value = model.value[0]\n if (!value) {\n return\n }\n\n document\n ?.getElementById(getOptionId(value))\n ?.scrollIntoView({ block: 'nearest' })\n }, 10)\n }\n})\n\n// Set the top option as active when searching\nwatch(\n () => query.value,\n () => (activeRef.value = withAdd.value[0]),\n)\n\n/** The filtered list of options */\nconst filtered = computed<O[]>(() =>\n filterFn(query.value, options.value, groups.value),\n)\n\n/** The list of filtered options with the \"Add a new option\" option */\nconst withAdd = computed<Option[]>(() =>\n slots.add ? [...filtered.value, addOption] : filtered.value,\n)\n\nfunction toggleSelected(option: Option | undefined) {\n if (!option) {\n return\n }\n\n if (option.id === addOption.id) {\n addNew()\n return\n }\n\n if (multiselect) {\n // Remove from selection list\n if (model.value.some((o) => o.id === option.id)) {\n model.value = model.value.filter((o) => o.id !== option.id)\n }\n // Add to selection list\n else {\n model.value = [\n ...model.value,\n options.value.find((o) => o.id === option.id)!,\n ]\n }\n } else {\n // Set selection for single select mode\n model.value = [options.value.find((o) => o.id === option.id)!]\n }\n}\n\nfunction moveActive(dir: 1 | -1) {\n const list = withAdd.value\n\n // Find active index\n const activeIdx = list.findIndex(\n (option) => option.id === activeRef.value?.id,\n )\n\n // Calculate next index and exit if it's out of bounds\n const nextIdx = activeIdx + dir\n\n if (nextIdx < 0 || nextIdx > list.length - 1) {\n return\n }\n\n activeRef.value = list[nextIdx]! // We know it's in bounds from the check above\n\n if (!activeRef.value) {\n return\n }\n\n // Scroll to the active option\n document?.getElementById(getOptionId(activeRef.value))?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n })\n}\n\nfunction addNew() {\n emit('add')\n query.value = ''\n}\n\n// Manual autofocus for the input\nconst input = ref<HTMLInputElement | null>(null)\n\n// This must be a setTimeout to ensure there is no scroll jump. nextTick does not work here.\nonMounted(() => setTimeout(() => input.value?.focus(), 0))\n</script>\n<template>\n <div class=\"relative flex\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-c-3 size-4\" />\n <input\n ref=\"input\"\n v-model=\"query\"\n :aria-activedescendant=\"activeRef ? getOptionId(activeRef) : undefined\"\n aria-autocomplete=\"list\"\n :aria-controls=\"id\"\n :aria-label=\"inputLabel\"\n class=\"min-w-0 flex-1 rounded border-0 py-2.5 pl-8 pr-3 leading-none text-c-1 -outline-offset-1\"\n data-1p-ignore\n :placeholder\n role=\"combobox\"\n tabindex=\"0\"\n type=\"text\"\n @keydown.down.prevent=\"moveActive(1)\"\n @keydown.enter.prevent=\"activeRef && toggleSelected(activeRef)\"\n @keydown.esc.prevent=\"close?.()\"\n @keydown.up.prevent=\"moveActive(-1)\" />\n </div>\n <ul\n v-show=\"filtered.length || slots.add || noResults\"\n :id=\"id\"\n :aria-multiselectable=\"multiselect\"\n class=\"border-t p-0.75 custom-scroll overscroll-contain flex-1 min-h-0\"\n role=\"listbox\"\n tabindex=\"-1\">\n <li\n v-if=\"!filtered.length && !slots.add && noResults\"\n class=\"text-c-3 px-2.5 py-2\"\n role=\"status\">\n {{ noResults }}\n </li>\n <ComboboxOptionGroup\n v-for=\"(group, i) in groups\"\n :id=\"`${id}-group-${i}`\"\n :key=\"i\"\n :hidden=\"\n // Only show the group label if there are some results\n !group.options.some((o) => filtered.some((f) => f.id === o.id)) ||\n // And it has a label\n !group.label\n \">\n <template #label>\n <slot\n v-if=\"$slots.group\"\n :group\n name=\"group\" />\n <template v-else>\n {{ group.label }}\n </template>\n </template>\n <template\n v-for=\"option in filtered\"\n :key=\"option.id\">\n <ComboboxOption\n v-if=\"group.options.some((o) => o.id === option.id)\"\n :id=\"getOptionId(option)\"\n v-slot=\"{ active, selected }\"\n :active=\"activeRef?.id === option.id\"\n :selected=\"model.some((o) => o.id === option.id)\"\n @click=\"toggleSelected(option)\"\n @mousedown.prevent\n @mouseenter=\"activeRef = option\">\n <slot\n v-if=\"$slots.option\"\n :active\n name=\"option\"\n :option\n :selected />\n <template v-else>\n <ScalarListboxCheckbox\n :multiselect\n :selected=\"model.some((o) => o.id === option.id)\" />\n <span class=\"inline-block min-w-0 flex-1 truncate text-c-1\">\n {{ option.label }}\n </span>\n </template>\n </ComboboxOption>\n </template>\n </ComboboxOptionGroup>\n <ComboboxOption\n v-if=\"slots.add\"\n :id=\"getOptionId(addOption)\"\n v-slot=\"{ active }\"\n :active=\"activeRef?.id === addOption.id\"\n @click=\"addNew\"\n @mousedown.prevent\n @mouseenter=\"activeRef = addOption\">\n <ScalarIconPlus class=\"size-4 p-px\" />\n <slot\n :active\n name=\"add\" />\n </ComboboxOption>\n </ul>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarComboboxOptions.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Options component\n *\n * Renders the searchable option list for a combobox, including\n * filtering, keyboard navigation, and an optional \"add new\" action.\n *\n * @example\n * <ScalarComboboxOptions v-model=\"selected\" :options=\"options\" />\n */\nexport default {}\n</script>\n\n<!-- prettier-ignore-attribute generic -->\n<script\n setup\n lang=\"ts\"\n generic=\"O extends Option = Option, G extends OptionGroup<O> = OptionGroup<O>\">\nimport { filterByOptionLabel } from '@/components/ScalarCombobox/filter-by-option-label'\nimport { ScalarIconMagnifyingGlass, ScalarIconPlus } from '@scalar/icons'\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport { ScalarListboxCheckbox } from '../ScalarListbox'\nimport ComboboxOption from './ScalarComboboxOption.vue'\nimport ComboboxOptionGroup from './ScalarComboboxOptionGroup.vue'\nimport {\n type ComboboxEmits,\n type ComboboxSlots,\n type FilterFunction,\n type Option,\n type OptionGroup,\n type OptionsOrGroups,\n isGroups,\n} from './types'\n\nconst {\n options: optionsOrGroups,\n placeholder,\n inputLabel,\n noResults,\n close,\n filterFn = filterByOptionLabel,\n multiselect,\n} = defineProps<{\n /** The options to display in the combobox */\n options: OptionsOrGroups<O, G>\n /** The placeholder text to display in the combobox */\n placeholder?: string\n /** An accessible label for the search input */\n inputLabel?: string\n /** The message to display when filtering returns no options */\n noResults?: string\n /** Close the popover without changing the current selection */\n close?: () => void\n /**\n * A function to filter the options based on a query,\n * if not provided, the options will be filtered by option label\n *\n * @see {@link FilterFunction} for more information\n */\n filterFn?: FilterFunction<O, G>\n /** Whether the combobox is in multiselect mode, defaults to false */\n multiselect?: boolean\n}>()\n\nconst emit = defineEmits<ComboboxEmits>()\n\nconst model = defineModel<O[]>({ default: () => [] })\n\nconst slots = defineSlots<Omit<ComboboxSlots<O, G>, 'default'>>()\n\ndefineOptions({ inheritAttrs: false })\n\n/** A unique ID for the combobox */\nconst id = `scalar-combobox-items-${useId()}`\n\n/** A static option entry for the \"Add a new option\" option */\nconst addOption: Option = { id: `${useId()}-add`, label: 'Add a new option' }\n\n/** Generate a unique ID for an option */\nfunction getOptionId(option: Option) {\n return `${id}-${option.id}`\n}\n\n/** A flat list of all options */\nconst options = computed<O[]>(() =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups.flatMap((group) => group.options)\n : optionsOrGroups,\n)\n\n/** An list of all groups */\nconst groups = computed<G[]>(\n () =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups // G extends OptionGroup<O>\n : /*\n We know G is an unextended OptionGroup<O> here because of the\n structure of the component props so we can cast it to G\n */\n [{ label: '', options: optionsOrGroups } as G], // G is OptionGroup<O>\n)\n\nconst query = ref<string>('')\nconst activeRef = ref<Option | undefined>(model.value?.[0] ?? options.value[0])\n\n// Clear the query on open and close\nonMounted(() => {\n // Clear the query\n query.value = ''\n\n // Set the active option to the selected option or the first option\n activeRef.value = model.value?.[0] ?? options.value[0]\n\n // Scroll to the selected option\n const selected = model.value[0]\n if (selected) {\n setTimeout(() => {\n const value = model.value[0]\n if (!value) {\n return\n }\n\n document\n ?.getElementById(getOptionId(value))\n ?.scrollIntoView({ block: 'nearest' })\n }, 10)\n }\n})\n\n// Set the top option as active when searching\nwatch(\n () => query.value,\n () => (activeRef.value = withAdd.value[0]),\n)\n\n/** The filtered list of options */\nconst filtered = computed<O[]>(() =>\n filterFn(query.value, options.value, groups.value),\n)\n\n/** The list of filtered options with the \"Add a new option\" option */\nconst withAdd = computed<Option[]>(() =>\n slots.add ? [...filtered.value, addOption] : filtered.value,\n)\n\nfunction toggleSelected(option: Option | undefined) {\n if (!option) {\n return\n }\n\n if (option.id === addOption.id) {\n addNew()\n return\n }\n\n if (multiselect) {\n // Remove from selection list\n if (model.value.some((o) => o.id === option.id)) {\n model.value = model.value.filter((o) => o.id !== option.id)\n }\n // Add to selection list\n else {\n model.value = [\n ...model.value,\n options.value.find((o) => o.id === option.id)!,\n ]\n }\n } else {\n // Set selection for single select mode\n model.value = [options.value.find((o) => o.id === option.id)!]\n }\n}\n\nfunction moveActive(dir: 1 | -1) {\n const list = withAdd.value\n\n // Find active index\n const activeIdx = list.findIndex(\n (option) => option.id === activeRef.value?.id,\n )\n\n // Calculate next index and exit if it's out of bounds\n const nextIdx = activeIdx + dir\n\n if (nextIdx < 0 || nextIdx > list.length - 1) {\n return\n }\n\n activeRef.value = list[nextIdx]! // We know it's in bounds from the check above\n\n if (!activeRef.value) {\n return\n }\n\n // Scroll to the active option\n document?.getElementById(getOptionId(activeRef.value))?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n })\n}\n\nfunction addNew() {\n emit('add')\n query.value = ''\n}\n\n// Manual autofocus for the input\nconst input = ref<HTMLInputElement | null>(null)\n\n// This must be a setTimeout to ensure there is no scroll jump. nextTick does not work here.\nonMounted(() => setTimeout(() => input.value?.focus(), 0))\n</script>\n<template>\n <div class=\"relative flex\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-c-3 size-4\" />\n <input\n ref=\"input\"\n v-model=\"query\"\n :aria-activedescendant=\"activeRef ? getOptionId(activeRef) : undefined\"\n aria-autocomplete=\"list\"\n :aria-controls=\"id\"\n :aria-expanded=\"Boolean(filtered.length || slots.add || noResults)\"\n :aria-label=\"inputLabel\"\n class=\"min-w-0 flex-1 rounded border-0 py-2.5 pl-8 pr-3 leading-none text-c-1 -outline-offset-1\"\n data-1p-ignore\n :placeholder\n role=\"combobox\"\n tabindex=\"0\"\n type=\"text\"\n @keydown.down.prevent=\"moveActive(1)\"\n @keydown.enter.prevent=\"activeRef && toggleSelected(activeRef)\"\n @keydown.esc.prevent=\"close?.()\"\n @keydown.up.prevent=\"moveActive(-1)\" />\n </div>\n <ul\n v-show=\"filtered.length || slots.add || noResults\"\n :id=\"id\"\n :aria-multiselectable=\"multiselect\"\n class=\"border-t p-0.75 custom-scroll overscroll-contain flex-1 min-h-0\"\n role=\"listbox\"\n tabindex=\"-1\">\n <li\n v-if=\"!filtered.length && !slots.add && noResults\"\n class=\"text-c-3 px-2.5 py-2\"\n role=\"status\">\n {{ noResults }}\n </li>\n <ComboboxOptionGroup\n v-for=\"(group, i) in groups\"\n :id=\"`${id}-group-${i}`\"\n :key=\"i\"\n :hidden=\"\n // Only show the group label if there are some results\n !group.options.some((o) => filtered.some((f) => f.id === o.id)) ||\n // And it has a label\n !group.label\n \">\n <template #label>\n <slot\n v-if=\"$slots.group\"\n :group\n name=\"group\" />\n <template v-else>\n {{ group.label }}\n </template>\n </template>\n <template\n v-for=\"option in filtered\"\n :key=\"option.id\">\n <ComboboxOption\n v-if=\"group.options.some((o) => o.id === option.id)\"\n :id=\"getOptionId(option)\"\n v-slot=\"{ active, selected }\"\n :active=\"activeRef?.id === option.id\"\n :selected=\"model.some((o) => o.id === option.id)\"\n @click=\"toggleSelected(option)\"\n @mousedown.prevent\n @mouseenter=\"activeRef = option\">\n <slot\n v-if=\"$slots.option\"\n :active\n name=\"option\"\n :option\n :selected />\n <template v-else>\n <ScalarListboxCheckbox\n :multiselect\n :selected=\"model.some((o) => o.id === option.id)\" />\n <span class=\"inline-block min-w-0 flex-1 truncate text-c-1\">\n {{ option.label }}\n </span>\n </template>\n </ComboboxOption>\n </template>\n </ComboboxOptionGroup>\n <ComboboxOption\n v-if=\"slots.add\"\n :id=\"getOptionId(addOption)\"\n v-slot=\"{ active }\"\n :active=\"activeRef?.id === addOption.id\"\n @click=\"addNew\"\n @mousedown.prevent\n @mouseenter=\"activeRef = addOption\">\n <ScalarIconPlus class=\"size-4 p-px\" />\n <slot\n :active\n name=\"add\" />\n </ComboboxOption>\n </ul>\n</template>\n"],"mappings":""}
|
|
@@ -9,6 +9,7 @@ import { ScalarIconMagnifyingGlass, ScalarIconPlus } from "@scalar/icons";
|
|
|
9
9
|
var _hoisted_1 = { class: "relative flex" };
|
|
10
10
|
var _hoisted_2 = [
|
|
11
11
|
"aria-activedescendant",
|
|
12
|
+
"aria-expanded",
|
|
12
13
|
"aria-label",
|
|
13
14
|
"placeholder"
|
|
14
15
|
];
|
|
@@ -111,6 +112,7 @@ var ScalarComboboxOptions_vue_vue_type_script_setup_true_lang_default = /*@__PUR
|
|
|
111
112
|
"aria-activedescendant": activeRef.value ? getOptionId(activeRef.value) : void 0,
|
|
112
113
|
"aria-autocomplete": "list",
|
|
113
114
|
"aria-controls": id,
|
|
115
|
+
"aria-expanded": Boolean(filtered.value.length || slots.add || __props.noResults),
|
|
114
116
|
"aria-label": __props.inputLabel,
|
|
115
117
|
class: "min-w-0 flex-1 rounded border-0 py-2.5 pl-8 pr-3 leading-none text-c-1 -outline-offset-1",
|
|
116
118
|
"data-1p-ignore": "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarComboboxOptions.vue.script.js","names":["$slots"],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Options component\n *\n * Renders the searchable option list for a combobox, including\n * filtering, keyboard navigation, and an optional \"add new\" action.\n *\n * @example\n * <ScalarComboboxOptions v-model=\"selected\" :options=\"options\" />\n */\nexport default {}\n</script>\n\n<!-- prettier-ignore-attribute generic -->\n<script\n setup\n lang=\"ts\"\n generic=\"O extends Option = Option, G extends OptionGroup<O> = OptionGroup<O>\">\nimport { filterByOptionLabel } from '@/components/ScalarCombobox/filter-by-option-label'\nimport { ScalarIconMagnifyingGlass, ScalarIconPlus } from '@scalar/icons'\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport { ScalarListboxCheckbox } from '../ScalarListbox'\nimport ComboboxOption from './ScalarComboboxOption.vue'\nimport ComboboxOptionGroup from './ScalarComboboxOptionGroup.vue'\nimport {\n type ComboboxEmits,\n type ComboboxSlots,\n type FilterFunction,\n type Option,\n type OptionGroup,\n type OptionsOrGroups,\n isGroups,\n} from './types'\n\nconst {\n options: optionsOrGroups,\n placeholder,\n inputLabel,\n noResults,\n close,\n filterFn = filterByOptionLabel,\n multiselect,\n} = defineProps<{\n /** The options to display in the combobox */\n options: OptionsOrGroups<O, G>\n /** The placeholder text to display in the combobox */\n placeholder?: string\n /** An accessible label for the search input */\n inputLabel?: string\n /** The message to display when filtering returns no options */\n noResults?: string\n /** Close the popover without changing the current selection */\n close?: () => void\n /**\n * A function to filter the options based on a query,\n * if not provided, the options will be filtered by option label\n *\n * @see {@link FilterFunction} for more information\n */\n filterFn?: FilterFunction<O, G>\n /** Whether the combobox is in multiselect mode, defaults to false */\n multiselect?: boolean\n}>()\n\nconst emit = defineEmits<ComboboxEmits>()\n\nconst model = defineModel<O[]>({ default: () => [] })\n\nconst slots = defineSlots<Omit<ComboboxSlots<O, G>, 'default'>>()\n\ndefineOptions({ inheritAttrs: false })\n\n/** A unique ID for the combobox */\nconst id = `scalar-combobox-items-${useId()}`\n\n/** A static option entry for the \"Add a new option\" option */\nconst addOption: Option = { id: `${useId()}-add`, label: 'Add a new option' }\n\n/** Generate a unique ID for an option */\nfunction getOptionId(option: Option) {\n return `${id}-${option.id}`\n}\n\n/** A flat list of all options */\nconst options = computed<O[]>(() =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups.flatMap((group) => group.options)\n : optionsOrGroups,\n)\n\n/** An list of all groups */\nconst groups = computed<G[]>(\n () =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups // G extends OptionGroup<O>\n : /*\n We know G is an unextended OptionGroup<O> here because of the\n structure of the component props so we can cast it to G\n */\n [{ label: '', options: optionsOrGroups } as G], // G is OptionGroup<O>\n)\n\nconst query = ref<string>('')\nconst activeRef = ref<Option | undefined>(model.value?.[0] ?? options.value[0])\n\n// Clear the query on open and close\nonMounted(() => {\n // Clear the query\n query.value = ''\n\n // Set the active option to the selected option or the first option\n activeRef.value = model.value?.[0] ?? options.value[0]\n\n // Scroll to the selected option\n const selected = model.value[0]\n if (selected) {\n setTimeout(() => {\n const value = model.value[0]\n if (!value) {\n return\n }\n\n document\n ?.getElementById(getOptionId(value))\n ?.scrollIntoView({ block: 'nearest' })\n }, 10)\n }\n})\n\n// Set the top option as active when searching\nwatch(\n () => query.value,\n () => (activeRef.value = withAdd.value[0]),\n)\n\n/** The filtered list of options */\nconst filtered = computed<O[]>(() =>\n filterFn(query.value, options.value, groups.value),\n)\n\n/** The list of filtered options with the \"Add a new option\" option */\nconst withAdd = computed<Option[]>(() =>\n slots.add ? [...filtered.value, addOption] : filtered.value,\n)\n\nfunction toggleSelected(option: Option | undefined) {\n if (!option) {\n return\n }\n\n if (option.id === addOption.id) {\n addNew()\n return\n }\n\n if (multiselect) {\n // Remove from selection list\n if (model.value.some((o) => o.id === option.id)) {\n model.value = model.value.filter((o) => o.id !== option.id)\n }\n // Add to selection list\n else {\n model.value = [\n ...model.value,\n options.value.find((o) => o.id === option.id)!,\n ]\n }\n } else {\n // Set selection for single select mode\n model.value = [options.value.find((o) => o.id === option.id)!]\n }\n}\n\nfunction moveActive(dir: 1 | -1) {\n const list = withAdd.value\n\n // Find active index\n const activeIdx = list.findIndex(\n (option) => option.id === activeRef.value?.id,\n )\n\n // Calculate next index and exit if it's out of bounds\n const nextIdx = activeIdx + dir\n\n if (nextIdx < 0 || nextIdx > list.length - 1) {\n return\n }\n\n activeRef.value = list[nextIdx]! // We know it's in bounds from the check above\n\n if (!activeRef.value) {\n return\n }\n\n // Scroll to the active option\n document?.getElementById(getOptionId(activeRef.value))?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n })\n}\n\nfunction addNew() {\n emit('add')\n query.value = ''\n}\n\n// Manual autofocus for the input\nconst input = ref<HTMLInputElement | null>(null)\n\n// This must be a setTimeout to ensure there is no scroll jump. nextTick does not work here.\nonMounted(() => setTimeout(() => input.value?.focus(), 0))\n</script>\n<template>\n <div class=\"relative flex\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-c-3 size-4\" />\n <input\n ref=\"input\"\n v-model=\"query\"\n :aria-activedescendant=\"activeRef ? getOptionId(activeRef) : undefined\"\n aria-autocomplete=\"list\"\n :aria-controls=\"id\"\n :aria-label=\"inputLabel\"\n class=\"min-w-0 flex-1 rounded border-0 py-2.5 pl-8 pr-3 leading-none text-c-1 -outline-offset-1\"\n data-1p-ignore\n :placeholder\n role=\"combobox\"\n tabindex=\"0\"\n type=\"text\"\n @keydown.down.prevent=\"moveActive(1)\"\n @keydown.enter.prevent=\"activeRef && toggleSelected(activeRef)\"\n @keydown.esc.prevent=\"close?.()\"\n @keydown.up.prevent=\"moveActive(-1)\" />\n </div>\n <ul\n v-show=\"filtered.length || slots.add || noResults\"\n :id=\"id\"\n :aria-multiselectable=\"multiselect\"\n class=\"border-t p-0.75 custom-scroll overscroll-contain flex-1 min-h-0\"\n role=\"listbox\"\n tabindex=\"-1\">\n <li\n v-if=\"!filtered.length && !slots.add && noResults\"\n class=\"text-c-3 px-2.5 py-2\"\n role=\"status\">\n {{ noResults }}\n </li>\n <ComboboxOptionGroup\n v-for=\"(group, i) in groups\"\n :id=\"`${id}-group-${i}`\"\n :key=\"i\"\n :hidden=\"\n // Only show the group label if there are some results\n !group.options.some((o) => filtered.some((f) => f.id === o.id)) ||\n // And it has a label\n !group.label\n \">\n <template #label>\n <slot\n v-if=\"$slots.group\"\n :group\n name=\"group\" />\n <template v-else>\n {{ group.label }}\n </template>\n </template>\n <template\n v-for=\"option in filtered\"\n :key=\"option.id\">\n <ComboboxOption\n v-if=\"group.options.some((o) => o.id === option.id)\"\n :id=\"getOptionId(option)\"\n v-slot=\"{ active, selected }\"\n :active=\"activeRef?.id === option.id\"\n :selected=\"model.some((o) => o.id === option.id)\"\n @click=\"toggleSelected(option)\"\n @mousedown.prevent\n @mouseenter=\"activeRef = option\">\n <slot\n v-if=\"$slots.option\"\n :active\n name=\"option\"\n :option\n :selected />\n <template v-else>\n <ScalarListboxCheckbox\n :multiselect\n :selected=\"model.some((o) => o.id === option.id)\" />\n <span class=\"inline-block min-w-0 flex-1 truncate text-c-1\">\n {{ option.label }}\n </span>\n </template>\n </ComboboxOption>\n </template>\n </ComboboxOptionGroup>\n <ComboboxOption\n v-if=\"slots.add\"\n :id=\"getOptionId(addOption)\"\n v-slot=\"{ active }\"\n :active=\"activeRef?.id === addOption.id\"\n @click=\"addNew\"\n @mousedown.prevent\n @mouseenter=\"activeRef = addOption\">\n <ScalarIconPlus class=\"size-4 p-px\" />\n <slot\n :active\n name=\"add\" />\n </ComboboxOption>\n </ul>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ScalarComboboxOptions.vue.script.js","names":["$slots"],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Options component\n *\n * Renders the searchable option list for a combobox, including\n * filtering, keyboard navigation, and an optional \"add new\" action.\n *\n * @example\n * <ScalarComboboxOptions v-model=\"selected\" :options=\"options\" />\n */\nexport default {}\n</script>\n\n<!-- prettier-ignore-attribute generic -->\n<script\n setup\n lang=\"ts\"\n generic=\"O extends Option = Option, G extends OptionGroup<O> = OptionGroup<O>\">\nimport { filterByOptionLabel } from '@/components/ScalarCombobox/filter-by-option-label'\nimport { ScalarIconMagnifyingGlass, ScalarIconPlus } from '@scalar/icons'\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport { ScalarListboxCheckbox } from '../ScalarListbox'\nimport ComboboxOption from './ScalarComboboxOption.vue'\nimport ComboboxOptionGroup from './ScalarComboboxOptionGroup.vue'\nimport {\n type ComboboxEmits,\n type ComboboxSlots,\n type FilterFunction,\n type Option,\n type OptionGroup,\n type OptionsOrGroups,\n isGroups,\n} from './types'\n\nconst {\n options: optionsOrGroups,\n placeholder,\n inputLabel,\n noResults,\n close,\n filterFn = filterByOptionLabel,\n multiselect,\n} = defineProps<{\n /** The options to display in the combobox */\n options: OptionsOrGroups<O, G>\n /** The placeholder text to display in the combobox */\n placeholder?: string\n /** An accessible label for the search input */\n inputLabel?: string\n /** The message to display when filtering returns no options */\n noResults?: string\n /** Close the popover without changing the current selection */\n close?: () => void\n /**\n * A function to filter the options based on a query,\n * if not provided, the options will be filtered by option label\n *\n * @see {@link FilterFunction} for more information\n */\n filterFn?: FilterFunction<O, G>\n /** Whether the combobox is in multiselect mode, defaults to false */\n multiselect?: boolean\n}>()\n\nconst emit = defineEmits<ComboboxEmits>()\n\nconst model = defineModel<O[]>({ default: () => [] })\n\nconst slots = defineSlots<Omit<ComboboxSlots<O, G>, 'default'>>()\n\ndefineOptions({ inheritAttrs: false })\n\n/** A unique ID for the combobox */\nconst id = `scalar-combobox-items-${useId()}`\n\n/** A static option entry for the \"Add a new option\" option */\nconst addOption: Option = { id: `${useId()}-add`, label: 'Add a new option' }\n\n/** Generate a unique ID for an option */\nfunction getOptionId(option: Option) {\n return `${id}-${option.id}`\n}\n\n/** A flat list of all options */\nconst options = computed<O[]>(() =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups.flatMap((group) => group.options)\n : optionsOrGroups,\n)\n\n/** An list of all groups */\nconst groups = computed<G[]>(\n () =>\n isGroups(optionsOrGroups)\n ? optionsOrGroups // G extends OptionGroup<O>\n : /*\n We know G is an unextended OptionGroup<O> here because of the\n structure of the component props so we can cast it to G\n */\n [{ label: '', options: optionsOrGroups } as G], // G is OptionGroup<O>\n)\n\nconst query = ref<string>('')\nconst activeRef = ref<Option | undefined>(model.value?.[0] ?? options.value[0])\n\n// Clear the query on open and close\nonMounted(() => {\n // Clear the query\n query.value = ''\n\n // Set the active option to the selected option or the first option\n activeRef.value = model.value?.[0] ?? options.value[0]\n\n // Scroll to the selected option\n const selected = model.value[0]\n if (selected) {\n setTimeout(() => {\n const value = model.value[0]\n if (!value) {\n return\n }\n\n document\n ?.getElementById(getOptionId(value))\n ?.scrollIntoView({ block: 'nearest' })\n }, 10)\n }\n})\n\n// Set the top option as active when searching\nwatch(\n () => query.value,\n () => (activeRef.value = withAdd.value[0]),\n)\n\n/** The filtered list of options */\nconst filtered = computed<O[]>(() =>\n filterFn(query.value, options.value, groups.value),\n)\n\n/** The list of filtered options with the \"Add a new option\" option */\nconst withAdd = computed<Option[]>(() =>\n slots.add ? [...filtered.value, addOption] : filtered.value,\n)\n\nfunction toggleSelected(option: Option | undefined) {\n if (!option) {\n return\n }\n\n if (option.id === addOption.id) {\n addNew()\n return\n }\n\n if (multiselect) {\n // Remove from selection list\n if (model.value.some((o) => o.id === option.id)) {\n model.value = model.value.filter((o) => o.id !== option.id)\n }\n // Add to selection list\n else {\n model.value = [\n ...model.value,\n options.value.find((o) => o.id === option.id)!,\n ]\n }\n } else {\n // Set selection for single select mode\n model.value = [options.value.find((o) => o.id === option.id)!]\n }\n}\n\nfunction moveActive(dir: 1 | -1) {\n const list = withAdd.value\n\n // Find active index\n const activeIdx = list.findIndex(\n (option) => option.id === activeRef.value?.id,\n )\n\n // Calculate next index and exit if it's out of bounds\n const nextIdx = activeIdx + dir\n\n if (nextIdx < 0 || nextIdx > list.length - 1) {\n return\n }\n\n activeRef.value = list[nextIdx]! // We know it's in bounds from the check above\n\n if (!activeRef.value) {\n return\n }\n\n // Scroll to the active option\n document?.getElementById(getOptionId(activeRef.value))?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n })\n}\n\nfunction addNew() {\n emit('add')\n query.value = ''\n}\n\n// Manual autofocus for the input\nconst input = ref<HTMLInputElement | null>(null)\n\n// This must be a setTimeout to ensure there is no scroll jump. nextTick does not work here.\nonMounted(() => setTimeout(() => input.value?.focus(), 0))\n</script>\n<template>\n <div class=\"relative flex\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-c-3 size-4\" />\n <input\n ref=\"input\"\n v-model=\"query\"\n :aria-activedescendant=\"activeRef ? getOptionId(activeRef) : undefined\"\n aria-autocomplete=\"list\"\n :aria-controls=\"id\"\n :aria-expanded=\"Boolean(filtered.length || slots.add || noResults)\"\n :aria-label=\"inputLabel\"\n class=\"min-w-0 flex-1 rounded border-0 py-2.5 pl-8 pr-3 leading-none text-c-1 -outline-offset-1\"\n data-1p-ignore\n :placeholder\n role=\"combobox\"\n tabindex=\"0\"\n type=\"text\"\n @keydown.down.prevent=\"moveActive(1)\"\n @keydown.enter.prevent=\"activeRef && toggleSelected(activeRef)\"\n @keydown.esc.prevent=\"close?.()\"\n @keydown.up.prevent=\"moveActive(-1)\" />\n </div>\n <ul\n v-show=\"filtered.length || slots.add || noResults\"\n :id=\"id\"\n :aria-multiselectable=\"multiselect\"\n class=\"border-t p-0.75 custom-scroll overscroll-contain flex-1 min-h-0\"\n role=\"listbox\"\n tabindex=\"-1\">\n <li\n v-if=\"!filtered.length && !slots.add && noResults\"\n class=\"text-c-3 px-2.5 py-2\"\n role=\"status\">\n {{ noResults }}\n </li>\n <ComboboxOptionGroup\n v-for=\"(group, i) in groups\"\n :id=\"`${id}-group-${i}`\"\n :key=\"i\"\n :hidden=\"\n // Only show the group label if there are some results\n !group.options.some((o) => filtered.some((f) => f.id === o.id)) ||\n // And it has a label\n !group.label\n \">\n <template #label>\n <slot\n v-if=\"$slots.group\"\n :group\n name=\"group\" />\n <template v-else>\n {{ group.label }}\n </template>\n </template>\n <template\n v-for=\"option in filtered\"\n :key=\"option.id\">\n <ComboboxOption\n v-if=\"group.options.some((o) => o.id === option.id)\"\n :id=\"getOptionId(option)\"\n v-slot=\"{ active, selected }\"\n :active=\"activeRef?.id === option.id\"\n :selected=\"model.some((o) => o.id === option.id)\"\n @click=\"toggleSelected(option)\"\n @mousedown.prevent\n @mouseenter=\"activeRef = option\">\n <slot\n v-if=\"$slots.option\"\n :active\n name=\"option\"\n :option\n :selected />\n <template v-else>\n <ScalarListboxCheckbox\n :multiselect\n :selected=\"model.some((o) => o.id === option.id)\" />\n <span class=\"inline-block min-w-0 flex-1 truncate text-c-1\">\n {{ option.label }}\n </span>\n </template>\n </ComboboxOption>\n </template>\n </ComboboxOptionGroup>\n <ComboboxOption\n v-if=\"slots.add\"\n :id=\"getOptionId(addOption)\"\n v-slot=\"{ active }\"\n :active=\"activeRef?.id === addOption.id\"\n @click=\"addNew\"\n @mousedown.prevent\n @mouseenter=\"activeRef = addOption\">\n <ScalarIconPlus class=\"size-4 p-px\" />\n <slot\n :active\n name=\"add\" />\n </ComboboxOption>\n </ul>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarMarkdownSummary.vue.js","names":[],"sources":["../../../src/components/ScalarMarkdown/ScalarMarkdownSummary.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Markdown Summary component\n *\n * Displays a truncated preview of markdown content with\n * a \"More\" / \"Show Less\" toggle to expand and collapse.\n *\n * @example\n * <ScalarMarkdownSummary value=\"Long markdown text...\" :clamp=\"2\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { useResizeObserver } from '@vueuse/core'\nimport { onMounted, useId, useTemplateRef } from 'vue'\n\nimport ScalarMarkdown from './ScalarMarkdown.vue'\nimport type { ScalarMarkdownSummaryProps } from './types'\n\nconst { clamp = 1, ...props } = defineProps<ScalarMarkdownSummaryProps>()\n\nconst id = useId()\n\n/** * Whether the summary is open. */\nconst open = defineModel<boolean>({ default: false })\n\n/** Whether the markdown is being truncated */\nconst truncated = defineModel<boolean>('truncated', { default: false })\n\nconst markdown = useTemplateRef('scalar-markdown')\n\nuseResizeObserver(() => markdown.value?.el, checkTruncation)\n\n/** Check if the markdown is being truncated */\nfunction checkTruncation() {\n const el = markdown.value?.el\n if (!el) {\n return\n }\n truncated.value =\n el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth\n}\n\nonMounted(checkTruncation)\n\nconst { cx } = useBindCx()\ndefineOptions({ inheritAttrs: false })\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'group/summary flex gap-0.5',\n open ? 'flex-col' : 'flex-row items-baseline',\n )\n \">\n <ScalarMarkdown\n v-bind=\"props\"\n :id=\"id\"\n ref=\"scalar-markdown\"\n :clamp=\"open ? undefined : clamp\"\n :class=\"{ 'markdown-summary truncate': !open }\" />\n <button\n v-if=\"!controlled && (truncated || open)\"\n :aria-controls=\"id\"\n :aria-expanded=\"open\"\n class=\"whitespace-nowrap font-medium hover:underline\"\n :class=\"{ 'self-end': open }\"\n type=\"button\"\n @click=\"open = !open\">\n <slot\n name=\"button\"\n :open=\"open\">\n {{ open ? ' Show Less' : 'More' }}\n </slot>\n </button>\n </div>\n</template>\n<style>\n.scalar-app {\n /*\n Summarized markdown styles\n Doubled up to bump specificity\n */\n .markdown.markdown-summary.markdown-summary {\n /* Hide the before and after pseudo-elements for all elements */\n *:before,\n *:after {\n content: none;\n }\n\n *:not(strong, em, a) {\n /* Disable formatting for non-inline elements */\n display: contents;\n font-size: inherit;\n font-weight: inherit;\n line-height: var(--markdown-line-height);\n }\n\n /* Hide elements that don't make sense in the summary */\n img,\n svg,\n hr,\n pre {\n display: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarMarkdownSummary.vue.js","names":[],"sources":["../../../src/components/ScalarMarkdown/ScalarMarkdownSummary.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Markdown Summary component\n *\n * Displays a truncated preview of markdown content with\n * a \"More\" / \"Show Less\" toggle to expand and collapse.\n *\n * @example\n * <ScalarMarkdownSummary value=\"Long markdown text...\" :clamp=\"2\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { useResizeObserver } from '@vueuse/core'\nimport { onMounted, useId, useTemplateRef } from 'vue'\n\nimport ScalarMarkdown from './ScalarMarkdown.vue'\nimport type { ScalarMarkdownSummaryProps } from './types'\n\nconst { clamp = 1, ...props } = defineProps<ScalarMarkdownSummaryProps>()\n\nconst id = useId()\n\n/** * Whether the summary is open. */\nconst open = defineModel<boolean>({ default: false })\n\n/** Whether the markdown is being truncated */\nconst truncated = defineModel<boolean>('truncated', { default: false })\n\nconst markdown = useTemplateRef('scalar-markdown')\n\nuseResizeObserver(() => markdown.value?.el, checkTruncation)\n\n/** Check if the markdown is being truncated */\nfunction checkTruncation() {\n const el = markdown.value?.el\n if (!el) {\n return\n }\n truncated.value =\n el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth\n}\n\nonMounted(checkTruncation)\n\nconst { cx } = useBindCx()\ndefineOptions({ inheritAttrs: false })\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'group/summary flex gap-0.5',\n open ? 'flex-col' : 'flex-row items-baseline',\n )\n \">\n <ScalarMarkdown\n v-bind=\"props\"\n :id=\"id\"\n ref=\"scalar-markdown\"\n :clamp=\"open ? undefined : clamp\"\n :class=\"{ 'markdown-summary truncate': !open }\" />\n <button\n v-if=\"!controlled && (truncated || open)\"\n :aria-controls=\"id\"\n :aria-expanded=\"open\"\n class=\"whitespace-nowrap font-medium hover:underline\"\n :class=\"{ 'self-end': open }\"\n type=\"button\"\n @click=\"open = !open\">\n <slot\n name=\"button\"\n :open=\"open\">\n {{ open ? ' Show Less' : 'More' }}\n </slot>\n </button>\n </div>\n</template>\n<style>\n.scalar-app {\n /*\n Summarized markdown styles\n Doubled up to bump specificity\n */\n .markdown.markdown-summary.markdown-summary {\n /* Hide the before and after pseudo-elements for all elements */\n *:before,\n *:after {\n content: none;\n }\n\n *:not(strong, em, a, code) {\n /* Disable formatting for non-inline elements */\n display: contents;\n font-size: inherit;\n font-weight: inherit;\n line-height: var(--markdown-line-height);\n }\n\n /* Hide elements that don't make sense in the summary */\n img,\n svg,\n hr,\n pre {\n display: none;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarMarkdownSummary.vue.script.js","names":[],"sources":["../../../src/components/ScalarMarkdown/ScalarMarkdownSummary.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Markdown Summary component\n *\n * Displays a truncated preview of markdown content with\n * a \"More\" / \"Show Less\" toggle to expand and collapse.\n *\n * @example\n * <ScalarMarkdownSummary value=\"Long markdown text...\" :clamp=\"2\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { useResizeObserver } from '@vueuse/core'\nimport { onMounted, useId, useTemplateRef } from 'vue'\n\nimport ScalarMarkdown from './ScalarMarkdown.vue'\nimport type { ScalarMarkdownSummaryProps } from './types'\n\nconst { clamp = 1, ...props } = defineProps<ScalarMarkdownSummaryProps>()\n\nconst id = useId()\n\n/** * Whether the summary is open. */\nconst open = defineModel<boolean>({ default: false })\n\n/** Whether the markdown is being truncated */\nconst truncated = defineModel<boolean>('truncated', { default: false })\n\nconst markdown = useTemplateRef('scalar-markdown')\n\nuseResizeObserver(() => markdown.value?.el, checkTruncation)\n\n/** Check if the markdown is being truncated */\nfunction checkTruncation() {\n const el = markdown.value?.el\n if (!el) {\n return\n }\n truncated.value =\n el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth\n}\n\nonMounted(checkTruncation)\n\nconst { cx } = useBindCx()\ndefineOptions({ inheritAttrs: false })\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'group/summary flex gap-0.5',\n open ? 'flex-col' : 'flex-row items-baseline',\n )\n \">\n <ScalarMarkdown\n v-bind=\"props\"\n :id=\"id\"\n ref=\"scalar-markdown\"\n :clamp=\"open ? undefined : clamp\"\n :class=\"{ 'markdown-summary truncate': !open }\" />\n <button\n v-if=\"!controlled && (truncated || open)\"\n :aria-controls=\"id\"\n :aria-expanded=\"open\"\n class=\"whitespace-nowrap font-medium hover:underline\"\n :class=\"{ 'self-end': open }\"\n type=\"button\"\n @click=\"open = !open\">\n <slot\n name=\"button\"\n :open=\"open\">\n {{ open ? ' Show Less' : 'More' }}\n </slot>\n </button>\n </div>\n</template>\n<style>\n.scalar-app {\n /*\n Summarized markdown styles\n Doubled up to bump specificity\n */\n .markdown.markdown-summary.markdown-summary {\n /* Hide the before and after pseudo-elements for all elements */\n *:before,\n *:after {\n content: none;\n }\n\n *:not(strong, em, a) {\n /* Disable formatting for non-inline elements */\n display: contents;\n font-size: inherit;\n font-weight: inherit;\n line-height: var(--markdown-line-height);\n }\n\n /* Hide elements that don't make sense in the summary */\n img,\n svg,\n hr,\n pre {\n display: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBA,MAAM,QAA0B,qBAAA,SAAA,CAAA,OAAA,CAAA;EAEhC,MAAM,KAAK,MAAM;;EAGjB,MAAM,OAAO,SAAoB,SAAA,YAAmB;;EAGpD,MAAM,YAAY,SAAoB,SAAC,WAA+B;EAEtE,MAAM,WAAW,eAAe,iBAAiB;EAEjD,wBAAwB,SAAS,OAAO,IAAI,eAAe;;EAG3D,SAAS,kBAAkB;GACzB,MAAM,KAAK,SAAS,OAAO;GAC3B,IAAI,CAAC,IACH;GAEF,UAAU,QACR,GAAG,eAAe,GAAG,gBAAgB,GAAG,cAAc,GAAG;EAC7D;EAEA,UAAU,eAAe;EAEzB,MAAM,EAAE,OAAO,UAAU;;uBAIvB,mBA2BM,OAAA,eAAA,mBA1BW,MAAA,EAAA,CAAE,CAAA,8BAAgD,KAAA,QAAI,aAAA,yBAAA,CAAA,CAAA,GAAA,CAMrE,YAKoD,wBALpD,WACU,OAAK;IACZ,IAAI,MAAA,EAAA;IACL,KAAI;IACH,OAAO,KAAA,QAAO,KAAA,IAAY,QAAA;IAC1B,OAAK,EAAA,6BAAA,CAAkC,KAAA,MAAI;;;;;QAErC,QAAA,eAAe,UAAA,SAAa,KAAA,UAAA,UAAA,GADrC,mBAaS,UAAA;;IAXN,iBAAe,MAAA,EAAA;IACf,iBAAe,KAAA;IAChB,OAAK,eAAA,CAAC,iDAA+C,EAAA,YAC/B,KAAA,MAAI,CAAA,CAAA;IAC1B,MAAK;IACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAA,QAAI,CAAI,KAAA;OAChB,WAIO,KAAA,QAAA,UAAA,EAFJ,MAAM,KAAA,MAAI,SAEN,CAAA,gBAAA,gBADF,KAAA,QAAI,eAAA,MAAA,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA"}
|
|
1
|
+
{"version":3,"file":"ScalarMarkdownSummary.vue.script.js","names":[],"sources":["../../../src/components/ScalarMarkdown/ScalarMarkdownSummary.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Markdown Summary component\n *\n * Displays a truncated preview of markdown content with\n * a \"More\" / \"Show Less\" toggle to expand and collapse.\n *\n * @example\n * <ScalarMarkdownSummary value=\"Long markdown text...\" :clamp=\"2\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { useResizeObserver } from '@vueuse/core'\nimport { onMounted, useId, useTemplateRef } from 'vue'\n\nimport ScalarMarkdown from './ScalarMarkdown.vue'\nimport type { ScalarMarkdownSummaryProps } from './types'\n\nconst { clamp = 1, ...props } = defineProps<ScalarMarkdownSummaryProps>()\n\nconst id = useId()\n\n/** * Whether the summary is open. */\nconst open = defineModel<boolean>({ default: false })\n\n/** Whether the markdown is being truncated */\nconst truncated = defineModel<boolean>('truncated', { default: false })\n\nconst markdown = useTemplateRef('scalar-markdown')\n\nuseResizeObserver(() => markdown.value?.el, checkTruncation)\n\n/** Check if the markdown is being truncated */\nfunction checkTruncation() {\n const el = markdown.value?.el\n if (!el) {\n return\n }\n truncated.value =\n el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth\n}\n\nonMounted(checkTruncation)\n\nconst { cx } = useBindCx()\ndefineOptions({ inheritAttrs: false })\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'group/summary flex gap-0.5',\n open ? 'flex-col' : 'flex-row items-baseline',\n )\n \">\n <ScalarMarkdown\n v-bind=\"props\"\n :id=\"id\"\n ref=\"scalar-markdown\"\n :clamp=\"open ? undefined : clamp\"\n :class=\"{ 'markdown-summary truncate': !open }\" />\n <button\n v-if=\"!controlled && (truncated || open)\"\n :aria-controls=\"id\"\n :aria-expanded=\"open\"\n class=\"whitespace-nowrap font-medium hover:underline\"\n :class=\"{ 'self-end': open }\"\n type=\"button\"\n @click=\"open = !open\">\n <slot\n name=\"button\"\n :open=\"open\">\n {{ open ? ' Show Less' : 'More' }}\n </slot>\n </button>\n </div>\n</template>\n<style>\n.scalar-app {\n /*\n Summarized markdown styles\n Doubled up to bump specificity\n */\n .markdown.markdown-summary.markdown-summary {\n /* Hide the before and after pseudo-elements for all elements */\n *:before,\n *:after {\n content: none;\n }\n\n *:not(strong, em, a, code) {\n /* Disable formatting for non-inline elements */\n display: contents;\n font-size: inherit;\n font-weight: inherit;\n line-height: var(--markdown-line-height);\n }\n\n /* Hide elements that don't make sense in the summary */\n img,\n svg,\n hr,\n pre {\n display: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBA,MAAM,QAA0B,qBAAA,SAAA,CAAA,OAAA,CAAA;EAEhC,MAAM,KAAK,MAAM;;EAGjB,MAAM,OAAO,SAAoB,SAAA,YAAmB;;EAGpD,MAAM,YAAY,SAAoB,SAAC,WAA+B;EAEtE,MAAM,WAAW,eAAe,iBAAiB;EAEjD,wBAAwB,SAAS,OAAO,IAAI,eAAe;;EAG3D,SAAS,kBAAkB;GACzB,MAAM,KAAK,SAAS,OAAO;GAC3B,IAAI,CAAC,IACH;GAEF,UAAU,QACR,GAAG,eAAe,GAAG,gBAAgB,GAAG,cAAc,GAAG;EAC7D;EAEA,UAAU,eAAe;EAEzB,MAAM,EAAE,OAAO,UAAU;;uBAIvB,mBA2BM,OAAA,eAAA,mBA1BW,MAAA,EAAA,CAAE,CAAA,8BAAgD,KAAA,QAAI,aAAA,yBAAA,CAAA,CAAA,GAAA,CAMrE,YAKoD,wBALpD,WACU,OAAK;IACZ,IAAI,MAAA,EAAA;IACL,KAAI;IACH,OAAO,KAAA,QAAO,KAAA,IAAY,QAAA;IAC1B,OAAK,EAAA,6BAAA,CAAkC,KAAA,MAAI;;;;;QAErC,QAAA,eAAe,UAAA,SAAa,KAAA,UAAA,UAAA,GADrC,mBAaS,UAAA;;IAXN,iBAAe,MAAA,EAAA;IACf,iBAAe,KAAA;IAChB,OAAK,eAAA,CAAC,iDAA+C,EAAA,YAC/B,KAAA,MAAI,CAAA,CAAA;IAC1B,MAAK;IACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAA,QAAI,CAAI,KAAA;OAChB,WAIO,KAAA,QAAA,UAAA,EAFJ,MAAM,KAAA,MAAI,SAEN,CAAA,gBAAA,gBADF,KAAA,QAAI,eAAA,MAAA,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarSidebarFooter.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarSidebarFooter.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"names":[],"mappings":"AAuEA;;;;;;;;;;;;;;;;GAgBG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,8CAA8C;gBAClC,OAAO;IACnB,0CAA0C;oBAC1B,OAAO;IACvB,qCAAqC;eAC1B,OAAO;EAwEhB,CAAC;AACL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarSidebarFooter.vue.js","names":[],"sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Sidebar Footer component\n *\n * A footer for the sidebar, be default it contains a\n * \"Powered by Scalar\" link and a {@link ScalarColorModeToggle} toggle.\n *\n * @example\n * <ScalarSidebarFooter>\n * <!-- Footer content at the top of the footer -->\n * <template #description>\n * <!-- Replaces the Powered by Scalar link -->\n * </template>\n * <template #toggle>\n * <!-- Replaces the color mode toggle -->\n * </template>\n * </ScalarSidebarFooter>\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport { ScalarColorModeToggle } from '../ScalarColorModeToggle'\n\ndefineSlots<{\n /** Footer content at the top of the footer */\n default?(): unknown\n /** Replaces the Powered by Scalar link */\n description?(): unknown\n /** Replaces the color mode toggle */\n toggle?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div v-bind=\"cx('flex flex-col gap-3 px-3 pb-3 border-sidebar-border')\">\n <slot />\n <div class=\"flex items-center\">\n <div class=\"flex-1 min-w-0 flex items-center text-sm text-sidebar-c-2\">\n <slot name=\"description\">\n <a\n class=\"no-underline hover:underline\"\n href=\"
|
|
1
|
+
{"version":3,"file":"ScalarSidebarFooter.vue.js","names":[],"sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Sidebar Footer component\n *\n * A footer for the sidebar, be default it contains a\n * \"Powered by Scalar\" link and a {@link ScalarColorModeToggle} toggle.\n *\n * @example\n * <ScalarSidebarFooter>\n * <!-- Footer content at the top of the footer -->\n * <template #description>\n * <!-- Replaces the Powered by Scalar link -->\n * </template>\n * <template #toggle>\n * <!-- Replaces the color mode toggle -->\n * </template>\n * </ScalarSidebarFooter>\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { makePoweredByUrl } from '@scalar/helpers/url/make-powered-by-url'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport { ScalarColorModeToggle } from '../ScalarColorModeToggle'\n\ndefineSlots<{\n /** Footer content at the top of the footer */\n default?(): unknown\n /** Replaces the Powered by Scalar link */\n description?(): unknown\n /** Replaces the color mode toggle */\n toggle?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** The footer does not know the integration, so the link goes out without a campaign */\nconst poweredByUrl = makePoweredByUrl()\n</script>\n<template>\n <div v-bind=\"cx('flex flex-col gap-3 px-3 pb-3 border-sidebar-border')\">\n <slot />\n <div class=\"flex items-center\">\n <div class=\"flex-1 min-w-0 flex items-center text-sm text-sidebar-c-2\">\n <slot name=\"description\">\n <a\n class=\"no-underline hover:underline\"\n :href=\"poweredByUrl\"\n rel=\"noopener\"\n target=\"_blank\">\n Powered by Scalar\n </a>\n </slot>\n </div>\n <slot name=\"toggle\">\n <ScalarColorModeToggle />\n </slot>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import ScalarColorModeToggle_default from "../ScalarColorModeToggle/ScalarColorModeToggle.vue.js";
|
|
2
2
|
import { useBindCx } from "@scalar/use-hooks/useBindCx";
|
|
3
3
|
import { createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, normalizeProps, openBlock, renderSlot, unref } from "vue";
|
|
4
|
+
import { makePoweredByUrl } from "@scalar/helpers/url/make-powered-by-url";
|
|
4
5
|
//#region src/components/ScalarSidebar/ScalarSidebarFooter.vue?vue&type=script&setup=true&lang.ts
|
|
5
6
|
var _hoisted_1 = { class: "flex items-center" };
|
|
6
7
|
var _hoisted_2 = { class: "flex-1 min-w-0 flex items-center text-sm text-sidebar-c-2" };
|
|
8
|
+
var _hoisted_3 = ["href"];
|
|
7
9
|
var ScalarSidebarFooter_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
8
10
|
inheritAttrs: false,
|
|
9
11
|
__name: "ScalarSidebarFooter",
|
|
10
12
|
setup(__props) {
|
|
11
13
|
const { cx } = useBindCx();
|
|
14
|
+
/** The footer does not know the integration, so the link goes out without a campaign */
|
|
15
|
+
const poweredByUrl = makePoweredByUrl();
|
|
12
16
|
return (_ctx, _cache) => {
|
|
13
|
-
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("flex flex-col gap-3 px-3 pb-3 border-sidebar-border"))), [renderSlot(_ctx.$slots, "default"), createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "description", {}, () => [
|
|
17
|
+
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("flex flex-col gap-3 px-3 pb-3 border-sidebar-border"))), [renderSlot(_ctx.$slots, "default"), createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "description", {}, () => [createElementVNode("a", {
|
|
14
18
|
class: "no-underline hover:underline",
|
|
15
|
-
href:
|
|
19
|
+
href: unref(poweredByUrl),
|
|
20
|
+
rel: "noopener",
|
|
16
21
|
target: "_blank"
|
|
17
|
-
}, " Powered by Scalar ",
|
|
22
|
+
}, " Powered by Scalar ", 8, _hoisted_3)])]), renderSlot(_ctx.$slots, "toggle", {}, () => [createVNode(unref(ScalarColorModeToggle_default))])])], 16);
|
|
18
23
|
};
|
|
19
24
|
}
|
|
20
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarSidebarFooter.vue.script.js","names":[],"sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Sidebar Footer component\n *\n * A footer for the sidebar, be default it contains a\n * \"Powered by Scalar\" link and a {@link ScalarColorModeToggle} toggle.\n *\n * @example\n * <ScalarSidebarFooter>\n * <!-- Footer content at the top of the footer -->\n * <template #description>\n * <!-- Replaces the Powered by Scalar link -->\n * </template>\n * <template #toggle>\n * <!-- Replaces the color mode toggle -->\n * </template>\n * </ScalarSidebarFooter>\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport { ScalarColorModeToggle } from '../ScalarColorModeToggle'\n\ndefineSlots<{\n /** Footer content at the top of the footer */\n default?(): unknown\n /** Replaces the Powered by Scalar link */\n description?(): unknown\n /** Replaces the color mode toggle */\n toggle?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div v-bind=\"cx('flex flex-col gap-3 px-3 pb-3 border-sidebar-border')\">\n <slot />\n <div class=\"flex items-center\">\n <div class=\"flex-1 min-w-0 flex items-center text-sm text-sidebar-c-2\">\n <slot name=\"description\">\n <a\n class=\"no-underline hover:underline\"\n href=\"
|
|
1
|
+
{"version":3,"file":"ScalarSidebarFooter.vue.script.js","names":[],"sources":["../../../src/components/ScalarSidebar/ScalarSidebarFooter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Sidebar Footer component\n *\n * A footer for the sidebar, be default it contains a\n * \"Powered by Scalar\" link and a {@link ScalarColorModeToggle} toggle.\n *\n * @example\n * <ScalarSidebarFooter>\n * <!-- Footer content at the top of the footer -->\n * <template #description>\n * <!-- Replaces the Powered by Scalar link -->\n * </template>\n * <template #toggle>\n * <!-- Replaces the color mode toggle -->\n * </template>\n * </ScalarSidebarFooter>\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { makePoweredByUrl } from '@scalar/helpers/url/make-powered-by-url'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport { ScalarColorModeToggle } from '../ScalarColorModeToggle'\n\ndefineSlots<{\n /** Footer content at the top of the footer */\n default?(): unknown\n /** Replaces the Powered by Scalar link */\n description?(): unknown\n /** Replaces the color mode toggle */\n toggle?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** The footer does not know the integration, so the link goes out without a campaign */\nconst poweredByUrl = makePoweredByUrl()\n</script>\n<template>\n <div v-bind=\"cx('flex flex-col gap-3 px-3 pb-3 border-sidebar-border')\">\n <slot />\n <div class=\"flex items-center\">\n <div class=\"flex-1 min-w-0 flex items-center text-sm text-sidebar-c-2\">\n <slot name=\"description\">\n <a\n class=\"no-underline hover:underline\"\n :href=\"poweredByUrl\"\n rel=\"noopener\"\n target=\"_blank\">\n Powered by Scalar\n </a>\n </slot>\n </div>\n <slot name=\"toggle\">\n <ScalarColorModeToggle />\n </slot>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;EAoCA,MAAM,EAAE,OAAO,UAAU;;EAGzB,MAAM,eAAe,iBAAiB;;uBAGpC,mBAkBM,OAAA,eAAA,mBAlBO,MAAA,EAAA,CAAE,CAAA,qDAAA,CAAA,CAAA,GAAA,CACb,WAAQ,KAAA,QAAA,SAAA,GACR,mBAeM,OAfN,YAeM,CAdJ,mBAUM,OAVN,YAUM,CATJ,WAQO,KAAA,QAAA,eAAA,CAAA,SAAA,CAPL,mBAMI,KAAA;IALF,OAAM;IACL,MAAM,MAAA,YAAA;IACP,KAAI;IACJ,QAAO;MAAS,uBAElB,GAAA,UAAA,CAAA,CAAA,CAAA,CAAA,GAGJ,WAEO,KAAA,QAAA,UAAA,CAAA,SAAA,CADL,YAAyB,MAAA,6BAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA"}
|
|
@@ -14,6 +14,8 @@ export default _default;
|
|
|
14
14
|
declare const __VLS_export: import("vue").DefineComponent<{
|
|
15
15
|
/** Text content to display */
|
|
16
16
|
content: string;
|
|
17
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
18
|
+
label?: string;
|
|
17
19
|
/** Language label for the copy button */
|
|
18
20
|
lang?: StandardLanguageKey | string;
|
|
19
21
|
/** Copy button visibility: 'always', 'hover', or false */
|
|
@@ -23,6 +25,8 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
23
25
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
|
|
24
26
|
/** Text content to display */
|
|
25
27
|
content: string;
|
|
28
|
+
/** Accessible name of the keyboard-scrollable code region. */
|
|
29
|
+
label?: string;
|
|
26
30
|
/** Language label for the copy button */
|
|
27
31
|
lang?: StandardLanguageKey | string;
|
|
28
32
|
/** Copy button visibility: 'always', 'hover', or false */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarVirtualCodeBlock.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarVirtualCodeBlock.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"names":[],"mappings":"AA8EA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAKnE;;;;;;;;;GASG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,8BAA8B;aACrB,MAAM;IACf,8DAA8D;YACtD,MAAM;IACd,yCAAyC;WAClC,mBAAmB,GAAG,MAAM;IACnC,0DAA0D;WACnD,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC,oCAAoC;iBACvB,MAAM;;IATnB,8BAA8B;aACrB,MAAM;IACf,8DAA8D;YACtD,MAAM;IACd,yCAAyC;WAClC,mBAAmB,GAAG,MAAM;IACnC,0DAA0D;WACnD,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC,oCAAoC;iBACvB,MAAM;iGA2GjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarVirtualCodeBlock.vue.js","names":[],"sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Virtual Code Block component\n *\n * Renders large blocks of code using virtualized scrolling with a copy button.\n * Used as a fallback for ScalarCodeBlock when content exceeds the performance\n * threshold for syntax highlighting.\n *\n * @example\n * <ScalarVirtualCodeBlock content=\"large json string\" lang=\"json\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarCodeBlockCopy from '../ScalarCodeBlock/ScalarCodeBlockCopy.vue'\nimport type { StandardLanguageKey } from '../ScalarCodeBlock/types'\nimport ScalarCopyBackdrop from '../ScalarCopy/ScalarCopyBackdrop.vue'\nimport ScalarVirtualText from '../ScalarVirtualText/ScalarVirtualText.vue'\n\nconst {\n content,\n lang = 'plaintext',\n copy = 'hover',\n lineHeight = 20,\n} = defineProps<{\n /** Text content to display */\n content: string\n /** Language label for the copy button */\n lang?: StandardLanguageKey | string\n /** Copy button visibility: 'always', 'hover', or false */\n copy?: 'always' | 'hover' | false\n /** Height of each line in pixels */\n lineHeight?: number\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0',\n )\n \">\n <ScalarVirtualText\n containerClass=\"custom-scroll overflow-auto flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"lineHeight\"\n :text=\"content\" />\n <ScalarCodeBlockCopy\n v-if=\"copy\"\n class=\"scalar-code-copy absolute top-2.5 right-2.5\"\n :class=\"[{ 'opacity-100': copy === 'always' }]\"\n :content=\"content\"\n :
|
|
1
|
+
{"version":3,"file":"ScalarVirtualCodeBlock.vue.js","names":[],"sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Virtual Code Block component\n *\n * Renders large blocks of code using virtualized scrolling with a copy button.\n * Used as a fallback for ScalarCodeBlock when content exceeds the performance\n * threshold for syntax highlighting.\n *\n * @example\n * <ScalarVirtualCodeBlock content=\"large json string\" lang=\"json\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarCodeBlockCopy from '../ScalarCodeBlock/ScalarCodeBlockCopy.vue'\nimport type { StandardLanguageKey } from '../ScalarCodeBlock/types'\nimport ScalarCopyBackdrop from '../ScalarCopy/ScalarCopyBackdrop.vue'\nimport ScalarVirtualText from '../ScalarVirtualText/ScalarVirtualText.vue'\n\nconst {\n content,\n label = 'Code sample',\n lang = 'plaintext',\n copy = 'hover',\n lineHeight = 20,\n} = defineProps<{\n /** Text content to display */\n content: string\n /** Accessible name of the keyboard-scrollable code region. */\n label?: string\n /** Language label for the copy button */\n lang?: StandardLanguageKey | string\n /** Copy button visibility: 'always', 'hover', or false */\n copy?: 'always' | 'hover' | false\n /** Height of each line in pixels */\n lineHeight?: number\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0',\n )\n \">\n <ScalarVirtualText\n :aria-label=\"label\"\n containerClass=\"custom-scroll overflow-auto flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"lineHeight\"\n role=\"region\"\n :text=\"content\" />\n <ScalarCodeBlockCopy\n v-if=\"copy\"\n class=\"scalar-code-copy absolute top-2.5 right-2.5\"\n :class=\"[{ 'opacity-100': copy === 'always' }]\"\n :content=\"content\"\n :lang=\"lang\"\n :showLang=\"true\">\n <template #backdrop>\n <ScalarCopyBackdrop\n class=\"scalar-code-copy-backdrop -right-1.5 -top-1\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -8,6 +8,7 @@ var ScalarVirtualCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
8
8
|
__name: "ScalarVirtualCodeBlock",
|
|
9
9
|
props: {
|
|
10
10
|
content: {},
|
|
11
|
+
label: { default: "Code sample" },
|
|
11
12
|
lang: { default: "plaintext" },
|
|
12
13
|
copy: {
|
|
13
14
|
type: [String, Boolean],
|
|
@@ -19,16 +20,22 @@ var ScalarVirtualCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
19
20
|
const { cx } = useBindCx();
|
|
20
21
|
return (_ctx, _cache) => {
|
|
21
22
|
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("scalar-code-block group/code-block flex flex-col", "relative bg-b-1 min-h-0 min-w-0"))), [createVNode(ScalarVirtualText_default, {
|
|
23
|
+
"aria-label": __props.label,
|
|
22
24
|
containerClass: "custom-scroll overflow-auto flex flex-1 max-h-screen",
|
|
23
25
|
contentClass: "language-plaintext whitespace-pre font-code text-base p-2",
|
|
24
26
|
lineHeight: __props.lineHeight,
|
|
27
|
+
role: "region",
|
|
25
28
|
text: __props.content
|
|
26
|
-
}, null, 8, [
|
|
29
|
+
}, null, 8, [
|
|
30
|
+
"aria-label",
|
|
31
|
+
"lineHeight",
|
|
32
|
+
"text"
|
|
33
|
+
]), __props.copy ? (openBlock(), createBlock(ScalarCodeBlockCopy_default, {
|
|
27
34
|
key: 0,
|
|
28
35
|
class: normalizeClass(["scalar-code-copy absolute top-2.5 right-2.5", [{ "opacity-100": __props.copy === "always" }]]),
|
|
29
36
|
content: __props.content,
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
lang: __props.lang,
|
|
38
|
+
showLang: true
|
|
32
39
|
}, {
|
|
33
40
|
backdrop: withCtx(() => [createVNode(ScalarCopyBackdrop_default, { class: "scalar-code-copy-backdrop -right-1.5 -top-1" })]),
|
|
34
41
|
_: 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarVirtualCodeBlock.vue.script.js","names":[],"sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Virtual Code Block component\n *\n * Renders large blocks of code using virtualized scrolling with a copy button.\n * Used as a fallback for ScalarCodeBlock when content exceeds the performance\n * threshold for syntax highlighting.\n *\n * @example\n * <ScalarVirtualCodeBlock content=\"large json string\" lang=\"json\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarCodeBlockCopy from '../ScalarCodeBlock/ScalarCodeBlockCopy.vue'\nimport type { StandardLanguageKey } from '../ScalarCodeBlock/types'\nimport ScalarCopyBackdrop from '../ScalarCopy/ScalarCopyBackdrop.vue'\nimport ScalarVirtualText from '../ScalarVirtualText/ScalarVirtualText.vue'\n\nconst {\n content,\n lang = 'plaintext',\n copy = 'hover',\n lineHeight = 20,\n} = defineProps<{\n /** Text content to display */\n content: string\n /** Language label for the copy button */\n lang?: StandardLanguageKey | string\n /** Copy button visibility: 'always', 'hover', or false */\n copy?: 'always' | 'hover' | false\n /** Height of each line in pixels */\n lineHeight?: number\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0',\n )\n \">\n <ScalarVirtualText\n containerClass=\"custom-scroll overflow-auto flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"lineHeight\"\n :text=\"content\" />\n <ScalarCodeBlockCopy\n v-if=\"copy\"\n class=\"scalar-code-copy absolute top-2.5 right-2.5\"\n :class=\"[{ 'opacity-100': copy === 'always' }]\"\n :content=\"content\"\n :
|
|
1
|
+
{"version":3,"file":"ScalarVirtualCodeBlock.vue.script.js","names":[],"sources":["../../../src/components/ScalarVirtualCodeBlock/ScalarVirtualCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Virtual Code Block component\n *\n * Renders large blocks of code using virtualized scrolling with a copy button.\n * Used as a fallback for ScalarCodeBlock when content exceeds the performance\n * threshold for syntax highlighting.\n *\n * @example\n * <ScalarVirtualCodeBlock content=\"large json string\" lang=\"json\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarCodeBlockCopy from '../ScalarCodeBlock/ScalarCodeBlockCopy.vue'\nimport type { StandardLanguageKey } from '../ScalarCodeBlock/types'\nimport ScalarCopyBackdrop from '../ScalarCopy/ScalarCopyBackdrop.vue'\nimport ScalarVirtualText from '../ScalarVirtualText/ScalarVirtualText.vue'\n\nconst {\n content,\n label = 'Code sample',\n lang = 'plaintext',\n copy = 'hover',\n lineHeight = 20,\n} = defineProps<{\n /** Text content to display */\n content: string\n /** Accessible name of the keyboard-scrollable code region. */\n label?: string\n /** Language label for the copy button */\n lang?: StandardLanguageKey | string\n /** Copy button visibility: 'always', 'hover', or false */\n copy?: 'always' | 'hover' | false\n /** Height of each line in pixels */\n lineHeight?: number\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'scalar-code-block group/code-block flex flex-col',\n 'relative bg-b-1 min-h-0 min-w-0',\n )\n \">\n <ScalarVirtualText\n :aria-label=\"label\"\n containerClass=\"custom-scroll overflow-auto flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"lineHeight\"\n role=\"region\"\n :text=\"content\" />\n <ScalarCodeBlockCopy\n v-if=\"copy\"\n class=\"scalar-code-copy absolute top-2.5 right-2.5\"\n :class=\"[{ 'opacity-100': copy === 'always' }]\"\n :content=\"content\"\n :lang=\"lang\"\n :showLang=\"true\">\n <template #backdrop>\n <ScalarCopyBackdrop\n class=\"scalar-code-copy-backdrop -right-1.5 -top-1\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAyCA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,mBA0BM,OAAA,eAAA,mBAzBW,MAAA,EAAA,CAAE,CAAA,oDAAA,iCAAA,CAAA,CAAA,GAAA,CAMjB,YAMoB,2BAAA;IALjB,cAAY,QAAA;IACb,gBAAe;IACf,cAAa;IACZ,YAAY,QAAA;IACb,MAAK;IACJ,MAAM,QAAA;;;;;OAED,QAAA,QAAA,UAAA,GADR,YAWsB,6BAAA;;IATpB,OAAK,eAAA,CAAC,+CAA6C,CAAA,EAAA,eACzB,QAAA,SAAI,SAAA,CAAA,CAAA,CAAA;IAC7B,SAAS,QAAA;IACT,MAAM,QAAA;IACN,UAAU;;IACA,UAAQ,cAEuC,CADxD,YACwD,4BAAA,EAAtD,OAAM,8CAA6C,CAAA,CAAA,CAAA"}
|
package/dist/style.css
CHANGED
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
--scalar-background-3: #e7e7e7;
|
|
214
214
|
--scalar-background-accent: #8ab4f81f;
|
|
215
215
|
--scalar-color-1: #1b1b1b;
|
|
216
|
-
--scalar-color-2: #
|
|
216
|
+
--scalar-color-2: #707070;
|
|
217
217
|
--scalar-color-3: #8e8e8e;
|
|
218
218
|
--scalar-color-accent: #09f;
|
|
219
219
|
--scalar-border-color: #dfdfdf;
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
--scalar-color-green: #069061;
|
|
262
262
|
--scalar-color-red: #ef0006;
|
|
263
263
|
--scalar-color-yellow: #edbe20;
|
|
264
|
-
--scalar-color-blue: #
|
|
264
|
+
--scalar-color-blue: #0072bd;
|
|
265
265
|
--scalar-color-orange: #ff5800;
|
|
266
266
|
--scalar-color-purple: #5203d1;
|
|
267
267
|
--scalar-color-query: #007b80;
|
|
@@ -379,7 +379,7 @@
|
|
|
379
379
|
--scalar-color-green: color(display-p3 .023529 .564706 .380392);
|
|
380
380
|
--scalar-color-red: color(display-p3 .937255 0 .023529);
|
|
381
381
|
--scalar-color-yellow: color(display-p3 .929412 .745098 .12549);
|
|
382
|
-
--scalar-color-blue: color(display-p3 0 .
|
|
382
|
+
--scalar-color-blue: color(display-p3 0 .447059 .741176);
|
|
383
383
|
--scalar-color-orange: color(display-p3 1 .4 .02);
|
|
384
384
|
--scalar-color-purple: color(display-p3 .321569 .011765 .819608);
|
|
385
385
|
}
|
|
@@ -411,9 +411,19 @@
|
|
|
411
411
|
:where(.scalar-app) {
|
|
412
412
|
font-family: var(--scalar-font);
|
|
413
413
|
color: var(--scalar-color-1);
|
|
414
|
+
--scalar-focus-color: var(--scalar-color-accent);
|
|
415
|
+
line-height: 1.15;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
419
|
+
:where(.scalar-app) {
|
|
420
|
+
--scalar-focus-color: color-mix(in srgb, var(--scalar-color-accent), var(--scalar-color-1) 20%);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
:where(.scalar-app) {
|
|
414
425
|
-webkit-text-size-adjust: 100%;
|
|
415
426
|
tab-size: 4;
|
|
416
|
-
line-height: 1.15;
|
|
417
427
|
}
|
|
418
428
|
|
|
419
429
|
:where(.scalar-app) *, :where(.scalar-app) :before, :where(.scalar-app) :after {
|
|
@@ -504,6 +514,10 @@
|
|
|
504
514
|
outline-offset: -1px;
|
|
505
515
|
}
|
|
506
516
|
|
|
517
|
+
:where(.scalar-app) a:focus-visible, :where(.scalar-app) button:focus-visible, :where(.scalar-app) [role="button"]:focus-visible {
|
|
518
|
+
outline-color: var(--scalar-focus-color);
|
|
519
|
+
}
|
|
520
|
+
|
|
507
521
|
:where(.scalar-app) button, :where(.scalar-app) [role="button"] {
|
|
508
522
|
cursor: pointer;
|
|
509
523
|
}
|
|
@@ -4481,7 +4495,7 @@ ol > li::marker,
|
|
|
4481
4495
|
*:after {
|
|
4482
4496
|
content: none;
|
|
4483
4497
|
}
|
|
4484
|
-
*:not(strong, em, a) {
|
|
4498
|
+
*:not(strong, em, a, code) {
|
|
4485
4499
|
/* Disable formatting for non-inline elements */
|
|
4486
4500
|
display: contents;
|
|
4487
4501
|
font-size: inherit;
|
package/dist/vue-styles.css
CHANGED
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"url": "git+https://github.com/scalar/scalar.git",
|
|
11
11
|
"directory": "packages/components"
|
|
12
12
|
},
|
|
13
|
-
"version": "0.30.
|
|
13
|
+
"version": "0.30.5",
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=22"
|
|
16
16
|
},
|
|
@@ -217,10 +217,10 @@
|
|
|
217
217
|
"@headlessui/tailwindcss": "^0.2.2",
|
|
218
218
|
"@headlessui/vue": "1.7.23",
|
|
219
219
|
"@scalar/code-highlight": "0.4.7",
|
|
220
|
-
"@scalar/helpers": "0.
|
|
220
|
+
"@scalar/helpers": "0.16.0",
|
|
221
221
|
"@scalar/icons": "0.7.6",
|
|
222
|
-
"@scalar/themes": "0.18.
|
|
223
|
-
"@scalar/use-hooks": "0.4.
|
|
222
|
+
"@scalar/themes": "0.18.1",
|
|
223
|
+
"@scalar/use-hooks": "0.4.16",
|
|
224
224
|
"@vueuse/core": "13.9.0",
|
|
225
225
|
"cva": "1.0.0-beta.4",
|
|
226
226
|
"radix-vue": "^1.9.17",
|