@scalar/components 0.30.5 → 0.30.7
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 +44 -8
- 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 +6 -3
- package/dist/components/ScalarCodeBlock/ScalarCodeBlock.vue.script.js.map +1 -1
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.d.ts +16 -0
- 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 +23 -4
- package/dist/components/ScalarCodeBlock/ScalarCodeBlockCopy.vue.script.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOption.vue.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOption.vue.script.js +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxOption.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 +19 -3
- package/dist/components/ScalarCombobox/ScalarComboboxOptions.vue.script.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxPopover.vue.js.map +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxPopover.vue.script.js +1 -1
- package/dist/components/ScalarCombobox/ScalarComboboxPopover.vue.script.js.map +1 -1
- package/dist/components/ScalarDropdown/ScalarDropdownMenu.vue.d.ts.map +1 -1
- package/dist/components/ScalarDropdown/ScalarDropdownMenu.vue.js.map +1 -1
- package/dist/components/ScalarDropdown/ScalarDropdownMenu.vue.script.js +3 -3
- package/dist/components/ScalarDropdown/ScalarDropdownMenu.vue.script.js.map +1 -1
- package/dist/components/ScalarFloating/ScalarFloatingBackdrop.vue.d.ts +3 -0
- package/dist/components/ScalarFloating/ScalarFloatingBackdrop.vue.d.ts.map +1 -1
- package/dist/components/ScalarFloating/ScalarFloatingBackdrop.vue.js.map +1 -1
- package/dist/components/ScalarFloating/ScalarFloatingBackdrop.vue.script.js +4 -2
- package/dist/components/ScalarFloating/ScalarFloatingBackdrop.vue.script.js.map +1 -1
- package/dist/components/ScalarListbox/ScalarListbox.vue.js.map +1 -1
- package/dist/components/ScalarListbox/ScalarListbox.vue.script.js +1 -1
- package/dist/components/ScalarListbox/ScalarListbox.vue.script.js.map +1 -1
- package/dist/components/ScalarListbox/ScalarListboxItem.vue.js.map +1 -1
- package/dist/components/ScalarListbox/ScalarListboxItem.vue.script.js +1 -1
- package/dist/components/ScalarListbox/ScalarListboxItem.vue.script.js.map +1 -1
- package/dist/components/ScalarPopover/ScalarPopover.vue.js.map +1 -1
- package/dist/components/ScalarPopover/ScalarPopover.vue.script.js +1 -1
- package/dist/components/ScalarPopover/ScalarPopover.vue.script.js.map +1 -1
- package/dist/style.css +45 -12
- package/package.json +4 -4
|
@@ -11,26 +11,44 @@ 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;
|
|
16
14
|
content?: string | object;
|
|
17
15
|
prettyPrintedContent?: string;
|
|
18
16
|
lang?: StandardLanguageKey | string;
|
|
19
17
|
lineNumbers?: boolean;
|
|
20
18
|
hideCredentials?: string | string[];
|
|
21
19
|
copy?: "always" | "hover" | false;
|
|
20
|
+
/**
|
|
21
|
+
* Accessible name for the code scroller. Long code scrolls inside a focusable region so
|
|
22
|
+
* keyboard users can reach it, and a focusable region needs a name or screen readers announce
|
|
23
|
+
* nothing when focus lands on it.
|
|
24
|
+
*/
|
|
25
|
+
label?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Accessible name for the copy button. The visible label only reads "Copy" (and the language)
|
|
28
|
+
* while the block is hovered, so the name has to say what is copied on its own.
|
|
29
|
+
*/
|
|
30
|
+
copyLabel?: string;
|
|
22
31
|
} & {
|
|
23
32
|
/** Raw unformatted object or string content */
|
|
24
33
|
content: string | object;
|
|
25
34
|
}) | ({
|
|
26
|
-
/** Accessible name of the keyboard-scrollable code region. */
|
|
27
|
-
label?: string;
|
|
28
35
|
content?: string | object;
|
|
29
36
|
prettyPrintedContent?: string;
|
|
30
37
|
lang?: StandardLanguageKey | string;
|
|
31
38
|
lineNumbers?: boolean;
|
|
32
39
|
hideCredentials?: string | string[];
|
|
33
40
|
copy?: "always" | "hover" | false;
|
|
41
|
+
/**
|
|
42
|
+
* Accessible name for the code scroller. Long code scrolls inside a focusable region so
|
|
43
|
+
* keyboard users can reach it, and a focusable region needs a name or screen readers announce
|
|
44
|
+
* nothing when focus lands on it.
|
|
45
|
+
*/
|
|
46
|
+
label?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Accessible name for the copy button. The visible label only reads "Copy" (and the language)
|
|
49
|
+
* while the block is hovered, so the name has to say what is copied on its own.
|
|
50
|
+
*/
|
|
51
|
+
copyLabel?: string;
|
|
34
52
|
} & {
|
|
35
53
|
/**
|
|
36
54
|
* Pre-pretty printed content string for better performance
|
|
@@ -39,26 +57,44 @@ declare const __VLS_export: import("vue").DefineComponent<({
|
|
|
39
57
|
*/
|
|
40
58
|
prettyPrintedContent: string;
|
|
41
59
|
}), {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<({
|
|
42
|
-
/** Accessible name of the keyboard-scrollable code region. */
|
|
43
|
-
label?: string;
|
|
44
60
|
content?: string | object;
|
|
45
61
|
prettyPrintedContent?: string;
|
|
46
62
|
lang?: StandardLanguageKey | string;
|
|
47
63
|
lineNumbers?: boolean;
|
|
48
64
|
hideCredentials?: string | string[];
|
|
49
65
|
copy?: "always" | "hover" | false;
|
|
66
|
+
/**
|
|
67
|
+
* Accessible name for the code scroller. Long code scrolls inside a focusable region so
|
|
68
|
+
* keyboard users can reach it, and a focusable region needs a name or screen readers announce
|
|
69
|
+
* nothing when focus lands on it.
|
|
70
|
+
*/
|
|
71
|
+
label?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Accessible name for the copy button. The visible label only reads "Copy" (and the language)
|
|
74
|
+
* while the block is hovered, so the name has to say what is copied on its own.
|
|
75
|
+
*/
|
|
76
|
+
copyLabel?: string;
|
|
50
77
|
} & {
|
|
51
78
|
/** Raw unformatted object or string content */
|
|
52
79
|
content: string | object;
|
|
53
80
|
}) | ({
|
|
54
|
-
/** Accessible name of the keyboard-scrollable code region. */
|
|
55
|
-
label?: string;
|
|
56
81
|
content?: string | object;
|
|
57
82
|
prettyPrintedContent?: string;
|
|
58
83
|
lang?: StandardLanguageKey | string;
|
|
59
84
|
lineNumbers?: boolean;
|
|
60
85
|
hideCredentials?: string | string[];
|
|
61
86
|
copy?: "always" | "hover" | false;
|
|
87
|
+
/**
|
|
88
|
+
* Accessible name for the code scroller. Long code scrolls inside a focusable region so
|
|
89
|
+
* keyboard users can reach it, and a focusable region needs a name or screen readers announce
|
|
90
|
+
* nothing when focus lands on it.
|
|
91
|
+
*/
|
|
92
|
+
label?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Accessible name for the copy button. The visible label only reads "Copy" (and the language)
|
|
95
|
+
* while the block is hovered, so the name has to say what is copied on its own.
|
|
96
|
+
*/
|
|
97
|
+
copyLabel?: string;
|
|
62
98
|
} & {
|
|
63
99
|
/**
|
|
64
100
|
* Pre-pretty printed content string for better performance
|
|
@@ -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":"AAuMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAGlD;;;;;;;;GAQG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;cAEN,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC;;;;OAIG;YACK,MAAM;IACd;;;OAGG;gBACS,MAAM;;IAWV,+CAA+C;aACtC,MAAM,GAAG,MAAM;;cA5BtB,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC;;;;OAIG;YACK,MAAM;IACd;;;OAGG;gBACS,MAAM;;IAeV;;;;OAIG;0BACmB,MAAM;;cApC1B,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC;;;;OAIG;YACK,MAAM;IACd;;;OAGG;gBACS,MAAM;;IAWV,+CAA+C;aACtC,MAAM,GAAG,MAAM;;cA5BtB,MAAM,GAAG,MAAM;2BACF,MAAM;WACtB,mBAAmB,GAAG,MAAM;kBACrB,OAAO;sBACH,MAAM,GAAG,MAAM,EAAE;WAC5B,QAAQ,GAAG,OAAO,GAAG,KAAK;IACjC;;;;OAIG;YACK,MAAM;IACd;;;OAGG;gBACS,MAAM;;IAeV;;;;OAIG;0BACmB,MAAM;kGA+LlC,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
|
|
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 * Accessible name for the code scroller. Long code scrolls inside a focusable region so\n * keyboard users can reach it, and a focusable region needs a name or screen readers announce\n * nothing when focus lands on it.\n */\n label?: string\n /**\n * Accessible name for the copy button. The visible label only reads \"Copy\" (and the language)\n * while the block is hovered, so the name has to say what is copied on its own.\n */\n copyLabel?: string\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 label = 'Code sample',\n copyLabel,\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 <!--\n Inherits the corners so the inset focus ring follows a rounded code block.\n Always a tab stop so code that scrolls stays reachable by keyboard (WCAG 2.1.1). A named\n group rather than a region, so every code sample does not add a landmark.\n -->\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=\"group\"\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 :copyLabel=\"copyLabel\"\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":""}
|
|
@@ -11,7 +11,6 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
11
11
|
inheritAttrs: false,
|
|
12
12
|
__name: "ScalarCodeBlock",
|
|
13
13
|
props: {
|
|
14
|
-
label: { default: "Code sample" },
|
|
15
14
|
content: {},
|
|
16
15
|
prettyPrintedContent: {},
|
|
17
16
|
lang: { default: "plaintext" },
|
|
@@ -23,7 +22,9 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
23
22
|
copy: {
|
|
24
23
|
type: [String, Boolean],
|
|
25
24
|
default: "hover"
|
|
26
|
-
}
|
|
25
|
+
},
|
|
26
|
+
label: { default: "Code sample" },
|
|
27
|
+
copyLabel: {}
|
|
27
28
|
},
|
|
28
29
|
setup(__props) {
|
|
29
30
|
/** Base id for the code block */
|
|
@@ -56,7 +57,7 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
56
57
|
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
58
|
"aria-label": __props.label,
|
|
58
59
|
class: "custom-scroll overflow-x-auto p-2 -outline-offset-2 rounded-[inherit] min-h-0 min-w-0 flex-1",
|
|
59
|
-
role: "
|
|
60
|
+
role: "group",
|
|
60
61
|
tabindex: "0"
|
|
61
62
|
}, [createElementVNode("pre", {
|
|
62
63
|
id: unref(id),
|
|
@@ -67,6 +68,7 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
67
68
|
"aria-controls": unref(id),
|
|
68
69
|
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" }]]),
|
|
69
70
|
content: prettyContent.value,
|
|
71
|
+
copyLabel: __props.copyLabel,
|
|
70
72
|
lang: __props.lang,
|
|
71
73
|
showLang: !isOneLine.value
|
|
72
74
|
}, {
|
|
@@ -76,6 +78,7 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
76
78
|
"aria-controls",
|
|
77
79
|
"class",
|
|
78
80
|
"content",
|
|
81
|
+
"copyLabel",
|
|
79
82
|
"lang",
|
|
80
83
|
"showLang"
|
|
81
84
|
])) : createCommentVNode("", true)], 16);
|
|
@@ -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
|
|
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 * Accessible name for the code scroller. Long code scrolls inside a focusable region so\n * keyboard users can reach it, and a focusable region needs a name or screen readers announce\n * nothing when focus lands on it.\n */\n label?: string\n /**\n * Accessible name for the copy button. The visible label only reads \"Copy\" (and the language)\n * while the block is hovered, so the name has to say what is copied on its own.\n */\n copyLabel?: string\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 label = 'Code sample',\n copyLabel,\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 <!--\n Inherits the corners so the inset focus ring follows a rounded code block.\n Always a tab stop so code that scrolls stays reachable by keyboard (WCAG 2.1.1). A named\n group rather than a region, so every code sample does not add a landmark.\n -->\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=\"group\"\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 :copyLabel=\"copyLabel\"\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2EA,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,mBA+CM,OAAA,eAAA,mBA9CW,MAAA,EAAA,CAAE,CAAA,oDAAA,uDAAA,CAAA,CAAA,GAAA,CAWjB,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,YAuBsB,MAAA,2BAAA,GAAA;;IArBnB,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,WAAW,QAAA;IACX,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"}
|
|
@@ -17,6 +17,14 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
17
17
|
content: string | object;
|
|
18
18
|
/** Language of the code block */
|
|
19
19
|
lang?: StandardLanguageKey | string;
|
|
20
|
+
/**
|
|
21
|
+
* Accessible name for the copy button.
|
|
22
|
+
*
|
|
23
|
+
* The visible label is hidden until the code block is hovered, so without an explicit name
|
|
24
|
+
* screen readers announce an empty button (found by an accessibility audit). Defaults to
|
|
25
|
+
* "Copy <language> code" so the name contains every word the button can show.
|
|
26
|
+
*/
|
|
27
|
+
copyLabel?: string;
|
|
20
28
|
} & {
|
|
21
29
|
copied?: boolean;
|
|
22
30
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
@@ -28,6 +36,14 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
28
36
|
content: string | object;
|
|
29
37
|
/** Language of the code block */
|
|
30
38
|
lang?: StandardLanguageKey | string;
|
|
39
|
+
/**
|
|
40
|
+
* Accessible name for the copy button.
|
|
41
|
+
*
|
|
42
|
+
* The visible label is hidden until the code block is hovered, so without an explicit name
|
|
43
|
+
* screen readers announce an empty button (found by an accessibility audit). Defaults to
|
|
44
|
+
* "Copy <language> code" so the name contains every word the button can show.
|
|
45
|
+
*/
|
|
46
|
+
copyLabel?: string;
|
|
31
47
|
} & {
|
|
32
48
|
copied?: boolean;
|
|
33
49
|
}> & Readonly<{
|
|
@@ -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":"AA0HA,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;IACnC;;;;;;OAMG;gBACS,MAAM;;aAgDT,OAAO;;;;IA7DhB,yCAAyC;eAC9B,OAAO;IAClB,mCAAmC;aAC1B,MAAM,GAAG,MAAM;IACxB,iCAAiC;WAC1B,mBAAmB,GAAG,MAAM;IACnC;;;;;;OAMG;gBACS,MAAM;;aAgDT,OAAO;;;;eAyGH,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 v-model:copied=\"copied\"\n aria-label=\"
|
|
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, lang, showLang, copyLabel } = 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 * Accessible name for the copy button.\n *\n * The visible label is hidden until the code block is hovered, so without an explicit name\n * screen readers announce an empty button (found by an accessibility audit). Defaults to\n * \"Copy <language> code\" so the name contains every word the button can show.\n */\n copyLabel?: 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\n/** The human readable language name shown next to the copy label */\nconst langLabel = computed<string | undefined>(() => {\n if (!lang) {\n return undefined\n }\n return isStandardLanguage(lang) ? LANGUAGE_LABELS[lang] : lang\n})\n\n/**\n * Names the button in every state, including while its visible label is `display: none`.\n * The language is included only when it is shown, so the name always contains the visible text\n * (WCAG 2.5.3) and request and response buttons on one page get distinct names.\n */\nconst accessibleName = computed<string>(() => {\n if (copyLabel) {\n return copyLabel\n }\n return showLang && langLabel.value\n ? `Copy ${langLabel.value} code`\n : 'Copy code'\n})\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <!-- The aria-label comes first so a consumer supplied aria-label attr still wins in the merge -->\n <ScalarCopy\n v-model:copied=\"copied\"\n :aria-label=\"accessibleName\"\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 {{ langLabel }}\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":""}
|
|
@@ -10,7 +10,8 @@ var ScalarCodeBlockCopy_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
|
|
|
10
10
|
props: /*@__PURE__*/ mergeModels({
|
|
11
11
|
showLang: { type: Boolean },
|
|
12
12
|
content: {},
|
|
13
|
-
lang: {}
|
|
13
|
+
lang: {},
|
|
14
|
+
copyLabel: {}
|
|
14
15
|
}, {
|
|
15
16
|
"copied": {
|
|
16
17
|
type: Boolean,
|
|
@@ -27,12 +28,26 @@ var ScalarCodeBlockCopy_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
|
|
|
27
28
|
});
|
|
28
29
|
/** Type guard to check if the language is a standard language */
|
|
29
30
|
const isStandardLanguage = (lang) => lang in LANGUAGE_LABELS;
|
|
31
|
+
/** The human readable language name shown next to the copy label */
|
|
32
|
+
const langLabel = computed(() => {
|
|
33
|
+
if (!__props.lang) return;
|
|
34
|
+
return isStandardLanguage(__props.lang) ? LANGUAGE_LABELS[__props.lang] : __props.lang;
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Names the button in every state, including while its visible label is `display: none`.
|
|
38
|
+
* The language is included only when it is shown, so the name always contains the visible text
|
|
39
|
+
* (WCAG 2.5.3) and request and response buttons on one page get distinct names.
|
|
40
|
+
*/
|
|
41
|
+
const accessibleName = computed(() => {
|
|
42
|
+
if (__props.copyLabel) return __props.copyLabel;
|
|
43
|
+
return __props.showLang && langLabel.value ? `Copy ${langLabel.value} code` : "Copy code";
|
|
44
|
+
});
|
|
30
45
|
const { cx } = useBindCx();
|
|
31
46
|
return (_ctx, _cache) => {
|
|
32
47
|
return openBlock(), createBlock(unref(ScalarCopy_default), mergeProps({
|
|
33
48
|
copied: copied.value,
|
|
34
49
|
"onUpdate:copied": _cache[0] || (_cache[0] = ($event) => copied.value = $event),
|
|
35
|
-
"aria-label":
|
|
50
|
+
"aria-label": accessibleName.value,
|
|
36
51
|
content: contentToCopy.value,
|
|
37
52
|
placement: "left",
|
|
38
53
|
showLabel: ""
|
|
@@ -44,9 +59,13 @@ var ScalarCodeBlockCopy_vue_vue_type_script_setup_true_lang_default = /*@__PURE_
|
|
|
44
59
|
fn: withCtx(() => [createElementVNode("span", _hoisted_1, [__props.showLang ? (openBlock(), createElementBlock("span", {
|
|
45
60
|
key: 0,
|
|
46
61
|
class: normalizeClass(["group-hocus/copy-button:sr-only", { capitalize: !isStandardLanguage(__props.lang) }])
|
|
47
|
-
}, toDisplayString(
|
|
62
|
+
}, toDisplayString(langLabel.value), 3)) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass({ "group-hocus/copy-button:not-sr-only sr-only": __props.showLang }) }, "Copy", 2)])]),
|
|
48
63
|
key: "0"
|
|
49
|
-
} : void 0]), 1040, [
|
|
64
|
+
} : void 0]), 1040, [
|
|
65
|
+
"copied",
|
|
66
|
+
"aria-label",
|
|
67
|
+
"content"
|
|
68
|
+
]);
|
|
50
69
|
};
|
|
51
70
|
}
|
|
52
71
|
});
|
|
@@ -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 v-model:copied=\"copied\"\n aria-label=\"
|
|
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, lang, showLang, copyLabel } = 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 * Accessible name for the copy button.\n *\n * The visible label is hidden until the code block is hovered, so without an explicit name\n * screen readers announce an empty button (found by an accessibility audit). Defaults to\n * \"Copy <language> code\" so the name contains every word the button can show.\n */\n copyLabel?: 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\n/** The human readable language name shown next to the copy label */\nconst langLabel = computed<string | undefined>(() => {\n if (!lang) {\n return undefined\n }\n return isStandardLanguage(lang) ? LANGUAGE_LABELS[lang] : lang\n})\n\n/**\n * Names the button in every state, including while its visible label is `display: none`.\n * The language is included only when it is shown, so the name always contains the visible text\n * (WCAG 2.5.3) and request and response buttons on one page get distinct names.\n */\nconst accessibleName = computed<string>(() => {\n if (copyLabel) {\n return copyLabel\n }\n return showLang && langLabel.value\n ? `Copy ${langLabel.value} code`\n : 'Copy code'\n})\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n\n<template>\n <!-- The aria-label comes first so a consumer supplied aria-label attr still wins in the merge -->\n <ScalarCopy\n v-model:copied=\"copied\"\n :aria-label=\"accessibleName\"\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 {{ langLabel }}\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":";;;;;;;;;;;;;;;;;;;;;;;EAqCA,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,YAAY,eAAmC;GACnD,IAAI,CAAC,QAAA,MACH;GAEF,OAAO,mBAAmB,QAAA,IAAI,IAAI,gBAAgB,QAAA,QAAQ,QAAA;EAC5D,CAAC;;;;;;EAOD,MAAM,iBAAiB,eAAuB;GAC5C,IAAI,QAAA,WACF,OAAO,QAAA;GAET,OAAO,QAAA,YAAY,UAAU,QACzB,QAAQ,UAAU,MAAM,SACxB;EACN,CAAC;EAGD,MAAM,EAAE,OAAO,UAAU;;uBAKvB,YAkCa,MAAA,kBAAA,GAlCb,WAkCa;IAjCH,QAAQ,OAAA;oEAAM,QAAA;IACrB,cAAY,eAAA;IACZ,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,UAAA,KAAS,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAEd,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":"ScalarComboboxOption.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOption.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Option component\n *\n * A single selectable option within a combobox list.\n * Handles active and selected states.\n *\n * @example\n * <ScalarComboboxOption :active=\"true\" :selected=\"false\">\n * Option Label\n * </ScalarComboboxOption>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nconst { active, selected } = defineProps<{\n active?: boolean\n selected?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Group\n 'group/item',\n // Layout\n 'flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n },\n})\n</script>\n<template>\n <li\n :aria-selected=\"selected\"\n :class=\"cx(variants({ active, selected }))\"\n role=\"option\"\n tabindex=\"-1\">\n <slot\n :active\n :selected />\n </li>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarComboboxOption.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOption.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Option component\n *\n * A single selectable option within a combobox list.\n * Handles active and selected states.\n *\n * @example\n * <ScalarComboboxOption :active=\"true\" :selected=\"false\">\n * Option Label\n * </ScalarComboboxOption>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nconst { active, selected } = defineProps<{\n active?: boolean\n selected?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Group\n 'group/item',\n // Layout\n 'flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n },\n})\n</script>\n<template>\n <li\n :aria-selected=\"selected\"\n :class=\"cx(variants({ active, selected }))\"\n role=\"option\"\n tabindex=\"-1\">\n <slot\n :active\n :selected />\n </li>\n</template>\n"],"mappings":""}
|
|
@@ -12,7 +12,7 @@ var ScalarComboboxOption_vue_vue_type_script_setup_true_lang_default = /*@__PURE
|
|
|
12
12
|
const variants = cva({
|
|
13
13
|
base: [
|
|
14
14
|
"group/item",
|
|
15
|
-
"flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left",
|
|
15
|
+
"flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left",
|
|
16
16
|
"truncate bg-transparent text-c-1",
|
|
17
17
|
"cursor-pointer hover:bg-b-2"
|
|
18
18
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarComboboxOption.vue.script.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOption.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Option component\n *\n * A single selectable option within a combobox list.\n * Handles active and selected states.\n *\n * @example\n * <ScalarComboboxOption :active=\"true\" :selected=\"false\">\n * Option Label\n * </ScalarComboboxOption>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nconst { active, selected } = defineProps<{\n active?: boolean\n selected?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Group\n 'group/item',\n // Layout\n 'flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n },\n})\n</script>\n<template>\n <li\n :aria-selected=\"selected\"\n :class=\"cx(variants({ active, selected }))\"\n role=\"option\"\n tabindex=\"-1\">\n <slot\n :active\n :selected />\n </li>\n</template>\n"],"mappings":";;;;;;;;;;;EAuBA,MAAM,WAAW,IAAI;GACnB,MAAM;IAEJ;IAEA;IAEA;IAEA;GACF;GACA,UAAU;IACR,UAAU,EAAE,MAAM,WAAW;IAC7B,QAAQ,EAAE,MAAM,SAAS;GAC3B;EACF,CAAC;;uBAGC,mBAQK,MAAA;IAPF,iBAAe,QAAA;IACf,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAA,QAAG,QAAA;KAAM,UAAE,QAAA;IAAQ,CAAA,CAAA,CAAA;IACtC,MAAK;IACL,UAAS;OACT,WAEc,KAAA,QAAA,WAAA;IADX,QAAA,QAAA;IACA,UAAA,QAAA"}
|
|
1
|
+
{"version":3,"file":"ScalarComboboxOption.vue.script.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxOption.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Option component\n *\n * A single selectable option within a combobox list.\n * Handles active and selected states.\n *\n * @example\n * <ScalarComboboxOption :active=\"true\" :selected=\"false\">\n * Option Label\n * </ScalarComboboxOption>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nconst { active, selected } = defineProps<{\n active?: boolean\n selected?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Group\n 'group/item',\n // Layout\n 'flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n },\n})\n</script>\n<template>\n <li\n :aria-selected=\"selected\"\n :class=\"cx(variants({ active, selected }))\"\n role=\"option\"\n tabindex=\"-1\">\n <slot\n :active\n :selected />\n </li>\n</template>\n"],"mappings":";;;;;;;;;;;EAuBA,MAAM,WAAW,IAAI;GACnB,MAAM;IAEJ;IAEA;IAEA;IAEA;GACF;GACA,UAAU;IACR,UAAU,EAAE,MAAM,WAAW;IAC7B,QAAQ,EAAE,MAAM,SAAS;GAC3B;EACF,CAAC;;uBAGC,mBAQK,MAAA;IAPF,iBAAe,QAAA;IACf,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAA,QAAG,QAAA;KAAM,UAAE,QAAA;IAAQ,CAAA,CAAA,CAAA;IACtC,MAAK;IACL,UAAS;OACT,WAEc,KAAA,QAAA,WAAA;IADX,QAAA,QAAA;IACA,UAAA,QAAA"}
|
|
@@ -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":"AAyVA,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;WAyhBO,OAAO,KAAK,EAAE,WAAW,GAAG,mBAAmB,CAAC;QAvhBtD,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;;qBA6LV,CAAC,EAAE;;;;KAwU2E,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-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":""}
|
|
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/**\n * Space toggles the active option in multiselect mode while the search box is empty.\n *\n * The options look like checkboxes, so keyboard users press Space to toggle them. Without this\n * handler Space types a leading space into the query, which filters out every option whose label\n * has no space and hides the whole list without any feedback (found by an accessibility audit).\n * Once a query has been typed, Space keeps inserting a character so multi-word labels stay\n * searchable. Key auto-repeat is ignored so holding Space after opening the popover with it does\n * not toggle the option repeatedly, and IME composition is left alone.\n */\nconst handleSpace = (event: KeyboardEvent): void => {\n if (!multiselect || query.value !== '' || event.isComposing) {\n return\n }\n // Suppress repeated spaces too, so holding the key does not start filtering.\n event.preventDefault()\n if (!event.repeat) {\n toggleSelected(activeRef.value)\n }\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 <!-- Inset to line up with the options below -->\n <div class=\"relative flex m-1 mb-0.75\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-1.75 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-md border-0 py-1.5 pl-7.25 pr-1.75 leading-none text-c-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.space=\"handleSpace\"\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":""}
|
|
@@ -6,7 +6,7 @@ import { isGroups } from "./types.js";
|
|
|
6
6
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, onMounted, openBlock, ref, renderList, renderSlot, toDisplayString, unref, useId, useModel, useSlots, vModelText, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
7
7
|
import { ScalarIconMagnifyingGlass, ScalarIconPlus } from "@scalar/icons";
|
|
8
8
|
//#region src/components/ScalarCombobox/ScalarComboboxOptions.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
-
var _hoisted_1 = { class: "relative flex" };
|
|
9
|
+
var _hoisted_1 = { class: "relative flex m-1 mb-0.75" };
|
|
10
10
|
var _hoisted_2 = [
|
|
11
11
|
"aria-activedescendant",
|
|
12
12
|
"aria-expanded",
|
|
@@ -102,10 +102,25 @@ var ScalarComboboxOptions_vue_vue_type_script_setup_true_lang_default = /*@__PUR
|
|
|
102
102
|
emit("add");
|
|
103
103
|
query.value = "";
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Space toggles the active option in multiselect mode while the search box is empty.
|
|
107
|
+
*
|
|
108
|
+
* The options look like checkboxes, so keyboard users press Space to toggle them. Without this
|
|
109
|
+
* handler Space types a leading space into the query, which filters out every option whose label
|
|
110
|
+
* has no space and hides the whole list without any feedback (found by an accessibility audit).
|
|
111
|
+
* Once a query has been typed, Space keeps inserting a character so multi-word labels stay
|
|
112
|
+
* searchable. Key auto-repeat is ignored so holding Space after opening the popover with it does
|
|
113
|
+
* not toggle the option repeatedly, and IME composition is left alone.
|
|
114
|
+
*/
|
|
115
|
+
const handleSpace = (event) => {
|
|
116
|
+
if (!__props.multiselect || query.value !== "" || event.isComposing) return;
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
if (!event.repeat) toggleSelected(activeRef.value);
|
|
119
|
+
};
|
|
105
120
|
const input = ref(null);
|
|
106
121
|
onMounted(() => setTimeout(() => input.value?.focus(), 0));
|
|
107
122
|
return (_ctx, _cache) => {
|
|
108
|
-
return openBlock(), createElementBlock(Fragment, null, [createElementVNode("div", _hoisted_1, [createVNode(unref(ScalarIconMagnifyingGlass), { class: "pointer-events-none absolute left-
|
|
123
|
+
return openBlock(), createElementBlock(Fragment, null, [createElementVNode("div", _hoisted_1, [createVNode(unref(ScalarIconMagnifyingGlass), { class: "pointer-events-none absolute left-1.75 top-1/2 -translate-y-1/2 text-c-3 size-4" }), withDirectives(createElementVNode("input", {
|
|
109
124
|
ref_key: "input",
|
|
110
125
|
ref: input,
|
|
111
126
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => query.value = $event),
|
|
@@ -114,7 +129,7 @@ var ScalarComboboxOptions_vue_vue_type_script_setup_true_lang_default = /*@__PUR
|
|
|
114
129
|
"aria-controls": id,
|
|
115
130
|
"aria-expanded": Boolean(filtered.value.length || slots.add || __props.noResults),
|
|
116
131
|
"aria-label": __props.inputLabel,
|
|
117
|
-
class: "min-w-0 flex-1 rounded border-0 py-
|
|
132
|
+
class: "min-w-0 flex-1 rounded-md border-0 py-1.5 pl-7.25 pr-1.75 leading-none text-c-1",
|
|
118
133
|
"data-1p-ignore": "",
|
|
119
134
|
placeholder: __props.placeholder,
|
|
120
135
|
role: "combobox",
|
|
@@ -124,6 +139,7 @@ var ScalarComboboxOptions_vue_vue_type_script_setup_true_lang_default = /*@__PUR
|
|
|
124
139
|
_cache[1] || (_cache[1] = withKeys(withModifiers(($event) => moveActive(1), ["prevent"]), ["down"])),
|
|
125
140
|
_cache[2] || (_cache[2] = withKeys(withModifiers(($event) => activeRef.value && toggleSelected(activeRef.value), ["prevent"]), ["enter"])),
|
|
126
141
|
_cache[3] || (_cache[3] = withKeys(withModifiers(($event) => __props.close?.(), ["prevent"]), ["esc"])),
|
|
142
|
+
withKeys(handleSpace, ["space"]),
|
|
127
143
|
_cache[4] || (_cache[4] = withKeys(withModifiers(($event) => moveActive(-1), ["prevent"]), ["up"]))
|
|
128
144
|
]
|
|
129
145
|
}, null, 40, _hoisted_2), [[vModelText, query.value]])]), withDirectives(createElementVNode("ul", {
|
|
@@ -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-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
|
+
{"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/**\n * Space toggles the active option in multiselect mode while the search box is empty.\n *\n * The options look like checkboxes, so keyboard users press Space to toggle them. Without this\n * handler Space types a leading space into the query, which filters out every option whose label\n * has no space and hides the whole list without any feedback (found by an accessibility audit).\n * Once a query has been typed, Space keeps inserting a character so multi-word labels stay\n * searchable. Key auto-repeat is ignored so holding Space after opening the popover with it does\n * not toggle the option repeatedly, and IME composition is left alone.\n */\nconst handleSpace = (event: KeyboardEvent): void => {\n if (!multiselect || query.value !== '' || event.isComposing) {\n return\n }\n // Suppress repeated spaces too, so holding the key does not start filtering.\n event.preventDefault()\n if (!event.repeat) {\n toggleSelected(activeRef.value)\n }\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 <!-- Inset to line up with the options below -->\n <div class=\"relative flex m-1 mb-0.75\">\n <ScalarIconMagnifyingGlass\n class=\"pointer-events-none absolute left-1.75 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-md border-0 py-1.5 pl-7.25 pr-1.75 leading-none text-c-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.space=\"handleSpace\"\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":"ScalarComboboxPopover.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Popover component\n *\n * Wraps combobox content in a floating popover panel.\n * Handles open/close behavior and keyboard interactions.\n *\n * @example\n * <ScalarComboboxPopover>\n * <button>Toggle</button>\n * <template #popover=\"{ close }\">Content</template>\n * </ScalarComboboxPopover>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { ref } from 'vue'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { ScalarPopoverSlots } from '../ScalarPopover'\n\ndefineProps<ScalarFloatingOptions>()\n\ndefineSlots<ScalarPopoverSlots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** Expose the popover button so we can close the popup */\nconst popoverButtonRef = ref<typeof PopoverButton | null>(null)\n\n/** Open the popover of the up or down arrows are pressed */\nconst handleKeydown = (e: KeyboardEvent) => {\n if (!['ArrowUp', 'ArrowDown'].includes(e.key)) {\n return\n }\n e.preventDefault()\n e.target?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n}\n\ndefineExpose({ popoverButtonRef })\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton\n ref=\"popoverButtonRef\"\n as=\"template\"\n @keydown=\"handleKeydown\">\n <slot :open />\n </PopoverButton>\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :style=\"{ width }\"\n v-bind=\"\n cx('relative flex flex-col max-h-[inherit] w-40 rounded text-sm')\n \">\n <slot\n :close\n name=\"popover\"\n :open />\n <ScalarFloatingBackdrop />\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarComboboxPopover.vue.js","names":[],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Popover component\n *\n * Wraps combobox content in a floating popover panel.\n * Handles open/close behavior and keyboard interactions.\n *\n * @example\n * <ScalarComboboxPopover>\n * <button>Toggle</button>\n * <template #popover=\"{ close }\">Content</template>\n * </ScalarComboboxPopover>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { ref } from 'vue'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { ScalarPopoverSlots } from '../ScalarPopover'\n\ndefineProps<ScalarFloatingOptions>()\n\ndefineSlots<ScalarPopoverSlots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** Expose the popover button so we can close the popup */\nconst popoverButtonRef = ref<typeof PopoverButton | null>(null)\n\n/** Open the popover of the up or down arrows are pressed */\nconst handleKeydown = (e: KeyboardEvent) => {\n if (!['ArrowUp', 'ArrowDown'].includes(e.key)) {\n return\n }\n e.preventDefault()\n e.target?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n}\n\ndefineExpose({ popoverButtonRef })\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton\n ref=\"popoverButtonRef\"\n as=\"template\"\n @keydown=\"handleKeydown\">\n <slot :open />\n </PopoverButton>\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :style=\"{ width }\"\n v-bind=\"\n cx('relative flex flex-col max-h-[inherit] w-40 rounded-xl text-sm')\n \">\n <slot\n :close\n name=\"popover\"\n :open />\n <ScalarFloatingBackdrop />\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":""}
|
|
@@ -44,7 +44,7 @@ var ScalarComboboxPopover_vue_vue_type_script_setup_true_lang_default = /*@__PUR
|
|
|
44
44
|
_: 2
|
|
45
45
|
}, [open ? {
|
|
46
46
|
name: "floating",
|
|
47
|
-
fn: withCtx(({ width }) => [createVNode(unref(PopoverPanel), mergeProps({ style: { width } }, unref(cx)("relative flex flex-col max-h-[inherit] w-40 rounded text-sm")), {
|
|
47
|
+
fn: withCtx(({ width }) => [createVNode(unref(PopoverPanel), mergeProps({ style: { width } }, unref(cx)("relative flex flex-col max-h-[inherit] w-40 rounded-xl text-sm")), {
|
|
48
48
|
default: withCtx(({ close }) => [renderSlot(_ctx.$slots, "popover", {
|
|
49
49
|
close,
|
|
50
50
|
open
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarComboboxPopover.vue.script.js","names":["$props"],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Popover component\n *\n * Wraps combobox content in a floating popover panel.\n * Handles open/close behavior and keyboard interactions.\n *\n * @example\n * <ScalarComboboxPopover>\n * <button>Toggle</button>\n * <template #popover=\"{ close }\">Content</template>\n * </ScalarComboboxPopover>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { ref } from 'vue'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { ScalarPopoverSlots } from '../ScalarPopover'\n\ndefineProps<ScalarFloatingOptions>()\n\ndefineSlots<ScalarPopoverSlots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** Expose the popover button so we can close the popup */\nconst popoverButtonRef = ref<typeof PopoverButton | null>(null)\n\n/** Open the popover of the up or down arrows are pressed */\nconst handleKeydown = (e: KeyboardEvent) => {\n if (!['ArrowUp', 'ArrowDown'].includes(e.key)) {\n return\n }\n e.preventDefault()\n e.target?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n}\n\ndefineExpose({ popoverButtonRef })\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton\n ref=\"popoverButtonRef\"\n as=\"template\"\n @keydown=\"handleKeydown\">\n <slot :open />\n </PopoverButton>\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :style=\"{ width }\"\n v-bind=\"\n cx('relative flex flex-col max-h-[inherit] w-40 rounded text-sm')\n \">\n <slot\n :close\n name=\"popover\"\n :open />\n <ScalarFloatingBackdrop />\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAiCA,MAAM,EAAE,OAAO,UAAU;;EAGzB,MAAM,mBAAmB,IAAiC,IAAI;;EAG9D,MAAM,iBAAiB,MAAqB;GAC1C,IAAI,CAAC,CAAC,WAAW,WAAW,CAAC,CAAC,SAAS,EAAE,GAAG,GAC1C;GAEF,EAAE,eAAe;GACjB,EAAE,QAAQ,cAAc,IAAI,cAAc,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;EACxE;EAEA,SAAa,EAAE,iBAAiB,CAAC;;uBAG/B,YA2BU,MAAA,OAAA,GAAA,EAzBR,IAAG,WAAU,GAAA;sBAwBI,EAzBP,WAAI,CAEd,YAuBiB,MAAA,sBAAA,GAAA,eAAA,mBAvBOA,KAAAA,MAAM,CAAA,GAAA,YAAA;4BAMZ,CALhB,YAKgB,MAAA,aAAA,GAAA;eAJV;MAAJ,KAAI;MACJ,IAAG;MACF,WAAS;;6BACI,CAAd,WAAc,KAAA,QAAA,WAAA,EAAP,KAAI,CAAA,CAAA,CAAA;;;;QAGL,OAAA;WACL;kBAYc,EAZF,YAAK,CAClB,YAWe,MAAA,YAAA,GAXf,WAWe,EATZ,OAAK,EAAI,MAAK,EAAA,GACM,MAAA,EAAA,CAAE,CAAA,
|
|
1
|
+
{"version":3,"file":"ScalarComboboxPopover.vue.script.js","names":["$props"],"sources":["../../../src/components/ScalarCombobox/ScalarComboboxPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Combobox Popover component\n *\n * Wraps combobox content in a floating popover panel.\n * Handles open/close behavior and keyboard interactions.\n *\n * @example\n * <ScalarComboboxPopover>\n * <button>Toggle</button>\n * <template #popover=\"{ close }\">Content</template>\n * </ScalarComboboxPopover>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { ref } from 'vue'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { ScalarPopoverSlots } from '../ScalarPopover'\n\ndefineProps<ScalarFloatingOptions>()\n\ndefineSlots<ScalarPopoverSlots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n\n/** Expose the popover button so we can close the popup */\nconst popoverButtonRef = ref<typeof PopoverButton | null>(null)\n\n/** Open the popover of the up or down arrows are pressed */\nconst handleKeydown = (e: KeyboardEvent) => {\n if (!['ArrowUp', 'ArrowDown'].includes(e.key)) {\n return\n }\n e.preventDefault()\n e.target?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n}\n\ndefineExpose({ popoverButtonRef })\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton\n ref=\"popoverButtonRef\"\n as=\"template\"\n @keydown=\"handleKeydown\">\n <slot :open />\n </PopoverButton>\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :style=\"{ width }\"\n v-bind=\"\n cx('relative flex flex-col max-h-[inherit] w-40 rounded-xl text-sm')\n \">\n <slot\n :close\n name=\"popover\"\n :open />\n <ScalarFloatingBackdrop />\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAiCA,MAAM,EAAE,OAAO,UAAU;;EAGzB,MAAM,mBAAmB,IAAiC,IAAI;;EAG9D,MAAM,iBAAiB,MAAqB;GAC1C,IAAI,CAAC,CAAC,WAAW,WAAW,CAAC,CAAC,SAAS,EAAE,GAAG,GAC1C;GAEF,EAAE,eAAe;GACjB,EAAE,QAAQ,cAAc,IAAI,cAAc,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;EACxE;EAEA,SAAa,EAAE,iBAAiB,CAAC;;uBAG/B,YA2BU,MAAA,OAAA,GAAA,EAzBR,IAAG,WAAU,GAAA;sBAwBI,EAzBP,WAAI,CAEd,YAuBiB,MAAA,sBAAA,GAAA,eAAA,mBAvBOA,KAAAA,MAAM,CAAA,GAAA,YAAA;4BAMZ,CALhB,YAKgB,MAAA,aAAA,GAAA;eAJV;MAAJ,KAAI;MACJ,IAAG;MACF,WAAS;;6BACI,CAAd,WAAc,KAAA,QAAA,WAAA,EAAP,KAAI,CAAA,CAAA,CAAA;;;;QAGL,OAAA;WACL;kBAYc,EAZF,YAAK,CAClB,YAWe,MAAA,YAAA,GAXf,WAWe,EATZ,OAAK,EAAI,MAAK,EAAA,GACM,MAAA,EAAA,CAAE,CAAA,gEAAA,CAAA,GAAA;wBAMb,EARA,YAAK,CAKf,WAGU,KAAA,QAAA,WAAA;OAFP;OAEA;UACH,YAA0B,MAAA,8BAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarDropdownMenu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"names":[],"mappings":"AAgEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,KAAK,CAAA;AAKpC;;;;;;;;;;;;;;GAcG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,8BAA8B;SACzB,MAAM,GAAG,SAAS;;IADvB,8BAA8B;SACzB,MAAM,GAAG,SAAS;;IAKvB,wBAAwB;gBACZ,OAAO;IACnB,6CAA6C;aACpC,OAAO;IAChB,8CAA8C;iBACjC,OAAO;
|
|
1
|
+
{"version":3,"file":"ScalarDropdownMenu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"names":[],"mappings":"AAgEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,KAAK,CAAA;AAKpC;;;;;;;;;;;;;;GAcG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,8BAA8B;SACzB,MAAM,GAAG,SAAS;;IADvB,8BAA8B;SACzB,MAAM,GAAG,SAAS;;IAKvB,wBAAwB;gBACZ,OAAO;IACnB,6CAA6C;aACpC,OAAO;IAChB,8CAA8C;iBACjC,OAAO;EAyElB,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":"ScalarDropdownMenu.vue.js","names":[],"sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar dropdown menu component\n *\n * Provides a scrollable container for dropdown items\n * This is used internally by the ScalarDropdown component\n *\n * If you're looking to create a dropdown menu with a trigger\n * button you probably want the ScalarDropdown component\n *\n * @example\n * <ScalarDropdownMenu>\n * <ScalarDropdownItem>Item 1</ScalarDropdownItem>\n * <ScalarDropdownItem>Item 2</ScalarDropdownItem>\n * </ScalarDropdownMenu>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { type Component } from 'vue'\n\nimport { ScalarFloatingBackdrop } from '../ScalarFloating'\n\ndefineProps<{\n /** The component to render */\n is?: string | Component\n}>()\n\ndefineSlots<{\n /** The menu contents */\n default?(): unknown\n /** Overrides the normal menu list element */\n menu?(): unknown\n /** Overrides the backdrop for the dropdown */\n backdrop?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <!-- Background container -->\n <component\n :is=\"is ?? 'div'\"\n v-bind=\"cx('relative flex w-56')\">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Menu items -->\n <slot name=\"menu\">\n <div class=\"flex flex-col p-0.75\">\n <slot />\n </div>\n </slot>\n <slot name=\"backdrop\">\n <ScalarFloatingBackdrop
|
|
1
|
+
{"version":3,"file":"ScalarDropdownMenu.vue.js","names":[],"sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar dropdown menu component\n *\n * Provides a scrollable container for dropdown items\n * This is used internally by the ScalarDropdown component\n *\n * If you're looking to create a dropdown menu with a trigger\n * button you probably want the ScalarDropdown component\n *\n * @example\n * <ScalarDropdownMenu>\n * <ScalarDropdownItem>Item 1</ScalarDropdownItem>\n * <ScalarDropdownItem>Item 2</ScalarDropdownItem>\n * </ScalarDropdownMenu>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { type Component } from 'vue'\n\nimport { ScalarFloatingBackdrop } from '../ScalarFloating'\n\ndefineProps<{\n /** The component to render */\n is?: string | Component\n}>()\n\ndefineSlots<{\n /** The menu contents */\n default?(): unknown\n /** Overrides the normal menu list element */\n menu?(): unknown\n /** Overrides the backdrop for the dropdown */\n backdrop?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <!-- Background container -->\n <component\n :is=\"is ?? 'div'\"\n v-bind=\"cx('relative flex w-56 rounded-xl')\">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1 rounded-[inherit]\">\n <!-- Menu items -->\n <slot name=\"menu\">\n <div class=\"flex flex-col p-0.75\">\n <slot />\n </div>\n </slot>\n <slot name=\"backdrop\">\n <ScalarFloatingBackdrop />\n </slot>\n </div>\n </component>\n</template>\n"],"mappings":""}
|
|
@@ -2,7 +2,7 @@ import ScalarFloatingBackdrop_default from "../ScalarFloating/ScalarFloatingBack
|
|
|
2
2
|
import { useBindCx } from "@scalar/use-hooks/useBindCx";
|
|
3
3
|
import { createBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, normalizeProps, openBlock, renderSlot, resolveDynamicComponent, unref, withCtx } from "vue";
|
|
4
4
|
//#region src/components/ScalarDropdown/ScalarDropdownMenu.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
-
var _hoisted_1 = { class: "custom-scroll min-h-0 flex-1" };
|
|
5
|
+
var _hoisted_1 = { class: "custom-scroll min-h-0 flex-1 rounded-[inherit]" };
|
|
6
6
|
var _hoisted_2 = { class: "flex flex-col p-0.75" };
|
|
7
7
|
var ScalarDropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
8
8
|
inheritAttrs: false,
|
|
@@ -11,8 +11,8 @@ var ScalarDropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
|
|
|
11
11
|
setup(__props) {
|
|
12
12
|
const { cx } = useBindCx();
|
|
13
13
|
return (_ctx, _cache) => {
|
|
14
|
-
return openBlock(), createBlock(resolveDynamicComponent(__props.is ?? "div"), normalizeProps(guardReactiveProps(unref(cx)("relative flex w-56"))), {
|
|
15
|
-
default: withCtx(() => [createElementVNode("div", _hoisted_1, [renderSlot(_ctx.$slots, "menu", {}, () => [createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "default")])]), renderSlot(_ctx.$slots, "backdrop", {}, () => [createVNode(unref(ScalarFloatingBackdrop_default)
|
|
14
|
+
return openBlock(), createBlock(resolveDynamicComponent(__props.is ?? "div"), normalizeProps(guardReactiveProps(unref(cx)("relative flex w-56 rounded-xl"))), {
|
|
15
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1, [renderSlot(_ctx.$slots, "menu", {}, () => [createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "default")])]), renderSlot(_ctx.$slots, "backdrop", {}, () => [createVNode(unref(ScalarFloatingBackdrop_default))])])]),
|
|
16
16
|
_: 3
|
|
17
17
|
}, 16);
|
|
18
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarDropdownMenu.vue.script.js","names":[],"sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar dropdown menu component\n *\n * Provides a scrollable container for dropdown items\n * This is used internally by the ScalarDropdown component\n *\n * If you're looking to create a dropdown menu with a trigger\n * button you probably want the ScalarDropdown component\n *\n * @example\n * <ScalarDropdownMenu>\n * <ScalarDropdownItem>Item 1</ScalarDropdownItem>\n * <ScalarDropdownItem>Item 2</ScalarDropdownItem>\n * </ScalarDropdownMenu>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { type Component } from 'vue'\n\nimport { ScalarFloatingBackdrop } from '../ScalarFloating'\n\ndefineProps<{\n /** The component to render */\n is?: string | Component\n}>()\n\ndefineSlots<{\n /** The menu contents */\n default?(): unknown\n /** Overrides the normal menu list element */\n menu?(): unknown\n /** Overrides the backdrop for the dropdown */\n backdrop?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <!-- Background container -->\n <component\n :is=\"is ?? 'div'\"\n v-bind=\"cx('relative flex w-56')\">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Menu items -->\n <slot name=\"menu\">\n <div class=\"flex flex-col p-0.75\">\n <slot />\n </div>\n </slot>\n <slot name=\"backdrop\">\n <ScalarFloatingBackdrop
|
|
1
|
+
{"version":3,"file":"ScalarDropdownMenu.vue.script.js","names":[],"sources":["../../../src/components/ScalarDropdown/ScalarDropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar dropdown menu component\n *\n * Provides a scrollable container for dropdown items\n * This is used internally by the ScalarDropdown component\n *\n * If you're looking to create a dropdown menu with a trigger\n * button you probably want the ScalarDropdown component\n *\n * @example\n * <ScalarDropdownMenu>\n * <ScalarDropdownItem>Item 1</ScalarDropdownItem>\n * <ScalarDropdownItem>Item 2</ScalarDropdownItem>\n * </ScalarDropdownMenu>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { type Component } from 'vue'\n\nimport { ScalarFloatingBackdrop } from '../ScalarFloating'\n\ndefineProps<{\n /** The component to render */\n is?: string | Component\n}>()\n\ndefineSlots<{\n /** The menu contents */\n default?(): unknown\n /** Overrides the normal menu list element */\n menu?(): unknown\n /** Overrides the backdrop for the dropdown */\n backdrop?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <!-- Background container -->\n <component\n :is=\"is ?? 'div'\"\n v-bind=\"cx('relative flex w-56 rounded-xl')\">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1 rounded-[inherit]\">\n <!-- Menu items -->\n <slot name=\"menu\">\n <div class=\"flex flex-col p-0.75\">\n <slot />\n </div>\n </slot>\n <slot name=\"backdrop\">\n <ScalarFloatingBackdrop />\n </slot>\n </div>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;EAuCA,MAAM,EAAE,OAAO,UAAU;;uBAIvB,YAeY,wBAdL,QAAA,MAAE,KAAA,GAAA,eAAA,mBACC,MAAA,EAAA,CAAE,CAAA,+BAAA,CAAA,CAAA,GAAA;2BAYJ,CAVN,mBAUM,OAVN,YAUM,CARJ,WAIO,KAAA,QAAA,QAAA,CAAA,SAAA,CAHL,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,CAAA,CAAA,GAGZ,WAEO,KAAA,QAAA,YAAA,CAAA,SAAA,CADL,YAA0B,MAAA,8BAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* You can use this component directly if you need to customize the backdrop
|
|
9
9
|
* for a floating component
|
|
10
10
|
*
|
|
11
|
+
* The backdrop inherits its border radius from its parent, so set the radius
|
|
12
|
+
* on the floating element itself and the backdrop will follow it
|
|
13
|
+
*
|
|
11
14
|
* @example
|
|
12
15
|
* <ScalarDropdown>
|
|
13
16
|
* <!-- Menu stuff -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarFloatingBackdrop.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScalarFloatingBackdrop.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"names":[],"mappings":"AAuDA;;;;;;;;;;;;;;;;;;;;GAoBG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;cAwCJ,CAAC,KAAK,IAAgB,KAAK,GAAG;EAQxC,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":"ScalarFloatingBackdrop.vue.js","names":[],"sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar floating backdrop component\n *\n * Provides an absolutely positioned backdrop for the floating element\n * This is used internally by a number of the Scalar floating components\n * (e.g. Dropdown, Popover, Listbox)\n *\n * You can use this component directly if you need to customize the backdrop\n * for a floating component\n *\n * @example\n * <ScalarDropdown>\n * <!-- Menu stuff -->\n * <template #backdrop>\n * <ScalarFloatingBackdrop />\n * </template>\n * </ScalarDropdown>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx('absolute inset-0 -z-1 rounded bg-b-1 shadow-
|
|
1
|
+
{"version":3,"file":"ScalarFloatingBackdrop.vue.js","names":[],"sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar floating backdrop component\n *\n * Provides an absolutely positioned backdrop for the floating element\n * This is used internally by a number of the Scalar floating components\n * (e.g. Dropdown, Popover, Listbox)\n *\n * You can use this component directly if you need to customize the backdrop\n * for a floating component\n *\n * The backdrop inherits its border radius from its parent, so set the radius\n * on the floating element itself and the backdrop will follow it\n *\n * @example\n * <ScalarDropdown>\n * <!-- Menu stuff -->\n * <template #backdrop>\n * <ScalarFloatingBackdrop />\n * </template>\n * </ScalarDropdown>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'absolute inset-0 -z-1 overflow-hidden rounded-[inherit] bg-b-1 shadow-md',\n // Browsers round sub-pixel borders up to a full pixel, so the hairline\n // is an inset shadow painted above the lifted layer instead\n 'after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:shadow-border',\n )\n \">\n <!--\n The lifted brightness lives on an inner layer so it does not also\n brighten the border, which would stop it matching adjacent inputs\n -->\n <div class=\"absolute inset-0 bg-inherit brightness-lifted\">\n <slot />\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useBindCx } from "@scalar/use-hooks/useBindCx";
|
|
2
|
-
import { createElementBlock, defineComponent, guardReactiveProps, normalizeProps, openBlock, renderSlot, unref } from "vue";
|
|
2
|
+
import { createElementBlock, createElementVNode, defineComponent, guardReactiveProps, normalizeProps, openBlock, renderSlot, unref } from "vue";
|
|
3
|
+
//#region src/components/ScalarFloating/ScalarFloatingBackdrop.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
+
var _hoisted_1 = { class: "absolute inset-0 bg-inherit brightness-lifted" };
|
|
3
5
|
var ScalarFloatingBackdrop_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
4
6
|
inheritAttrs: false,
|
|
5
7
|
__name: "ScalarFloatingBackdrop",
|
|
6
8
|
setup(__props) {
|
|
7
9
|
const { cx } = useBindCx();
|
|
8
10
|
return (_ctx, _cache) => {
|
|
9
|
-
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("absolute inset-0 -z-1 rounded bg-b-1 shadow-
|
|
11
|
+
return openBlock(), createElementBlock("div", normalizeProps(guardReactiveProps(unref(cx)("absolute inset-0 -z-1 overflow-hidden rounded-[inherit] bg-b-1 shadow-md", "after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:shadow-border"))), [createElementVNode("div", _hoisted_1, [renderSlot(_ctx.$slots, "default")])], 16);
|
|
10
12
|
};
|
|
11
13
|
}
|
|
12
14
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarFloatingBackdrop.vue.script.js","names":[],"sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar floating backdrop component\n *\n * Provides an absolutely positioned backdrop for the floating element\n * This is used internally by a number of the Scalar floating components\n * (e.g. Dropdown, Popover, Listbox)\n *\n * You can use this component directly if you need to customize the backdrop\n * for a floating component\n *\n * @example\n * <ScalarDropdown>\n * <!-- Menu stuff -->\n * <template #backdrop>\n * <ScalarFloatingBackdrop />\n * </template>\n * </ScalarDropdown>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx('absolute inset-0 -z-1 rounded bg-b-1 shadow-
|
|
1
|
+
{"version":3,"file":"ScalarFloatingBackdrop.vue.script.js","names":[],"sources":["../../../src/components/ScalarFloating/ScalarFloatingBackdrop.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar floating backdrop component\n *\n * Provides an absolutely positioned backdrop for the floating element\n * This is used internally by a number of the Scalar floating components\n * (e.g. Dropdown, Popover, Listbox)\n *\n * You can use this component directly if you need to customize the backdrop\n * for a floating component\n *\n * The backdrop inherits its border radius from its parent, so set the radius\n * on the floating element itself and the backdrop will follow it\n *\n * @example\n * <ScalarDropdown>\n * <!-- Menu stuff -->\n * <template #backdrop>\n * <ScalarFloatingBackdrop />\n * </template>\n * </ScalarDropdown>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <div\n v-bind=\"\n cx(\n 'absolute inset-0 -z-1 overflow-hidden rounded-[inherit] bg-b-1 shadow-md',\n // Browsers round sub-pixel borders up to a full pixel, so the hairline\n // is an inset shadow painted above the lifted layer instead\n 'after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:shadow-border',\n )\n \">\n <!--\n The lifted brightness lives on an inner layer so it does not also\n brighten the border, which would stop it matching adjacent inputs\n -->\n <div class=\"absolute inset-0 bg-inherit brightness-lifted\">\n <slot />\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;EA4BA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,mBAgBM,OAAA,eAAA,mBAfW,MAAA,EAAA,CAAE,CAAA,4EAAA,oGAAA,CAAA,CAAA,GAAA,CAYjB,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,CAAA,GAAA,EAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarListbox.vue.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListbox.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox component\n *\n * A dropdown select component built on HeadlessUI Listbox.\n * Supports single and multiple selection modes.\n *\n * @example\n * <ScalarListbox v-model=\"selected\" :options=\"options\">\n * <ScalarButton>{{ selected?.label ?? 'Select' }}</ScalarButton>\n * </ScalarListbox>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n Listbox,\n ListboxButton,\n ListboxLabel,\n ListboxOptions,\n} from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport ScalarListboxOption from './ScalarListboxItem.vue'\nimport type { Option } from './types'\n\ntype SingleSelectListboxProps = {\n multiple?: false | undefined\n modelValue?: Option | undefined\n}\n\ntype MultipleSelectListboxProps = {\n multiple: true\n modelValue?: Option[] | undefined\n}\n\ndefineProps<\n {\n options: Option[]\n id?: string\n label?: string\n } & (SingleSelectListboxProps | MultipleSelectListboxProps) &\n ScalarFloatingOptions\n>()\n\ndefineEmits<{\n (e: 'update:modelValue', v: Option): void\n}>()\n\ndefineSlots<{\n /** The reference element for the listbox */\n default(props: {\n /** Whether or not the listbox is open */\n open: boolean\n }): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Listbox\n v-slot=\"{ open }\"\n :modelValue=\"modelValue\"\n :multiple=\"multiple\"\n @update:modelValue=\"(v) => $emit('update:modelValue', v)\">\n <ListboxLabel\n v-if=\"label\"\n class=\"sr-only\">\n {{ label }}\n </ListboxLabel>\n <ScalarFloating\n v-bind=\"$props\"\n :placement=\"placement ?? 'bottom-start'\">\n <ListboxButton\n :id=\"id\"\n as=\"template\">\n <slot :open=\"open\" />\n </ListboxButton>\n <template #floating=\"{ width }\">\n <!-- Background container -->\n <div\n v-if=\"open\"\n :style=\"{ width }\"\n v-bind=\"\n cx(\n 'relative flex max-h-[inherit] w-40 max-w-[inherit] rounded text-sm',\n )\n \">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Options list -->\n <ListboxOptions\n class=\"flex flex-col gap-0.75 p-0.75 -outline-offset-1\">\n <ScalarListboxOption\n v-for=\"option in options\"\n :key=\"option.id\"\n :multiselect=\"multiple\"\n :option=\"option\" />\n </ListboxOptions>\n </div>\n <ScalarFloatingBackdrop />\n </div>\n </template>\n </ScalarFloating>\n </Listbox>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarListbox.vue.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListbox.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox component\n *\n * A dropdown select component built on HeadlessUI Listbox.\n * Supports single and multiple selection modes.\n *\n * @example\n * <ScalarListbox v-model=\"selected\" :options=\"options\">\n * <ScalarButton>{{ selected?.label ?? 'Select' }}</ScalarButton>\n * </ScalarListbox>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n Listbox,\n ListboxButton,\n ListboxLabel,\n ListboxOptions,\n} from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport ScalarListboxOption from './ScalarListboxItem.vue'\nimport type { Option } from './types'\n\ntype SingleSelectListboxProps = {\n multiple?: false | undefined\n modelValue?: Option | undefined\n}\n\ntype MultipleSelectListboxProps = {\n multiple: true\n modelValue?: Option[] | undefined\n}\n\ndefineProps<\n {\n options: Option[]\n id?: string\n label?: string\n } & (SingleSelectListboxProps | MultipleSelectListboxProps) &\n ScalarFloatingOptions\n>()\n\ndefineEmits<{\n (e: 'update:modelValue', v: Option): void\n}>()\n\ndefineSlots<{\n /** The reference element for the listbox */\n default(props: {\n /** Whether or not the listbox is open */\n open: boolean\n }): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Listbox\n v-slot=\"{ open }\"\n :modelValue=\"modelValue\"\n :multiple=\"multiple\"\n @update:modelValue=\"(v) => $emit('update:modelValue', v)\">\n <ListboxLabel\n v-if=\"label\"\n class=\"sr-only\">\n {{ label }}\n </ListboxLabel>\n <ScalarFloating\n v-bind=\"$props\"\n :placement=\"placement ?? 'bottom-start'\">\n <ListboxButton\n :id=\"id\"\n as=\"template\">\n <slot :open=\"open\" />\n </ListboxButton>\n <template #floating=\"{ width }\">\n <!-- Background container -->\n <div\n v-if=\"open\"\n :style=\"{ width }\"\n v-bind=\"\n cx(\n 'relative flex max-h-[inherit] w-40 max-w-[inherit] rounded-xl text-sm',\n )\n \">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Options list -->\n <ListboxOptions\n class=\"flex flex-col gap-0.75 p-0.75 -outline-offset-1\">\n <ScalarListboxOption\n v-for=\"option in options\"\n :key=\"option.id\"\n :multiselect=\"multiple\"\n :option=\"option\" />\n </ListboxOptions>\n </div>\n <ScalarFloatingBackdrop />\n </div>\n </template>\n </ScalarFloating>\n </Listbox>\n</template>\n"],"mappings":""}
|
|
@@ -45,7 +45,7 @@ var ScalarListbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
45
45
|
floating: withCtx(({ width }) => [open ? (openBlock(), createElementBlock("div", mergeProps({
|
|
46
46
|
key: 0,
|
|
47
47
|
style: { width }
|
|
48
|
-
}, unref(cx)("relative flex max-h-[inherit] w-40 max-w-[inherit] rounded text-sm")), [createElementVNode("div", _hoisted_1, [createVNode(unref(ListboxOptions), { class: "flex flex-col gap-0.75 p-0.75 -outline-offset-1" }, {
|
|
48
|
+
}, unref(cx)("relative flex max-h-[inherit] w-40 max-w-[inherit] rounded-xl text-sm")), [createElementVNode("div", _hoisted_1, [createVNode(unref(ListboxOptions), { class: "flex flex-col gap-0.75 p-0.75 -outline-offset-1" }, {
|
|
49
49
|
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
|
|
50
50
|
return openBlock(), createBlock(ScalarListboxItem_default, {
|
|
51
51
|
key: option.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarListbox.vue.script.js","names":["$emit","$props"],"sources":["../../../src/components/ScalarListbox/ScalarListbox.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox component\n *\n * A dropdown select component built on HeadlessUI Listbox.\n * Supports single and multiple selection modes.\n *\n * @example\n * <ScalarListbox v-model=\"selected\" :options=\"options\">\n * <ScalarButton>{{ selected?.label ?? 'Select' }}</ScalarButton>\n * </ScalarListbox>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n Listbox,\n ListboxButton,\n ListboxLabel,\n ListboxOptions,\n} from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport ScalarListboxOption from './ScalarListboxItem.vue'\nimport type { Option } from './types'\n\ntype SingleSelectListboxProps = {\n multiple?: false | undefined\n modelValue?: Option | undefined\n}\n\ntype MultipleSelectListboxProps = {\n multiple: true\n modelValue?: Option[] | undefined\n}\n\ndefineProps<\n {\n options: Option[]\n id?: string\n label?: string\n } & (SingleSelectListboxProps | MultipleSelectListboxProps) &\n ScalarFloatingOptions\n>()\n\ndefineEmits<{\n (e: 'update:modelValue', v: Option): void\n}>()\n\ndefineSlots<{\n /** The reference element for the listbox */\n default(props: {\n /** Whether or not the listbox is open */\n open: boolean\n }): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Listbox\n v-slot=\"{ open }\"\n :modelValue=\"modelValue\"\n :multiple=\"multiple\"\n @update:modelValue=\"(v) => $emit('update:modelValue', v)\">\n <ListboxLabel\n v-if=\"label\"\n class=\"sr-only\">\n {{ label }}\n </ListboxLabel>\n <ScalarFloating\n v-bind=\"$props\"\n :placement=\"placement ?? 'bottom-start'\">\n <ListboxButton\n :id=\"id\"\n as=\"template\">\n <slot :open=\"open\" />\n </ListboxButton>\n <template #floating=\"{ width }\">\n <!-- Background container -->\n <div\n v-if=\"open\"\n :style=\"{ width }\"\n v-bind=\"\n cx(\n 'relative flex max-h-[inherit] w-40 max-w-[inherit] rounded text-sm',\n )\n \">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Options list -->\n <ListboxOptions\n class=\"flex flex-col gap-0.75 p-0.75 -outline-offset-1\">\n <ScalarListboxOption\n v-for=\"option in options\"\n :key=\"option.id\"\n :multiselect=\"multiple\"\n :option=\"option\" />\n </ListboxOptions>\n </div>\n <ScalarFloatingBackdrop />\n </div>\n </template>\n </ScalarFloating>\n </Listbox>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,YA4CU,MAAA,OAAA,GAAA;IA1CP,YAAY,QAAA;IACZ,UAAU,QAAA;IACV,uBAAiB,OAAA,OAAA,OAAA,MAAG,MAAMA,KAAAA,MAAK,qBAAsB,CAAC;;sBAKxC,EARL,WAAI,CAKN,QAAA,SAAA,UAAA,GADR,YAIe,MAAA,YAAA,GAAA;;KAFb,OAAM;;4BACK,CAAA,gBAAA,gBAAR,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA;;wCAEV,YAiCiB,MAAA,sBAAA,GAjCjB,WACUC,KAgCO,QAhCD,EACb,WAAW,QAAA,aAAS,eAAA,CAAA,GAAA;KAMV,UAAQ,SAuBX,EAvBe,YAAK,CAGlB,QAAA,UAAA,GADR,mBAqBM,OArBN,WAqBM;;MAnBH,OAAK,EAAI,MAAK;QACM,MAAA,EAAA,CAAE,CAAA,
|
|
1
|
+
{"version":3,"file":"ScalarListbox.vue.script.js","names":["$emit","$props"],"sources":["../../../src/components/ScalarListbox/ScalarListbox.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox component\n *\n * A dropdown select component built on HeadlessUI Listbox.\n * Supports single and multiple selection modes.\n *\n * @example\n * <ScalarListbox v-model=\"selected\" :options=\"options\">\n * <ScalarButton>{{ selected?.label ?? 'Select' }}</ScalarButton>\n * </ScalarListbox>\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n Listbox,\n ListboxButton,\n ListboxLabel,\n ListboxOptions,\n} from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport ScalarListboxOption from './ScalarListboxItem.vue'\nimport type { Option } from './types'\n\ntype SingleSelectListboxProps = {\n multiple?: false | undefined\n modelValue?: Option | undefined\n}\n\ntype MultipleSelectListboxProps = {\n multiple: true\n modelValue?: Option[] | undefined\n}\n\ndefineProps<\n {\n options: Option[]\n id?: string\n label?: string\n } & (SingleSelectListboxProps | MultipleSelectListboxProps) &\n ScalarFloatingOptions\n>()\n\ndefineEmits<{\n (e: 'update:modelValue', v: Option): void\n}>()\n\ndefineSlots<{\n /** The reference element for the listbox */\n default(props: {\n /** Whether or not the listbox is open */\n open: boolean\n }): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Listbox\n v-slot=\"{ open }\"\n :modelValue=\"modelValue\"\n :multiple=\"multiple\"\n @update:modelValue=\"(v) => $emit('update:modelValue', v)\">\n <ListboxLabel\n v-if=\"label\"\n class=\"sr-only\">\n {{ label }}\n </ListboxLabel>\n <ScalarFloating\n v-bind=\"$props\"\n :placement=\"placement ?? 'bottom-start'\">\n <ListboxButton\n :id=\"id\"\n as=\"template\">\n <slot :open=\"open\" />\n </ListboxButton>\n <template #floating=\"{ width }\">\n <!-- Background container -->\n <div\n v-if=\"open\"\n :style=\"{ width }\"\n v-bind=\"\n cx(\n 'relative flex max-h-[inherit] w-40 max-w-[inherit] rounded-xl text-sm',\n )\n \">\n <!-- Scroll container -->\n <div class=\"custom-scroll min-h-0 flex-1\">\n <!-- Options list -->\n <ListboxOptions\n class=\"flex flex-col gap-0.75 p-0.75 -outline-offset-1\">\n <ScalarListboxOption\n v-for=\"option in options\"\n :key=\"option.id\"\n :multiselect=\"multiple\"\n :option=\"option\" />\n </ListboxOptions>\n </div>\n <ScalarFloatingBackdrop />\n </div>\n </template>\n </ScalarFloating>\n </Listbox>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,YA4CU,MAAA,OAAA,GAAA;IA1CP,YAAY,QAAA;IACZ,UAAU,QAAA;IACV,uBAAiB,OAAA,OAAA,OAAA,MAAG,MAAMA,KAAAA,MAAK,qBAAsB,CAAC;;sBAKxC,EARL,WAAI,CAKN,QAAA,SAAA,UAAA,GADR,YAIe,MAAA,YAAA,GAAA;;KAFb,OAAM;;4BACK,CAAA,gBAAA,gBAAR,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA;;wCAEV,YAiCiB,MAAA,sBAAA,GAjCjB,WACUC,KAgCO,QAhCD,EACb,WAAW,QAAA,aAAS,eAAA,CAAA,GAAA;KAMV,UAAQ,SAuBX,EAvBe,YAAK,CAGlB,QAAA,UAAA,GADR,mBAqBM,OArBN,WAqBM;;MAnBH,OAAK,EAAI,MAAK;QACM,MAAA,EAAA,CAAE,CAAA,uEAAA,CAAA,GAAA,CAMvB,mBAUM,OAVN,YAUM,CARJ,YAOiB,MAAA,cAAA,GAAA,EANf,OAAM,kDAAiD,GAAA;6BAE5B,EAAA,UAAA,IAAA,GAD3B,mBAIqB,UAAA,MAAA,WAHF,QAAA,UAAV,WAAM;2BADf,YAIqB,2BAAA;QAFlB,KAAK,OAAO;QACZ,aAAa,QAAA;QACL;;;;WAGf,YAA0B,MAAA,8BAAA,CAAA,CAAA,GAAA,EAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA;4BAvBd,CAJhB,YAIgB,MAAA,aAAA,GAAA;MAHb,IAAI,QAAA;MACL,IAAG;;6BACkB,CAArB,WAAqB,KAAA,QAAA,WAAA,EAAR,KAAI,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarListboxItem.vue.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListboxItem.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox Item component\n *\n * A single option within a ScalarListbox dropdown.\n * Handles active, selected, and disabled states.\n *\n * @example\n * <ScalarListboxItem :option=\"{ id: '1', label: 'Option 1' }\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ListboxOption } from '@headlessui/vue'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarListboxCheckbox from './ScalarListboxCheckbox.vue'\nimport type { Option } from './types'\n\ndefineProps<{\n option: Option\n multiselect?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Layout\n 'group/item',\n 'flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n disabled: { true: 'pointer-events-none opacity-50' },\n },\n})\n</script>\n<template>\n <ListboxOption\n v-slot=\"{ active, selected }\"\n as=\"template\"\n :disabled=\"option.disabled\"\n :value=\"option\">\n <li :class=\"cx(variants({ active, selected, disabled: option.disabled }))\">\n <ScalarListboxCheckbox\n :multiselect\n :selected />\n <span\n class=\"inline-block min-w-0 flex-1 truncate\"\n :class=\"option.color ? option.color : 'text-c-1'\">\n {{ option.label }}\n </span>\n </li>\n </ListboxOption>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarListboxItem.vue.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListboxItem.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox Item component\n *\n * A single option within a ScalarListbox dropdown.\n * Handles active, selected, and disabled states.\n *\n * @example\n * <ScalarListboxItem :option=\"{ id: '1', label: 'Option 1' }\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ListboxOption } from '@headlessui/vue'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarListboxCheckbox from './ScalarListboxCheckbox.vue'\nimport type { Option } from './types'\n\ndefineProps<{\n option: Option\n multiselect?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Layout\n 'group/item',\n 'flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n disabled: { true: 'pointer-events-none opacity-50' },\n },\n})\n</script>\n<template>\n <ListboxOption\n v-slot=\"{ active, selected }\"\n as=\"template\"\n :disabled=\"option.disabled\"\n :value=\"option\">\n <li :class=\"cx(variants({ active, selected, disabled: option.disabled }))\">\n <ScalarListboxCheckbox\n :multiselect\n :selected />\n <span\n class=\"inline-block min-w-0 flex-1 truncate\"\n :class=\"option.color ? option.color : 'text-c-1'\">\n {{ option.label }}\n </span>\n </li>\n </ListboxOption>\n</template>\n"],"mappings":""}
|
|
@@ -12,7 +12,7 @@ var ScalarListboxItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
12
12
|
const variants = cva({
|
|
13
13
|
base: [
|
|
14
14
|
"group/item",
|
|
15
|
-
"flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left",
|
|
15
|
+
"flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left",
|
|
16
16
|
"truncate bg-transparent text-c-1",
|
|
17
17
|
"cursor-pointer hover:bg-b-2"
|
|
18
18
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarListboxItem.vue.script.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListboxItem.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox Item component\n *\n * A single option within a ScalarListbox dropdown.\n * Handles active, selected, and disabled states.\n *\n * @example\n * <ScalarListboxItem :option=\"{ id: '1', label: 'Option 1' }\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ListboxOption } from '@headlessui/vue'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarListboxCheckbox from './ScalarListboxCheckbox.vue'\nimport type { Option } from './types'\n\ndefineProps<{\n option: Option\n multiselect?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Layout\n 'group/item',\n 'flex min-w-0 items-center gap-1.5 rounded px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n disabled: { true: 'pointer-events-none opacity-50' },\n },\n})\n</script>\n<template>\n <ListboxOption\n v-slot=\"{ active, selected }\"\n as=\"template\"\n :disabled=\"option.disabled\"\n :value=\"option\">\n <li :class=\"cx(variants({ active, selected, disabled: option.disabled }))\">\n <ScalarListboxCheckbox\n :multiselect\n :selected />\n <span\n class=\"inline-block min-w-0 flex-1 truncate\"\n :class=\"option.color ? option.color : 'text-c-1'\">\n {{ option.label }}\n </span>\n </li>\n </ListboxOption>\n</template>\n"],"mappings":";;;;;;;;;;;EAyBA,MAAM,WAAW,IAAI;GACnB,MAAM;IAEJ;IACA;IAEA;IAEA;GACF;GACA,UAAU;IACR,UAAU,EAAE,MAAM,WAAW;IAC7B,QAAQ,EAAE,MAAM,SAAS;IACzB,UAAU,EAAE,MAAM,iCAAiC;GACrD;EACF,CAAC;;uBAGC,YAegB,MAAA,aAAA,GAAA;IAbd,IAAG;IACF,UAAU,QAAA,OAAO;IACjB,OAAO,QAAA;;sBAUH,EAbK,QAAQ,eAAQ,CAI1B,mBASK,MAAA,EATA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAG;KAAQ;KAAQ,UAAY,QAAA,OAAO;IAAQ,CAAA,CAAA,CAAA,EAAA,GAAA,CACnE,YAEc,+BAAA;KADX,aAAA,QAAA;KACA;8CACH,mBAIO,QAAA,EAHL,OAAK,eAAA,CAAC,wCACE,QAAA,OAAO,QAAQ,QAAA,OAAO,QAAK,UAAA,CAAA,EAAA,GAAA,gBAChC,QAAA,OAAO,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"ScalarListboxItem.vue.script.js","names":[],"sources":["../../../src/components/ScalarListbox/ScalarListboxItem.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Listbox Item component\n *\n * A single option within a ScalarListbox dropdown.\n * Handles active, selected, and disabled states.\n *\n * @example\n * <ScalarListboxItem :option=\"{ id: '1', label: 'Option 1' }\" />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ListboxOption } from '@headlessui/vue'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\n\nimport ScalarListboxCheckbox from './ScalarListboxCheckbox.vue'\nimport type { Option } from './types'\n\ndefineProps<{\n option: Option\n multiselect?: boolean\n}>()\n\nconst variants = cva({\n base: [\n // Layout\n 'group/item',\n 'flex min-w-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-left',\n // Text / background style\n 'truncate bg-transparent text-c-1',\n // Interaction\n 'cursor-pointer hover:bg-b-2',\n ],\n variants: {\n selected: { true: 'text-c-1' },\n active: { true: 'bg-b-2' },\n disabled: { true: 'pointer-events-none opacity-50' },\n },\n})\n</script>\n<template>\n <ListboxOption\n v-slot=\"{ active, selected }\"\n as=\"template\"\n :disabled=\"option.disabled\"\n :value=\"option\">\n <li :class=\"cx(variants({ active, selected, disabled: option.disabled }))\">\n <ScalarListboxCheckbox\n :multiselect\n :selected />\n <span\n class=\"inline-block min-w-0 flex-1 truncate\"\n :class=\"option.color ? option.color : 'text-c-1'\">\n {{ option.label }}\n </span>\n </li>\n </ListboxOption>\n</template>\n"],"mappings":";;;;;;;;;;;EAyBA,MAAM,WAAW,IAAI;GACnB,MAAM;IAEJ;IACA;IAEA;IAEA;GACF;GACA,UAAU;IACR,UAAU,EAAE,MAAM,WAAW;IAC7B,QAAQ,EAAE,MAAM,SAAS;IACzB,UAAU,EAAE,MAAM,iCAAiC;GACrD;EACF,CAAC;;uBAGC,YAegB,MAAA,aAAA,GAAA;IAbd,IAAG;IACF,UAAU,QAAA,OAAO;IACjB,OAAO,QAAA;;sBAUH,EAbK,QAAQ,eAAQ,CAI1B,mBASK,MAAA,EATA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAG;KAAQ;KAAQ,UAAY,QAAA,OAAO;IAAQ,CAAA,CAAA,CAAA,EAAA,GAAA,CACnE,YAEc,+BAAA;KADX,aAAA,QAAA;KACA;8CACH,mBAIO,QAAA,EAHL,OAAK,eAAA,CAAC,wCACE,QAAA,OAAO,QAAQ,QAAA,OAAO,QAAK,UAAA,CAAA,EAAA,GAAA,gBAChC,QAAA,OAAO,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarPopover.vue.js","names":[],"sources":["../../../src/components/ScalarPopover/ScalarPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Popover component\n *\n * A floating panel that appears when a trigger element is clicked.\n * Built on HeadlessUI Popover with Floating UI positioning.\n *\n * @example\n * <ScalarPopover>\n * <button>Open</button>\n * <template #popover=\"{ close }\">Popover content</template>\n * </ScalarPopover>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { Slots } from './types'\n\ndefineProps<\n {\n /** Automatically focus the popover panel when it opens */\n focus?: boolean\n } & ScalarFloatingOptions\n>()\n\ndefineSlots<Slots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton as=\"template\">\n <slot :open=\"open\" />\n </PopoverButton>\n <template #floating=\"{ width, height }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :focus=\"focus\"\n :style=\"{ width, height }\"\n v-bind=\"cx('relative flex flex-col p-0.75')\">\n <slot\n :close=\"() => close()\"\n name=\"popover\"\n :open=\"open\" />\n <slot\n name=\"backdrop\"\n :open=\"open\">\n <ScalarFloatingBackdrop />\n </slot>\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ScalarPopover.vue.js","names":[],"sources":["../../../src/components/ScalarPopover/ScalarPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Popover component\n *\n * A floating panel that appears when a trigger element is clicked.\n * Built on HeadlessUI Popover with Floating UI positioning.\n *\n * @example\n * <ScalarPopover>\n * <button>Open</button>\n * <template #popover=\"{ close }\">Popover content</template>\n * </ScalarPopover>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { Slots } from './types'\n\ndefineProps<\n {\n /** Automatically focus the popover panel when it opens */\n focus?: boolean\n } & ScalarFloatingOptions\n>()\n\ndefineSlots<Slots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton as=\"template\">\n <slot :open=\"open\" />\n </PopoverButton>\n <template #floating=\"{ width, height }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :focus=\"focus\"\n :style=\"{ width, height }\"\n v-bind=\"cx('relative flex flex-col rounded-xl p-0.75')\">\n <slot\n :close=\"() => close()\"\n name=\"popover\"\n :open=\"open\" />\n <slot\n name=\"backdrop\"\n :open=\"open\">\n <ScalarFloatingBackdrop />\n </slot>\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":""}
|
|
@@ -30,7 +30,7 @@ var ScalarPopover_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
30
30
|
width,
|
|
31
31
|
height
|
|
32
32
|
}
|
|
33
|
-
}, unref(cx)("relative flex flex-col p-0.75")), {
|
|
33
|
+
}, unref(cx)("relative flex flex-col rounded-xl p-0.75")), {
|
|
34
34
|
default: withCtx(({ close }) => [renderSlot(_ctx.$slots, "popover", {
|
|
35
35
|
close: () => close(),
|
|
36
36
|
open
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScalarPopover.vue.script.js","names":["$props"],"sources":["../../../src/components/ScalarPopover/ScalarPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Popover component\n *\n * A floating panel that appears when a trigger element is clicked.\n * Built on HeadlessUI Popover with Floating UI positioning.\n *\n * @example\n * <ScalarPopover>\n * <button>Open</button>\n * <template #popover=\"{ close }\">Popover content</template>\n * </ScalarPopover>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { Slots } from './types'\n\ndefineProps<\n {\n /** Automatically focus the popover panel when it opens */\n focus?: boolean\n } & ScalarFloatingOptions\n>()\n\ndefineSlots<Slots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton as=\"template\">\n <slot :open=\"open\" />\n </PopoverButton>\n <template #floating=\"{ width, height }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :focus=\"focus\"\n :style=\"{ width, height }\"\n v-bind=\"cx('relative flex flex-col p-0.75')\">\n <slot\n :close=\"() => close()\"\n name=\"popover\"\n :open=\"open\" />\n <slot\n name=\"backdrop\"\n :open=\"open\">\n <ScalarFloatingBackdrop />\n </slot>\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAoCA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,YAyBU,MAAA,OAAA,GAAA,EAvBR,IAAG,WAAU,GAAA;sBAsBI,EAvBP,WAAI,CAEd,YAqBiB,MAAA,sBAAA,GAAA,eAAA,mBArBOA,KAAAA,MAAM,CAAA,GAAA;KAIjB,UAAQ,SAeF,EAfM,OAAO,aAAM,CAClC,YAce,MAAA,YAAA,GAdf,WAce;MAZZ,OAAO,QAAA;MACP,OAAK;OAAI;OAAO;MAAM;QACf,MAAA,EAAA,CAAE,CAAA
|
|
1
|
+
{"version":3,"file":"ScalarPopover.vue.script.js","names":["$props"],"sources":["../../../src/components/ScalarPopover/ScalarPopover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Popover component\n *\n * A floating panel that appears when a trigger element is clicked.\n * Built on HeadlessUI Popover with Floating UI positioning.\n *\n * @example\n * <ScalarPopover>\n * <button>Open</button>\n * <template #popover=\"{ close }\">Popover content</template>\n * </ScalarPopover>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\n\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n type ScalarFloatingOptions,\n} from '../ScalarFloating'\nimport type { Slots } from './types'\n\ndefineProps<\n {\n /** Automatically focus the popover panel when it opens */\n focus?: boolean\n } & ScalarFloatingOptions\n>()\n\ndefineSlots<Slots>()\n\ndefineOptions({ inheritAttrs: false })\nconst { cx } = useBindCx()\n</script>\n<template>\n <Popover\n v-slot=\"{ open }\"\n as=\"template\">\n <ScalarFloating v-bind=\"$props\">\n <PopoverButton as=\"template\">\n <slot :open=\"open\" />\n </PopoverButton>\n <template #floating=\"{ width, height }\">\n <PopoverPanel\n v-slot=\"{ close }\"\n :focus=\"focus\"\n :style=\"{ width, height }\"\n v-bind=\"cx('relative flex flex-col rounded-xl p-0.75')\">\n <slot\n :close=\"() => close()\"\n name=\"popover\"\n :open=\"open\" />\n <slot\n name=\"backdrop\"\n :open=\"open\">\n <ScalarFloatingBackdrop />\n </slot>\n </PopoverPanel>\n </template>\n </ScalarFloating>\n </Popover>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAoCA,MAAM,EAAE,OAAO,UAAU;;uBAGvB,YAyBU,MAAA,OAAA,GAAA,EAvBR,IAAG,WAAU,GAAA;sBAsBI,EAvBP,WAAI,CAEd,YAqBiB,MAAA,sBAAA,GAAA,eAAA,mBArBOA,KAAAA,MAAM,CAAA,GAAA;KAIjB,UAAQ,SAeF,EAfM,OAAO,aAAM,CAClC,YAce,MAAA,YAAA,GAdf,WAce;MAZZ,OAAO,QAAA;MACP,OAAK;OAAI;OAAO;MAAM;QACf,MAAA,EAAA,CAAE,CAAA,0CAAA,CAAA,GAAA;wBAIO,EAPP,YAAK,CAIf,WAGiB,KAAA,QAAA,WAAA;OAFd,aAAa,MAAK;OAEZ;UACT,WAIO,KAAA,QAAA,YAAA,EAFE,KAAI,SAEN,CADL,YAA0B,MAAA,8BAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;4BAdhB,CAFhB,YAEgB,MAAA,aAAA,GAAA,EAFD,IAAG,WAAU,GAAA;6BACL,CAArB,WAAqB,KAAA,QAAA,WAAA,EAAR,KAAI,CAAA,CAAA,CAAA"}
|
package/dist/style.css
CHANGED
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
--tw-backdrop-sepia: initial;
|
|
87
87
|
--tw-duration: initial;
|
|
88
88
|
--tw-ease: initial;
|
|
89
|
+
--tw-content: "";
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -783,6 +784,10 @@
|
|
|
783
784
|
left: 0;
|
|
784
785
|
}
|
|
785
786
|
|
|
787
|
+
.scalar-app .left-1\.75 {
|
|
788
|
+
left: 7px;
|
|
789
|
+
}
|
|
790
|
+
|
|
786
791
|
.scalar-app .left-1\/2 {
|
|
787
792
|
left: 50%;
|
|
788
793
|
}
|
|
@@ -791,10 +796,6 @@
|
|
|
791
796
|
left: 8px;
|
|
792
797
|
}
|
|
793
798
|
|
|
794
|
-
.scalar-app .left-2\.5 {
|
|
795
|
-
left: 10px;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
799
|
.scalar-app .left-3 {
|
|
799
800
|
left: 12px;
|
|
800
801
|
}
|
|
@@ -1035,6 +1036,10 @@
|
|
|
1035
1036
|
margin-bottom: 0;
|
|
1036
1037
|
}
|
|
1037
1038
|
|
|
1039
|
+
.scalar-app .mb-0\.75 {
|
|
1040
|
+
margin-bottom: 3px;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1038
1043
|
.scalar-app .-ml-0\.5 {
|
|
1039
1044
|
margin-left: -2px;
|
|
1040
1045
|
}
|
|
@@ -2165,10 +2170,6 @@
|
|
|
2165
2170
|
padding-block: 8px;
|
|
2166
2171
|
}
|
|
2167
2172
|
|
|
2168
|
-
.scalar-app .py-2\.5 {
|
|
2169
|
-
padding-block: 10px;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
2173
|
.scalar-app .py-3 {
|
|
2173
2174
|
padding-block: 12px;
|
|
2174
2175
|
}
|
|
@@ -2201,8 +2202,8 @@
|
|
|
2201
2202
|
padding-right: 6px;
|
|
2202
2203
|
}
|
|
2203
2204
|
|
|
2204
|
-
.scalar-app .pr-
|
|
2205
|
-
padding-right:
|
|
2205
|
+
.scalar-app .pr-1\.75 {
|
|
2206
|
+
padding-right: 7px;
|
|
2206
2207
|
}
|
|
2207
2208
|
|
|
2208
2209
|
.scalar-app .pr-6 {
|
|
@@ -2229,8 +2230,8 @@
|
|
|
2229
2230
|
padding-left: 12px;
|
|
2230
2231
|
}
|
|
2231
2232
|
|
|
2232
|
-
.scalar-app .pl-
|
|
2233
|
-
padding-left:
|
|
2233
|
+
.scalar-app .pl-7\.25 {
|
|
2234
|
+
padding-left: 29px;
|
|
2234
2235
|
}
|
|
2235
2236
|
|
|
2236
2237
|
.scalar-app .pl-\[100\%\] {
|
|
@@ -2766,6 +2767,32 @@
|
|
|
2766
2767
|
font-family: inherit;
|
|
2767
2768
|
}
|
|
2768
2769
|
|
|
2770
|
+
.scalar-app .after\:pointer-events-none:after {
|
|
2771
|
+
content: var(--tw-content);
|
|
2772
|
+
pointer-events: none;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
.scalar-app .after\:absolute:after {
|
|
2776
|
+
content: var(--tw-content);
|
|
2777
|
+
position: absolute;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
.scalar-app .after\:inset-0:after {
|
|
2781
|
+
content: var(--tw-content);
|
|
2782
|
+
inset: 0;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
.scalar-app .after\:rounded-\[inherit\]:after {
|
|
2786
|
+
content: var(--tw-content);
|
|
2787
|
+
border-radius: inherit;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
.scalar-app .after\:shadow-border:after {
|
|
2791
|
+
content: var(--tw-content);
|
|
2792
|
+
--tw-shadow: inset 0 0 0 var(--scalar-border-width) var(--scalar-border-color);
|
|
2793
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2769
2796
|
.scalar-app .first\:rounded-t-\[inherit\]:first-child {
|
|
2770
2797
|
border-top-left-radius: inherit;
|
|
2771
2798
|
border-top-right-radius: inherit;
|
|
@@ -3501,6 +3528,12 @@
|
|
|
3501
3528
|
inherits: false
|
|
3502
3529
|
}
|
|
3503
3530
|
|
|
3531
|
+
@property --tw-content {
|
|
3532
|
+
syntax: "*";
|
|
3533
|
+
inherits: false;
|
|
3534
|
+
initial-value: "";
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3504
3537
|
@keyframes pulse {
|
|
3505
3538
|
50% {
|
|
3506
3539
|
opacity: .5;
|
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.7",
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=22"
|
|
16
16
|
},
|
|
@@ -216,11 +216,11 @@
|
|
|
216
216
|
"@floating-ui/vue": "1.1.9",
|
|
217
217
|
"@headlessui/tailwindcss": "^0.2.2",
|
|
218
218
|
"@headlessui/vue": "1.7.23",
|
|
219
|
-
"@scalar/code-highlight": "0.4.
|
|
220
|
-
"@scalar/helpers": "0.16.
|
|
219
|
+
"@scalar/code-highlight": "0.4.8",
|
|
220
|
+
"@scalar/helpers": "0.16.1",
|
|
221
221
|
"@scalar/icons": "0.7.6",
|
|
222
222
|
"@scalar/themes": "0.18.1",
|
|
223
|
-
"@scalar/use-hooks": "0.4.
|
|
223
|
+
"@scalar/use-hooks": "0.4.18",
|
|
224
224
|
"@vueuse/core": "13.9.0",
|
|
225
225
|
"cva": "1.0.0-beta.4",
|
|
226
226
|
"radix-vue": "^1.9.17",
|