srcdev-nuxt-components 6.1.0 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/accordian/AccordianCore.vue +1 -1
- package/app/components/content-grid/ContentGrid.vue +6 -8
- package/app/components/display-dialog/DisplayDialogCore.vue +47 -33
- package/app/components/display-prompt/DisplayPromptCore.vue +34 -21
- package/app/components/display-prompt/variants/DisplayPromptError.vue +3 -5
- package/app/components/expanding-panel/ExpandingPanel.vue +8 -8
- package/app/components/responsive-header/ResponsiveHeader.vue +157 -115
- package/app/components/skip-links/SkipLinks.vue +5 -4
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="ui-content-grid" :class="[applyClasses]" :data-testid="dataTestid">
|
|
3
|
-
<div v-if="
|
|
3
|
+
<div v-if="slots.slot1" class="col-1">
|
|
4
4
|
<slot name="slot1"></slot>
|
|
5
5
|
</div>
|
|
6
|
-
<div v-if="
|
|
6
|
+
<div v-if="slots.slot2" class="col-2">
|
|
7
7
|
<slot name="slot2"></slot>
|
|
8
8
|
</div>
|
|
9
9
|
</div>
|
|
@@ -13,17 +13,15 @@
|
|
|
13
13
|
const props = defineProps({
|
|
14
14
|
dataTestid: {
|
|
15
15
|
type: String,
|
|
16
|
-
default:
|
|
16
|
+
default: "ui-content-grid",
|
|
17
17
|
},
|
|
18
18
|
applyClasses: {
|
|
19
19
|
type: String,
|
|
20
|
-
default:
|
|
20
|
+
default: "",
|
|
21
21
|
},
|
|
22
|
-
})
|
|
22
|
+
})
|
|
23
23
|
|
|
24
|
-
const slots = useSlots()
|
|
25
|
-
const hasSlot1 = ref(slots.slot1 !== undefined);
|
|
26
|
-
const hasSlot2 = ref(slots.slot2 !== undefined);
|
|
24
|
+
const slots = useSlots()
|
|
27
25
|
</script>
|
|
28
26
|
|
|
29
27
|
<style lang="css">
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<dialog
|
|
2
|
+
<dialog
|
|
3
|
+
class="display-dialog-core"
|
|
4
|
+
:class="[elementClasses]"
|
|
5
|
+
role="dialog"
|
|
6
|
+
:align-dialog
|
|
7
|
+
:justify-dialog
|
|
8
|
+
:open
|
|
9
|
+
:data-dialog-id="dataDialogId"
|
|
10
|
+
ref="dialogRef"
|
|
11
|
+
>
|
|
3
12
|
<focus-trap v-model:active="open" :clickOutsideDeactivates="true" @deactivate="closeDialog()">
|
|
4
13
|
<div class="inner" :class="[variant]">
|
|
5
14
|
<div class="header">
|
|
6
|
-
<div v-if="
|
|
15
|
+
<div v-if="slots.dialogTitle" class="col-left">
|
|
7
16
|
<slot name="dialogTitle"></slot>
|
|
8
17
|
</div>
|
|
9
18
|
|
|
@@ -11,16 +20,24 @@
|
|
|
11
20
|
<p class="text-normal wght-700">Center col</p>
|
|
12
21
|
</div>
|
|
13
22
|
<div class="col-right">
|
|
14
|
-
<button
|
|
23
|
+
<button
|
|
24
|
+
@click.prevent="closeDialog()"
|
|
25
|
+
data-test-id="display-dialog-header-close"
|
|
26
|
+
class="display-prompt-action"
|
|
27
|
+
>
|
|
15
28
|
<Icon name="bitcoin-icons:cross-filled" class="icon" />
|
|
16
29
|
<span class="sr-only">Really Close</span>
|
|
17
30
|
</button>
|
|
18
31
|
</div>
|
|
19
32
|
</div>
|
|
20
|
-
<div
|
|
33
|
+
<div
|
|
34
|
+
v-if="slots.dialogContent"
|
|
35
|
+
class="dialog-content"
|
|
36
|
+
:class="[{ 'allow-content-scroll': allowContentScroll }]"
|
|
37
|
+
>
|
|
21
38
|
<slot name="dialogContent"></slot>
|
|
22
39
|
</div>
|
|
23
|
-
<div v-if="
|
|
40
|
+
<div v-if="slots.actionButtons" class="footer">
|
|
24
41
|
<slot name="actionButtons"></slot>
|
|
25
42
|
</div>
|
|
26
43
|
</div>
|
|
@@ -29,7 +46,7 @@
|
|
|
29
46
|
</template>
|
|
30
47
|
|
|
31
48
|
<script setup lang="ts">
|
|
32
|
-
import { FocusTrap } from
|
|
49
|
+
import { FocusTrap } from "focus-trap-vue"
|
|
33
50
|
const props = defineProps({
|
|
34
51
|
styleClassPassthrough: {
|
|
35
52
|
type: Array as PropType<string[]>,
|
|
@@ -37,18 +54,18 @@ const props = defineProps({
|
|
|
37
54
|
},
|
|
38
55
|
variant: {
|
|
39
56
|
type: String,
|
|
40
|
-
default:
|
|
41
|
-
validator: (val) => [
|
|
57
|
+
default: "dialog",
|
|
58
|
+
validator: (val) => ["dialog", "modal", "confirm"].includes(val as string),
|
|
42
59
|
},
|
|
43
60
|
justifyDialog: {
|
|
44
61
|
type: String,
|
|
45
|
-
default:
|
|
46
|
-
validator: (val) => [
|
|
62
|
+
default: "center",
|
|
63
|
+
validator: (val) => ["start", "center", "end"].includes(val as string),
|
|
47
64
|
},
|
|
48
65
|
alignDialog: {
|
|
49
66
|
type: String,
|
|
50
|
-
default:
|
|
51
|
-
validator: (val) => [
|
|
67
|
+
default: "center",
|
|
68
|
+
validator: (val) => ["start", "center", "end"].includes(val as string),
|
|
52
69
|
},
|
|
53
70
|
lockViewport: {
|
|
54
71
|
type: Boolean,
|
|
@@ -62,33 +79,30 @@ const props = defineProps({
|
|
|
62
79
|
type: String,
|
|
63
80
|
required: true,
|
|
64
81
|
},
|
|
65
|
-
})
|
|
82
|
+
})
|
|
66
83
|
|
|
67
|
-
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
84
|
+
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
68
85
|
|
|
69
|
-
const open = defineModel<boolean>()
|
|
70
|
-
const bodyTag = ref<HTMLBodyElement | null>(null)
|
|
71
|
-
const lockViewport = toRef<boolean>(props.lockViewport)
|
|
86
|
+
const open = defineModel<boolean>()
|
|
87
|
+
const bodyTag = ref<HTMLBodyElement | null>(null)
|
|
88
|
+
const lockViewport = toRef<boolean>(props.lockViewport)
|
|
72
89
|
|
|
73
90
|
const closeDialog = () => {
|
|
74
|
-
open.value = false
|
|
91
|
+
open.value = false
|
|
75
92
|
|
|
76
93
|
if (lockViewport.value && bodyTag.value !== null) {
|
|
77
|
-
bodyTag.value.classList.remove(
|
|
94
|
+
bodyTag.value.classList.remove("lock")
|
|
78
95
|
}
|
|
79
|
-
}
|
|
96
|
+
}
|
|
80
97
|
|
|
81
|
-
const slots = useSlots()
|
|
82
|
-
const hasDialogTitle = computed(() => slots.dialogTitle !== undefined);
|
|
83
|
-
const hasDialogContent = computed(() => slots.dialogContent !== undefined);
|
|
84
|
-
const hasActionButtons = computed(() => slots.actionButtons !== undefined);
|
|
98
|
+
const slots = useSlots()
|
|
85
99
|
|
|
86
100
|
onMounted(() => {
|
|
87
|
-
bodyTag.value = document.querySelector(
|
|
101
|
+
bodyTag.value = document.querySelector("body")
|
|
88
102
|
if (lockViewport.value && bodyTag.value !== null) {
|
|
89
|
-
bodyTag.value.classList.add(
|
|
103
|
+
bodyTag.value.classList.add("lock")
|
|
90
104
|
}
|
|
91
|
-
})
|
|
105
|
+
})
|
|
92
106
|
</script>
|
|
93
107
|
|
|
94
108
|
<style lang="css">
|
|
@@ -121,26 +135,26 @@ onMounted(() => {
|
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
/* * Positioning the dialog */
|
|
124
|
-
&[justify-dialog=
|
|
138
|
+
&[justify-dialog="start"] {
|
|
125
139
|
justify-content: flex-start;
|
|
126
140
|
}
|
|
127
141
|
|
|
128
|
-
&[justify-dialog=
|
|
142
|
+
&[justify-dialog="center"] {
|
|
129
143
|
justify-content: center;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
|
-
&[justify-dialog=
|
|
146
|
+
&[justify-dialog="end"] {
|
|
133
147
|
justify-content: flex-end;
|
|
134
148
|
}
|
|
135
149
|
|
|
136
|
-
&[align-dialog=
|
|
150
|
+
&[align-dialog="start"] {
|
|
137
151
|
align-items: flex-start;
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
&[align-dialog=
|
|
154
|
+
&[align-dialog="center"] {
|
|
141
155
|
align-items: center;
|
|
142
156
|
}
|
|
143
|
-
&[align-dialog=
|
|
157
|
+
&[align-dialog="end"] {
|
|
144
158
|
align-items: flex-end;
|
|
145
159
|
}
|
|
146
160
|
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="display-prompt-core"
|
|
4
|
+
:class="[{ dismissed: hide }, { 'use-local-style-overrides': useLocalStyleOverrides }]"
|
|
5
|
+
:data-test-id="`display-prompt-core-${theme}`"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
class="display-prompt-wrapper"
|
|
9
|
+
:data-component-theme="theme"
|
|
10
|
+
:class="[elementClasses]"
|
|
11
|
+
data-test-id="display-prompt"
|
|
12
|
+
>
|
|
4
13
|
<div class="display-prompt-inner">
|
|
5
14
|
<div class="display-prompt-icon" data-test-id="prompt-icon">
|
|
6
15
|
<slot name="customDecoratorIcon">
|
|
7
|
-
<Icon :name="displayPromptIcons[theme]" class="icon" :color="iconColor" />
|
|
16
|
+
<Icon :name="displayPromptIcons[theme] ?? 'akar-icons:circle-alert'" class="icon" :color="iconColor" />
|
|
8
17
|
</slot>
|
|
9
18
|
</div>
|
|
10
19
|
<div class="display-prompt-content">
|
|
11
20
|
<p class="title" data-test-id="display-prompt-title">
|
|
12
21
|
<slot name="title"></slot>
|
|
13
22
|
</p>
|
|
14
|
-
<p v-if="
|
|
23
|
+
<p v-if="slots.content" class="text" data-test-id="display-prompt-content">
|
|
15
24
|
<slot name="content"></slot>
|
|
16
25
|
</p>
|
|
17
26
|
</div>
|
|
18
|
-
<button
|
|
27
|
+
<button
|
|
28
|
+
v-if="dismissible"
|
|
29
|
+
@click.prevent="dismissPrompt()"
|
|
30
|
+
data-test-id="display-prompt-action"
|
|
31
|
+
class="display-prompt-action"
|
|
32
|
+
>
|
|
19
33
|
<slot name="customCloseIcon">
|
|
20
34
|
<Icon name="bitcoin-icons:cross-filled" class="icon" />
|
|
21
35
|
</slot>
|
|
@@ -36,9 +50,9 @@ const props = defineProps({
|
|
|
36
50
|
},
|
|
37
51
|
theme: {
|
|
38
52
|
type: String,
|
|
39
|
-
default:
|
|
53
|
+
default: "error",
|
|
40
54
|
validator(value: string) {
|
|
41
|
-
return [
|
|
55
|
+
return ["error", "info", "success", "warning", "secondary"].includes(value)
|
|
42
56
|
},
|
|
43
57
|
},
|
|
44
58
|
styleClassPassthrough: {
|
|
@@ -47,9 +61,9 @@ const props = defineProps({
|
|
|
47
61
|
},
|
|
48
62
|
iconColor: {
|
|
49
63
|
type: String as PropType<string>,
|
|
50
|
-
default:
|
|
64
|
+
default: "dark-grey",
|
|
51
65
|
validator(value: string) {
|
|
52
|
-
return [
|
|
66
|
+
return ["dark-grey", "white"].includes(value)
|
|
53
67
|
},
|
|
54
68
|
},
|
|
55
69
|
useLocalStyleOverrides: {
|
|
@@ -59,24 +73,23 @@ const props = defineProps({
|
|
|
59
73
|
displayPromptIcons: {
|
|
60
74
|
type: Object as PropType<Record<string, string>>,
|
|
61
75
|
default: () => ({
|
|
62
|
-
error:
|
|
63
|
-
info:
|
|
64
|
-
success:
|
|
65
|
-
warning:
|
|
66
|
-
secondary:
|
|
76
|
+
error: "akar-icons:circle-alert",
|
|
77
|
+
info: "akar-icons:info",
|
|
78
|
+
success: "akar-icons:check",
|
|
79
|
+
warning: "akar-icons:circle-alert",
|
|
80
|
+
secondary: "akar-icons:info",
|
|
67
81
|
}),
|
|
68
82
|
},
|
|
69
|
-
})
|
|
83
|
+
})
|
|
70
84
|
|
|
71
|
-
const slots = useSlots()
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
85
|
+
const slots = useSlots()
|
|
86
|
+
const hide = ref(false)
|
|
87
|
+
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
75
88
|
|
|
76
89
|
const dismissPrompt = () => {
|
|
77
90
|
// styleClassPassthrough.value = '';
|
|
78
|
-
hide.value = true
|
|
79
|
-
}
|
|
91
|
+
hide.value = true
|
|
92
|
+
}
|
|
80
93
|
</script>
|
|
81
94
|
|
|
82
95
|
<style lang="css">
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<template #title>
|
|
7
7
|
<slot name="title"></slot>
|
|
8
8
|
</template>
|
|
9
|
-
<template v-if="
|
|
9
|
+
<template v-if="slots.content" #content>
|
|
10
10
|
<slot name="content"></slot>
|
|
11
11
|
</template>
|
|
12
12
|
</DisplayPromptCore>
|
|
@@ -22,11 +22,9 @@ const props = defineProps({
|
|
|
22
22
|
type: Array as PropType<string[]>,
|
|
23
23
|
default: () => [],
|
|
24
24
|
},
|
|
25
|
-
})
|
|
25
|
+
})
|
|
26
26
|
|
|
27
|
-
const slots = useSlots()
|
|
28
|
-
const hasContent = ref(slots.content !== undefined);
|
|
29
|
-
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
27
|
+
const slots = useSlots()
|
|
30
28
|
</script>
|
|
31
29
|
|
|
32
30
|
<style lang="css">
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
const props = defineProps({
|
|
25
25
|
name: {
|
|
26
26
|
type: String,
|
|
27
|
-
default:
|
|
27
|
+
default: "",
|
|
28
28
|
},
|
|
29
29
|
iconSize: {
|
|
30
30
|
type: String,
|
|
31
|
-
default:
|
|
31
|
+
default: "medium",
|
|
32
32
|
},
|
|
33
33
|
animationDuration: {
|
|
34
34
|
type: Number,
|
|
@@ -38,15 +38,15 @@ const props = defineProps({
|
|
|
38
38
|
type: Array as PropType<string[]>,
|
|
39
39
|
default: () => [],
|
|
40
40
|
},
|
|
41
|
-
})
|
|
41
|
+
})
|
|
42
42
|
|
|
43
|
-
const name = computed(() => props.name || useId())
|
|
43
|
+
const name = computed(() => props.name || useId())
|
|
44
44
|
|
|
45
|
-
const triggerId = computed(() => `id-${name.value}-trigger`)
|
|
46
|
-
const contentId = computed(() => `id-${name.value}-content`)
|
|
47
|
-
const animationDurationStr = computed(() => `${props.animationDuration}ms`)
|
|
45
|
+
const triggerId = computed(() => `id-${name.value}-trigger`)
|
|
46
|
+
const contentId = computed(() => `id-${name.value}-content`)
|
|
47
|
+
const animationDurationStr = computed(() => `${props.animationDuration}ms`)
|
|
48
48
|
|
|
49
|
-
const { elementClasses
|
|
49
|
+
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<style lang="css">
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="navigation" :class="[elementClasses, { loaded: navLoaded }]" ref="navigationWrapper" role="banner">
|
|
3
3
|
<nav class="main-navigation" ref="mainNav" aria-label="Main navigation">
|
|
4
|
-
<ul
|
|
4
|
+
<ul
|
|
5
|
+
v-for="(navGroup, groupKey) in responsiveNavLinks"
|
|
6
|
+
:key="groupKey"
|
|
7
|
+
class="main-navigation-list"
|
|
8
|
+
:ref="el => setNavRef(String(groupKey), el as HTMLUListElement | null)"
|
|
9
|
+
>
|
|
5
10
|
<template v-for="(link, localIndex) in navGroup" :key="localIndex">
|
|
6
11
|
<li
|
|
7
12
|
v-if="link.path"
|
|
8
13
|
class="main-navigation-item"
|
|
9
|
-
:class="{
|
|
10
|
-
|
|
14
|
+
:class="{
|
|
15
|
+
'visually-hidden': !mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.visible,
|
|
16
|
+
}"
|
|
17
|
+
:style="{
|
|
18
|
+
'--_main-navigation-item-width':
|
|
19
|
+
mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.width + 'px',
|
|
20
|
+
}"
|
|
11
21
|
ref="mainNavigationItems"
|
|
12
22
|
:data-group-key="groupKey"
|
|
13
23
|
:data-local-index="localIndex"
|
|
@@ -22,8 +32,13 @@
|
|
|
22
32
|
<li
|
|
23
33
|
v-else
|
|
24
34
|
class="main-navigation-item"
|
|
25
|
-
:class="{
|
|
26
|
-
|
|
35
|
+
:class="{
|
|
36
|
+
'visually-hidden': !mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.visible,
|
|
37
|
+
}"
|
|
38
|
+
:style="{
|
|
39
|
+
'--_main-navigation-item-width':
|
|
40
|
+
mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.width + 'px',
|
|
41
|
+
}"
|
|
27
42
|
ref="mainNavigationItems"
|
|
28
43
|
:data-group-key="groupKey"
|
|
29
44
|
:data-local-index="localIndex"
|
|
@@ -45,7 +60,9 @@
|
|
|
45
60
|
<div class="main-navigation-sub-nav" role="menu" :aria-labelledby="`summary-${groupKey}-${localIndex}`">
|
|
46
61
|
<ul class="main-navigation-sub-nav-list">
|
|
47
62
|
<li class="main-navigation-sub-nav-item" v-for="childLink in link.childLinks" :key="childLink.name">
|
|
48
|
-
<NuxtLink :to="childLink.path" class="main-navigation-sub-nav-link" role="menuitem">
|
|
63
|
+
<NuxtLink :to="childLink.path" class="main-navigation-sub-nav-link" role="menuitem">
|
|
64
|
+
{{ childLink.name }}
|
|
65
|
+
</NuxtLink>
|
|
49
66
|
</li>
|
|
50
67
|
</ul>
|
|
51
68
|
</div>
|
|
@@ -55,16 +72,31 @@
|
|
|
55
72
|
</ul>
|
|
56
73
|
</nav>
|
|
57
74
|
<nav class="secondary-navigation" ref="secondaryNav" aria-label="Secondary navigation">
|
|
58
|
-
<details
|
|
75
|
+
<details
|
|
76
|
+
class="overflow-details"
|
|
77
|
+
:class="[{ 'visually-hidden': !navLoaded || !showOverflowDetails }]"
|
|
78
|
+
ref="overflowDetails"
|
|
79
|
+
name="overflow-group"
|
|
80
|
+
>
|
|
59
81
|
<summary class="overflow-details-summary has-toggle-icon">
|
|
60
|
-
<Icon
|
|
61
|
-
|
|
82
|
+
<Icon
|
|
83
|
+
:name="overflowDetailsSummaryIcons.more ?? 'gravity-ui:ellipsis'"
|
|
84
|
+
class="icon"
|
|
85
|
+
:class="[{ show: !allowNavigationCollapse }]"
|
|
86
|
+
:aria-hidden="true"
|
|
87
|
+
/>
|
|
88
|
+
<Icon
|
|
89
|
+
:name="overflowDetailsSummaryIcons.burger ?? 'gravity-ui:bars'"
|
|
90
|
+
class="icon"
|
|
91
|
+
:class="[{ show: allowNavigationCollapse }]"
|
|
92
|
+
:aria-hidden="true"
|
|
93
|
+
/>
|
|
62
94
|
</summary>
|
|
63
95
|
<div class="overflow-details-nav" role="menu">
|
|
64
96
|
<NavigationItems :main-navigation-state="mainNavigationState" />
|
|
65
97
|
</div>
|
|
66
98
|
</details>
|
|
67
|
-
<slot v-if="
|
|
99
|
+
<slot v-if="slots.secondaryNavigation" name="secondaryNavigation"></slot>
|
|
68
100
|
</nav>
|
|
69
101
|
<LayoutRow tag="div" variant="full" :style-class-passthrough="['mb-20', 'debug-grid']">
|
|
70
102
|
<ClientOnly>
|
|
@@ -85,8 +117,8 @@
|
|
|
85
117
|
</template>
|
|
86
118
|
|
|
87
119
|
<script setup lang="ts">
|
|
88
|
-
import { useResizeObserver, onClickOutside } from
|
|
89
|
-
import type { ResponsiveHeaderProp, ResponsiveHeaderState, IFlooredRect } from
|
|
120
|
+
import { useResizeObserver, onClickOutside } from "@vueuse/core"
|
|
121
|
+
import type { ResponsiveHeaderProp, ResponsiveHeaderState, IFlooredRect } from "@/types/responsiveHeader"
|
|
90
122
|
|
|
91
123
|
const props = defineProps({
|
|
92
124
|
responsiveNavLinks: {
|
|
@@ -100,8 +132,8 @@ const props = defineProps({
|
|
|
100
132
|
overflowDetailsSummaryIcons: {
|
|
101
133
|
type: Object as PropType<Record<string, string>>,
|
|
102
134
|
default: {
|
|
103
|
-
more:
|
|
104
|
-
burger:
|
|
135
|
+
more: "gravity-ui:ellipsis",
|
|
136
|
+
burger: "gravity-ui:bars",
|
|
105
137
|
},
|
|
106
138
|
},
|
|
107
139
|
collapseBreakpoint: {
|
|
@@ -120,67 +152,68 @@ const props = defineProps({
|
|
|
120
152
|
type: Boolean,
|
|
121
153
|
default: true,
|
|
122
154
|
},
|
|
123
|
-
})
|
|
155
|
+
})
|
|
124
156
|
|
|
125
|
-
const slots = useSlots()
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
157
|
+
const slots = useSlots()
|
|
158
|
+
const collapseNavigationBelowWidth = computed(
|
|
159
|
+
() => props.collapseBreakpoint !== null || props.collapseAtMainNavIntersection
|
|
160
|
+
)
|
|
161
|
+
const collapseBreakpoint = ref(props.collapseBreakpoint)
|
|
162
|
+
const navLoaded = ref(false)
|
|
163
|
+
const navigationWrapperRef = useTemplateRef("navigationWrapper")
|
|
131
164
|
|
|
132
165
|
const closeAllNavigationDetails = () => {
|
|
133
166
|
navigationDetailsRefs.value?.forEach((element) => {
|
|
134
|
-
element?.removeAttribute(
|
|
135
|
-
})
|
|
136
|
-
overflowDetailsRef.value?.removeAttribute(
|
|
137
|
-
}
|
|
167
|
+
element?.removeAttribute("open")
|
|
168
|
+
})
|
|
169
|
+
overflowDetailsRef.value?.removeAttribute("open")
|
|
170
|
+
}
|
|
138
171
|
|
|
139
172
|
const toggleDetailsElement = (event: Event) => {
|
|
140
|
-
const summaryElement = event.currentTarget as HTMLElement
|
|
141
|
-
const parentDetailsElement = summaryElement.closest(
|
|
142
|
-
if (!parentDetailsElement) return
|
|
173
|
+
const summaryElement = event.currentTarget as HTMLElement
|
|
174
|
+
const parentDetailsElement = summaryElement.closest("details")
|
|
175
|
+
if (!parentDetailsElement) return
|
|
143
176
|
|
|
144
|
-
if (parentDetailsElement.hasAttribute(
|
|
145
|
-
parentDetailsElement.removeAttribute(
|
|
177
|
+
if (parentDetailsElement.hasAttribute("open")) {
|
|
178
|
+
parentDetailsElement.removeAttribute("open")
|
|
146
179
|
} else {
|
|
147
|
-
parentDetailsElement.setAttribute(
|
|
180
|
+
parentDetailsElement.setAttribute("open", "")
|
|
148
181
|
}
|
|
149
|
-
overflowDetailsRef.value?.removeAttribute(
|
|
150
|
-
}
|
|
182
|
+
overflowDetailsRef.value?.removeAttribute("open")
|
|
183
|
+
}
|
|
151
184
|
|
|
152
185
|
const handleSummaryHover = (event: MouseEvent | FocusEvent) => {
|
|
153
186
|
if (!props.allowExpandOnGesture) {
|
|
154
|
-
return
|
|
187
|
+
return
|
|
155
188
|
}
|
|
156
189
|
|
|
157
190
|
// Close all other open navigation details first
|
|
158
|
-
const summaryElement = event.currentTarget as HTMLElement
|
|
159
|
-
const parentDetailsElement = summaryElement.closest(
|
|
191
|
+
const summaryElement = event.currentTarget as HTMLElement
|
|
192
|
+
const parentDetailsElement = summaryElement.closest("details")
|
|
160
193
|
|
|
161
194
|
navigationDetailsRefs.value?.forEach((element) => {
|
|
162
195
|
if (element !== parentDetailsElement) {
|
|
163
|
-
element?.removeAttribute(
|
|
196
|
+
element?.removeAttribute("open")
|
|
164
197
|
}
|
|
165
|
-
})
|
|
166
|
-
overflowDetailsRef.value?.removeAttribute(
|
|
198
|
+
})
|
|
199
|
+
overflowDetailsRef.value?.removeAttribute("open")
|
|
167
200
|
|
|
168
201
|
// Then toggle the current one
|
|
169
|
-
toggleDetailsElement(event)
|
|
170
|
-
}
|
|
202
|
+
toggleDetailsElement(event)
|
|
203
|
+
}
|
|
171
204
|
|
|
172
205
|
const handleNavigationItemHover = () => {
|
|
173
206
|
if (!props.allowExpandOnGesture) {
|
|
174
|
-
return
|
|
207
|
+
return
|
|
175
208
|
}
|
|
176
209
|
|
|
177
210
|
// Close all open navigation details when hovering over regular nav items
|
|
178
|
-
closeAllNavigationDetails()
|
|
179
|
-
}
|
|
211
|
+
closeAllNavigationDetails()
|
|
212
|
+
}
|
|
180
213
|
|
|
181
214
|
const handleSummaryAction = (event: MouseEvent | KeyboardEvent) => {
|
|
182
|
-
toggleDetailsElement(event)
|
|
183
|
-
}
|
|
215
|
+
toggleDetailsElement(event)
|
|
216
|
+
}
|
|
184
217
|
|
|
185
218
|
const mainNavigationState = ref<ResponsiveHeaderState>({
|
|
186
219
|
navListVisibility: {
|
|
@@ -189,51 +222,53 @@ const mainNavigationState = ref<ResponsiveHeaderState>({
|
|
|
189
222
|
},
|
|
190
223
|
clonedNavLinks: props.responsiveNavLinks,
|
|
191
224
|
hasSecondNav: Object.keys(props.responsiveNavLinks).length > 1,
|
|
192
|
-
})
|
|
225
|
+
})
|
|
193
226
|
|
|
194
|
-
const navRefs = ref<Record<string, HTMLUListElement | null>>({})
|
|
227
|
+
const navRefs = ref<Record<string, HTMLUListElement | null>>({})
|
|
195
228
|
|
|
196
229
|
const setNavRef = (key: string, el: HTMLUListElement | null) => {
|
|
197
|
-
navRefs.value[key] = el
|
|
198
|
-
}
|
|
230
|
+
navRefs.value[key] = el
|
|
231
|
+
}
|
|
199
232
|
|
|
200
|
-
const navigationWrapperRects = ref<IFlooredRect | null>(null)
|
|
201
|
-
const firstNavRef = ref<HTMLUListElement | null>(null)
|
|
202
|
-
const firstNavRects = ref<IFlooredRect | null>(null)
|
|
233
|
+
const navigationWrapperRects = ref<IFlooredRect | null>(null)
|
|
234
|
+
const firstNavRef = ref<HTMLUListElement | null>(null)
|
|
235
|
+
const firstNavRects = ref<IFlooredRect | null>(null)
|
|
203
236
|
|
|
204
|
-
const secondNavRef = ref<HTMLUListElement | null>(null)
|
|
205
|
-
const secondNavRects = ref<IFlooredRect | null>(null)
|
|
237
|
+
const secondNavRef = ref<HTMLUListElement | null>(null)
|
|
238
|
+
const secondNavRects = ref<IFlooredRect | null>(null)
|
|
206
239
|
|
|
207
|
-
const secondaryNavRef = useTemplateRef(
|
|
208
|
-
const secondaryNavRects = ref<IFlooredRect | null>(null)
|
|
240
|
+
const secondaryNavRef = useTemplateRef("secondaryNav")
|
|
241
|
+
const secondaryNavRects = ref<IFlooredRect | null>(null)
|
|
209
242
|
|
|
210
|
-
const mainNavigationItemsRefs = useTemplateRef<HTMLLIElement[]>(
|
|
243
|
+
const mainNavigationItemsRefs = useTemplateRef<HTMLLIElement[]>("mainNavigationItems")
|
|
211
244
|
|
|
212
|
-
const navigationDetailsRefs = useTemplateRef<HTMLElement[]>(
|
|
245
|
+
const navigationDetailsRefs = useTemplateRef<HTMLElement[]>("navigationDetails")
|
|
213
246
|
|
|
214
|
-
const overflowDetailsRef = useTemplateRef(
|
|
247
|
+
const overflowDetailsRef = useTemplateRef("overflowDetails")
|
|
215
248
|
|
|
216
249
|
const showOverflowDetails = computed(() => {
|
|
217
|
-
const hasHiddenNav =
|
|
218
|
-
|
|
219
|
-
|
|
250
|
+
const hasHiddenNav =
|
|
251
|
+
!mainNavigationState.value.navListVisibility["firstNav"] ||
|
|
252
|
+
(!mainNavigationState.value.navListVisibility["secondNav"] && mainNavigationState.value.hasSecondNav)
|
|
253
|
+
return hasHiddenNav
|
|
254
|
+
})
|
|
220
255
|
|
|
221
256
|
const mainNavigationMarginBlockEnd = computed(() => {
|
|
222
|
-
return secondaryNavRects.value ? secondaryNavRects.value.width + props.gapBetweenFirstAndSecondNav : 0
|
|
223
|
-
})
|
|
257
|
+
return secondaryNavRects.value ? secondaryNavRects.value.width + props.gapBetweenFirstAndSecondNav : 0
|
|
258
|
+
})
|
|
224
259
|
|
|
225
260
|
const mainNavigationMarginBlockEndStr = computed(() => {
|
|
226
|
-
return mainNavigationMarginBlockEnd.value +
|
|
227
|
-
})
|
|
261
|
+
return mainNavigationMarginBlockEnd.value + "px"
|
|
262
|
+
})
|
|
228
263
|
|
|
229
264
|
const initTemplateRefs = async () => {
|
|
230
|
-
firstNavRef.value = navRefs.value[
|
|
231
|
-
secondNavRef.value = navRefs.value[
|
|
232
|
-
return
|
|
233
|
-
}
|
|
265
|
+
firstNavRef.value = navRefs.value["firstNav"] as HTMLUListElement | null
|
|
266
|
+
secondNavRef.value = navRefs.value["secondNav"] as HTMLUListElement | null
|
|
267
|
+
return
|
|
268
|
+
}
|
|
234
269
|
|
|
235
270
|
const getFlooredRect = (rect: DOMRect | null) => {
|
|
236
|
-
if (!rect) return null
|
|
271
|
+
if (!rect) return null
|
|
237
272
|
return {
|
|
238
273
|
left: Math.floor(rect.left),
|
|
239
274
|
right: Math.floor(rect.right),
|
|
@@ -241,19 +276,22 @@ const getFlooredRect = (rect: DOMRect | null) => {
|
|
|
241
276
|
bottom: Math.floor(rect.bottom),
|
|
242
277
|
width: Math.floor(rect.width),
|
|
243
278
|
height: Math.floor(rect.height),
|
|
244
|
-
}
|
|
245
|
-
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
246
281
|
|
|
247
282
|
const updateNavigationConfig = async (source: string) => {
|
|
248
|
-
navigationWrapperRects.value =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
283
|
+
navigationWrapperRects.value =
|
|
284
|
+
getFlooredRect((navigationWrapperRef.value && navigationWrapperRef.value.getBoundingClientRect()) ?? null) || null
|
|
285
|
+
secondaryNavRects.value =
|
|
286
|
+
getFlooredRect((secondaryNavRef.value && secondaryNavRef.value.getBoundingClientRect()) ?? null) || null
|
|
287
|
+
firstNavRects.value = getFlooredRect((firstNavRef.value && firstNavRef.value.getBoundingClientRect()) ?? null) || null
|
|
288
|
+
secondNavRects.value =
|
|
289
|
+
getFlooredRect((secondNavRef.value && secondNavRef.value.getBoundingClientRect()) ?? null) || null
|
|
252
290
|
|
|
253
291
|
if (collapseNavigationBelowWidth.value && firstNavRects.value) {
|
|
254
|
-
collapseBreakpoint.value = firstNavRects.value?.right
|
|
292
|
+
collapseBreakpoint.value = firstNavRects.value?.right
|
|
255
293
|
}
|
|
256
|
-
}
|
|
294
|
+
}
|
|
257
295
|
|
|
258
296
|
const allowNavigationCollapse = computed(() => {
|
|
259
297
|
return (
|
|
@@ -261,32 +299,35 @@ const allowNavigationCollapse = computed(() => {
|
|
|
261
299
|
navigationWrapperRects.value &&
|
|
262
300
|
secondaryNavRects.value !== null &&
|
|
263
301
|
Math.floor(secondaryNavRects.value.left - props.gapBetweenFirstAndSecondNav) <= collapseBreakpoint.value
|
|
264
|
-
)
|
|
265
|
-
})
|
|
302
|
+
)
|
|
303
|
+
})
|
|
266
304
|
|
|
267
305
|
const determineNavigationItemVisibility = (rect: DOMRect) => {
|
|
268
306
|
// Check if navigation should be collapsed based on width breakpoint
|
|
269
307
|
if (allowNavigationCollapse.value) {
|
|
270
|
-
return false
|
|
308
|
+
return false
|
|
271
309
|
}
|
|
272
310
|
|
|
273
311
|
// Use default responsive visibility logic if wrapper exists
|
|
274
312
|
if (navigationWrapperRects.value) {
|
|
275
|
-
return
|
|
313
|
+
return (
|
|
314
|
+
Math.floor(rect.right + mainNavigationMarginBlockEnd.value + props.gapBetweenFirstAndSecondNav) <
|
|
315
|
+
navigationWrapperRects.value.right
|
|
316
|
+
)
|
|
276
317
|
}
|
|
277
318
|
|
|
278
319
|
// Default to visible
|
|
279
|
-
return true
|
|
280
|
-
}
|
|
320
|
+
return true
|
|
321
|
+
}
|
|
281
322
|
|
|
282
323
|
const initMainNavigationState = () => {
|
|
283
|
-
if (!mainNavigationItemsRefs.value) return
|
|
324
|
+
if (!mainNavigationItemsRefs.value) return
|
|
284
325
|
|
|
285
326
|
mainNavigationItemsRefs.value.forEach((item, index) => {
|
|
286
|
-
const rect = item.getBoundingClientRect()
|
|
327
|
+
const rect = item.getBoundingClientRect()
|
|
287
328
|
|
|
288
|
-
const groupKey = item.dataset.groupKey
|
|
289
|
-
const localIndex = item.dataset.localIndex ? parseInt(item.dataset.localIndex, 10) : 0
|
|
329
|
+
const groupKey = item.dataset.groupKey
|
|
330
|
+
const localIndex = item.dataset.localIndex ? parseInt(item.dataset.localIndex, 10) : 0
|
|
290
331
|
if (
|
|
291
332
|
groupKey !== undefined &&
|
|
292
333
|
groupKey !== null &&
|
|
@@ -302,56 +343,57 @@ const initMainNavigationState = () => {
|
|
|
302
343
|
width: item.offsetWidth,
|
|
303
344
|
visible: determineNavigationItemVisibility(rect),
|
|
304
345
|
},
|
|
305
|
-
}
|
|
346
|
+
}
|
|
306
347
|
}
|
|
307
348
|
|
|
308
349
|
// Check if a single item has visible set to false and set the visibility of the group accordingly
|
|
309
350
|
if (
|
|
310
|
-
typeof groupKey ===
|
|
351
|
+
typeof groupKey === "string" &&
|
|
311
352
|
mainNavigationState.value.clonedNavLinks &&
|
|
312
353
|
mainNavigationState.value.clonedNavLinks[groupKey] &&
|
|
354
|
+
mainNavigationState.value.clonedNavLinks[groupKey][localIndex] &&
|
|
313
355
|
mainNavigationState.value.clonedNavLinks[groupKey][localIndex].config?.visible === false
|
|
314
356
|
) {
|
|
315
|
-
mainNavigationState.value.navListVisibility[groupKey] = false
|
|
316
|
-
} else if (typeof groupKey ===
|
|
317
|
-
mainNavigationState.value.navListVisibility[groupKey] = true
|
|
357
|
+
mainNavigationState.value.navListVisibility[groupKey] = false
|
|
358
|
+
} else if (typeof groupKey === "string") {
|
|
359
|
+
mainNavigationState.value.navListVisibility[groupKey] = true
|
|
318
360
|
}
|
|
319
|
-
})
|
|
320
|
-
}
|
|
361
|
+
})
|
|
362
|
+
}
|
|
321
363
|
|
|
322
364
|
onMounted(async () => {
|
|
323
365
|
await initTemplateRefs().then(() => {
|
|
324
366
|
setTimeout(() => {
|
|
325
|
-
navLoaded.value = true
|
|
326
|
-
}, 100)
|
|
327
|
-
})
|
|
367
|
+
navLoaded.value = true
|
|
368
|
+
}, 100)
|
|
369
|
+
})
|
|
328
370
|
|
|
329
371
|
navigationDetailsRefs.value?.forEach((element, index) => {
|
|
330
372
|
onClickOutside(element, () => {
|
|
331
|
-
navigationDetailsRefs.value?.[index]?.removeAttribute(
|
|
332
|
-
})
|
|
333
|
-
})
|
|
373
|
+
navigationDetailsRefs.value?.[index]?.removeAttribute("open")
|
|
374
|
+
})
|
|
375
|
+
})
|
|
334
376
|
// Add onClickOutside to overflowDetailsRef
|
|
335
377
|
overflowDetailsRef.value &&
|
|
336
378
|
onClickOutside(overflowDetailsRef.value, () => {
|
|
337
|
-
overflowDetailsRef.value?.removeAttribute(
|
|
338
|
-
})
|
|
339
|
-
})
|
|
379
|
+
overflowDetailsRef.value?.removeAttribute("open")
|
|
380
|
+
})
|
|
381
|
+
})
|
|
340
382
|
|
|
341
383
|
useResizeObserver(navigationWrapperRef, async () => {
|
|
342
|
-
await updateNavigationConfig(
|
|
343
|
-
initMainNavigationState()
|
|
344
|
-
})
|
|
345
|
-
})
|
|
384
|
+
await updateNavigationConfig("useResizeObserver").then(() => {
|
|
385
|
+
initMainNavigationState()
|
|
386
|
+
})
|
|
387
|
+
})
|
|
346
388
|
|
|
347
|
-
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
389
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
348
390
|
|
|
349
391
|
watch(
|
|
350
392
|
() => props.styleClassPassthrough,
|
|
351
393
|
() => {
|
|
352
|
-
resetElementClasses(props.styleClassPassthrough)
|
|
394
|
+
resetElementClasses(props.styleClassPassthrough)
|
|
353
395
|
}
|
|
354
|
-
)
|
|
396
|
+
)
|
|
355
397
|
</script>
|
|
356
398
|
|
|
357
399
|
<style lang="css">
|
|
@@ -378,7 +420,7 @@ watch(
|
|
|
378
420
|
|
|
379
421
|
/* flex-grow: 1; */
|
|
380
422
|
display: grid;
|
|
381
|
-
grid-template-areas:
|
|
423
|
+
grid-template-areas: "navStack";
|
|
382
424
|
|
|
383
425
|
margin: 12px;
|
|
384
426
|
border-radius: 8px;
|
|
@@ -599,7 +641,7 @@ watch(
|
|
|
599
641
|
--_transition-duration: 0.2s;
|
|
600
642
|
|
|
601
643
|
display: grid;
|
|
602
|
-
grid-template-areas:
|
|
644
|
+
grid-template-areas: "icon";
|
|
603
645
|
align-items: center;
|
|
604
646
|
justify-content: center;
|
|
605
647
|
padding-inline: 5px;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<ul class="home-link-navigation" aria-label="Home Navigation">
|
|
3
|
-
<li v-if="
|
|
3
|
+
<li v-if="slots.homeLink" class="home-link">
|
|
4
4
|
<slot name="homeLink">
|
|
5
5
|
<NuxtLink to="/" class="home-link-default">Logo</NuxtLink>
|
|
6
6
|
</slot>
|
|
7
7
|
</li>
|
|
8
8
|
<li class="skip-links">
|
|
9
9
|
<ul class="skip-links-nav">
|
|
10
|
-
<li class="skip-link-item"
|
|
10
|
+
<li class="skip-link-item">
|
|
11
|
+
<a href="#main-content" class="skip-link" ref="skipLink">Skip to main content</a>
|
|
12
|
+
</li>
|
|
11
13
|
<li class="skip-link-item"><a href="#footer" class="skip-link">Skip to footer</a></li>
|
|
12
14
|
</ul>
|
|
13
15
|
</li>
|
|
@@ -15,8 +17,7 @@
|
|
|
15
17
|
</template>
|
|
16
18
|
|
|
17
19
|
<script lang="ts" setup>
|
|
18
|
-
const slots = useSlots()
|
|
19
|
-
const hasHomeLink = ref(slots.homeLink !== undefined);
|
|
20
|
+
const slots = useSlots()
|
|
20
21
|
</script>
|
|
21
22
|
|
|
22
23
|
<style lang="css">
|
package/package.json
CHANGED