vectis-ui 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/VColorPicker/color.js +2 -2
- package/dist/components/VColorPicker/color.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +10 -0
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +177 -159
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.css +1 -1
- package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDialog/VDialog.css +1 -1
- package/dist/components/VDialog/VDialog.js.map +1 -1
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.css +1 -1
- package/dist/components/VDrawer/VDrawer.js.map +1 -1
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMeter/VMeter.css +1 -1
- package/dist/components/VMeter/VMeter.d.ts +24 -8
- package/dist/components/VMeter/VMeter.js.map +1 -1
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js +65 -48
- package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMeterGroup/VMeterGroup.css +2 -0
- package/dist/components/VMeterGroup/VMeterGroup.d.ts +101 -0
- package/dist/components/VMeterGroup/VMeterGroup.js +9 -0
- package/dist/components/VMeterGroup/VMeterGroup.js.map +1 -0
- package/dist/components/VMeterGroup/VMeterGroup.vue_vue_type_script_setup_true_lang.js +117 -0
- package/dist/components/VMeterGroup/VMeterGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/VNumberInput/number.js +2 -2
- package/dist/components/VNumberInput/number.js.map +1 -1
- package/dist/components/VProgressCircular/VProgressCircular.d.ts +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.d.ts +12 -13
- package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js +65 -48
- package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.css +1 -1
- package/dist/components/VSeparator/VSeparator.d.ts +42 -2
- package/dist/components/VSeparator/VSeparator.js.map +1 -1
- package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js +50 -9
- package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.d.ts +6 -0
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +11 -2
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSnackbar/state.js +2 -3
- package/dist/components/VSnackbar/state.js.map +1 -1
- package/dist/components/VToast/state.js +2 -3
- package/dist/components/VToast/state.js.map +1 -1
- package/dist/composables/useAriaLabel.d.ts +5 -0
- package/dist/composables/useAriaLabel.js +5 -1
- package/dist/composables/useAriaLabel.js.map +1 -1
- package/dist/composables/useListbox.d.ts +2 -0
- package/dist/composables/useListbox.js +71 -68
- package/dist/composables/useListbox.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/fr.js +3 -1
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/types.d.ts +6 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +21 -20
- package/dist/styles.css +1 -1
- package/dist/tokens/index.d.ts +10 -5
- package/dist/tokens/semantic.d.ts +10 -5
- package/dist/tokens/semantic.js +10 -5
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens.json +39 -14
- package/package.json +3 -3
|
@@ -1,17 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { iconProps as e } from "../VIcon/iconProps.js";
|
|
2
|
+
import t from "../VIcon/VIcon.js";
|
|
3
|
+
import { createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, defineComponent as s, mergeProps as c, normalizeProps as l, normalizeStyle as u, openBlock as d, renderSlot as f, toDisplayString as p, unref as m, useSlots as h } from "vue";
|
|
2
4
|
//#region src/components/VSeparator/VSeparator.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
-
var
|
|
5
|
+
var g = [
|
|
6
|
+
"data-orientation",
|
|
7
|
+
"data-variant",
|
|
8
|
+
"data-size",
|
|
9
|
+
"aria-orientation"
|
|
10
|
+
], _ = [
|
|
11
|
+
"data-orientation",
|
|
12
|
+
"data-variant",
|
|
13
|
+
"data-size",
|
|
14
|
+
"data-content-position",
|
|
15
|
+
"aria-orientation",
|
|
16
|
+
"aria-label"
|
|
17
|
+
], v = { class: "v-separator-content" }, y = /*@__PURE__*/ s({
|
|
4
18
|
__name: "VSeparator",
|
|
5
|
-
props: {
|
|
6
|
-
|
|
7
|
-
|
|
19
|
+
props: {
|
|
20
|
+
orientation: { default: "horizontal" },
|
|
21
|
+
variant: { default: "solid" },
|
|
22
|
+
size: { default: "sm" },
|
|
23
|
+
color: { default: void 0 },
|
|
24
|
+
label: { default: void 0 },
|
|
25
|
+
icon: { default: void 0 },
|
|
26
|
+
contentPosition: { default: "center" }
|
|
27
|
+
},
|
|
28
|
+
setup(s) {
|
|
29
|
+
let y = h();
|
|
30
|
+
function b() {
|
|
31
|
+
return y.default !== void 0;
|
|
32
|
+
}
|
|
33
|
+
return (h, y) => !s.label && !s.icon && !b() ? (d(), i("hr", {
|
|
34
|
+
key: 0,
|
|
8
35
|
class: "v-separator",
|
|
9
|
-
"data-orientation":
|
|
10
|
-
"
|
|
11
|
-
|
|
36
|
+
"data-orientation": s.orientation,
|
|
37
|
+
"data-variant": s.variant,
|
|
38
|
+
"data-size": s.size,
|
|
39
|
+
"aria-orientation": s.orientation === "vertical" ? "vertical" : void 0,
|
|
40
|
+
style: u(s.color === void 0 ? void 0 : { "--separator-color": s.color })
|
|
41
|
+
}, null, 12, g)) : (d(), i("div", {
|
|
42
|
+
key: 1,
|
|
43
|
+
role: "separator",
|
|
44
|
+
class: "v-separator",
|
|
45
|
+
"data-orientation": s.orientation,
|
|
46
|
+
"data-variant": s.variant,
|
|
47
|
+
"data-size": s.size,
|
|
48
|
+
"data-content-position": s.contentPosition,
|
|
49
|
+
"aria-orientation": s.orientation === "vertical" ? "vertical" : void 0,
|
|
50
|
+
"aria-label": s.label,
|
|
51
|
+
style: u(s.color === void 0 ? void 0 : { "--separator-color": s.color })
|
|
52
|
+
}, [a("span", v, [s.icon ? (d(), n(t, l(c({ key: 0 }, m(e)(s.icon))), null, 16)) : r("", !0), f(h.$slots, "default", {}, () => [o(p(s.label), 1)])])], 12, _));
|
|
12
53
|
}
|
|
13
54
|
});
|
|
14
55
|
//#endregion
|
|
15
|
-
export {
|
|
56
|
+
export { y as default };
|
|
16
57
|
|
|
17
58
|
//# sourceMappingURL=VSeparator.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSeparator.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_hoisted_1","VSeparator_vue_vue_type_script_setup_true_lang_default","openBlock","createElementBlock","
|
|
1
|
+
{"version":3,"file":"VSeparator.vue_vue_type_script_setup_true_lang.js","names":["_useSlots","_openBlock","_createElementBlock","_normalizeStyle","_createElementVNode","_createBlock","VIcon","_normalizeProps","_mergeProps","_unref","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","VIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","VSeparator_vue_vue_type_script_setup_true_lang_default","slots","useSlots","hasSlot","__props","openBlock","createElementBlock","normalizeStyle","createElementVNode","createBlock","normalizeProps","mergeProps","unref","iconProps","createCommentVNode","renderSlot","_ctx","createTextVNode","toDisplayString"],"sources":["../../../src/components/VSeparator/VSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Use a native hr for separator semantics and borders for visibility in forced colours. */\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\n/** Which way the rule runs. */\nexport type SeparatorOrientation = 'horizontal' | 'vertical'\n/** How the rule is stroked. */\nexport type SeparatorVariant = 'solid' | 'dashed' | 'dotted'\n/** Where the content sits along the rule. */\nexport type SeparatorContentPosition = 'start' | 'center' | 'end'\n/** How thick the rule is. */\nexport type SeparatorSize = 'sm' | 'md' | 'lg'\n\ninterface SeparatorProps {\n /** The direction the rule runs in: across by default, or down the page under `vertical`. */\n orientation?: SeparatorOrientation\n /** How the rule is stroked: a continuous line, dashes or dots. */\n variant?: SeparatorVariant\n /**\n * How thick the rule is: a 1px hairline (`sm`, the default), 2px or 4px. A thicker rule marks\n * a stronger break, and makes `dotted` read as dots.\n */\n size?: SeparatorSize\n /**\n * A colour of your own (hex, CSS name or `oklch()`) for the rule. The label and icon keep the\n * muted text colour, so their contrast stays the design system's.\n */\n color?: string\n /** Visible text breaking the rule. It also becomes the separator's accessible name. */\n label?: string\n /** An icon breaking the rule, before the label when both are set (the VIcon contract). */\n icon?: IconSource\n /** Where the label and icon sit along the rule: its start, centre or end. */\n contentPosition?: SeparatorContentPosition\n}\n\nwithDefaults(defineProps<SeparatorProps>(), {\n orientation: 'horizontal',\n variant: 'solid',\n size: 'sm',\n color: undefined,\n label: undefined,\n icon: undefined,\n contentPosition: 'center',\n})\n\nconst slots = defineSlots<{\n /** Rich content breaking the rule, after the icon and in place of the label. */\n default?(): unknown\n}>()\n\n// A function read by the template, never a `computed`: `slots` is not reactive.\nfunction hasSlot() {\n return slots.default !== undefined\n}\n</script>\n\n<template>\n <!--\n Aria-orientation is emitted only when it CONTRADICTS what the role already implies: a\n separator is horizontal by default, so stating it would merely restate the role. This is the\n same convention the VTabs tablist follows.\n -->\n <hr\n v-if=\"!label && !icon && !hasSlot()\"\n class=\"v-separator\"\n :data-orientation=\"orientation\"\n :data-variant=\"variant\"\n :data-size=\"size\"\n :aria-orientation=\"orientation === 'vertical' ? 'vertical' : undefined\"\n :style=\"color !== undefined ? { '--separator-color': color } : undefined\"\n />\n <!--\n An <hr> cannot hold content. A separator's children are presentational, so the label is\n repeated as its accessible name; slot-only content is named by the consumer's aria-label.\n -->\n <div\n v-else\n role=\"separator\"\n class=\"v-separator\"\n :data-orientation=\"orientation\"\n :data-variant=\"variant\"\n :data-size=\"size\"\n :data-content-position=\"contentPosition\"\n :aria-orientation=\"orientation === 'vertical' ? 'vertical' : undefined\"\n :aria-label=\"label\"\n :style=\"color !== undefined ? { '--separator-color': color } : undefined\"\n >\n <span class=\"v-separator-content\">\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" />\n <slot>{{ label }}</slot>\n </span>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-separator {\n --separator-color: var(--vectis-color-border);\n --separator-thickness: var(--vectis-control-size-separator-sm);\n\n flex: none;\n /*\n * `.v-menu-separator` and `.v-listbox-separator` override that margin from THEIR own\n * sheet, and which of the two sheets the consumer's bundler puts last is not ours to\n * decide. Both are therefore written as the compound `.v-separator.v-x-separator`, and\n * every declaration added here falls under the same obligation.\n */\n margin: 0;\n border: 0 solid var(--separator-color);\n }\n\n .v-separator[data-size='md'] {\n --separator-thickness: var(--vectis-control-size-separator-md);\n }\n\n .v-separator[data-size='lg'] {\n --separator-thickness: var(--vectis-control-size-separator-lg);\n }\n\n .v-separator[data-variant='dashed'] {\n border-style: dashed;\n }\n\n .v-separator[data-variant='dotted'] {\n border-style: dotted;\n }\n\n /*\n * In ordinary flow it collapses to nothing, silently and with no error; there it is up to the\n * consumer to give it a block-size.\n */\n .v-separator[data-orientation='vertical'] {\n align-self: stretch;\n }\n\n hr.v-separator[data-orientation='horizontal'] {\n border-block-start-width: var(--separator-thickness);\n }\n\n hr.v-separator[data-orientation='vertical'] {\n border-inline-start-width: var(--separator-thickness);\n }\n\n /* With content, the root only lays out; its two pseudo-elements are the strokes. */\n div.v-separator {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-3);\n color: var(--vectis-color-text-muted);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-caption-weight);\n line-height: var(--vectis-text-caption-leading);\n }\n\n div.v-separator::before,\n div.v-separator::after {\n content: '';\n flex: 1;\n /* The initial width is `medium` (3px): every side but the stroke's must be zeroed. */\n border-width: 0;\n border-color: inherit;\n border-style: inherit;\n }\n\n div.v-separator[data-orientation='horizontal']::before,\n div.v-separator[data-orientation='horizontal']::after {\n border-block-start-width: var(--separator-thickness);\n }\n\n div.v-separator[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n div.v-separator[data-orientation='vertical']::before,\n div.v-separator[data-orientation='vertical']::after {\n border-inline-start-width: var(--separator-thickness);\n }\n\n div.v-separator[data-content-position='start']::before,\n div.v-separator[data-content-position='end']::after {\n display: none;\n }\n\n .v-separator-content {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDA,IAAMmB,IAAQnB,EAAA;EAMd,SAASqB,IAAU;GACjB,OAAOF,EAAM,YAAY,KAAA;EAC3B;mBAUW,CAAAG,EAAA,SAAK,CAAKA,EAAA,QAAI,CAAKD,EAAO,KADnCpB,EAAA,GAAAC,EAQE,MAAA;;GANA,OAAM;GACL,oBAAkBoB,EAAA;GAClB,gBAAcA,EAAA;GACd,aAAWA,EAAA;GACX,oBAAkBA,EAAA,gBAAW,aAAA,aAA+B,KAAA;GAC5D,OAAKnB,EAAEmB,EAAA,UAAU,KAAA,IAA6C,KAAA,IAApC,EAAA,qBAA0BA,EAAA,MAAK,CAAc;EAM1ErB,GAAAA,MAAAA,IAAAA,CAAAA,MAAAA,EAAA,GAAAC,EAgBM,OAAA;;GAdJ,MAAK;GACL,OAAM;GACL,oBAAkBoB,EAAA;GAClB,gBAAcA,EAAA;GACd,aAAWA,EAAA;GACX,yBAAuBA,EAAA;GACvB,oBAAkBA,EAAA,gBAAW,aAAA,aAA+B,KAAA;GAC5D,cAAYA,EAAA;GACZ,OAAKnB,EAAEmB,EAAA,UAAU,KAAA,IAA6C,KAAA,IAApC,EAAA,qBAA0BA,EAAA,MAAK,CAAc;EAExElB,GAAAA,CAAAA,EAGO,QAHPa,GAGO,CAFQK,EAAA,QAAbrB,EAAA,GAAAI,EAA8CC,GAAAC,EAAAC,EAAA,EAAA,KAAA,EAAA,GAAnBC,EAAAsB,CAAA,CAAS,CAACT,EAAA,IAAI,CAAA,CAAA,GAAA,MAAA,EAAA,KAAAU,EAAA,IAAA,EAAA,GACzCrB,EAAwBuB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAftB,EAAAC,EAAAS,EAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAAN,CAAA"}
|
|
@@ -22,6 +22,11 @@ interface SideNavigationProps {
|
|
|
22
22
|
* by 180°.
|
|
23
23
|
*/
|
|
24
24
|
collapseIcon?: IconSource;
|
|
25
|
+
/**
|
|
26
|
+
* Draws a vertical line beside the subitems of each branch, under the middle of the
|
|
27
|
+
* parent's icon, to show which row they belong to.
|
|
28
|
+
*/
|
|
29
|
+
guides?: boolean;
|
|
25
30
|
}
|
|
26
31
|
type __VLS_Slots = {
|
|
27
32
|
/** The first level of the tree: items, groups and separators. */
|
|
@@ -33,6 +38,7 @@ declare const __VLS_base: import('vue').DefineComponent<SideNavigationProps, {},
|
|
|
33
38
|
expandIcon: IconSource;
|
|
34
39
|
collapseIcon: IconSource;
|
|
35
40
|
exclusive: boolean;
|
|
41
|
+
guides: boolean;
|
|
36
42
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
37
43
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
38
44
|
declare const _default: typeof __VLS_export;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSideNavigation.js","names":["_sfc_main","VSideNavigation_vue_vue_type_script_setup_true_lang_default","VSideNavigation_default"],"sources":["../../../src/components/VSideNavigation/VSideNavigation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Native details folds branches; JavaScript bridges open models and adds arrow navigation\n * without replacing native links or buttons.\n */\n\nimport { provide, ref, useId } from 'vue'\n\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type SideNavigationSize = 'sm' | 'md'\n\ninterface SideNavigationProps {\n /**\n * What screen readers announce for this navigation. A page often has several (a\n * main one, a sidebar, a footer) and this is what tells them apart. It falls back\n * to the design system dictionary.\n */\n label?: string\n /** The height of the rows, 32 or 40 pixels, inherited by every level. */\n size?: SideNavigationSize\n /**\n * Keeps a single section open at a time WITHIN EACH LEVEL, which the browser does on\n * its own. It is off by default: a sidebar normally lets several sections stay open.\n */\n exclusive?: boolean\n /** The chevron of a closed section: an icon name, or an explicit render. */\n expandIcon?: IconSource\n /**\n * The chevron of an open section. Leave it out and the closed one is simply rotated\n * by 180°.\n */\n collapseIcon?: IconSource\n}\n\nconst props = withDefaults(defineProps<SideNavigationProps>(), {\n label: undefined,\n size: 'md',\n exclusive: false,\n expandIcon: () => expandMoreIcon,\n collapseIcon: undefined,\n})\n\ndefineSlots<{\n /** The first level of the tree: items, groups and separators. */\n default(): unknown\n}>()\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.sideNavigation.label)\n\n// The name shared by the first level's sections. Each item mints another for its own children,\n// which keeps the exclusivity local to a level (see context.ts).\nconst rootName = useId()\n\nprovide(sideNavigationKey, {\n get name() {\n return props.exclusive ? rootName : undefined\n },\n get exclusive() {\n return props.exclusive\n },\n get expandIcon() {\n return props.expandIcon\n },\n get collapseIcon() {\n return props.collapseIcon\n },\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/**\n * What the arrow keys may move to: the header of a branch, and the link of a leaf. Both forms\n * of disabling have to be excluded, since `:disabled` only ever matches a button; an inert link\n * and a disabled branch header are marked with `aria-disabled` instead, exactly as in VMenu's\n * panel.\n */\nconst ROW_SELECTOR = ':is(summary, .v-side-nav-action):not(:disabled):not([aria-disabled=\"true\"])'\n\n// @keyboard @a11y\n/**\n * This cannot be left to the shared helper, which skips whatever is not displayed. The content\n * of a closed disclosure element is not hidden that way: the browser passes over it by a rule\n * of its own, and it still reports itself as displayed.\n */\nfunction reachable(el: HTMLElement, root: HTMLElement): boolean {\n for (\n let child: HTMLElement = el, parent = el.parentElement;\n parent && child !== root;\n child = parent, parent = parent.parentElement\n ) {\n if (parent.tagName === 'DETAILS' && !(parent as HTMLDetailsElement).open) {\n if (child.tagName !== 'SUMMARY') return false\n }\n }\n return true\n}\n\n// @keyboard @a11y\n/*\n * The component's only behavioural JavaScript, and it exists because nothing native moves the\n * focus from one link to the next. It follows the design system's contract: the arrows MOVE the\n * focus and never activate anything.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const root = rootEl.value\n if (!root || event.defaultPrevented) return\n // Only a key pressed on a row: a field or a menu in a row's `#end` slot is a descendant too,\n // and its caret or its own list must keep the arrows and Home/End.\n if (!(event.target as Element | null)?.matches(ROW_SELECTOR)) return\n arrowNavigate(\n event,\n root,\n () => [...root.querySelectorAll<HTMLElement>(ROW_SELECTOR)].filter((el) => reachable(el, root)),\n { vertical: true },\n )\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n class=\"v-side-nav v-control\"\n :data-size=\"size\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <ul class=\"v-side-nav-list\">\n <slot />\n </ul>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n .v-side-nav {\n /* Every level inherits it. */\n --side-nav-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n font-family: var(--vectis-text-family);\n }\n\n /*\n * They are laid out as flex columns and not as ordinary blocks, which removes margin\n * collapsing entirely; otherwise a group's margin could escape a branch that is closed, and\n * therefore of zero height, and push the rows apart under it.\n */\n .v-side-nav-list,\n .v-side-nav-children,\n .v-side-nav-group-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VSideNavigation.js","names":["_sfc_main","VSideNavigation_vue_vue_type_script_setup_true_lang_default","VSideNavigation_default"],"sources":["../../../src/components/VSideNavigation/VSideNavigation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Native details folds branches; JavaScript bridges open models and adds arrow navigation\n * without replacing native links or buttons.\n */\n\nimport { provide, ref, useId } from 'vue'\n\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type SideNavigationSize = 'sm' | 'md'\n\ninterface SideNavigationProps {\n /**\n * What screen readers announce for this navigation. A page often has several (a\n * main one, a sidebar, a footer) and this is what tells them apart. It falls back\n * to the design system dictionary.\n */\n label?: string\n /** The height of the rows, 32 or 40 pixels, inherited by every level. */\n size?: SideNavigationSize\n /**\n * Keeps a single section open at a time WITHIN EACH LEVEL, which the browser does on\n * its own. It is off by default: a sidebar normally lets several sections stay open.\n */\n exclusive?: boolean\n /** The chevron of a closed section: an icon name, or an explicit render. */\n expandIcon?: IconSource\n /**\n * The chevron of an open section. Leave it out and the closed one is simply rotated\n * by 180°.\n */\n collapseIcon?: IconSource\n /**\n * Draws a vertical line beside the subitems of each branch, under the middle of the\n * parent's icon, to show which row they belong to.\n */\n guides?: boolean\n}\n\nconst props = withDefaults(defineProps<SideNavigationProps>(), {\n label: undefined,\n size: 'md',\n exclusive: false,\n expandIcon: () => expandMoreIcon,\n collapseIcon: undefined,\n guides: false,\n})\n\ndefineSlots<{\n /** The first level of the tree: items, groups and separators. */\n default(): unknown\n}>()\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.sideNavigation.label)\n\n// The name shared by the first level's sections. Each item mints another for its own children,\n// which keeps the exclusivity local to a level (see context.ts).\nconst rootName = useId()\n\nprovide(sideNavigationKey, {\n get name() {\n return props.exclusive ? rootName : undefined\n },\n get exclusive() {\n return props.exclusive\n },\n get expandIcon() {\n return props.expandIcon\n },\n get collapseIcon() {\n return props.collapseIcon\n },\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/**\n * What the arrow keys may move to: the header of a branch, and the link of a leaf. Both forms\n * of disabling have to be excluded, since `:disabled` only ever matches a button; an inert link\n * and a disabled branch header are marked with `aria-disabled` instead, exactly as in VMenu's\n * panel.\n */\nconst ROW_SELECTOR = ':is(summary, .v-side-nav-action):not(:disabled):not([aria-disabled=\"true\"])'\n\n// @keyboard @a11y\n/**\n * This cannot be left to the shared helper, which skips whatever is not displayed. The content\n * of a closed disclosure element is not hidden that way: the browser passes over it by a rule\n * of its own, and it still reports itself as displayed.\n */\nfunction reachable(el: HTMLElement, root: HTMLElement): boolean {\n for (\n let child: HTMLElement = el, parent = el.parentElement;\n parent && child !== root;\n child = parent, parent = parent.parentElement\n ) {\n if (parent.tagName === 'DETAILS' && !(parent as HTMLDetailsElement).open) {\n if (child.tagName !== 'SUMMARY') return false\n }\n }\n return true\n}\n\n// @keyboard @a11y\n/*\n * The component's only behavioural JavaScript, and it exists because nothing native moves the\n * focus from one link to the next. It follows the design system's contract: the arrows MOVE the\n * focus and never activate anything.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const root = rootEl.value\n if (!root || event.defaultPrevented) return\n // Only a key pressed on a row: a field or a menu in a row's `#end` slot is a descendant too,\n // and its caret or its own list must keep the arrows and Home/End.\n if (!(event.target as Element | null)?.matches(ROW_SELECTOR)) return\n arrowNavigate(\n event,\n root,\n () => [...root.querySelectorAll<HTMLElement>(ROW_SELECTOR)].filter((el) => reachable(el, root)),\n { vertical: true },\n )\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n class=\"v-side-nav v-control\"\n :data-size=\"size\"\n :data-guides=\"guides ? '' : undefined\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <ul class=\"v-side-nav-list\">\n <slot />\n </ul>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n .v-side-nav {\n /* Every level inherits it. */\n --side-nav-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n font-family: var(--vectis-text-family);\n }\n\n /*\n * They are laid out as flex columns and not as ordinary blocks, which removes margin\n * collapsing entirely; otherwise a group's margin could escape a branch that is closed, and\n * therefore of zero height, and push the rows apart under it.\n */\n .v-side-nav-list,\n .v-side-nav-children,\n .v-side-nav-group-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n}\n</style>\n"],"mappings":""}
|
package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -5,7 +5,11 @@ import { arrowNavigate as r } from "../../utils/arrowNav.js";
|
|
|
5
5
|
import { sideNavigationKey as i } from "./context.js";
|
|
6
6
|
import { createElementBlock as a, createElementVNode as o, defineComponent as s, openBlock as c, provide as l, ref as u, renderSlot as d, unref as f, useId as p } from "vue";
|
|
7
7
|
//#region src/components/VSideNavigation/VSideNavigation.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
-
var m = [
|
|
8
|
+
var m = [
|
|
9
|
+
"data-size",
|
|
10
|
+
"data-guides",
|
|
11
|
+
"aria-label"
|
|
12
|
+
], h = { class: "v-side-nav-list" }, g = ":is(summary, .v-side-nav-action):not(:disabled):not([aria-disabled=\"true\"])", _ = /*@__PURE__*/ s({
|
|
9
13
|
__name: "VSideNavigation",
|
|
10
14
|
props: {
|
|
11
15
|
label: { default: void 0 },
|
|
@@ -15,7 +19,11 @@ var m = ["data-size", "aria-label"], h = { class: "v-side-nav-list" }, g = ":is(
|
|
|
15
19
|
default: !1
|
|
16
20
|
},
|
|
17
21
|
expandIcon: { default: () => n },
|
|
18
|
-
collapseIcon: { default: void 0 }
|
|
22
|
+
collapseIcon: { default: void 0 },
|
|
23
|
+
guides: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: !1
|
|
26
|
+
}
|
|
19
27
|
},
|
|
20
28
|
setup(n) {
|
|
21
29
|
let s = n, _ = e(), v = t(() => s.label ?? _.value.sideNavigation.label), y = p();
|
|
@@ -47,6 +55,7 @@ var m = ["data-size", "aria-label"], h = { class: "v-side-nav-list" }, g = ":is(
|
|
|
47
55
|
ref: b,
|
|
48
56
|
class: "v-side-nav v-control",
|
|
49
57
|
"data-size": n.size,
|
|
58
|
+
"data-guides": n.guides ? "" : void 0,
|
|
50
59
|
"aria-label": f(v),
|
|
51
60
|
onKeydown: S
|
|
52
61
|
}, [o("ul", h, [d(e.$slots, "default")])], 40, m));
|
package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSideNavigation.vue_vue_type_script_setup_true_lang.js","names":["expandMoreIcon","_openBlock","_createElementBlock","_unref","_createElementVNode","_renderSlot","_hoisted_1","_hoisted_2","ROW_SELECTOR","VSideNavigation_vue_vue_type_script_setup_true_lang_default","expand_more","props","__props","m","useMessages","ariaLabel","useAriaLabel","rootName","useId","sideNavigationKey","rootEl","ref","reachable","child","el","parent","root","onKeydown","event","arrowNavigate","openBlock","createElementBlock","unref","createElementVNode","renderSlot","_ctx"],"sources":["../../../src/components/VSideNavigation/VSideNavigation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Native details folds branches; JavaScript bridges open models and adds arrow navigation\n * without replacing native links or buttons.\n */\n\nimport { provide, ref, useId } from 'vue'\n\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type SideNavigationSize = 'sm' | 'md'\n\ninterface SideNavigationProps {\n /**\n * What screen readers announce for this navigation. A page often has several (a\n * main one, a sidebar, a footer) and this is what tells them apart. It falls back\n * to the design system dictionary.\n */\n label?: string\n /** The height of the rows, 32 or 40 pixels, inherited by every level. */\n size?: SideNavigationSize\n /**\n * Keeps a single section open at a time WITHIN EACH LEVEL, which the browser does on\n * its own. It is off by default: a sidebar normally lets several sections stay open.\n */\n exclusive?: boolean\n /** The chevron of a closed section: an icon name, or an explicit render. */\n expandIcon?: IconSource\n /**\n * The chevron of an open section. Leave it out and the closed one is simply rotated\n * by 180°.\n */\n collapseIcon?: IconSource\n}\n\nconst props = withDefaults(defineProps<SideNavigationProps>(), {\n label: undefined,\n size: 'md',\n exclusive: false,\n expandIcon: () => expandMoreIcon,\n collapseIcon: undefined,\n})\n\ndefineSlots<{\n /** The first level of the tree: items, groups and separators. */\n default(): unknown\n}>()\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.sideNavigation.label)\n\n// The name shared by the first level's sections. Each item mints another for its own children,\n// which keeps the exclusivity local to a level (see context.ts).\nconst rootName = useId()\n\nprovide(sideNavigationKey, {\n get name() {\n return props.exclusive ? rootName : undefined\n },\n get exclusive() {\n return props.exclusive\n },\n get expandIcon() {\n return props.expandIcon\n },\n get collapseIcon() {\n return props.collapseIcon\n },\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/**\n * What the arrow keys may move to: the header of a branch, and the link of a leaf. Both forms\n * of disabling have to be excluded, since `:disabled` only ever matches a button; an inert link\n * and a disabled branch header are marked with `aria-disabled` instead, exactly as in VMenu's\n * panel.\n */\nconst ROW_SELECTOR = ':is(summary, .v-side-nav-action):not(:disabled):not([aria-disabled=\"true\"])'\n\n// @keyboard @a11y\n/**\n * This cannot be left to the shared helper, which skips whatever is not displayed. The content\n * of a closed disclosure element is not hidden that way: the browser passes over it by a rule\n * of its own, and it still reports itself as displayed.\n */\nfunction reachable(el: HTMLElement, root: HTMLElement): boolean {\n for (\n let child: HTMLElement = el, parent = el.parentElement;\n parent && child !== root;\n child = parent, parent = parent.parentElement\n ) {\n if (parent.tagName === 'DETAILS' && !(parent as HTMLDetailsElement).open) {\n if (child.tagName !== 'SUMMARY') return false\n }\n }\n return true\n}\n\n// @keyboard @a11y\n/*\n * The component's only behavioural JavaScript, and it exists because nothing native moves the\n * focus from one link to the next. It follows the design system's contract: the arrows MOVE the\n * focus and never activate anything.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const root = rootEl.value\n if (!root || event.defaultPrevented) return\n // Only a key pressed on a row: a field or a menu in a row's `#end` slot is a descendant too,\n // and its caret or its own list must keep the arrows and Home/End.\n if (!(event.target as Element | null)?.matches(ROW_SELECTOR)) return\n arrowNavigate(\n event,\n root,\n () => [...root.querySelectorAll<HTMLElement>(ROW_SELECTOR)].filter((el) => reachable(el, root)),\n { vertical: true },\n )\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n class=\"v-side-nav v-control\"\n :data-size=\"size\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <ul class=\"v-side-nav-list\">\n <slot />\n </ul>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n .v-side-nav {\n /* Every level inherits it. */\n --side-nav-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n font-family: var(--vectis-text-family);\n }\n\n /*\n * They are laid out as flex columns and not as ordinary blocks, which removes margin\n * collapsing entirely; otherwise a group's margin could escape a branch that is closed, and\n * therefore of zero height, and push the rows apart under it.\n */\n .v-side-nav-list,\n .v-side-nav-children,\n .v-side-nav-group-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n}\n</style>\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"VSideNavigation.vue_vue_type_script_setup_true_lang.js","names":["expandMoreIcon","_openBlock","_createElementBlock","_unref","_createElementVNode","_renderSlot","_hoisted_1","_hoisted_2","ROW_SELECTOR","VSideNavigation_vue_vue_type_script_setup_true_lang_default","expand_more","props","__props","m","useMessages","ariaLabel","useAriaLabel","rootName","useId","sideNavigationKey","rootEl","ref","reachable","child","el","parent","root","onKeydown","event","arrowNavigate","openBlock","createElementBlock","unref","createElementVNode","renderSlot","_ctx"],"sources":["../../../src/components/VSideNavigation/VSideNavigation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Native details folds branches; JavaScript bridges open models and adds arrow navigation\n * without replacing native links or buttons.\n */\n\nimport { provide, ref, useId } from 'vue'\n\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type SideNavigationSize = 'sm' | 'md'\n\ninterface SideNavigationProps {\n /**\n * What screen readers announce for this navigation. A page often has several (a\n * main one, a sidebar, a footer) and this is what tells them apart. It falls back\n * to the design system dictionary.\n */\n label?: string\n /** The height of the rows, 32 or 40 pixels, inherited by every level. */\n size?: SideNavigationSize\n /**\n * Keeps a single section open at a time WITHIN EACH LEVEL, which the browser does on\n * its own. It is off by default: a sidebar normally lets several sections stay open.\n */\n exclusive?: boolean\n /** The chevron of a closed section: an icon name, or an explicit render. */\n expandIcon?: IconSource\n /**\n * The chevron of an open section. Leave it out and the closed one is simply rotated\n * by 180°.\n */\n collapseIcon?: IconSource\n /**\n * Draws a vertical line beside the subitems of each branch, under the middle of the\n * parent's icon, to show which row they belong to.\n */\n guides?: boolean\n}\n\nconst props = withDefaults(defineProps<SideNavigationProps>(), {\n label: undefined,\n size: 'md',\n exclusive: false,\n expandIcon: () => expandMoreIcon,\n collapseIcon: undefined,\n guides: false,\n})\n\ndefineSlots<{\n /** The first level of the tree: items, groups and separators. */\n default(): unknown\n}>()\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.sideNavigation.label)\n\n// The name shared by the first level's sections. Each item mints another for its own children,\n// which keeps the exclusivity local to a level (see context.ts).\nconst rootName = useId()\n\nprovide(sideNavigationKey, {\n get name() {\n return props.exclusive ? rootName : undefined\n },\n get exclusive() {\n return props.exclusive\n },\n get expandIcon() {\n return props.expandIcon\n },\n get collapseIcon() {\n return props.collapseIcon\n },\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/**\n * What the arrow keys may move to: the header of a branch, and the link of a leaf. Both forms\n * of disabling have to be excluded, since `:disabled` only ever matches a button; an inert link\n * and a disabled branch header are marked with `aria-disabled` instead, exactly as in VMenu's\n * panel.\n */\nconst ROW_SELECTOR = ':is(summary, .v-side-nav-action):not(:disabled):not([aria-disabled=\"true\"])'\n\n// @keyboard @a11y\n/**\n * This cannot be left to the shared helper, which skips whatever is not displayed. The content\n * of a closed disclosure element is not hidden that way: the browser passes over it by a rule\n * of its own, and it still reports itself as displayed.\n */\nfunction reachable(el: HTMLElement, root: HTMLElement): boolean {\n for (\n let child: HTMLElement = el, parent = el.parentElement;\n parent && child !== root;\n child = parent, parent = parent.parentElement\n ) {\n if (parent.tagName === 'DETAILS' && !(parent as HTMLDetailsElement).open) {\n if (child.tagName !== 'SUMMARY') return false\n }\n }\n return true\n}\n\n// @keyboard @a11y\n/*\n * The component's only behavioural JavaScript, and it exists because nothing native moves the\n * focus from one link to the next. It follows the design system's contract: the arrows MOVE the\n * focus and never activate anything.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const root = rootEl.value\n if (!root || event.defaultPrevented) return\n // Only a key pressed on a row: a field or a menu in a row's `#end` slot is a descendant too,\n // and its caret or its own list must keep the arrows and Home/End.\n if (!(event.target as Element | null)?.matches(ROW_SELECTOR)) return\n arrowNavigate(\n event,\n root,\n () => [...root.querySelectorAll<HTMLElement>(ROW_SELECTOR)].filter((el) => reachable(el, root)),\n { vertical: true },\n )\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n class=\"v-side-nav v-control\"\n :data-size=\"size\"\n :data-guides=\"guides ? '' : undefined\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <ul class=\"v-side-nav-list\">\n <slot />\n </ul>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n .v-side-nav {\n /* Every level inherits it. */\n --side-nav-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n font-family: var(--vectis-text-family);\n }\n\n /*\n * They are laid out as flex columns and not as ordinary blocks, which removes margin\n * collapsing entirely; otherwise a group's margin could escape a branch that is closed, and\n * therefore of zero height, and push the rows apart under it.\n */\n .v-side-nav-list,\n .v-side-nav-children,\n .v-side-nav-group-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;qCA6FMQ,IAAe;;;;;;;;;;;;;;;;;EA7CrB,IAAMG,IAAQC,GAcRC,IAAIC,EAAY,GAChBC,IAAYC,QAAmBL,EAAM,SAASE,EAAE,MAAM,eAAe,KAAK,GAI1EI,IAAWC,EAAM;EAEvB,EAAQC,GAAmB;GACzB,IAAI,OAAO;IACT,OAAOR,EAAM,YAAYM,IAAW,KAAA;GACtC;GACA,IAAI,YAAY;IACd,OAAON,EAAM;GACf;GACA,IAAI,aAAa;IACf,OAAOA,EAAM;GACf;GACA,IAAI,eAAe;IACjB,OAAOA,EAAM;GACf;EACF,CAAC;EAED,IAAMS,IAASC,EAAwB,IAAI;EAiB3C,SAASC,EAAU,GAAiB,GAA4B;GAC9D,KACE,IAAIC,IAAqBC,GAAIC,IAASD,EAAG,eACzCC,KAAUF,MAAUG,GACpB,IAAQD,GAAQ,IAASA,EAAO,eAEhC,IAAIA,EAAO,YAAY,aAAa,CAAEA,EAA8B,QAC9DF,EAAM,YAAY,WAAW,OAAO;GAG5C,OAAO;EACT;EAQA,SAASI,EAAU,GAAsB;GACvC,IAAMD,IAAON,EAAO;GACpB,AAAKM,KAAQ,GAAM,oBAGbE,EAAM,QAA2B,QAAQpB,CAAY,KAC3DqB,EACED,GACAF,SACM,CAAC,GAAGA,EAAK,iBAA8BlB,CAAY,CAAC,CAAC,CAAC,QAAQ,MAAOc,EAAUE,GAAIE,CAAI,CAAC,GAC9F,EAAE,UAAU,GAAK,CACnB;EACF;oBAIEzB,EAAA,GAAAC,EAWM,OAAA;GAVA,SAAA;GAAJ,KAAIkB;GACJ,OAAM;GACL,aAAWR,EAAA;GACX,eAAaA,EAAA,SAAM,KAAQ,KAAA;GAC3B,cAAYT,EAAAY,CAAA;GACH;EAEVX,GAAAA,CAAAA,EAEK,MAFLG,GAEK,CADHF,EAAQ8B,EAAA,QAAA,SAAA,CAAA,CAAA,CAAA,GAAA,IAAA7B,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
|
|
2
|
-
@layer vectis.components{.v-side-nav-item{--side-nav-parent-level:var(--side-nav-level,0)}.v-side-nav-children{--side-nav-level:calc(var(--side-nav-parent-level) + 1)}.v-side-nav-row{--side-nav-row-radius:min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));align-items:center;gap:var(--control-gap);min-block-size:var(--control-height);padding-block:var(--vectis-space-1);padding-inline:calc(var(--control-padding-inline) + var(--side-nav-level,0) * var(--side-nav-indent)) var(--control-padding-inline);border-radius:var(--side-nav-row-radius);color:var(--vectis-color-text);font-size:var(--control-font-size);line-height:var(--vectis-text-body-md-leading);cursor:pointer;display:flex;position:relative}.v-side-nav-action{align-items:center;gap:var(--control-gap);min-inline-size:0;color:inherit;font-family:inherit;font-size:inherit;line-height:inherit;text-align:start;cursor:inherit;background:0 0;border:none;flex:1;padding:0;text-decoration:none;display:flex}.v-side-nav-action:after{content:"";border-radius:var(--side-nav-row-radius);position:absolute;inset:0}.v-side-nav-content{flex-direction:column;flex:1;min-inline-size:0;display:flex}.v-side-nav-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.v-side-nav-sublabel{font-size:var(--vectis-text-caption-size);color:var(--vectis-color-text-muted);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.v-side-nav-icon{color:var(--vectis-color-text-muted);flex:none}.v-side-nav-end{align-items:center;gap:var(--vectis-space-1);flex:none;display:flex;position:relative}.v-side-nav-row:hover:not([data-disabled]){background:var(--vectis-color-surface-muted)}.v-side-nav-row:focus-visible,.v-side-nav-action:focus-visible:after{outline:var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);outline-offset:calc(-1 * var(--vectis-focus-ring-width))}.v-side-nav-action:focus-visible{outline:none}.v-side-nav-row[data-current]{background:var(--vectis-color-accent-surface);color:var(--vectis-color-accent-text)}.v-side-nav-row[data-current] .v-side-nav-label{font-weight:var(--vectis-text-label-weight)}.v-side-nav-row[data-current] .v-side-nav-icon,.v-side-nav-row[data-current] .v-side-nav-sublabel,.v-side-nav-row[data-current] .v-side-nav-chevron{color:inherit}.v-side-nav-row[data-current]:not([data-disabled]):hover{background:color-mix(in oklab, var(--vectis-color-accent-surface), var(--vectis-color-accent-text) 8%)}.v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])>.v-side-nav-row:not([data-current],[data-disabled]){color:var(--vectis-color-accent-text)}.v-side-nav-row[data-disabled]{color:var(--vectis-color-text-subtle);cursor:not-allowed}.v-side-nav-row[data-disabled] .v-side-nav-icon,.v-side-nav-row[data-disabled] .v-side-nav-sublabel{color:inherit}@media (forced-colors:active){.v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current]{forced-color-adjust:none;color:highlighttext;background:highlight}.v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled]{color:graytext;background:canvas}.v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])>.v-side-nav-row:not([data-current],[data-disabled]) .v-side-nav-label{text-decoration:underline}}}
|
|
2
|
+
@layer vectis.components{.v-side-nav-item{--side-nav-parent-level:var(--side-nav-level,0)}.v-side-nav-children{--side-nav-level:calc(var(--side-nav-parent-level) + 1)}.v-side-nav-row{--side-nav-row-radius:min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));align-items:center;gap:var(--control-gap);min-block-size:var(--control-height);padding-block:var(--vectis-space-1);padding-inline:calc(var(--control-padding-inline) + var(--side-nav-level,0) * var(--side-nav-indent)) var(--control-padding-inline);border-radius:var(--side-nav-row-radius);color:var(--vectis-color-text);font-size:var(--control-font-size);line-height:var(--vectis-text-body-md-leading);cursor:pointer;display:flex;position:relative}.v-side-nav-action{align-items:center;gap:var(--control-gap);min-inline-size:0;color:inherit;font-family:inherit;font-size:inherit;line-height:inherit;text-align:start;cursor:inherit;background:0 0;border:none;flex:1;padding:0;text-decoration:none;display:flex}.v-side-nav-action:after{content:"";border-radius:var(--side-nav-row-radius);position:absolute;inset:0}.v-side-nav-content{flex-direction:column;flex:1;min-inline-size:0;display:flex}.v-side-nav-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.v-side-nav-sublabel{font-size:var(--vectis-text-caption-size);color:var(--vectis-color-text-muted);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.v-side-nav-icon{color:var(--vectis-color-text-muted);flex:none}.v-side-nav-end{align-items:center;gap:var(--vectis-space-1);flex:none;display:flex;position:relative}.v-side-nav-row:hover:not([data-disabled]){background:var(--vectis-color-surface-muted)}.v-side-nav-row:focus-visible,.v-side-nav-action:focus-visible:after{outline:var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);outline-offset:calc(-1 * var(--vectis-focus-ring-width))}.v-side-nav-action:focus-visible{outline:none}.v-side-nav-row[data-current]{background:var(--vectis-color-accent-surface);color:var(--vectis-color-accent-text)}.v-side-nav-row[data-current] .v-side-nav-label{font-weight:var(--vectis-text-label-weight)}.v-side-nav-row[data-current] .v-side-nav-icon,.v-side-nav-row[data-current] .v-side-nav-sublabel,.v-side-nav-row[data-current] .v-side-nav-chevron{color:inherit}.v-side-nav-row[data-current]:not([data-disabled]):hover{background:color-mix(in oklab, var(--vectis-color-accent-surface), var(--vectis-color-accent-text) 8%)}.v-side-nav[data-guides] .v-side-nav-children{position:relative}.v-side-nav[data-guides] .v-side-nav-children:before{content:"";border-inline-start:1px solid var(--vectis-color-border);position:absolute;inset-block:0;inset-inline-start:calc(var(--control-padding-inline) + var(--side-nav-parent-level) * var(--side-nav-indent) + var(--vectis-icon-size) / 2)}.v-side-nav[data-guides] .v-side-nav-children .v-side-nav-row{margin-inline-start:calc(var(--control-padding-inline) + (var(--side-nav-level) - 1) * var(--side-nav-indent) + var(--vectis-icon-size) / 2 + var(--control-gap));padding-inline-start:calc(var(--vectis-icon-size) / 2)}.v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])>.v-side-nav-row:not([data-current],[data-disabled]){color:var(--vectis-color-accent-text)}.v-side-nav-row[data-disabled]{color:var(--vectis-color-text-subtle);cursor:not-allowed}.v-side-nav-row[data-disabled] .v-side-nav-icon,.v-side-nav-row[data-disabled] .v-side-nav-sublabel{color:inherit}@media (forced-colors:active){.v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current]{forced-color-adjust:none;color:highlighttext;background:highlight}.v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled]{color:graytext;background:canvas}.v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])>.v-side-nav-row:not([data-current],[data-disabled]) .v-side-nav-label{text-decoration:underline}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSideNavigationItem.js","names":["_sfc_main","VSideNavigationItem_vue_vue_type_script_setup_true_lang_default","VSideNavigationItem_default"],"sources":["../../../src/components/VSideNavigation/VSideNavigationItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Branches use native details and leaves use links or buttons. JavaScript synchronizes open\n * state, disables summaries and makes disabled links inert.\n */\n\nimport { computed, inject, provide, ref, useId, useSlots } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { useDetailsOpen } from '../../composables/useDetailsOpen'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\ninterface SideNavigationItemProps {\n /** What the row says, and where it goes. The default slot replaces it. */\n label?: string\n /** A second line under the label, for a status or a short explanation. */\n sublabel?: string\n /**\n * An icon before the label: an icon name, or an explicit render. The `#icon` slot\n * replaces it.\n */\n icon?: IconSource\n /**\n * Where this row leads, which makes it a link. It is IGNORED on a row that has\n * subitems: such a row opens and closes rather than navigating.\n */\n href?: string\n /**\n * Marks this row as the page currently being viewed. It is highlighted, and\n * announced as the current page (`aria-current`).\n */\n current?: boolean\n /**\n * Makes the row unusable: it greys out through the colour tokens and leaves the\n * keyboard path.\n */\n disabled?: boolean\n /** Renders a branch already open. Bind `v-model:open` to drive it instead. */\n defaultOpen?: boolean\n}\n\n// Without redirecting them, `target`, `rel`, `download` and the aria-* would land on that list\n// item instead of on the link or the branch header.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SideNavigationItemProps>(), {\n label: undefined,\n sublabel: undefined,\n icon: undefined,\n href: undefined,\n current: false,\n disabled: false,\n defaultOpen: false,\n})\n\n// \"not bound\" is written as `null` and not `undefined`. A model typed as a plain boolean is\n// declared as such at runtime, and Vue casts an ABSENT boolean prop to `false`, which would\n// silently overwrite `defaultOpen` on every branch.\n/**\n * Whether the branch is open, when the consumer wants to drive or observe it. Left\n * unbound, the browser keeps that state to itself, `defaultOpen` giving only the initial\n * value. `null` means unbound.\n */\nconst open = defineModel<boolean | null>('open', { default: null })\n\nconst emit = defineEmits<{\n // Never declare a `click` emit alongside it. Vue removes a declared event from the forwarded\n // attributes, and a consumer's own `@click` would then stop reaching the link entirely.\n /** A row WITHOUT subitems was activated, by click or by keyboard. */\n select: []\n}>()\n\ndefineSlots<{\n /**\n * The label of the row, replacing the `label` prop. One of the two is REQUIRED: a\n * navigation row must say where it goes.\n */\n default?(): unknown\n /** A second line made of markup, replacing the `sublabel` prop. */\n sublabel?(): unknown\n /** Free content before the label, which takes the place of `icon`. */\n icon?(): unknown\n /**\n * Free content at the end of the row, before the chevron: a counter, a badge. On a\n * BRANCH it must not be focusable, a control inside the header being a control nested\n * inside another.\n */\n end?(): unknown\n /**\n * The subitems, which turn this row into a branch. Nesting is not limited. The slot\n * must be statically present or absent: whether a row is a branch is decided when it\n * is created.\n */\n children?(): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst slots = useSlots()\nconst tag = computed(() => (props.href !== undefined ? 'a' : 'button'))\n\nconst parent = inject(sideNavigationKey, null)\n\n/**\n * Both chevrons are chosen on the navigation as a whole. The fallback is what keeps an\n * item rendering correctly when it is used outside one.\n */\nconst expandIcon = computed(() => parent?.expandIcon ?? expandMoreIcon)\nconst collapseIcon = computed(() => parent?.collapseIcon)\n\n// @ssr\n// Each level owns its exclusive group, including children supplied after the first render.\nconst childrenName = useId()\nprovide(sideNavigationKey, {\n get name() {\n return parent?.exclusive ? childrenName : undefined\n },\n get exclusive() {\n return parent?.exclusive ?? false\n },\n get expandIcon() {\n return expandIcon.value\n },\n get collapseIcon() {\n return collapseIcon.value\n },\n})\n\nconst { openAttr, onToggle, onSummaryClick } = useDetailsOpen(open, {\n defaultOpen: () => props.defaultOpen,\n disabled: () => props.disabled,\n})\n\n/*\n * Stopping the event from travelling would not help: folding is not a listener anyone\n * registered, it is the click's default action, and only cancelling that prevents it. The\n * exception is a click that already landed on something activable.\n */\n// @core\nfunction onEndClick(event: MouseEvent) {\n const target = event.target as Element | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n}\n\n// A template ref on the item reaches the list item, a layout box; what is focused and\n// named is its row: the branch header, or the leaf's link or button.\nconst rowEl = ref<HTMLElement | null>(null)\nconst actionEl = ref<HTMLElement | null>(null)\nconst control = () => (slots.children ? rowEl.value : actionEl.value)\ndefineExpose({\n /** Moves the focus to the row: the branch header, or the leaf's link or button. */\n focus: (options?: FocusOptions) => control()?.focus(options),\n /** The row, which is also where the consumer's attributes land. */\n get el() {\n return control()\n },\n})\n\n// @a11y\n// A leaf link has no `disabled`: the address, the consumer's click listeners and the\n// generic role of an `<a>` without `href` are handled by the shared inert link.\nconst link = useInertLink({\n href: () => (tag.value === 'a' ? props.href : undefined),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\n// @core\nfunction onActionClick(event: MouseEvent) {\n // A disabled button never receives the click at all; an inert link does, so it is\n // cancelled here.\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select')\n}\n</script>\n\n<template>\n <li class=\"v-side-nav-item\" :class=\"rootClass\" :style=\"rootStyle\">\n <details\n v-if=\"$slots.children\"\n class=\"v-side-nav-branch v-disclosure\"\n :name=\"parent?.name\"\n :open=\"openAttr\"\n :data-swap=\"collapseIcon ? '' : undefined\"\n @toggle=\"onToggle\"\n >\n <summary\n ref=\"rowEl\"\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-current=\"current ? 'true' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n v-bind=\"forwardedAttrs\"\n @click=\"onSummaryClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n <!-- This slot sits INSIDE the branch header, where ANY click folds the\n branch: without the handler, clicking a badge would close the section\n under it -->\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\" @click=\"onEndClick\"\n ><slot name=\"end\"\n /></span>\n <VIcon class=\"v-side-nav-chevron v-disclosure-chevron\" v-bind=\"iconProps(expandIcon)\" />\n <VIcon\n v-if=\"collapseIcon\"\n class=\"v-side-nav-chevron v-side-nav-chevron-open v-disclosure-chevron v-disclosure-chevron-open\"\n v-bind=\"iconProps(collapseIcon)\"\n />\n </summary>\n <ul class=\"v-side-nav-children\">\n <slot name=\"children\" />\n </ul>\n </details>\n\n <div\n v-else\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <component\n :is=\"tag\"\n ref=\"actionEl\"\n class=\"v-side-nav-action\"\n :type=\"tag === 'button' ? 'button' : undefined\"\n :disabled=\"tag === 'button' ? disabled : undefined\"\n :aria-disabled=\"link.isInertLink.value ? 'true' : undefined\"\n :aria-current=\"current ? (tag === 'a' ? 'page' : 'true') : undefined\"\n v-bind=\"link.attrs.value\"\n :href=\"link.linkHref.value\"\n @click=\"onActionClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n </component>\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\"><slot name=\"end\" /></span>\n </div>\n </li>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The obvious one-name form, incrementing a variable by reading itself, is a CYCLE as far as\n * CSS is concerned, even though the value being read is the inherited one. The property then\n * falls to invalid, every read falls back to zero, and the whole tree renders FLAT; with\n * nothing in the console to say why.\n */\n .v-side-nav-item {\n --side-nav-parent-level: var(--side-nav-level, 0);\n }\n\n .v-side-nav-children {\n --side-nav-level: calc(var(--side-nav-parent-level) + 1);\n }\n\n .v-side-nav-row {\n /*\n * The indent is computed here and not stored in a variable set higher up. A custom property\n * is substituted on the element that DECLARES it, so a padding computed on the nav would be\n * frozen at level zero for the whole tree.\n */\n\n /*\n * The cap has to stay derived from --control-height. A browser scales down any radius it\n * cannot fit, so a row exactly one control tall already paints half that height under a\n * pill override; min() applies the same reduction to a row that grew past it.\n */\n --side-nav-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(\n var(--control-padding-inline) + var(--side-nav-level, 0) * var(--side-nav-indent)\n )\n var(--control-padding-inline);\n border-radius: var(--side-nav-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n cursor: pointer;\n }\n\n .v-side-nav-action {\n /* On a leaf the link is a CHILD of the row, and the indent deliberately stays on\n the row itself: what has to run the full width is the hover background, not the\n link inside it. */\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n text-align: start;\n text-decoration: none;\n cursor: inherit;\n }\n\n /*\n * This is what stretches the link over the whole row without having to wrap the end slot\n * inside it. It is an invisible box, positioned, so it is painted above the chevron; which is\n * not positioned; while passing UNDER the end slot, which is positioned too and comes later\n * in the document.\n */\n .v-side-nav-action::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--side-nav-row-radius);\n }\n\n .v-side-nav-content {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n flex-direction: column;\n }\n\n .v-side-nav-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-sublabel {\n overflow: hidden;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* Positioned on purpose: that is what paints it above the stretched link, and\n therefore what keeps whatever it holds clickable. */\n .v-side-nav-end {\n position: relative;\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-side-nav-row:hover:not([data-disabled]) {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * A branch header takes focus itself; a leaf only takes it through its link, and the\n * ring is then drawn on the stretched box so that it frames the whole row rather than\n * just the text.\n *\n * The ring is pulled INWARDS in both cases: the content of a branch is clipped so it\n * can be animated open, and a ring drawn outside the row would be cut off there.\n */\n .v-side-nav-row:focus-visible,\n .v-side-nav-action:focus-visible::after {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-side-nav-action:focus-visible {\n outline: none;\n }\n\n .v-side-nav-row[data-current] {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-icon,\n .v-side-nav-row[data-current] .v-side-nav-sublabel,\n .v-side-nav-row[data-current] .v-side-nav-chevron {\n color: inherit;\n }\n\n .v-side-nav-row[data-current]:not([data-disabled]):hover {\n /*\n * The current row is already tinted, so its hover deepens that tint rather than replacing\n * it with the neutral highlight; the VMenuItem idiom.\n */\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n /* A CLOSED branch holding the current page keeps a mark of it, so the reader can see\n where they are without opening every section. The lookup is deliberately a\n descendant one: the page may be several levels down. */\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled]) {\n color: var(--vectis-color-accent-text);\n }\n\n /* A disabled row greys out through the colour tokens, never through opacity. */\n .v-side-nav-row[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /* The icon and the second line default to a colour DARKER than the one a disabled\n label takes: left alone they would come out stronger than the label itself, so they\n inherit it instead. The chevron gets the same treatment from `styles/disclosure.css`. */\n .v-side-nav-row[data-disabled] .v-side-nav-icon,\n .v-side-nav-row[data-disabled] .v-side-nav-sublabel {\n color: inherit;\n }\n\n /* Forced colours flatten the current row's tint to `Canvas` and its accent text to\n `CanvasText`, leaving a slightly bolder label as the only cue, and the mark on a closed\n branch is a colour alone. The current row takes the system selection pair, repeated to\n (0,4,0) above its own hover, and the closed branch underlines its label, a decoration\n the mode keeps. */\n @media (forced-colors: active) {\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current] {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled] {\n background: Canvas;\n color: GrayText;\n }\n\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled])\n .v-side-nav-label {\n text-decoration: underline;\n }\n }\n\n /* The disclosure animation, the chevron's rotation or swap and the WebKit marker come\n from `styles/disclosure.css`, reduced-motion block included. */\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VSideNavigationItem.js","names":["_sfc_main","VSideNavigationItem_vue_vue_type_script_setup_true_lang_default","VSideNavigationItem_default"],"sources":["../../../src/components/VSideNavigation/VSideNavigationItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Branches use native details and leaves use links or buttons. JavaScript synchronizes open\n * state, disables summaries and makes disabled links inert.\n */\n\nimport { computed, inject, provide, ref, useId, useSlots } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { useDetailsOpen } from '../../composables/useDetailsOpen'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\ninterface SideNavigationItemProps {\n /** What the row says, and where it goes. The default slot replaces it. */\n label?: string\n /** A second line under the label, for a status or a short explanation. */\n sublabel?: string\n /**\n * An icon before the label: an icon name, or an explicit render. The `#icon` slot\n * replaces it.\n */\n icon?: IconSource\n /**\n * Where this row leads, which makes it a link. It is IGNORED on a row that has\n * subitems: such a row opens and closes rather than navigating.\n */\n href?: string\n /**\n * Marks this row as the page currently being viewed. It is highlighted, and\n * announced as the current page (`aria-current`).\n */\n current?: boolean\n /**\n * Makes the row unusable: it greys out through the colour tokens and leaves the\n * keyboard path.\n */\n disabled?: boolean\n /** Renders a branch already open. Bind `v-model:open` to drive it instead. */\n defaultOpen?: boolean\n}\n\n// Without redirecting them, `target`, `rel`, `download` and the aria-* would land on that list\n// item instead of on the link or the branch header.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SideNavigationItemProps>(), {\n label: undefined,\n sublabel: undefined,\n icon: undefined,\n href: undefined,\n current: false,\n disabled: false,\n defaultOpen: false,\n})\n\n// \"not bound\" is written as `null` and not `undefined`. A model typed as a plain boolean is\n// declared as such at runtime, and Vue casts an ABSENT boolean prop to `false`, which would\n// silently overwrite `defaultOpen` on every branch.\n/**\n * Whether the branch is open, when the consumer wants to drive or observe it. Left\n * unbound, the browser keeps that state to itself, `defaultOpen` giving only the initial\n * value. `null` means unbound.\n */\nconst open = defineModel<boolean | null>('open', { default: null })\n\nconst emit = defineEmits<{\n // Never declare a `click` emit alongside it. Vue removes a declared event from the forwarded\n // attributes, and a consumer's own `@click` would then stop reaching the link entirely.\n /** A row WITHOUT subitems was activated, by click or by keyboard. */\n select: []\n}>()\n\ndefineSlots<{\n /**\n * The label of the row, replacing the `label` prop. One of the two is REQUIRED: a\n * navigation row must say where it goes.\n */\n default?(): unknown\n /** A second line made of markup, replacing the `sublabel` prop. */\n sublabel?(): unknown\n /** Free content before the label, which takes the place of `icon`. */\n icon?(): unknown\n /**\n * Free content at the end of the row, before the chevron: a counter, a badge. On a\n * BRANCH it must not be focusable, a control inside the header being a control nested\n * inside another.\n */\n end?(): unknown\n /**\n * The subitems, which turn this row into a branch. Nesting is not limited. The slot\n * must be statically present or absent: whether a row is a branch is decided when it\n * is created.\n */\n children?(): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst slots = useSlots()\nconst tag = computed(() => (props.href !== undefined ? 'a' : 'button'))\n\nconst parent = inject(sideNavigationKey, null)\n\n/**\n * Both chevrons are chosen on the navigation as a whole. The fallback is what keeps an\n * item rendering correctly when it is used outside one.\n */\nconst expandIcon = computed(() => parent?.expandIcon ?? expandMoreIcon)\nconst collapseIcon = computed(() => parent?.collapseIcon)\n\n// @ssr\n// Each level owns its exclusive group, including children supplied after the first render.\nconst childrenName = useId()\nprovide(sideNavigationKey, {\n get name() {\n return parent?.exclusive ? childrenName : undefined\n },\n get exclusive() {\n return parent?.exclusive ?? false\n },\n get expandIcon() {\n return expandIcon.value\n },\n get collapseIcon() {\n return collapseIcon.value\n },\n})\n\nconst { openAttr, onToggle, onSummaryClick } = useDetailsOpen(open, {\n defaultOpen: () => props.defaultOpen,\n disabled: () => props.disabled,\n})\n\n/*\n * Stopping the event from travelling would not help: folding is not a listener anyone\n * registered, it is the click's default action, and only cancelling that prevents it. The\n * exception is a click that already landed on something activable.\n */\n// @core\nfunction onEndClick(event: MouseEvent) {\n const target = event.target as Element | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n}\n\n// A template ref on the item reaches the list item, a layout box; what is focused and\n// named is its row: the branch header, or the leaf's link or button.\nconst rowEl = ref<HTMLElement | null>(null)\nconst actionEl = ref<HTMLElement | null>(null)\nconst control = () => (slots.children ? rowEl.value : actionEl.value)\ndefineExpose({\n /** Moves the focus to the row: the branch header, or the leaf's link or button. */\n focus: (options?: FocusOptions) => control()?.focus(options),\n /** The row, which is also where the consumer's attributes land. */\n get el() {\n return control()\n },\n})\n\n// @a11y\n// A leaf link has no `disabled`: the address, the consumer's click listeners and the\n// generic role of an `<a>` without `href` are handled by the shared inert link.\nconst link = useInertLink({\n href: () => (tag.value === 'a' ? props.href : undefined),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\n// @core\nfunction onActionClick(event: MouseEvent) {\n // A disabled button never receives the click at all; an inert link does, so it is\n // cancelled here.\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select')\n}\n</script>\n\n<template>\n <li class=\"v-side-nav-item\" :class=\"rootClass\" :style=\"rootStyle\">\n <details\n v-if=\"$slots.children\"\n class=\"v-side-nav-branch v-disclosure\"\n :name=\"parent?.name\"\n :open=\"openAttr\"\n :data-swap=\"collapseIcon ? '' : undefined\"\n @toggle=\"onToggle\"\n >\n <summary\n ref=\"rowEl\"\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-current=\"current ? 'true' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n v-bind=\"forwardedAttrs\"\n @click=\"onSummaryClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n <!-- This slot sits INSIDE the branch header, where ANY click folds the\n branch: without the handler, clicking a badge would close the section\n under it -->\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\" @click=\"onEndClick\"\n ><slot name=\"end\"\n /></span>\n <VIcon class=\"v-side-nav-chevron v-disclosure-chevron\" v-bind=\"iconProps(expandIcon)\" />\n <VIcon\n v-if=\"collapseIcon\"\n class=\"v-side-nav-chevron v-side-nav-chevron-open v-disclosure-chevron v-disclosure-chevron-open\"\n v-bind=\"iconProps(collapseIcon)\"\n />\n </summary>\n <ul class=\"v-side-nav-children\">\n <slot name=\"children\" />\n </ul>\n </details>\n\n <div\n v-else\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <component\n :is=\"tag\"\n ref=\"actionEl\"\n class=\"v-side-nav-action\"\n :type=\"tag === 'button' ? 'button' : undefined\"\n :disabled=\"tag === 'button' ? disabled : undefined\"\n :aria-disabled=\"link.isInertLink.value ? 'true' : undefined\"\n :aria-current=\"current ? (tag === 'a' ? 'page' : 'true') : undefined\"\n v-bind=\"link.attrs.value\"\n :href=\"link.linkHref.value\"\n @click=\"onActionClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n </component>\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\"><slot name=\"end\" /></span>\n </div>\n </li>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The obvious one-name form, incrementing a variable by reading itself, is a CYCLE as far as\n * CSS is concerned, even though the value being read is the inherited one. The property then\n * falls to invalid, every read falls back to zero, and the whole tree renders FLAT; with\n * nothing in the console to say why.\n */\n .v-side-nav-item {\n --side-nav-parent-level: var(--side-nav-level, 0);\n }\n\n .v-side-nav-children {\n --side-nav-level: calc(var(--side-nav-parent-level) + 1);\n }\n\n .v-side-nav-row {\n /*\n * The indent is computed here and not stored in a variable set higher up. A custom property\n * is substituted on the element that DECLARES it, so a padding computed on the nav would be\n * frozen at level zero for the whole tree.\n */\n\n /*\n * The cap has to stay derived from --control-height. A browser scales down any radius it\n * cannot fit, so a row exactly one control tall already paints half that height under a\n * pill override; min() applies the same reduction to a row that grew past it.\n */\n --side-nav-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(\n var(--control-padding-inline) + var(--side-nav-level, 0) * var(--side-nav-indent)\n )\n var(--control-padding-inline);\n border-radius: var(--side-nav-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n cursor: pointer;\n }\n\n .v-side-nav-action {\n /* On a leaf the link is a CHILD of the row, and the indent deliberately stays on\n the row itself: what has to run the full width is the hover background, not the\n link inside it. */\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n text-align: start;\n text-decoration: none;\n cursor: inherit;\n }\n\n /*\n * This is what stretches the link over the whole row without having to wrap the end slot\n * inside it. It is an invisible box, positioned, so it is painted above the chevron; which is\n * not positioned; while passing UNDER the end slot, which is positioned too and comes later\n * in the document.\n */\n .v-side-nav-action::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--side-nav-row-radius);\n }\n\n .v-side-nav-content {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n flex-direction: column;\n }\n\n .v-side-nav-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-sublabel {\n overflow: hidden;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* Positioned on purpose: that is what paints it above the stretched link, and\n therefore what keeps whatever it holds clickable. */\n .v-side-nav-end {\n position: relative;\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-side-nav-row:hover:not([data-disabled]) {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * A branch header takes focus itself; a leaf only takes it through its link, and the\n * ring is then drawn on the stretched box so that it frames the whole row rather than\n * just the text.\n *\n * The ring is pulled INWARDS in both cases: the content of a branch is clipped so it\n * can be animated open, and a ring drawn outside the row would be cut off there.\n */\n .v-side-nav-row:focus-visible,\n .v-side-nav-action:focus-visible::after {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-side-nav-action:focus-visible {\n outline: none;\n }\n\n .v-side-nav-row[data-current] {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-icon,\n .v-side-nav-row[data-current] .v-side-nav-sublabel,\n .v-side-nav-row[data-current] .v-side-nav-chevron {\n color: inherit;\n }\n\n .v-side-nav-row[data-current]:not([data-disabled]):hover {\n /*\n * The current row is already tinted, so its hover deepens that tint rather than replacing\n * it with the neutral highlight; the VMenuItem idiom.\n */\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n /*\n * The line sits under the middle of the parent's icon, whether or not it has one. A row\n * of the sublist then starts a gutter after the line, so its highlight never covers it, and\n * its padding takes up the rest: the label stays where the indent puts it.\n *\n * Painted with a border and not a background, which forced colours would turn to Canvas.\n */\n .v-side-nav[data-guides] .v-side-nav-children {\n position: relative;\n }\n\n .v-side-nav[data-guides] .v-side-nav-children::before {\n content: '';\n position: absolute;\n inset-block: 0;\n inset-inline-start: calc(\n var(--control-padding-inline) + var(--side-nav-parent-level) * var(--side-nav-indent) +\n var(--vectis-icon-size) / 2\n );\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n .v-side-nav[data-guides] .v-side-nav-children .v-side-nav-row {\n margin-inline-start: calc(\n var(--control-padding-inline) + (var(--side-nav-level) - 1) * var(--side-nav-indent) +\n var(--vectis-icon-size) / 2 + var(--control-gap)\n );\n padding-inline-start: calc(var(--vectis-icon-size) / 2);\n }\n\n /* A CLOSED branch holding the current page keeps a mark of it, so the reader can see\n where they are without opening every section. The lookup is deliberately a\n descendant one: the page may be several levels down. */\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled]) {\n color: var(--vectis-color-accent-text);\n }\n\n /* A disabled row greys out through the colour tokens, never through opacity. */\n .v-side-nav-row[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /* The icon and the second line default to a colour DARKER than the one a disabled\n label takes: left alone they would come out stronger than the label itself, so they\n inherit it instead. The chevron gets the same treatment from `styles/disclosure.css`. */\n .v-side-nav-row[data-disabled] .v-side-nav-icon,\n .v-side-nav-row[data-disabled] .v-side-nav-sublabel {\n color: inherit;\n }\n\n /* Forced colours flatten the current row's tint to `Canvas` and its accent text to\n `CanvasText`, leaving a slightly bolder label as the only cue, and the mark on a closed\n branch is a colour alone. The current row takes the system selection pair, repeated to\n (0,4,0) above its own hover, and the closed branch underlines its label, a decoration\n the mode keeps. */\n @media (forced-colors: active) {\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current] {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled] {\n background: Canvas;\n color: GrayText;\n }\n\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled])\n .v-side-nav-label {\n text-decoration: underline;\n }\n }\n\n /* The disclosure animation, the chevron's rotation or swap and the WebKit marker come\n from `styles/disclosure.css`, reduced-motion block included. */\n}\n</style>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VSideNavigationItem.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","expandMoreIcon","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createElementVNode","_mergeProps","_renderSlot","_createBlock","VIcon","_createCommentVNode","_createTextVNode","_toDisplayString","_createVNode","_resolveDynamicComponent","_withCtx","VIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","VSideNavigationItem_vue_vue_type_script_setup_true_lang_default","\"open\"","\"openModifiers\"","__expose","__emit","props","__props","open","useModel","emit","useRootAttrs","slots","useSlots","tag","computed","parent","inject","sideNavigationKey","expandIcon","expand_more","collapseIcon","childrenName","useId","useDetailsOpen","onEndClick","event","rowEl","ref","actionEl","control","options","link","useInertLink","forwardedAttrs","onActionClick","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","rootStyle","_ctx","openAttr","_cache","args","onToggle","createElementVNode","mergeProps","onSummaryClick","renderSlot","createBlock","iconProps","createCommentVNode","createTextVNode","toDisplayString","createVNode","resolveDynamicComponent","withCtx"],"sources":["../../../src/components/VSideNavigation/VSideNavigationItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Branches use native details and leaves use links or buttons. JavaScript synchronizes open\n * state, disables summaries and makes disabled links inert.\n */\n\nimport { computed, inject, provide, ref, useId, useSlots } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { useDetailsOpen } from '../../composables/useDetailsOpen'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\ninterface SideNavigationItemProps {\n /** What the row says, and where it goes. The default slot replaces it. */\n label?: string\n /** A second line under the label, for a status or a short explanation. */\n sublabel?: string\n /**\n * An icon before the label: an icon name, or an explicit render. The `#icon` slot\n * replaces it.\n */\n icon?: IconSource\n /**\n * Where this row leads, which makes it a link. It is IGNORED on a row that has\n * subitems: such a row opens and closes rather than navigating.\n */\n href?: string\n /**\n * Marks this row as the page currently being viewed. It is highlighted, and\n * announced as the current page (`aria-current`).\n */\n current?: boolean\n /**\n * Makes the row unusable: it greys out through the colour tokens and leaves the\n * keyboard path.\n */\n disabled?: boolean\n /** Renders a branch already open. Bind `v-model:open` to drive it instead. */\n defaultOpen?: boolean\n}\n\n// Without redirecting them, `target`, `rel`, `download` and the aria-* would land on that list\n// item instead of on the link or the branch header.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SideNavigationItemProps>(), {\n label: undefined,\n sublabel: undefined,\n icon: undefined,\n href: undefined,\n current: false,\n disabled: false,\n defaultOpen: false,\n})\n\n// \"not bound\" is written as `null` and not `undefined`. A model typed as a plain boolean is\n// declared as such at runtime, and Vue casts an ABSENT boolean prop to `false`, which would\n// silently overwrite `defaultOpen` on every branch.\n/**\n * Whether the branch is open, when the consumer wants to drive or observe it. Left\n * unbound, the browser keeps that state to itself, `defaultOpen` giving only the initial\n * value. `null` means unbound.\n */\nconst open = defineModel<boolean | null>('open', { default: null })\n\nconst emit = defineEmits<{\n // Never declare a `click` emit alongside it. Vue removes a declared event from the forwarded\n // attributes, and a consumer's own `@click` would then stop reaching the link entirely.\n /** A row WITHOUT subitems was activated, by click or by keyboard. */\n select: []\n}>()\n\ndefineSlots<{\n /**\n * The label of the row, replacing the `label` prop. One of the two is REQUIRED: a\n * navigation row must say where it goes.\n */\n default?(): unknown\n /** A second line made of markup, replacing the `sublabel` prop. */\n sublabel?(): unknown\n /** Free content before the label, which takes the place of `icon`. */\n icon?(): unknown\n /**\n * Free content at the end of the row, before the chevron: a counter, a badge. On a\n * BRANCH it must not be focusable, a control inside the header being a control nested\n * inside another.\n */\n end?(): unknown\n /**\n * The subitems, which turn this row into a branch. Nesting is not limited. The slot\n * must be statically present or absent: whether a row is a branch is decided when it\n * is created.\n */\n children?(): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst slots = useSlots()\nconst tag = computed(() => (props.href !== undefined ? 'a' : 'button'))\n\nconst parent = inject(sideNavigationKey, null)\n\n/**\n * Both chevrons are chosen on the navigation as a whole. The fallback is what keeps an\n * item rendering correctly when it is used outside one.\n */\nconst expandIcon = computed(() => parent?.expandIcon ?? expandMoreIcon)\nconst collapseIcon = computed(() => parent?.collapseIcon)\n\n// @ssr\n// Each level owns its exclusive group, including children supplied after the first render.\nconst childrenName = useId()\nprovide(sideNavigationKey, {\n get name() {\n return parent?.exclusive ? childrenName : undefined\n },\n get exclusive() {\n return parent?.exclusive ?? false\n },\n get expandIcon() {\n return expandIcon.value\n },\n get collapseIcon() {\n return collapseIcon.value\n },\n})\n\nconst { openAttr, onToggle, onSummaryClick } = useDetailsOpen(open, {\n defaultOpen: () => props.defaultOpen,\n disabled: () => props.disabled,\n})\n\n/*\n * Stopping the event from travelling would not help: folding is not a listener anyone\n * registered, it is the click's default action, and only cancelling that prevents it. The\n * exception is a click that already landed on something activable.\n */\n// @core\nfunction onEndClick(event: MouseEvent) {\n const target = event.target as Element | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n}\n\n// A template ref on the item reaches the list item, a layout box; what is focused and\n// named is its row: the branch header, or the leaf's link or button.\nconst rowEl = ref<HTMLElement | null>(null)\nconst actionEl = ref<HTMLElement | null>(null)\nconst control = () => (slots.children ? rowEl.value : actionEl.value)\ndefineExpose({\n /** Moves the focus to the row: the branch header, or the leaf's link or button. */\n focus: (options?: FocusOptions) => control()?.focus(options),\n /** The row, which is also where the consumer's attributes land. */\n get el() {\n return control()\n },\n})\n\n// @a11y\n// A leaf link has no `disabled`: the address, the consumer's click listeners and the\n// generic role of an `<a>` without `href` are handled by the shared inert link.\nconst link = useInertLink({\n href: () => (tag.value === 'a' ? props.href : undefined),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\n// @core\nfunction onActionClick(event: MouseEvent) {\n // A disabled button never receives the click at all; an inert link does, so it is\n // cancelled here.\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select')\n}\n</script>\n\n<template>\n <li class=\"v-side-nav-item\" :class=\"rootClass\" :style=\"rootStyle\">\n <details\n v-if=\"$slots.children\"\n class=\"v-side-nav-branch v-disclosure\"\n :name=\"parent?.name\"\n :open=\"openAttr\"\n :data-swap=\"collapseIcon ? '' : undefined\"\n @toggle=\"onToggle\"\n >\n <summary\n ref=\"rowEl\"\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-current=\"current ? 'true' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n v-bind=\"forwardedAttrs\"\n @click=\"onSummaryClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n <!-- This slot sits INSIDE the branch header, where ANY click folds the\n branch: without the handler, clicking a badge would close the section\n under it -->\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\" @click=\"onEndClick\"\n ><slot name=\"end\"\n /></span>\n <VIcon class=\"v-side-nav-chevron v-disclosure-chevron\" v-bind=\"iconProps(expandIcon)\" />\n <VIcon\n v-if=\"collapseIcon\"\n class=\"v-side-nav-chevron v-side-nav-chevron-open v-disclosure-chevron v-disclosure-chevron-open\"\n v-bind=\"iconProps(collapseIcon)\"\n />\n </summary>\n <ul class=\"v-side-nav-children\">\n <slot name=\"children\" />\n </ul>\n </details>\n\n <div\n v-else\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <component\n :is=\"tag\"\n ref=\"actionEl\"\n class=\"v-side-nav-action\"\n :type=\"tag === 'button' ? 'button' : undefined\"\n :disabled=\"tag === 'button' ? disabled : undefined\"\n :aria-disabled=\"link.isInertLink.value ? 'true' : undefined\"\n :aria-current=\"current ? (tag === 'a' ? 'page' : 'true') : undefined\"\n v-bind=\"link.attrs.value\"\n :href=\"link.linkHref.value\"\n @click=\"onActionClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n </component>\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\"><slot name=\"end\" /></span>\n </div>\n </li>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The obvious one-name form, incrementing a variable by reading itself, is a CYCLE as far as\n * CSS is concerned, even though the value being read is the inherited one. The property then\n * falls to invalid, every read falls back to zero, and the whole tree renders FLAT; with\n * nothing in the console to say why.\n */\n .v-side-nav-item {\n --side-nav-parent-level: var(--side-nav-level, 0);\n }\n\n .v-side-nav-children {\n --side-nav-level: calc(var(--side-nav-parent-level) + 1);\n }\n\n .v-side-nav-row {\n /*\n * The indent is computed here and not stored in a variable set higher up. A custom property\n * is substituted on the element that DECLARES it, so a padding computed on the nav would be\n * frozen at level zero for the whole tree.\n */\n\n /*\n * The cap has to stay derived from --control-height. A browser scales down any radius it\n * cannot fit, so a row exactly one control tall already paints half that height under a\n * pill override; min() applies the same reduction to a row that grew past it.\n */\n --side-nav-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(\n var(--control-padding-inline) + var(--side-nav-level, 0) * var(--side-nav-indent)\n )\n var(--control-padding-inline);\n border-radius: var(--side-nav-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n cursor: pointer;\n }\n\n .v-side-nav-action {\n /* On a leaf the link is a CHILD of the row, and the indent deliberately stays on\n the row itself: what has to run the full width is the hover background, not the\n link inside it. */\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n text-align: start;\n text-decoration: none;\n cursor: inherit;\n }\n\n /*\n * This is what stretches the link over the whole row without having to wrap the end slot\n * inside it. It is an invisible box, positioned, so it is painted above the chevron; which is\n * not positioned; while passing UNDER the end slot, which is positioned too and comes later\n * in the document.\n */\n .v-side-nav-action::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--side-nav-row-radius);\n }\n\n .v-side-nav-content {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n flex-direction: column;\n }\n\n .v-side-nav-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-sublabel {\n overflow: hidden;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* Positioned on purpose: that is what paints it above the stretched link, and\n therefore what keeps whatever it holds clickable. */\n .v-side-nav-end {\n position: relative;\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-side-nav-row:hover:not([data-disabled]) {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * A branch header takes focus itself; a leaf only takes it through its link, and the\n * ring is then drawn on the stretched box so that it frames the whole row rather than\n * just the text.\n *\n * The ring is pulled INWARDS in both cases: the content of a branch is clipped so it\n * can be animated open, and a ring drawn outside the row would be cut off there.\n */\n .v-side-nav-row:focus-visible,\n .v-side-nav-action:focus-visible::after {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-side-nav-action:focus-visible {\n outline: none;\n }\n\n .v-side-nav-row[data-current] {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-icon,\n .v-side-nav-row[data-current] .v-side-nav-sublabel,\n .v-side-nav-row[data-current] .v-side-nav-chevron {\n color: inherit;\n }\n\n .v-side-nav-row[data-current]:not([data-disabled]):hover {\n /*\n * The current row is already tinted, so its hover deepens that tint rather than replacing\n * it with the neutral highlight; the VMenuItem idiom.\n */\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n /* A CLOSED branch holding the current page keeps a mark of it, so the reader can see\n where they are without opening every section. The lookup is deliberately a\n descendant one: the page may be several levels down. */\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled]) {\n color: var(--vectis-color-accent-text);\n }\n\n /* A disabled row greys out through the colour tokens, never through opacity. */\n .v-side-nav-row[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /* The icon and the second line default to a colour DARKER than the one a disabled\n label takes: left alone they would come out stronger than the label itself, so they\n inherit it instead. The chevron gets the same treatment from `styles/disclosure.css`. */\n .v-side-nav-row[data-disabled] .v-side-nav-icon,\n .v-side-nav-row[data-disabled] .v-side-nav-sublabel {\n color: inherit;\n }\n\n /* Forced colours flatten the current row's tint to `Canvas` and its accent text to\n `CanvasText`, leaving a slightly bolder label as the only cue, and the mark on a closed\n branch is a colour alone. The current row takes the system selection pair, repeated to\n (0,4,0) above its own hover, and the closed branch underlines its label, a decoration\n the mode keeps. */\n @media (forced-colors: active) {\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current] {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled] {\n background: Canvas;\n color: GrayText;\n }\n\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled])\n .v-side-nav-label {\n text-decoration: underline;\n }\n }\n\n /* The disclosure animation, the chevron's rotation or swap and the WebKit marker come\n from `styles/disclosure.css`, reduced-motion block included. */\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VSideNavigationItem.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","expandMoreIcon","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createElementVNode","_mergeProps","_renderSlot","_createBlock","VIcon","_createCommentVNode","_createTextVNode","_toDisplayString","_createVNode","_resolveDynamicComponent","_withCtx","VIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","VSideNavigationItem_vue_vue_type_script_setup_true_lang_default","\"open\"","\"openModifiers\"","__expose","__emit","props","__props","open","useModel","emit","useRootAttrs","slots","useSlots","tag","computed","parent","inject","sideNavigationKey","expandIcon","expand_more","collapseIcon","childrenName","useId","useDetailsOpen","onEndClick","event","rowEl","ref","actionEl","control","options","link","useInertLink","forwardedAttrs","onActionClick","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","rootStyle","_ctx","openAttr","_cache","args","onToggle","createElementVNode","mergeProps","onSummaryClick","renderSlot","createBlock","iconProps","createCommentVNode","createTextVNode","toDisplayString","createVNode","resolveDynamicComponent","withCtx"],"sources":["../../../src/components/VSideNavigation/VSideNavigationItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Branches use native details and leaves use links or buttons. JavaScript synchronizes open\n * state, disables summaries and makes disabled links inert.\n */\n\nimport { computed, inject, provide, ref, useId, useSlots } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport { sideNavigationKey } from './context'\n\nimport { useDetailsOpen } from '../../composables/useDetailsOpen'\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\n\ninterface SideNavigationItemProps {\n /** What the row says, and where it goes. The default slot replaces it. */\n label?: string\n /** A second line under the label, for a status or a short explanation. */\n sublabel?: string\n /**\n * An icon before the label: an icon name, or an explicit render. The `#icon` slot\n * replaces it.\n */\n icon?: IconSource\n /**\n * Where this row leads, which makes it a link. It is IGNORED on a row that has\n * subitems: such a row opens and closes rather than navigating.\n */\n href?: string\n /**\n * Marks this row as the page currently being viewed. It is highlighted, and\n * announced as the current page (`aria-current`).\n */\n current?: boolean\n /**\n * Makes the row unusable: it greys out through the colour tokens and leaves the\n * keyboard path.\n */\n disabled?: boolean\n /** Renders a branch already open. Bind `v-model:open` to drive it instead. */\n defaultOpen?: boolean\n}\n\n// Without redirecting them, `target`, `rel`, `download` and the aria-* would land on that list\n// item instead of on the link or the branch header.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SideNavigationItemProps>(), {\n label: undefined,\n sublabel: undefined,\n icon: undefined,\n href: undefined,\n current: false,\n disabled: false,\n defaultOpen: false,\n})\n\n// \"not bound\" is written as `null` and not `undefined`. A model typed as a plain boolean is\n// declared as such at runtime, and Vue casts an ABSENT boolean prop to `false`, which would\n// silently overwrite `defaultOpen` on every branch.\n/**\n * Whether the branch is open, when the consumer wants to drive or observe it. Left\n * unbound, the browser keeps that state to itself, `defaultOpen` giving only the initial\n * value. `null` means unbound.\n */\nconst open = defineModel<boolean | null>('open', { default: null })\n\nconst emit = defineEmits<{\n // Never declare a `click` emit alongside it. Vue removes a declared event from the forwarded\n // attributes, and a consumer's own `@click` would then stop reaching the link entirely.\n /** A row WITHOUT subitems was activated, by click or by keyboard. */\n select: []\n}>()\n\ndefineSlots<{\n /**\n * The label of the row, replacing the `label` prop. One of the two is REQUIRED: a\n * navigation row must say where it goes.\n */\n default?(): unknown\n /** A second line made of markup, replacing the `sublabel` prop. */\n sublabel?(): unknown\n /** Free content before the label, which takes the place of `icon`. */\n icon?(): unknown\n /**\n * Free content at the end of the row, before the chevron: a counter, a badge. On a\n * BRANCH it must not be focusable, a control inside the header being a control nested\n * inside another.\n */\n end?(): unknown\n /**\n * The subitems, which turn this row into a branch. Nesting is not limited. The slot\n * must be statically present or absent: whether a row is a branch is decided when it\n * is created.\n */\n children?(): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst slots = useSlots()\nconst tag = computed(() => (props.href !== undefined ? 'a' : 'button'))\n\nconst parent = inject(sideNavigationKey, null)\n\n/**\n * Both chevrons are chosen on the navigation as a whole. The fallback is what keeps an\n * item rendering correctly when it is used outside one.\n */\nconst expandIcon = computed(() => parent?.expandIcon ?? expandMoreIcon)\nconst collapseIcon = computed(() => parent?.collapseIcon)\n\n// @ssr\n// Each level owns its exclusive group, including children supplied after the first render.\nconst childrenName = useId()\nprovide(sideNavigationKey, {\n get name() {\n return parent?.exclusive ? childrenName : undefined\n },\n get exclusive() {\n return parent?.exclusive ?? false\n },\n get expandIcon() {\n return expandIcon.value\n },\n get collapseIcon() {\n return collapseIcon.value\n },\n})\n\nconst { openAttr, onToggle, onSummaryClick } = useDetailsOpen(open, {\n defaultOpen: () => props.defaultOpen,\n disabled: () => props.disabled,\n})\n\n/*\n * Stopping the event from travelling would not help: folding is not a listener anyone\n * registered, it is the click's default action, and only cancelling that prevents it. The\n * exception is a click that already landed on something activable.\n */\n// @core\nfunction onEndClick(event: MouseEvent) {\n const target = event.target as Element | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n}\n\n// A template ref on the item reaches the list item, a layout box; what is focused and\n// named is its row: the branch header, or the leaf's link or button.\nconst rowEl = ref<HTMLElement | null>(null)\nconst actionEl = ref<HTMLElement | null>(null)\nconst control = () => (slots.children ? rowEl.value : actionEl.value)\ndefineExpose({\n /** Moves the focus to the row: the branch header, or the leaf's link or button. */\n focus: (options?: FocusOptions) => control()?.focus(options),\n /** The row, which is also where the consumer's attributes land. */\n get el() {\n return control()\n },\n})\n\n// @a11y\n// A leaf link has no `disabled`: the address, the consumer's click listeners and the\n// generic role of an `<a>` without `href` are handled by the shared inert link.\nconst link = useInertLink({\n href: () => (tag.value === 'a' ? props.href : undefined),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\n\n// @core\nfunction onActionClick(event: MouseEvent) {\n // A disabled button never receives the click at all; an inert link does, so it is\n // cancelled here.\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select')\n}\n</script>\n\n<template>\n <li class=\"v-side-nav-item\" :class=\"rootClass\" :style=\"rootStyle\">\n <details\n v-if=\"$slots.children\"\n class=\"v-side-nav-branch v-disclosure\"\n :name=\"parent?.name\"\n :open=\"openAttr\"\n :data-swap=\"collapseIcon ? '' : undefined\"\n @toggle=\"onToggle\"\n >\n <summary\n ref=\"rowEl\"\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :aria-current=\"current ? 'true' : undefined\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n v-bind=\"forwardedAttrs\"\n @click=\"onSummaryClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n <!-- This slot sits INSIDE the branch header, where ANY click folds the\n branch: without the handler, clicking a badge would close the section\n under it -->\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\" @click=\"onEndClick\"\n ><slot name=\"end\"\n /></span>\n <VIcon class=\"v-side-nav-chevron v-disclosure-chevron\" v-bind=\"iconProps(expandIcon)\" />\n <VIcon\n v-if=\"collapseIcon\"\n class=\"v-side-nav-chevron v-side-nav-chevron-open v-disclosure-chevron v-disclosure-chevron-open\"\n v-bind=\"iconProps(collapseIcon)\"\n />\n </summary>\n <ul class=\"v-side-nav-children\">\n <slot name=\"children\" />\n </ul>\n </details>\n\n <div\n v-else\n class=\"v-side-nav-row\"\n :data-current=\"current ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <component\n :is=\"tag\"\n ref=\"actionEl\"\n class=\"v-side-nav-action\"\n :type=\"tag === 'button' ? 'button' : undefined\"\n :disabled=\"tag === 'button' ? disabled : undefined\"\n :aria-disabled=\"link.isInertLink.value ? 'true' : undefined\"\n :aria-current=\"current ? (tag === 'a' ? 'page' : 'true') : undefined\"\n v-bind=\"link.attrs.value\"\n :href=\"link.linkHref.value\"\n @click=\"onActionClick\"\n >\n <slot name=\"icon\">\n <VIcon v-if=\"icon\" class=\"v-side-nav-icon\" v-bind=\"iconProps(icon)\" />\n </slot>\n <span class=\"v-side-nav-content\">\n <span class=\"v-side-nav-label\"\n ><slot>{{ label }}</slot></span\n >\n <span v-if=\"sublabel !== undefined || $slots.sublabel\" class=\"v-side-nav-sublabel\">\n <slot name=\"sublabel\">{{ sublabel }}</slot>\n </span>\n </span>\n </component>\n <span v-if=\"$slots.end\" class=\"v-side-nav-end\"><slot name=\"end\" /></span>\n </div>\n </li>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The obvious one-name form, incrementing a variable by reading itself, is a CYCLE as far as\n * CSS is concerned, even though the value being read is the inherited one. The property then\n * falls to invalid, every read falls back to zero, and the whole tree renders FLAT; with\n * nothing in the console to say why.\n */\n .v-side-nav-item {\n --side-nav-parent-level: var(--side-nav-level, 0);\n }\n\n .v-side-nav-children {\n --side-nav-level: calc(var(--side-nav-parent-level) + 1);\n }\n\n .v-side-nav-row {\n /*\n * The indent is computed here and not stored in a variable set higher up. A custom property\n * is substituted on the element that DECLARES it, so a padding computed on the nav would be\n * frozen at level zero for the whole tree.\n */\n\n /*\n * The cap has to stay derived from --control-height. A browser scales down any radius it\n * cannot fit, so a row exactly one control tall already paints half that height under a\n * pill override; min() applies the same reduction to a row that grew past it.\n */\n --side-nav-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(\n var(--control-padding-inline) + var(--side-nav-level, 0) * var(--side-nav-indent)\n )\n var(--control-padding-inline);\n border-radius: var(--side-nav-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n cursor: pointer;\n }\n\n .v-side-nav-action {\n /* On a leaf the link is a CHILD of the row, and the indent deliberately stays on\n the row itself: what has to run the full width is the hover background, not the\n link inside it. */\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n text-align: start;\n text-decoration: none;\n cursor: inherit;\n }\n\n /*\n * This is what stretches the link over the whole row without having to wrap the end slot\n * inside it. It is an invisible box, positioned, so it is painted above the chevron; which is\n * not positioned; while passing UNDER the end slot, which is positioned too and comes later\n * in the document.\n */\n .v-side-nav-action::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--side-nav-row-radius);\n }\n\n .v-side-nav-content {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n flex-direction: column;\n }\n\n .v-side-nav-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-sublabel {\n overflow: hidden;\n font-size: var(--vectis-text-caption-size);\n color: var(--vectis-color-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-side-nav-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* Positioned on purpose: that is what paints it above the stretched link, and\n therefore what keeps whatever it holds clickable. */\n .v-side-nav-end {\n position: relative;\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-side-nav-row:hover:not([data-disabled]) {\n background: var(--vectis-color-surface-muted);\n }\n\n /*\n * A branch header takes focus itself; a leaf only takes it through its link, and the\n * ring is then drawn on the stretched box so that it frames the whole row rather than\n * just the text.\n *\n * The ring is pulled INWARDS in both cases: the content of a branch is clipped so it\n * can be animated open, and a ring drawn outside the row would be cut off there.\n */\n .v-side-nav-row:focus-visible,\n .v-side-nav-action:focus-visible::after {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-side-nav-action:focus-visible {\n outline: none;\n }\n\n .v-side-nav-row[data-current] {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-side-nav-row[data-current] .v-side-nav-icon,\n .v-side-nav-row[data-current] .v-side-nav-sublabel,\n .v-side-nav-row[data-current] .v-side-nav-chevron {\n color: inherit;\n }\n\n .v-side-nav-row[data-current]:not([data-disabled]):hover {\n /*\n * The current row is already tinted, so its hover deepens that tint rather than replacing\n * it with the neutral highlight; the VMenuItem idiom.\n */\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n /*\n * The line sits under the middle of the parent's icon, whether or not it has one. A row\n * of the sublist then starts a gutter after the line, so its highlight never covers it, and\n * its padding takes up the rest: the label stays where the indent puts it.\n *\n * Painted with a border and not a background, which forced colours would turn to Canvas.\n */\n .v-side-nav[data-guides] .v-side-nav-children {\n position: relative;\n }\n\n .v-side-nav[data-guides] .v-side-nav-children::before {\n content: '';\n position: absolute;\n inset-block: 0;\n inset-inline-start: calc(\n var(--control-padding-inline) + var(--side-nav-parent-level) * var(--side-nav-indent) +\n var(--vectis-icon-size) / 2\n );\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n .v-side-nav[data-guides] .v-side-nav-children .v-side-nav-row {\n margin-inline-start: calc(\n var(--control-padding-inline) + (var(--side-nav-level) - 1) * var(--side-nav-indent) +\n var(--vectis-icon-size) / 2 + var(--control-gap)\n );\n padding-inline-start: calc(var(--vectis-icon-size) / 2);\n }\n\n /* A CLOSED branch holding the current page keeps a mark of it, so the reader can see\n where they are without opening every section. The lookup is deliberately a\n descendant one: the page may be several levels down. */\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled]) {\n color: var(--vectis-color-accent-text);\n }\n\n /* A disabled row greys out through the colour tokens, never through opacity. */\n .v-side-nav-row[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /* The icon and the second line default to a colour DARKER than the one a disabled\n label takes: left alone they would come out stronger than the label itself, so they\n inherit it instead. The chevron gets the same treatment from `styles/disclosure.css`. */\n .v-side-nav-row[data-disabled] .v-side-nav-icon,\n .v-side-nav-row[data-disabled] .v-side-nav-sublabel {\n color: inherit;\n }\n\n /* Forced colours flatten the current row's tint to `Canvas` and its accent text to\n `CanvasText`, leaving a slightly bolder label as the only cue, and the mark on a closed\n branch is a colour alone. The current row takes the system selection pair, repeated to\n (0,4,0) above its own hover, and the closed branch underlines its label, a decoration\n the mode keeps. */\n @media (forced-colors: active) {\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current] {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-side-nav-row.v-side-nav-row.v-side-nav-row[data-current][data-disabled] {\n background: Canvas;\n color: GrayText;\n }\n\n .v-side-nav-branch:not([open]):has(.v-side-nav-children [aria-current])\n > .v-side-nav-row:not([data-current], [data-disabled])\n .v-side-nav-label {\n text-decoration: underline;\n }\n }\n\n /* The disclosure animation, the chevron's rotation or swap and the WebKit marker come\n from `styles/disclosure.css`, reduced-motion block included. */\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -3,13 +3,12 @@ import { shallowRef as t } from "vue";
|
|
|
3
3
|
//#region src/components/VSnackbar/state.ts
|
|
4
4
|
var n = t(null), r = 0;
|
|
5
5
|
function i(t) {
|
|
6
|
-
e && typeof document > "u" && console.warn("[snackbar] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.");
|
|
7
6
|
let i = r++;
|
|
8
|
-
return n.value = {
|
|
7
|
+
return typeof document > "u" ? (e && console.warn("[snackbar] called during a server render: call it from a handler or a lifecycle hook that runs in the browser."), i) : (n.value = {
|
|
9
8
|
tone: "neutral",
|
|
10
9
|
...t,
|
|
11
10
|
id: i
|
|
12
|
-
}, i;
|
|
11
|
+
}, i);
|
|
13
12
|
}
|
|
14
13
|
function a(e) {
|
|
15
14
|
(e === void 0 || n.value?.id === e) && (n.value = null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","names":["current","shallowRef","nextId","snackbar","
|
|
1
|
+
{"version":3,"file":"state.js","names":["current","shallowRef","nextId","snackbar","id","isDev","options","dismissSnackbar"],"sources":["../../../src/components/VSnackbar/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * A snackbar confirms the last action a reader took, which separates it from a notification\n * reporting a state: notifications stack, because two things can be true at once, whereas only\n * the last action is worth offering to undo. So there is at most one snackbar at a time, and\n * raising a new one replaces the one before it on the spot.\n */\n\nimport { shallowRef } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a confirmation can mean. */\nexport type SnackbarTone = 'neutral' | 'danger'\n\n/** Where the bar appears. Along the bottom edge only, where it is out of the content's way. */\nexport type SnackbarPlacement = 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/** Everything one can say when raising a confirmation. */\nexport interface SnackbarOptions {\n /** What the confirmation says. One short sentence: there is no title to frame it. */\n message: string\n /** What it means, expressed as a colour: the plain inversion, or the failure colour. */\n tone?: SnackbarTone\n /**\n * An icon before the message. There is none by default, and none is deduced from the\n * tone: a confirmation is read, not scanned.\n */\n icon?: IconSource\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is replaced or\n * taken away by hand, and leaving it out takes the duration set on the VSnackbar.\n */\n duration?: number\n /** Which end of the bottom edge it appears at. Left out, it takes the one set on the VSnackbar. */\n placement?: SnackbarPlacement\n /**\n * What the single action does. Left out, the bar carries no button at all. Running it\n * always takes the bar away, so nothing has to be dismissed by hand afterwards.\n */\n action?: () => void\n /** The word drawn on that action. It falls back to the design system dictionary (\"Undo\"). */\n actionText?: string\n}\n\n/**\n * A confirmation once it is showing, with the choices the caller left out filled in and\n * an id of its own. Internal: it is what VSnackbar renders.\n */\nexport interface SnackbarItem extends SnackbarOptions {\n id: number\n tone: SnackbarTone\n}\n\n/** The bar currently showing, if any. Internal, read only by VSnackbar, and not public API. */\nexport const current = shallowRef<SnackbarItem | null>(null)\n\nlet nextId = 0\n\n/**\n * Raises a confirmation, replacing whatever was showing, and hands back an id that\n * `dismissSnackbar` can use to take it away again before its time.\n */\nexport function snackbar(options: SnackbarOptions): number {\n const id = nextId++\n // @ssr\n // The bar is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and never be dismissed, so it is dropped.\n if (typeof document === 'undefined') {\n // @devwarn\n if (isDev)\n console.warn(\n '[snackbar] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n return id\n }\n current.value = { tone: 'neutral', ...options, id }\n return id\n}\n\n/**\n * Takes a confirmation away, by its id or, called with no argument, whichever one is showing.\n * Passing an id is not a formality: it is checked against the bar actually on screen, and a\n * stale one is ignored.\n */\nexport function dismissSnackbar(id?: number): void {\n if (id === undefined || current.value?.id === id) current.value = null\n}\n"],"mappings":";;;AAwDA,IAAaA,IAAUC,EAAgC,IAAI,GAEvDC,IAAS;AAMb,SAAgBC,EAAS,GAAkC;CACzD,IAAMC,IAAK;CAaX,OATI,OAAO,WAAa,OAElBC,KACF,QAAQ,KACN,gHACF,GACKD,MAET,EAAQ,QAAQ;EAAE,MAAM;EAAW,GAAGE;EAAS;CAAG,GAC3CF;AACT;AAOA,SAAgBG,EAAgB,GAAmB;CACjD,CAAIH,MAAO,KAAA,KAAaJ,EAAQ,OAAO,OAAOI,OAAI,EAAQ,QAAQ;AACpE"}
|
|
@@ -3,15 +3,14 @@ import { reactive as t } from "vue";
|
|
|
3
3
|
//#region src/components/VToast/state.ts
|
|
4
4
|
var n = t([]), r = 0;
|
|
5
5
|
function i(t) {
|
|
6
|
-
e && typeof document > "u" && console.warn("[toast] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.");
|
|
7
6
|
let i = r++;
|
|
8
|
-
return n.push({
|
|
7
|
+
return typeof document > "u" ? (e && console.warn("[toast] called during a server render: call it from a handler or a lifecycle hook that runs in the browser."), i) : (n.push({
|
|
9
8
|
tone: "neutral",
|
|
10
9
|
variant: "soft",
|
|
11
10
|
hideClose: !1,
|
|
12
11
|
...t,
|
|
13
12
|
id: i
|
|
14
|
-
}), i;
|
|
13
|
+
}), i);
|
|
15
14
|
}
|
|
16
15
|
function a(e) {
|
|
17
16
|
if (e === void 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","names":["toasts","reactive","nextId","toast","
|
|
1
|
+
{"version":3,"file":"state.js","names":["toasts","reactive","nextId","toast","id","isDev","options","dismissToast","index","item"],"sources":["../../../src/components/VToast/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * Module state lets toast be called outside components. Call it only in browser handlers, never\n * during SSR, because the queue is shared across server requests.\n */\n\nimport { reactive } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a notification reports: nothing in particular, information, a success, a failure or a warning. */\nexport type ToastTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** How strongly a notification is painted: a tinted background with a border, or the full colour. */\nexport type ToastVariant = 'soft' | 'solid'\n\n/** The corner of the screen a notification appears in. */\nexport type ToastPlacement =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n/** Everything one can say when raising a notification. */\nexport interface ToastOptions {\n /** What the notification says. */\n message: string\n /** A short heading above the message, when the message alone needs framing. */\n title?: string\n /** What the notification means, expressed as a colour, and which icon it takes by default. */\n tone?: ToastTone\n /** How strongly it is painted: a tinted background with a border, or the full colour. */\n variant?: ToastVariant\n /**\n * The icon to show. Left out, the tone brings its own; `false` removes it\n * altogether.\n */\n icon?: IconSource | false\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is\n * dismissed by hand, and leaving it out takes the duration set on the VToaster.\n */\n duration?: number\n /** Which corner it appears in. Left out, it takes the one set on the VToaster. */\n placement?: ToastPlacement\n /**\n * Takes the close cross away, leaving the notification to its duration or to\n * `dismissToast`. Keep the cross on a notification that stays until dismissed by hand.\n */\n hideClose?: boolean\n /**\n * How wide it is: a number of pixels or any CSS length. It is never allowed past the\n * width of the viewport, margins included.\n */\n width?: number | string\n}\n\n/**\n * A notification once it is in the queue, with the choices the caller left out filled\n * in and an id of its own. Internal: it is what VToaster renders.\n */\nexport interface ToastItem extends ToastOptions {\n id: number\n tone: ToastTone\n variant: ToastVariant\n hideClose: boolean\n}\n\n/** The queue itself. It is internal, read only by VToaster, and not public API. */\nexport const toasts = reactive<ToastItem[]>([])\n\nlet nextId = 0\n\n/**\n * Raises a notification, and hands back an id that `dismissToast` can use to take it away again\n * before its time.\n */\nexport function toast(options: ToastOptions): number {\n const id = nextId++\n // @ssr\n // The queue is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and never be dismissed, so it is dropped.\n if (typeof document === 'undefined') {\n // @devwarn\n if (isDev)\n console.warn(\n '[toast] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n return id\n }\n toasts.push({ tone: 'neutral', variant: 'soft', hideClose: false, ...options, id })\n return id\n}\n\n/**\n * Takes a notification away by its id, or clears every one of them when called with\n * no argument.\n */\nexport function dismissToast(id?: number): void {\n if (id === undefined) {\n toasts.length = 0\n return\n }\n const index = toasts.findIndex((item) => item.id === id)\n if (index !== -1) toasts.splice(index, 1)\n}\n"],"mappings":";;;AAwEA,IAAaA,IAASC,EAAsB,CAAC,CAAC,GAE1CC,IAAS;AAMb,SAAgBC,EAAM,GAA+B;CACnD,IAAMC,IAAK;CAaX,OATI,OAAO,WAAa,OAElBC,KACF,QAAQ,KACN,6GACF,GACKD,MAETJ,EAAO,KAAK;EAAE,MAAM;EAAW,SAAS;EAAQ,WAAW;EAAO,GAAGM;EAAS;CAAG,CAAC,GAC3EF;AACT;AAMA,SAAgBG,EAAa,GAAmB;CAC9C,IAAIH,MAAO,KAAA,GAAW;EACpB,EAAO,SAAS;EAChB;CACF;CACA,IAAMI,IAAQR,EAAO,WAAW,MAASS,EAAK,OAAOL,CAAE;CACvD,AAAII,MAAU,MAAIR,EAAO,OAAOQ,GAAO,CAAC;AAC1C"}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { ComputedRef } from 'vue';
|
|
2
2
|
export declare function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined>;
|
|
3
|
+
/**
|
|
4
|
+
* The id of a visible label naming the element by reference, unless the consumer named it with
|
|
5
|
+
* `aria-label` or `aria-labelledby`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useLabelledBy(id: string, label: () => string | undefined): ComputedRef<string | undefined>;
|
|
@@ -4,7 +4,11 @@ function n(n) {
|
|
|
4
4
|
let r = t();
|
|
5
5
|
return e(() => r["aria-labelledby"] == null ? r["aria-label"] ?? n() : void 0);
|
|
6
6
|
}
|
|
7
|
+
function r(n, r) {
|
|
8
|
+
let i = t();
|
|
9
|
+
return e(() => r() && i["aria-label"] == null && i["aria-labelledby"] == null ? n : void 0);
|
|
10
|
+
}
|
|
7
11
|
//#endregion
|
|
8
|
-
export { n as useAriaLabel };
|
|
12
|
+
export { n as useAriaLabel, r as useLabelledBy };
|
|
9
13
|
|
|
10
14
|
//# sourceMappingURL=useAriaLabel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAriaLabel.js","names":["useAriaLabel","attrs","useAttrs","computed","label"],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n * Resolve accessible names in order: aria-labelledby, consumer aria-label, label fallback.\n * Suppress aria-label when aria-labelledby names the element.\n */\n\nimport { computed, useAttrs, type ComputedRef } from 'vue'\n\nexport function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n attrs['aria-labelledby'] != null\n ? undefined\n : ((attrs['aria-label'] as string | undefined) ?? label()),\n )\n}\n"],"mappings":";;AAQA,SAAgBA,EAAa,GAAkE;CAC7F,IAAMC,IAAQC,EAAS;CACvB,OAAOC,QACLF,EAAM,sBAAsB,OAEtBA,EAAM,iBAAwCG,EAAM,IADtD,KAAA,CAEN;AACF"}
|
|
1
|
+
{"version":3,"file":"useAriaLabel.js","names":["useAriaLabel","attrs","useAttrs","computed","label","useLabelledBy","id"],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n * Resolve accessible names in order: aria-labelledby, consumer aria-label, label fallback.\n * Suppress aria-label when aria-labelledby names the element.\n */\n\nimport { computed, useAttrs, type ComputedRef } from 'vue'\n\nexport function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n attrs['aria-labelledby'] != null\n ? undefined\n : ((attrs['aria-label'] as string | undefined) ?? label()),\n )\n}\n\n// @a11y\n/**\n * The id of a visible label naming the element by reference, unless the consumer named it with\n * `aria-label` or `aria-labelledby`.\n */\nexport function useLabelledBy(\n id: string,\n label: () => string | undefined,\n): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n label() && attrs['aria-label'] == null && attrs['aria-labelledby'] == null ? id : undefined,\n )\n}\n"],"mappings":";;AAQA,SAAgBA,EAAa,GAAkE;CAC7F,IAAMC,IAAQC,EAAS;CACvB,OAAOC,QACLF,EAAM,sBAAsB,OAEtBA,EAAM,iBAAwCG,EAAM,IADtD,KAAA,CAEN;AACF;AAOA,SAAgBC,EACd,GACA,GACiC;CACjC,IAAMJ,IAAQC,EAAS;CACvB,OAAOC,QACLC,EAAM,KAAKH,EAAM,iBAAiB,QAAQA,EAAM,sBAAsB,OAAOK,IAAK,KAAA,CACpF;AACF"}
|