@scalar/components 0.31.0 → 0.31.1

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.
@@ -1 +1 @@
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
+ {"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;kGAiMlC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ScalarCodeBlock.vue.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n /**\n * 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":""}
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 p-0 border-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":""}
@@ -61,7 +61,7 @@ var ScalarCodeBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
61
61
  tabindex: "0"
62
62
  }, [createElementVNode("pre", {
63
63
  id: unref(id),
64
- class: normalizeClass(["m-0 bg-transparent text-nowrap whitespace-pre w-fit", { "pr-6": reserveCopySpace.value }]),
64
+ class: normalizeClass(["m-0 p-0 border-0 bg-transparent text-nowrap whitespace-pre w-fit", { "pr-6": reserveCopySpace.value }]),
65
65
  innerHTML: highlightedCode.value
66
66
  }, null, 10, _hoisted_2)], 8, _hoisted_1), showCopy.value ? (openBlock(), createBlock(unref(ScalarCodeBlockCopy_default), {
67
67
  key: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"ScalarCodeBlock.vue.script.js","names":[],"sources":["../../../src/components/ScalarCodeBlock/ScalarCodeBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar Code Block component\n *\n * Renders syntax-highlighted code using highlight.js.\n * Supports line numbers, credential masking, and an optional copy button.\n *\n * @example\n * <ScalarCodeBlock content=\"const x = 1\" lang=\"javascript\" />\n */\nexport default {}\n</script>\n<script lang=\"ts\" setup>\nimport ScalarCopyBackdrop from '@/components/ScalarCopy/ScalarCopyBackdrop.vue'\nimport { standardLanguages, syntaxHighlight } from '@scalar/code-highlight'\nimport { prettyPrintJson } from '@scalar/helpers/json/pretty-print-json'\nimport { useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed, useId } from 'vue'\n\nimport { ScalarCodeBlockCopy } from '../ScalarCodeBlock'\nimport type { StandardLanguageKey } from './types'\n\ntype BaseProps = {\n content?: string | object\n prettyPrintedContent?: string\n lang?: StandardLanguageKey | string\n lineNumbers?: boolean\n hideCredentials?: string | string[]\n copy?: 'always' | 'hover' | false\n /**\n * 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"}
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 p-0 border-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,oEAAkE,EAAA,QACtD,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ScalarTooltip.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"names":[],"mappings":"AAuHA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAIjD;;;;;;;;;GASG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;cA6CJ,CAAC,KAAK,IAAgB,KAAK,GAAG;EAWxC,CAAC;AACL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"ScalarTooltip.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"names":[],"mappings":"AA6HA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAIjD;;;;;;;;;GASG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;cA6CJ,CAAC,KAAK,IAAgB,KAAK,GAAG;EAWxC,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":"ScalarTooltip.vue.js","names":[],"sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar tooltip component\n *\n * Adds a tooltip to an interactive element\n *\n * @example\n * <ScalarTooltip content=\"This is a tooltip\">\n * <ScalarButton>Hover Me</ScalarButton>\n * </ScalarTooltip>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { type Ref, computed, ref } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET } from './constants'\nimport type { ScalarTooltipProps } from './types'\nimport { useTooltip } from './useTooltip'\n\nconst {\n delay = DEFAULT_DELAY,\n content = '',\n placement = 'top',\n offset = DEFAULT_OFFSET,\n} = defineProps<ScalarTooltipProps>()\n\nconst wrapperRef: Ref<HTMLElement | null> = ref(null)\n\nuseTooltip({\n content: computed(() => content),\n delay: computed(() => delay),\n placement: computed(() => placement),\n offset: computed(() => offset),\n targetRef: computed(\n () => wrapperRef.value?.children?.[0] || wrapperRef.value || undefined,\n ),\n})\n</script>\n<template>\n <div\n ref=\"wrapperRef\"\n :class=\"{ contents: !!$slots.default }\">\n <slot />\n </div>\n</template>\n<style>\n@reference \"../../style.css\";\n\n/**\n * Global styles for the tooltip\n *\n * The tooltip element normally sits directly on the body, but it moves into a <dialog>\n * opened with showModal() so it can paint above the top layer. Both parents have to be\n * matched here or the tooltip loses every one of these styles the moment it is\n * reparented, and renders as unstyled text on a transparent background.\n */\n:where(body, dialog) > .scalar-tooltip {\n --scalar-tooltip-padding-x: 8px;\n --scalar-tooltip-padding-y: 6px;\n\n padding: var(--scalar-tooltip-padding-y) var(--scalar-tooltip-padding-x);\n\n @apply z-tooltip text-c-tooltip text-xs/4 font-normal break-words max-w-xs;\n}\n\n/**\n * The offset is the gap between the target and the tooltip, so it only belongs\n * on the side facing the target (opposite the placement side). Applying it to\n * every side shifts the visible box inward on `-start` / `-end` placements,\n * where Floating UI aligns the floating element's edge with the target's edge.\n */\n:where(body, dialog) > .scalar-tooltip[data-side='top'] {\n padding-bottom: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom'] {\n padding-top: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left'] {\n padding-right: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right'] {\n padding-left: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n\n:where(body, dialog) > .scalar-tooltip:before {\n content: '';\n inset: 0;\n @apply absolute rounded-lg bg-b-tooltip -z-1 backdrop-blur border shadow;\n}\n\n/* Leave the gap on the target-facing side so the other edges stay flush */\n:where(body, dialog) > .scalar-tooltip[data-side='top']:before {\n bottom: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom']:before {\n top: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left']:before {\n right: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right']:before {\n left: var(--scalar-tooltip-offset);\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"ScalarTooltip.vue.js","names":[],"sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar tooltip component\n *\n * Adds a tooltip to an interactive element\n *\n * @example\n * <ScalarTooltip content=\"This is a tooltip\">\n * <ScalarButton>Hover Me</ScalarButton>\n * </ScalarTooltip>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { type Ref, computed, ref } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET } from './constants'\nimport type { ScalarTooltipProps } from './types'\nimport { useTooltip } from './useTooltip'\n\nconst {\n delay = DEFAULT_DELAY,\n content = '',\n placement = 'top',\n offset = DEFAULT_OFFSET,\n} = defineProps<ScalarTooltipProps>()\n\nconst wrapperRef: Ref<HTMLElement | null> = ref(null)\n\nuseTooltip({\n content: computed(() => content),\n delay: computed(() => delay),\n placement: computed(() => placement),\n offset: computed(() => offset),\n targetRef: computed(\n () => wrapperRef.value?.children?.[0] || wrapperRef.value || undefined,\n ),\n})\n</script>\n<template>\n <div\n ref=\"wrapperRef\"\n :class=\"{ contents: !!$slots.default }\">\n <slot />\n </div>\n</template>\n<style>\n@reference \"../../style.css\";\n\n/**\n * Global styles for the tooltip\n *\n * The tooltip element normally sits directly on the body, but it moves into a <dialog>\n * opened with showModal() so it can paint above the top layer. Both parents have to be\n * matched here or the tooltip loses every one of these styles the moment it is\n * reparented, and renders as unstyled text on a transparent background.\n */\n:where(body, dialog) > .scalar-tooltip {\n --scalar-tooltip-padding-x: 8px;\n --scalar-tooltip-padding-y: 6px;\n\n padding: var(--scalar-tooltip-padding-y) var(--scalar-tooltip-padding-x);\n\n @apply z-tooltip text-c-tooltip text-xs/4 font-normal break-words max-w-xs;\n}\n\n/**\n * The offset is the gap between the target and the tooltip, so it only belongs\n * on the side facing the target (opposite the placement side). Applying it to\n * every side shifts the visible box inward on `-start` / `-end` placements,\n * where Floating UI aligns the floating element's edge with the target's edge.\n */\n:where(body, dialog) > .scalar-tooltip[data-side='top'] {\n padding-bottom: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom'] {\n padding-top: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left'] {\n padding-right: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right'] {\n padding-left: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n\n/*\n * The hairline is spelled out: the preflight that gives `border` its theme colour only reaches\n * descendants of `.scalar-app`, and this element is the `.scalar-app` root itself, so the\n * utility alone would fall back to currentColor (black on light, white on dark).\n */\n:where(body, dialog) > .scalar-tooltip:before {\n content: '';\n inset: 0;\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n @apply absolute rounded-lg bg-b-tooltip -z-1 backdrop-blur shadow;\n}\n\n/* Leave the gap on the target-facing side so the other edges stay flush */\n:where(body, dialog) > .scalar-tooltip[data-side='top']:before {\n bottom: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom']:before {\n top: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left']:before {\n right: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right']:before {\n left: var(--scalar-tooltip-offset);\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"ScalarTooltip.vue.script.js","names":["$slots"],"sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar tooltip component\n *\n * Adds a tooltip to an interactive element\n *\n * @example\n * <ScalarTooltip content=\"This is a tooltip\">\n * <ScalarButton>Hover Me</ScalarButton>\n * </ScalarTooltip>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { type Ref, computed, ref } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET } from './constants'\nimport type { ScalarTooltipProps } from './types'\nimport { useTooltip } from './useTooltip'\n\nconst {\n delay = DEFAULT_DELAY,\n content = '',\n placement = 'top',\n offset = DEFAULT_OFFSET,\n} = defineProps<ScalarTooltipProps>()\n\nconst wrapperRef: Ref<HTMLElement | null> = ref(null)\n\nuseTooltip({\n content: computed(() => content),\n delay: computed(() => delay),\n placement: computed(() => placement),\n offset: computed(() => offset),\n targetRef: computed(\n () => wrapperRef.value?.children?.[0] || wrapperRef.value || undefined,\n ),\n})\n</script>\n<template>\n <div\n ref=\"wrapperRef\"\n :class=\"{ contents: !!$slots.default }\">\n <slot />\n </div>\n</template>\n<style>\n@reference \"../../style.css\";\n\n/**\n * Global styles for the tooltip\n *\n * The tooltip element normally sits directly on the body, but it moves into a <dialog>\n * opened with showModal() so it can paint above the top layer. Both parents have to be\n * matched here or the tooltip loses every one of these styles the moment it is\n * reparented, and renders as unstyled text on a transparent background.\n */\n:where(body, dialog) > .scalar-tooltip {\n --scalar-tooltip-padding-x: 8px;\n --scalar-tooltip-padding-y: 6px;\n\n padding: var(--scalar-tooltip-padding-y) var(--scalar-tooltip-padding-x);\n\n @apply z-tooltip text-c-tooltip text-xs/4 font-normal break-words max-w-xs;\n}\n\n/**\n * The offset is the gap between the target and the tooltip, so it only belongs\n * on the side facing the target (opposite the placement side). Applying it to\n * every side shifts the visible box inward on `-start` / `-end` placements,\n * where Floating UI aligns the floating element's edge with the target's edge.\n */\n:where(body, dialog) > .scalar-tooltip[data-side='top'] {\n padding-bottom: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom'] {\n padding-top: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left'] {\n padding-right: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right'] {\n padding-left: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n\n:where(body, dialog) > .scalar-tooltip:before {\n content: '';\n inset: 0;\n @apply absolute rounded-lg bg-b-tooltip -z-1 backdrop-blur border shadow;\n}\n\n/* Leave the gap on the target-facing side so the other edges stay flush */\n:where(body, dialog) > .scalar-tooltip[data-side='top']:before {\n bottom: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom']:before {\n top: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left']:before {\n right: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right']:before {\n left: var(--scalar-tooltip-offset);\n}\n</style>\n"],"mappings":";;;;;;;;;;;EA2BA,MAAM,aAAsC,IAAI,IAAI;EAEpD,WAAW;GACT,SAAS,eAAe,QAAA,OAAO;GAC/B,OAAO,eAAe,QAAA,KAAK;GAC3B,WAAW,eAAe,QAAA,SAAS;GACnC,QAAQ,eAAe,QAAA,MAAM;GAC7B,WAAW,eACH,WAAW,OAAO,WAAW,MAAM,WAAW,SAAS,KAAA,CAC/D;EACF,CAAC;;uBAGC,mBAIM,OAAA;aAHA;IAAJ,KAAI;IACH,OAAK,eAAA,EAAA,UAAA,CAAA,CAAgBA,KAAAA,OAAO,QAAO,CAAA;OACpC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"ScalarTooltip.vue.script.js","names":["$slots"],"sources":["../../../src/components/ScalarTooltip/ScalarTooltip.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Scalar tooltip component\n *\n * Adds a tooltip to an interactive element\n *\n * @example\n * <ScalarTooltip content=\"This is a tooltip\">\n * <ScalarButton>Hover Me</ScalarButton>\n * </ScalarTooltip>\n */\nexport default {}\n</script>\n<script setup lang=\"ts\">\nimport { type Ref, computed, ref } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET } from './constants'\nimport type { ScalarTooltipProps } from './types'\nimport { useTooltip } from './useTooltip'\n\nconst {\n delay = DEFAULT_DELAY,\n content = '',\n placement = 'top',\n offset = DEFAULT_OFFSET,\n} = defineProps<ScalarTooltipProps>()\n\nconst wrapperRef: Ref<HTMLElement | null> = ref(null)\n\nuseTooltip({\n content: computed(() => content),\n delay: computed(() => delay),\n placement: computed(() => placement),\n offset: computed(() => offset),\n targetRef: computed(\n () => wrapperRef.value?.children?.[0] || wrapperRef.value || undefined,\n ),\n})\n</script>\n<template>\n <div\n ref=\"wrapperRef\"\n :class=\"{ contents: !!$slots.default }\">\n <slot />\n </div>\n</template>\n<style>\n@reference \"../../style.css\";\n\n/**\n * Global styles for the tooltip\n *\n * The tooltip element normally sits directly on the body, but it moves into a <dialog>\n * opened with showModal() so it can paint above the top layer. Both parents have to be\n * matched here or the tooltip loses every one of these styles the moment it is\n * reparented, and renders as unstyled text on a transparent background.\n */\n:where(body, dialog) > .scalar-tooltip {\n --scalar-tooltip-padding-x: 8px;\n --scalar-tooltip-padding-y: 6px;\n\n padding: var(--scalar-tooltip-padding-y) var(--scalar-tooltip-padding-x);\n\n @apply z-tooltip text-c-tooltip text-xs/4 font-normal break-words max-w-xs;\n}\n\n/**\n * The offset is the gap between the target and the tooltip, so it only belongs\n * on the side facing the target (opposite the placement side). Applying it to\n * every side shifts the visible box inward on `-start` / `-end` placements,\n * where Floating UI aligns the floating element's edge with the target's edge.\n */\n:where(body, dialog) > .scalar-tooltip[data-side='top'] {\n padding-bottom: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom'] {\n padding-top: calc(\n var(--scalar-tooltip-padding-y) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left'] {\n padding-right: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right'] {\n padding-left: calc(\n var(--scalar-tooltip-padding-x) + var(--scalar-tooltip-offset)\n );\n}\n\n/*\n * The hairline is spelled out: the preflight that gives `border` its theme colour only reaches\n * descendants of `.scalar-app`, and this element is the `.scalar-app` root itself, so the\n * utility alone would fall back to currentColor (black on light, white on dark).\n */\n:where(body, dialog) > .scalar-tooltip:before {\n content: '';\n inset: 0;\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n @apply absolute rounded-lg bg-b-tooltip -z-1 backdrop-blur shadow;\n}\n\n/* Leave the gap on the target-facing side so the other edges stay flush */\n:where(body, dialog) > .scalar-tooltip[data-side='top']:before {\n bottom: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='bottom']:before {\n top: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='left']:before {\n right: var(--scalar-tooltip-offset);\n}\n:where(body, dialog) > .scalar-tooltip[data-side='right']:before {\n left: var(--scalar-tooltip-offset);\n}\n</style>\n"],"mappings":";;;;;;;;;;;EA2BA,MAAM,aAAsC,IAAI,IAAI;EAEpD,WAAW;GACT,SAAS,eAAe,QAAA,OAAO;GAC/B,OAAO,eAAe,QAAA,KAAK;GAC3B,WAAW,eAAe,QAAA,SAAS;GACnC,QAAQ,eAAe,QAAA,MAAM;GAC7B,WAAW,eACH,WAAW,OAAO,WAAW,MAAM,WAAW,SAAS,KAAA,CAC/D;EACF,CAAC;;uBAGC,mBAIM,OAAA;aAHA;IAAJ,KAAI;IACH,OAAK,eAAA,EAAA,UAAA,CAAA,CAAgBA,KAAAA,OAAO,QAAO,CAAA;OACpC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTooltip.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarTooltip/useTooltip.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAS,oBAAoB,EAAE,MAAM,SAAS,CAAA;AA4J1D;;GAEG;AACH,wBAAgB,qBAAqB,SAGpC;AAgID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,oBAAoB,QA8EpD"}
1
+ {"version":3,"file":"useTooltip.d.ts","sourceRoot":"","sources":["../../../src/components/ScalarTooltip/useTooltip.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAS,oBAAoB,EAAE,MAAM,SAAS,CAAA;AA4J1D;;GAEG;AACH,wBAAgB,qBAAqB,SAGpC;AAqID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,oBAAoB,QA8EpD"}
@@ -151,12 +151,17 @@ function getAllParents(el) {
151
151
  }
152
152
  return parents;
153
153
  }
154
- /** Check if mouse moved off the target but onto the tooltip */
154
+ /**
155
+ * Check if the mouse moved off the target and the tooltip altogether
156
+ *
157
+ * Moving between the two keeps the tooltip open. Landing on a child of the target (an icon
158
+ * inside a button, say) counts as landing on the target: the pointer never left it.
159
+ */
155
160
  function isMovingOffElements(e) {
156
161
  const target = unref(config.value?.targetRef);
157
162
  if (e instanceof MouseEvent && e.relatedTarget instanceof Element && target) {
158
163
  const relatedTargetParents = getAllParents(e.relatedTarget);
159
- return e.relatedTarget.id !== "scalar-tooltip" && !relatedTargetParents.some((parent) => parent.id === "scalar-tooltip") && e.relatedTarget !== target;
164
+ return e.relatedTarget.id !== "scalar-tooltip" && !relatedTargetParents.some((parent) => parent.id === "scalar-tooltip") && !target.contains(e.relatedTarget);
160
165
  }
161
166
  return true;
162
167
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useTooltip.js","names":[],"sources":["../../../src/components/ScalarTooltip/useTooltip.ts"],"sourcesContent":["import { autoUpdate, flip, shift, useFloating } from '@floating-ui/vue'\nimport { computed, onScopeDispose, ref, unref, watch } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET, ELEMENT_CLASS, ELEMENT_ID } from './constants'\nimport type { Timer, TooltipConfiguration } from './types'\n\n// ---------------------------------------------------------------------------\n// State\n// ---------------------------------------------------------------------------\n\n/**\n * The delay timer for the tooltip\n *\n * If there's not a timer running it should be undefined\n */\nconst timer = ref<Timer>()\n\n/**\n * A reference to the tooltip element\n *\n * If the hook hasn't been initialized it should be undefined\n */\nconst el = ref<HTMLElement>()\n\n/**\n * The configuration for the active tooltip\n *\n * If no tooltip is active it should be undefined\n */\nconst config = ref<TooltipConfiguration>()\n\n// ---------------------------------------------------------------------------\n// Core watcher and floating UI setup\n// ---------------------------------------------------------------------------\n\n// Set up floating UI\nconst { floatingStyles, placement, update } = useFloating(\n computed(() => unref(config.value?.targetRef)),\n el,\n {\n placement: computed(() => unref(config.value?.placement)),\n whileElementsMounted: autoUpdate,\n middleware: computed(() => [flip(), shift()]),\n },\n)\n\n// Expose the resolved placement side (post-flip) so the CSS can apply the offset\n// gap only on the side facing the target. We key off the side (the part before\n// the '-') because that is where the tooltip sits relative to the target. The\n// initial side is set synchronously when the tooltip is shown (see the config\n// watcher below); this watcher keeps it in sync once Floating UI flips it.\nwatch(placement, (value) => {\n if (!el.value || !value) {\n return\n }\n el.value.dataset.side = value.split('-')[0]\n})\n\n// Update the tooltip element's positioning when Floating UI updates the styles\nwatch(floatingStyles, () => {\n if (!el.value) {\n return\n }\n\n el.value.style.position = floatingStyles.value.position\n el.value.style.top = floatingStyles.value.top\n el.value.style.left = floatingStyles.value.left\n el.value.style.transform = floatingStyles.value.transform ?? ''\n el.value.style.willChange = floatingStyles.value.willChange ?? ''\n})\n\n// Show or hide the tooltip when the config changes\nwatch(\n config,\n (opts) => {\n if (!el.value) {\n return\n }\n\n if (opts) {\n // Keep the element in the right host if the target moved into or out of a modal\n // dialog while the tooltip was already showing. Opening a tooltip is handled in\n // `showTooltip` before the config is assigned, so this only fires when `targetRef`\n // changes underneath an open tooltip, and it has to ask Floating UI for a fresh\n // position because a reparent is neither a scroll nor a resize.\n if (moveTooltipToHost(unref(opts.targetRef))) {\n update()\n }\n\n const contentTarget = unref(opts?.contentTarget) ?? 'textContent'\n\n // Update the tooltip content\n el.value[contentTarget] = unref(opts?.content) ?? ''\n\n // Show the tooltip\n const offset = unref(opts?.offset) ?? DEFAULT_OFFSET\n el.value.style.setProperty('--scalar-tooltip-offset', `${offset}px`)\n\n // Set the side the tooltip sits on so the offset gap is only applied to\n // the target-facing side. Floating UI defaults to 'bottom' when no\n // placement is provided; the placement watcher above refines this after\n // any flip.\n el.value.dataset.side = (unref(opts?.placement) ?? 'bottom').split('-')[0]\n\n el.value.style.setProperty('display', 'block')\n } else {\n // Clear the tooltip content\n el.value.innerHTML = ''\n\n // Hide the tooltip\n el.value.style.removeProperty('--scalar-tooltip-offset')\n el.value.style.setProperty('display', 'none')\n\n // Park the element back on the body. Left inside a dialog it would be removed from\n // the document when that dialog unmounts, taking the shared tooltip with it.\n moveTooltipToHost(undefined)\n }\n },\n { deep: true },\n)\n\n// ---------------------------------------------------------------------------\n// Lifecycle Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Initialize the tooltip element\n *\n * If the tooltip is already initialized it will be ignored\n */\nfunction initializeTooltipElement(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') {\n // Skip tooltip initialization during SSR\n return\n }\n\n if (el.value) {\n // Tooltip already initialized\n return\n }\n\n // See if the tooltip element already exists\n // (Sometimes this happens with HMR)\n const existingTooltipElement = document.getElementById(ELEMENT_ID)\n\n if (existingTooltipElement) {\n el.value = existingTooltipElement\n } else {\n // Create the tooltip element\n el.value = document.createElement('div')\n el.value.role = 'tooltip'\n el.value.id = ELEMENT_ID\n el.value.classList.add(ELEMENT_CLASS)\n el.value.classList.add('scalar-app')\n el.value.style.setProperty('display', 'none')\n el.value.addEventListener('mouseleave', hideTooltip)\n document.body.appendChild(el.value)\n }\n}\n\n/**\n * Cleanup and reset the tooltip element\n */\nexport function cleanupTooltipElement() {\n document.getElementById(ELEMENT_ID)?.remove()\n el.value = undefined\n}\n\n// ---------------------------------------------------------------------------\n// Helper Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Hide the tooltip\n *\n * If the mouse is moving between the tooltip and the target we don't hide the tooltip\n */\nfunction hideTooltip(_e: Event) {\n if (!isMovingOffElements(_e)) {\n // Don't hide the tooltip if the mouse is moving between the tooltip and the target\n return\n }\n\n // Clear any existing timer\n clearTimer()\n\n // Hide the tooltip\n config.value = undefined\n}\n\n/**\n * Handle the escape key\n *\n * If the escape key is pressed we need to hide the tooltip\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tooltip_role#keyboard_interactions\n */\nfunction handleEscape(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.stopPropagation()\n hideTooltip(e)\n }\n}\n\n/**\n * Find the modal dialog containing an element, if there is one\n *\n * Only a dialog opened with `showModal()` is promoted to the browser's top layer, and\n * `:modal` matches exactly those. The `open` property is not a substitute: it is also\n * true for `dialog.show()`, which leaves the dialog in the normal flow where the\n * tooltip already paints correctly.\n */\nfunction getModalDialog(target: Element | undefined | null): HTMLDialogElement | undefined {\n const dialog = target?.closest('dialog')\n\n if (!dialog) {\n return undefined\n }\n\n try {\n return dialog.matches(':modal') ? dialog : undefined\n } catch {\n // `:modal` reached Safari a couple of releases after `showModal()` did, and an\n // unknown pseudo-class throws. Treating it as \"not modal\" degrades to the old\n // behaviour for that one case instead of breaking every tooltip on the page.\n return undefined\n }\n}\n\n/**\n * Move the tooltip element into the host it needs to paint above\n *\n * Top layer content paints above the rest of the document whatever `z-index` anything\n * else carries, so the only way to sit above a modal dialog is to live inside it.\n * Everywhere else the tooltip belongs on the body.\n *\n * This doubles as a repair step: appending an element that is no longer in the document\n * puts it back, which covers a dialog being unmounted while the tooltip sat inside it.\n *\n * Returns whether the element actually moved.\n */\nfunction moveTooltipToHost(target: Element | undefined | null): boolean {\n if (!el.value) {\n return false\n }\n\n const host = getModalDialog(target) ?? document.body\n\n if (el.value.parentElement === host) {\n return false\n }\n\n host.appendChild(el.value)\n\n return true\n}\n\n/** Clears the current timer */\nfunction clearTimer() {\n if (timer.value) {\n clearTimeout(timer.value)\n timer.value = undefined\n }\n}\n\n/** Get all the parents of an element */\nfunction getAllParents(el: Element): Element[] {\n const parents: Element[] = []\n let parent = el.parentElement\n while (parent) {\n parents.push(parent)\n parent = parent.parentElement\n }\n return parents\n}\n\n/** Check if mouse moved off the target but onto the tooltip */\nfunction isMovingOffElements(e: Event): boolean {\n const target = unref(config.value?.targetRef)\n if (e instanceof MouseEvent && e.relatedTarget instanceof Element && target) {\n const relatedTargetParents = getAllParents(e.relatedTarget)\n return (\n e.relatedTarget.id !== ELEMENT_ID &&\n !relatedTargetParents.some((parent) => parent.id === ELEMENT_ID) &&\n e.relatedTarget !== target\n )\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Tooltip Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Create a tooltip\n *\n * If there isn't a tooltip element it will be created\n */\nexport function useTooltip(opts: TooltipConfiguration) {\n initializeTooltipElement()\n\n /**\n * Show the tooltip after the delay if configured\n */\n function showTooltipAfterDelay(_e: Event) {\n const delay = unref(opts.delay) ?? DEFAULT_DELAY\n clearTimer()\n\n // Show the tooltip after the delay\n if (delay > 0) {\n timer.value = setTimeout(() => showTooltip(_e), delay)\n } else {\n showTooltip(_e)\n }\n }\n\n /**\n * Show the tooltip\n */\n function showTooltip(_e: Event) {\n clearTimer()\n\n // Handle the escape key\n document.addEventListener('keydown', handleEscape, { once: true, capture: true })\n\n // Move the element before the config is assigned, not after. Floating UI watches the\n // target with `flush: 'sync'`, so it measures the position the instant the config\n // lands, and that measurement has to happen with the tooltip already in its final\n // parent or the first frame is positioned against the wrong offset parent.\n moveTooltipToHost(unref(opts.targetRef))\n\n // Show the tooltip\n config.value = opts\n }\n\n watch(\n () => unref(opts.targetRef),\n (newRef, oldRef) => {\n if (oldRef) {\n oldRef.removeEventListener('mouseenter', showTooltipAfterDelay)\n oldRef.removeEventListener('mouseleave', hideTooltip)\n oldRef.removeEventListener('focus', showTooltip)\n oldRef.removeEventListener('blur', hideTooltip)\n\n oldRef.removeAttribute('aria-describedby')\n }\n if (newRef) {\n newRef.addEventListener('mouseenter', showTooltipAfterDelay)\n newRef.addEventListener('mouseleave', hideTooltip)\n newRef.addEventListener('focus', showTooltip)\n newRef.addEventListener('blur', hideTooltip)\n\n newRef.setAttribute('aria-describedby', ELEMENT_ID)\n }\n },\n { immediate: true },\n )\n\n // Cleanup the tooltip when the component is unmounted\n onScopeDispose(() => {\n clearTimer()\n\n const target = unref(opts.targetRef)\n if (target) {\n target.removeEventListener('mouseenter', showTooltipAfterDelay)\n target.removeEventListener('mouseleave', hideTooltip)\n target.removeEventListener('focus', showTooltip)\n target.removeEventListener('blur', hideTooltip)\n target.removeAttribute('aria-describedby')\n }\n\n // If the tooltip is showing on this target hide it\n if (unref(config.value?.targetRef) === unref(opts.targetRef)) {\n config.value = undefined\n }\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useTooltip.js","names":[],"sources":["../../../src/components/ScalarTooltip/useTooltip.ts"],"sourcesContent":["import { autoUpdate, flip, shift, useFloating } from '@floating-ui/vue'\nimport { computed, onScopeDispose, ref, unref, watch } from 'vue'\n\nimport { DEFAULT_DELAY, DEFAULT_OFFSET, ELEMENT_CLASS, ELEMENT_ID } from './constants'\nimport type { Timer, TooltipConfiguration } from './types'\n\n// ---------------------------------------------------------------------------\n// State\n// ---------------------------------------------------------------------------\n\n/**\n * The delay timer for the tooltip\n *\n * If there's not a timer running it should be undefined\n */\nconst timer = ref<Timer>()\n\n/**\n * A reference to the tooltip element\n *\n * If the hook hasn't been initialized it should be undefined\n */\nconst el = ref<HTMLElement>()\n\n/**\n * The configuration for the active tooltip\n *\n * If no tooltip is active it should be undefined\n */\nconst config = ref<TooltipConfiguration>()\n\n// ---------------------------------------------------------------------------\n// Core watcher and floating UI setup\n// ---------------------------------------------------------------------------\n\n// Set up floating UI\nconst { floatingStyles, placement, update } = useFloating(\n computed(() => unref(config.value?.targetRef)),\n el,\n {\n placement: computed(() => unref(config.value?.placement)),\n whileElementsMounted: autoUpdate,\n middleware: computed(() => [flip(), shift()]),\n },\n)\n\n// Expose the resolved placement side (post-flip) so the CSS can apply the offset\n// gap only on the side facing the target. We key off the side (the part before\n// the '-') because that is where the tooltip sits relative to the target. The\n// initial side is set synchronously when the tooltip is shown (see the config\n// watcher below); this watcher keeps it in sync once Floating UI flips it.\nwatch(placement, (value) => {\n if (!el.value || !value) {\n return\n }\n el.value.dataset.side = value.split('-')[0]\n})\n\n// Update the tooltip element's positioning when Floating UI updates the styles\nwatch(floatingStyles, () => {\n if (!el.value) {\n return\n }\n\n el.value.style.position = floatingStyles.value.position\n el.value.style.top = floatingStyles.value.top\n el.value.style.left = floatingStyles.value.left\n el.value.style.transform = floatingStyles.value.transform ?? ''\n el.value.style.willChange = floatingStyles.value.willChange ?? ''\n})\n\n// Show or hide the tooltip when the config changes\nwatch(\n config,\n (opts) => {\n if (!el.value) {\n return\n }\n\n if (opts) {\n // Keep the element in the right host if the target moved into or out of a modal\n // dialog while the tooltip was already showing. Opening a tooltip is handled in\n // `showTooltip` before the config is assigned, so this only fires when `targetRef`\n // changes underneath an open tooltip, and it has to ask Floating UI for a fresh\n // position because a reparent is neither a scroll nor a resize.\n if (moveTooltipToHost(unref(opts.targetRef))) {\n update()\n }\n\n const contentTarget = unref(opts?.contentTarget) ?? 'textContent'\n\n // Update the tooltip content\n el.value[contentTarget] = unref(opts?.content) ?? ''\n\n // Show the tooltip\n const offset = unref(opts?.offset) ?? DEFAULT_OFFSET\n el.value.style.setProperty('--scalar-tooltip-offset', `${offset}px`)\n\n // Set the side the tooltip sits on so the offset gap is only applied to\n // the target-facing side. Floating UI defaults to 'bottom' when no\n // placement is provided; the placement watcher above refines this after\n // any flip.\n el.value.dataset.side = (unref(opts?.placement) ?? 'bottom').split('-')[0]\n\n el.value.style.setProperty('display', 'block')\n } else {\n // Clear the tooltip content\n el.value.innerHTML = ''\n\n // Hide the tooltip\n el.value.style.removeProperty('--scalar-tooltip-offset')\n el.value.style.setProperty('display', 'none')\n\n // Park the element back on the body. Left inside a dialog it would be removed from\n // the document when that dialog unmounts, taking the shared tooltip with it.\n moveTooltipToHost(undefined)\n }\n },\n { deep: true },\n)\n\n// ---------------------------------------------------------------------------\n// Lifecycle Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Initialize the tooltip element\n *\n * If the tooltip is already initialized it will be ignored\n */\nfunction initializeTooltipElement(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') {\n // Skip tooltip initialization during SSR\n return\n }\n\n if (el.value) {\n // Tooltip already initialized\n return\n }\n\n // See if the tooltip element already exists\n // (Sometimes this happens with HMR)\n const existingTooltipElement = document.getElementById(ELEMENT_ID)\n\n if (existingTooltipElement) {\n el.value = existingTooltipElement\n } else {\n // Create the tooltip element\n el.value = document.createElement('div')\n el.value.role = 'tooltip'\n el.value.id = ELEMENT_ID\n el.value.classList.add(ELEMENT_CLASS)\n el.value.classList.add('scalar-app')\n el.value.style.setProperty('display', 'none')\n el.value.addEventListener('mouseleave', hideTooltip)\n document.body.appendChild(el.value)\n }\n}\n\n/**\n * Cleanup and reset the tooltip element\n */\nexport function cleanupTooltipElement() {\n document.getElementById(ELEMENT_ID)?.remove()\n el.value = undefined\n}\n\n// ---------------------------------------------------------------------------\n// Helper Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Hide the tooltip\n *\n * If the mouse is moving between the tooltip and the target we don't hide the tooltip\n */\nfunction hideTooltip(_e: Event) {\n if (!isMovingOffElements(_e)) {\n // Don't hide the tooltip if the mouse is moving between the tooltip and the target\n return\n }\n\n // Clear any existing timer\n clearTimer()\n\n // Hide the tooltip\n config.value = undefined\n}\n\n/**\n * Handle the escape key\n *\n * If the escape key is pressed we need to hide the tooltip\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tooltip_role#keyboard_interactions\n */\nfunction handleEscape(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.stopPropagation()\n hideTooltip(e)\n }\n}\n\n/**\n * Find the modal dialog containing an element, if there is one\n *\n * Only a dialog opened with `showModal()` is promoted to the browser's top layer, and\n * `:modal` matches exactly those. The `open` property is not a substitute: it is also\n * true for `dialog.show()`, which leaves the dialog in the normal flow where the\n * tooltip already paints correctly.\n */\nfunction getModalDialog(target: Element | undefined | null): HTMLDialogElement | undefined {\n const dialog = target?.closest('dialog')\n\n if (!dialog) {\n return undefined\n }\n\n try {\n return dialog.matches(':modal') ? dialog : undefined\n } catch {\n // `:modal` reached Safari a couple of releases after `showModal()` did, and an\n // unknown pseudo-class throws. Treating it as \"not modal\" degrades to the old\n // behaviour for that one case instead of breaking every tooltip on the page.\n return undefined\n }\n}\n\n/**\n * Move the tooltip element into the host it needs to paint above\n *\n * Top layer content paints above the rest of the document whatever `z-index` anything\n * else carries, so the only way to sit above a modal dialog is to live inside it.\n * Everywhere else the tooltip belongs on the body.\n *\n * This doubles as a repair step: appending an element that is no longer in the document\n * puts it back, which covers a dialog being unmounted while the tooltip sat inside it.\n *\n * Returns whether the element actually moved.\n */\nfunction moveTooltipToHost(target: Element | undefined | null): boolean {\n if (!el.value) {\n return false\n }\n\n const host = getModalDialog(target) ?? document.body\n\n if (el.value.parentElement === host) {\n return false\n }\n\n host.appendChild(el.value)\n\n return true\n}\n\n/** Clears the current timer */\nfunction clearTimer() {\n if (timer.value) {\n clearTimeout(timer.value)\n timer.value = undefined\n }\n}\n\n/** Get all the parents of an element */\nfunction getAllParents(el: Element): Element[] {\n const parents: Element[] = []\n let parent = el.parentElement\n while (parent) {\n parents.push(parent)\n parent = parent.parentElement\n }\n return parents\n}\n\n/**\n * Check if the mouse moved off the target and the tooltip altogether\n *\n * Moving between the two keeps the tooltip open. Landing on a child of the target (an icon\n * inside a button, say) counts as landing on the target: the pointer never left it.\n */\nfunction isMovingOffElements(e: Event): boolean {\n const target = unref(config.value?.targetRef)\n if (e instanceof MouseEvent && e.relatedTarget instanceof Element && target) {\n const relatedTargetParents = getAllParents(e.relatedTarget)\n return (\n e.relatedTarget.id !== ELEMENT_ID &&\n !relatedTargetParents.some((parent) => parent.id === ELEMENT_ID) &&\n !target.contains(e.relatedTarget)\n )\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Tooltip Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Create a tooltip\n *\n * If there isn't a tooltip element it will be created\n */\nexport function useTooltip(opts: TooltipConfiguration) {\n initializeTooltipElement()\n\n /**\n * Show the tooltip after the delay if configured\n */\n function showTooltipAfterDelay(_e: Event) {\n const delay = unref(opts.delay) ?? DEFAULT_DELAY\n clearTimer()\n\n // Show the tooltip after the delay\n if (delay > 0) {\n timer.value = setTimeout(() => showTooltip(_e), delay)\n } else {\n showTooltip(_e)\n }\n }\n\n /**\n * Show the tooltip\n */\n function showTooltip(_e: Event) {\n clearTimer()\n\n // Handle the escape key\n document.addEventListener('keydown', handleEscape, { once: true, capture: true })\n\n // Move the element before the config is assigned, not after. Floating UI watches the\n // target with `flush: 'sync'`, so it measures the position the instant the config\n // lands, and that measurement has to happen with the tooltip already in its final\n // parent or the first frame is positioned against the wrong offset parent.\n moveTooltipToHost(unref(opts.targetRef))\n\n // Show the tooltip\n config.value = opts\n }\n\n watch(\n () => unref(opts.targetRef),\n (newRef, oldRef) => {\n if (oldRef) {\n oldRef.removeEventListener('mouseenter', showTooltipAfterDelay)\n oldRef.removeEventListener('mouseleave', hideTooltip)\n oldRef.removeEventListener('focus', showTooltip)\n oldRef.removeEventListener('blur', hideTooltip)\n\n oldRef.removeAttribute('aria-describedby')\n }\n if (newRef) {\n newRef.addEventListener('mouseenter', showTooltipAfterDelay)\n newRef.addEventListener('mouseleave', hideTooltip)\n newRef.addEventListener('focus', showTooltip)\n newRef.addEventListener('blur', hideTooltip)\n\n newRef.setAttribute('aria-describedby', ELEMENT_ID)\n }\n },\n { immediate: true },\n )\n\n // Cleanup the tooltip when the component is unmounted\n onScopeDispose(() => {\n clearTimer()\n\n const target = unref(opts.targetRef)\n if (target) {\n target.removeEventListener('mouseenter', showTooltipAfterDelay)\n target.removeEventListener('mouseleave', hideTooltip)\n target.removeEventListener('focus', showTooltip)\n target.removeEventListener('blur', hideTooltip)\n target.removeAttribute('aria-describedby')\n }\n\n // If the tooltip is showing on this target hide it\n if (unref(config.value?.targetRef) === unref(opts.targetRef)) {\n config.value = undefined\n }\n })\n}\n"],"mappings":"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"}
package/dist/style.css CHANGED
@@ -4637,7 +4637,6 @@ to {
4637
4637
  @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
4638
4638
  *, :before, :after, ::backdrop {
4639
4639
  --tw-font-weight: initial;
4640
- --tw-border-style: solid;
4641
4640
  --tw-shadow: 0 0 #0000;
4642
4641
  --tw-shadow-color: initial;
4643
4642
  --tw-shadow-alpha: 100%;
@@ -4691,10 +4690,9 @@ to {
4691
4690
  }
4692
4691
  :where(body, dialog) > .scalar-tooltip:before {
4693
4692
  content: "";
4693
+ border: var(--scalar-border-width) solid var(--scalar-border-color);
4694
4694
  z-index: calc(1 * -1);
4695
4695
  border-radius: var(--scalar-radius-lg);
4696
- border-style: var(--tw-border-style);
4697
- border-width: var(--scalar-border-width);
4698
4696
  background-color: var(--scalar-tooltip-background);
4699
4697
  --tw-shadow: var(--scalar-shadow-1);
4700
4698
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -4720,11 +4718,6 @@ to {
4720
4718
  syntax: "*";
4721
4719
  inherits: false
4722
4720
  }
4723
- @property --tw-border-style {
4724
- syntax: "*";
4725
- inherits: false;
4726
- initial-value: solid;
4727
- }
4728
4721
  @property --tw-shadow {
4729
4722
  syntax: "*";
4730
4723
  inherits: false;
@@ -1128,7 +1128,6 @@ to {
1128
1128
  @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
1129
1129
  *, :before, :after, ::backdrop {
1130
1130
  --tw-font-weight: initial;
1131
- --tw-border-style: solid;
1132
1131
  --tw-shadow: 0 0 #0000;
1133
1132
  --tw-shadow-color: initial;
1134
1133
  --tw-shadow-alpha: 100%;
@@ -1182,10 +1181,9 @@ to {
1182
1181
  }
1183
1182
  :where(body, dialog) > .scalar-tooltip:before {
1184
1183
  content: "";
1184
+ border: var(--scalar-border-width) solid var(--scalar-border-color);
1185
1185
  z-index: calc(1 * -1);
1186
1186
  border-radius: var(--scalar-radius-lg);
1187
- border-style: var(--tw-border-style);
1188
- border-width: var(--scalar-border-width);
1189
1187
  background-color: var(--scalar-tooltip-background);
1190
1188
  --tw-shadow: var(--scalar-shadow-1);
1191
1189
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1211,11 +1209,6 @@ to {
1211
1209
  syntax: "*";
1212
1210
  inherits: false
1213
1211
  }
1214
- @property --tw-border-style {
1215
- syntax: "*";
1216
- inherits: false;
1217
- initial-value: solid;
1218
- }
1219
1212
  @property --tw-shadow {
1220
1213
  syntax: "*";
1221
1214
  inherits: false;
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.31.0",
13
+ "version": "0.31.1",
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.8",
220
- "@scalar/helpers": "0.16.2",
219
+ "@scalar/code-highlight": "0.4.9",
220
+ "@scalar/helpers": "0.16.3",
221
221
  "@scalar/icons": "0.7.6",
222
- "@scalar/themes": "0.19.0",
223
- "@scalar/use-hooks": "0.4.19",
222
+ "@scalar/themes": "0.19.1",
223
+ "@scalar/use-hooks": "0.4.20",
224
224
  "@vueuse/core": "13.9.0",
225
225
  "cva": "1.0.0-beta.4",
226
226
  "radix-vue": "^1.9.17",