@explorer-1/vue 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.d.ts +3 -1
- package/dist/explorer-1-vue.js +4677 -4526
- package/dist/explorer-1-vue.umd.cjs +12 -12
- package/dist/src/components/BlockHeading/BlockHeading.vue.d.ts +25 -2
- package/dist/src/components/DetailHeadline/DetailHeadline.stories.d.ts +29 -1
- package/dist/src/components/DetailHeadline/DetailHeadline.vue.d.ts +26 -0
- package/dist/src/components/NavDesktop/NavDesktop.stories.d.ts +3 -0
- package/dist/src/components/NavDesktop/NavDesktop.vue.d.ts +1 -0
- package/dist/src/components/NavDesktop/NavDesktopDropdown.vue.d.ts +1 -0
- package/dist/src/components/NavDesktopEdu/NavDesktopEdu.stories.d.ts +1 -0
- package/dist/src/components/NavDropdownToggle/NavDropdownToggle.vue.d.ts +19 -6
- package/dist/src/components/NavJumpMenu/NavJumpMenu.stories.d.ts +31 -0
- package/dist/src/components/NavMobile/NavMobile.stories.d.ts +30 -3
- package/dist/src/components/NavMobile/NavMobile.vue.d.ts +1 -0
- package/dist/src/components/NavMobile/NavMobileDropdown.vue.d.ts +9 -1
- package/dist/src/components/NavMobile/NavMobileEdu.stories.d.ts +3 -0
- package/dist/src/components/NavMobile/NavMobileSecondaryDropdown.vue.d.ts +7 -0
- package/dist/src/components/NavSecondary/NavSecondary.stories.d.ts +8 -0
- package/dist/src/components/NavSecondary/NavSecondary.vue.d.ts +23 -1
- package/dist/src/components/NavSecondary/NavSecondaryDropdown.vue.d.ts +33 -2
- package/dist/src/components/NavSecondary/NavSecondaryDropdownContent.vue.d.ts +11 -1
- package/dist/src/components/NavSecondary/NavSecondaryLink.vue.d.ts +21 -3
- package/dist/src/interfaces.d.ts +7 -1
- package/dist/src/store/header.d.ts +2 -0
- package/dist/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.stories.d.ts → PageEduExplainerArticle/PageEduExplainerArticle.stories.d.ts} +3 -2
- package/dist/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.d.ts +1004 -6
- package/dist/src/utils/eventBus.d.ts +1 -0
- package/dist/src/utils/getHeadingId.d.ts +1 -0
- package/dist/src/utils/mixins.d.ts +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/components/BaseAudio/BaseAudio.vue +3 -4
- package/src/components/BaseLink/BaseLink.vue +2 -0
- package/src/components/BaseTag/BaseTag.vue +4 -4
- package/src/components/BlockHeading/BlockHeading.vue +28 -0
- package/src/components/BlockStreamfield/BlockStreamfield.vue +5 -1
- package/src/components/DetailHeadline/DetailHeadline.stories.js +27 -2
- package/src/components/DetailHeadline/DetailHeadline.vue +76 -33
- package/src/components/NavDesktop/NavDesktopDropdown.vue +2 -4
- package/src/components/NavDropdownToggle/NavDropdownToggle.vue +8 -3
- package/src/components/NavJumpMenu/NavJumpMenu.stories.js +47 -0
- package/src/components/NavJumpMenu/NavJumpMenu.vue +141 -0
- package/src/components/NavJumpMenu/NavJumpMenuContent.vue +74 -0
- package/src/components/NavMobile/NavMobile.vue +2 -4
- package/src/components/NavMobile/NavMobileDropdown.vue +8 -4
- package/src/components/NavMobile/NavMobileSecondaryDropdown.vue +4 -1
- package/src/components/NavSecondary/NavSecondary.stories.js +8 -3
- package/src/components/NavSecondary/NavSecondary.vue +26 -6
- package/src/components/NavSecondary/NavSecondaryDropdown.vue +52 -17
- package/src/components/NavSecondary/NavSecondaryDropdownContent.vue +5 -1
- package/src/components/NavSecondary/NavSecondaryLink.vue +38 -11
- package/src/interfaces.ts +7 -1
- package/src/store/header.ts +6 -1
- package/src/templates/edu/PageEduEventDetail/PageEduEventDetail.vue +2 -2
- package/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.stories.js → PageEduExplainerArticle/PageEduExplainerArticle.stories.js} +6 -6
- package/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.vue → PageEduExplainerArticle/PageEduExplainerArticle.vue} +4 -2
- package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.js +6 -4
- package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.vue +16 -2
- package/src/utils/eventBus.ts +3 -0
- package/src/utils/getHeadingId.ts +5 -0
- package/src/utils/mixins.ts +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explorer-1/vue",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dayjs": "^1.11.11",
|
|
22
22
|
"fast-qs": "^2.0.3",
|
|
23
23
|
"lodash": "^4.17.21",
|
|
24
|
+
"mitt": "^3.0.1",
|
|
24
25
|
"sass": "^1.77.4",
|
|
25
26
|
"swiper": "^11.1.3",
|
|
26
27
|
"tailwindcss": "^3.4.3",
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"vue": "^3.4.21",
|
|
29
30
|
"vue-observe-visibility": "^1.0.0",
|
|
30
31
|
"vue3-compare-image": "^1.2.5",
|
|
31
|
-
"@explorer-1/common": "1.1.
|
|
32
|
+
"@explorer-1/common": "1.1.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@vitejs/plugin-vue": "^5.0.4",
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
</template>
|
|
120
120
|
<script lang="ts">
|
|
121
121
|
import { defineComponent } from 'vue'
|
|
122
|
+
// import { eventBus } from './../../utils/eventBus'
|
|
122
123
|
import IconPlay from './../Icons/IconPlay.vue'
|
|
123
124
|
import IconPause from './../Icons/IconPause.vue'
|
|
124
125
|
import IconVolume from './../Icons/IconVolume.vue'
|
|
@@ -326,10 +327,8 @@ export default defineComponent({
|
|
|
326
327
|
this.audio.addEventListener('play', this._handlePlayPause)
|
|
327
328
|
this.audio.addEventListener('ended', this._handleEnded)
|
|
328
329
|
}
|
|
329
|
-
// TODO: VUE3:
|
|
330
|
-
//
|
|
331
|
-
// scoped slots? https://github.com/vuejs/vue/issues/4332
|
|
332
|
-
// this.$root?.$on('play', this.pauseOthers)
|
|
330
|
+
// TODO: VUE3: pass uuID to pauseOthers() method
|
|
331
|
+
// eventBus.on('play', () => this.pauseOthers())
|
|
333
332
|
},
|
|
334
333
|
getAudio() {
|
|
335
334
|
return this.$el.querySelectorAll('audio')[0]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { defineComponent } from 'vue'
|
|
3
|
+
import { eventBus } from './../../utils/eventBus'
|
|
3
4
|
import MixinAnimationCaret from './../MixinAnimationCaret/MixinAnimationCaret.vue'
|
|
4
5
|
|
|
5
6
|
interface Variants {
|
|
@@ -145,6 +146,7 @@ export default defineComponent({
|
|
|
145
146
|
clickEvent() {
|
|
146
147
|
this.$root?.$emit('linkClicked')
|
|
147
148
|
this.$emit('specificLinkClicked')
|
|
149
|
+
eventBus.emit('linkClicked')
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
})
|
|
@@ -9,9 +9,9 @@ const variantMap: Attributes = {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const sizeMap: Attributes = {
|
|
12
|
-
sm: 'text-xs
|
|
13
|
-
md: 'text-base
|
|
14
|
-
lg: 'text-lg
|
|
12
|
+
sm: 'text-xs border-t-2 py-1 px-2.5',
|
|
13
|
+
md: 'text-xs lg:text-base border-t py-1.5 px-3.5',
|
|
14
|
+
lg: 'text-base lg:text-lg border-t py-1.5 px-5'
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
interface BaseTagProps {
|
|
@@ -28,7 +28,7 @@ const props = withDefaults(defineProps<BaseTagProps>(), {
|
|
|
28
28
|
<template>
|
|
29
29
|
<p
|
|
30
30
|
:class="`${variantMap[props.variant]} ${sizeMap[props.size]}`"
|
|
31
|
-
class="ThemeVariantLight text-contrast-none inline-block text-white font-bold edu:font-extrabold rounded-full leading-tight m-0"
|
|
31
|
+
class="ThemeVariantLight text-contrast-none inline-block text-white font-bold edu:font-extrabold rounded-full leading-tight m-0 uppercase"
|
|
32
32
|
>
|
|
33
33
|
<slot />
|
|
34
34
|
<span class="sr-only">.</span>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<BaseHeading
|
|
3
3
|
v-if="data"
|
|
4
|
+
:id="getId"
|
|
4
5
|
:level="data.level"
|
|
6
|
+
class="BlockHeading"
|
|
7
|
+
:class="{ 'has-anchor': generateId }"
|
|
5
8
|
>
|
|
6
9
|
{{ data.heading }}
|
|
7
10
|
</BaseHeading>
|
|
@@ -9,6 +12,7 @@
|
|
|
9
12
|
|
|
10
13
|
<script lang="ts">
|
|
11
14
|
import { defineComponent } from 'vue'
|
|
15
|
+
import { getHeadingId } from '../../utils/getHeadingId'
|
|
12
16
|
import BaseHeading from './../BaseHeading/BaseHeading.vue'
|
|
13
17
|
|
|
14
18
|
export default defineComponent({
|
|
@@ -20,7 +24,31 @@ export default defineComponent({
|
|
|
20
24
|
data: {
|
|
21
25
|
type: Object,
|
|
22
26
|
required: false
|
|
27
|
+
},
|
|
28
|
+
index: {
|
|
29
|
+
type: Number,
|
|
30
|
+
required: false,
|
|
31
|
+
default: undefined
|
|
32
|
+
},
|
|
33
|
+
generateId: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
computed: {
|
|
39
|
+
getId() {
|
|
40
|
+
return this.generateId ? getHeadingId(this.data?.heading, this.index) : undefined
|
|
23
41
|
}
|
|
24
42
|
}
|
|
25
43
|
})
|
|
26
44
|
</script>
|
|
45
|
+
<style lang="scss">
|
|
46
|
+
.BlockHeading {
|
|
47
|
+
&:target {
|
|
48
|
+
@apply scroll-mt-14;
|
|
49
|
+
@screen lg {
|
|
50
|
+
@apply scroll-mt-20;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
indent="col-3"
|
|
12
12
|
class="mb-5"
|
|
13
13
|
>
|
|
14
|
-
<BlockHeading
|
|
14
|
+
<BlockHeading
|
|
15
|
+
:data="block"
|
|
16
|
+
:index="index"
|
|
17
|
+
generate-id
|
|
18
|
+
/>
|
|
15
19
|
</LayoutHelper>
|
|
16
20
|
|
|
17
21
|
<!-- custom margin bottom that matches BlockText styles if followed by InlineImageBlock -->
|
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
import DetailHeadline from './DetailHeadline.vue'
|
|
1
|
+
import DetailHeadline, { pillColorVariants } from './DetailHeadline.vue'
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
title: 'Components/Utilities/DetailHeadline',
|
|
5
5
|
component: DetailHeadline,
|
|
6
|
-
excludeStories: /.*Data
|
|
6
|
+
excludeStories: /.*Data$/,
|
|
7
|
+
argTypes: {
|
|
8
|
+
ariaLabel: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
description:
|
|
11
|
+
"ARIA label. Recommended if your button label isn't descriptive, or if it only contains an icon."
|
|
12
|
+
},
|
|
13
|
+
pillColor: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Color of pill',
|
|
16
|
+
control: {
|
|
17
|
+
type: 'select'
|
|
18
|
+
},
|
|
19
|
+
options: pillColorVariants,
|
|
20
|
+
table: {
|
|
21
|
+
defaultValue: { summary: 'primary' }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
7
25
|
}
|
|
8
26
|
|
|
9
27
|
export const DetailHeadlineData = {
|
|
@@ -67,3 +85,10 @@ export const NoAuthor = {
|
|
|
67
85
|
]
|
|
68
86
|
}
|
|
69
87
|
}
|
|
88
|
+
|
|
89
|
+
export const Pill = {
|
|
90
|
+
args: {
|
|
91
|
+
...DetailHeadlineData,
|
|
92
|
+
pill: true
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -1,45 +1,60 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="
|
|
2
|
+
<div v-if="hasData">
|
|
3
3
|
<div
|
|
4
|
-
v-if="
|
|
5
|
-
class="flex flex-wrap items-
|
|
4
|
+
v-if="hasEyebrow"
|
|
5
|
+
class="flex flex-wrap items-center mb-3"
|
|
6
6
|
>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
<template v-if="pill && pillLabel">
|
|
8
|
+
<BaseTag
|
|
9
|
+
:variant="pillColor"
|
|
10
|
+
size="lg"
|
|
11
|
+
class="mr-3"
|
|
12
|
+
>
|
|
13
|
+
{{ pillLabel }}
|
|
14
|
+
</BaseTag>
|
|
15
|
+
</template>
|
|
16
|
+
<template v-else>
|
|
17
|
+
<div
|
|
18
|
+
v-if="topics && topics.length"
|
|
19
|
+
class="inline"
|
|
16
20
|
>
|
|
17
|
-
<span :itemprop="schema ? 'articleSection' : undefined">
|
|
18
|
-
{{ topics[0].title }}
|
|
19
|
-
</span>
|
|
20
|
-
</BaseLink>
|
|
21
|
-
</div>
|
|
22
|
-
<span
|
|
23
|
-
v-else-if="label"
|
|
24
|
-
class="text-subtitle py-3 edu:text-primary"
|
|
25
|
-
>
|
|
26
|
-
<template v-if="!labelLink">
|
|
27
|
-
{{ label }}
|
|
28
|
-
</template>
|
|
29
|
-
<template v-else>
|
|
30
21
|
<BaseLink
|
|
31
22
|
variant="secondary"
|
|
32
|
-
:to="
|
|
23
|
+
:to="topics[0].url"
|
|
33
24
|
class="py-3"
|
|
34
25
|
:use-primary-color="themeStore.theme === 'ThemeEdu'"
|
|
35
26
|
>
|
|
36
|
-
|
|
27
|
+
<span :itemprop="schema ? 'articleSection' : undefined">
|
|
28
|
+
{{ topics[0].title }}
|
|
29
|
+
</span>
|
|
37
30
|
</BaseLink>
|
|
38
|
-
</
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
</div>
|
|
32
|
+
<span
|
|
33
|
+
v-else-if="label"
|
|
34
|
+
class="text-subtitle py-3 edu:text-primary"
|
|
35
|
+
>
|
|
36
|
+
<template v-if="!labelLink">
|
|
37
|
+
{{ label }}
|
|
38
|
+
</template>
|
|
39
|
+
<template v-else>
|
|
40
|
+
<BaseLink
|
|
41
|
+
variant="secondary"
|
|
42
|
+
:to="labelLink"
|
|
43
|
+
class="py-3"
|
|
44
|
+
:use-primary-color="themeStore.theme === 'ThemeEdu'"
|
|
45
|
+
>
|
|
46
|
+
{{ label }}
|
|
47
|
+
</BaseLink>
|
|
48
|
+
</template>
|
|
49
|
+
</span>
|
|
50
|
+
</template>
|
|
51
|
+
<span
|
|
52
|
+
v-if="hasTag"
|
|
53
|
+
class="sr-only"
|
|
54
|
+
>.</span
|
|
55
|
+
>
|
|
41
56
|
<span
|
|
42
|
-
:class="`${
|
|
57
|
+
:class="`${hasTag && !pill ? 'divide-gray-mid-dark border-l ml-3 pl-3 ' : ''} my-4 text-gray-mid-dark uppercase text-sm lg:text-base leading-none`"
|
|
43
58
|
>
|
|
44
59
|
{{ readTime }}
|
|
45
60
|
</span>
|
|
@@ -51,7 +66,7 @@
|
|
|
51
66
|
>{{ title }}
|
|
52
67
|
</BaseHeading>
|
|
53
68
|
<div
|
|
54
|
-
v-if="
|
|
69
|
+
v-if="hasByline"
|
|
55
70
|
class="lg:text-base text-gray-mid-dark divide-gray-mid-dark px-3 mt-5 -ml-3 text-sm leading-none"
|
|
56
71
|
>
|
|
57
72
|
<span
|
|
@@ -107,12 +122,16 @@ import { useThemeStore } from '../../store/theme'
|
|
|
107
122
|
import type { Topic, AuthorObject } from './../../interfaces'
|
|
108
123
|
import BaseLink from './../BaseLink/BaseLink.vue'
|
|
109
124
|
import BaseHeading from './../BaseHeading/BaseHeading.vue'
|
|
125
|
+
import BaseTag from '../BaseTag/BaseTag.vue'
|
|
126
|
+
|
|
127
|
+
export const pillColorVariants = ['primary', 'secondary', 'action']
|
|
110
128
|
|
|
111
129
|
export default defineComponent({
|
|
112
130
|
name: 'DetailHeadline',
|
|
113
131
|
components: {
|
|
114
132
|
BaseLink,
|
|
115
|
-
BaseHeading
|
|
133
|
+
BaseHeading,
|
|
134
|
+
BaseTag
|
|
116
135
|
},
|
|
117
136
|
props: {
|
|
118
137
|
title: {
|
|
@@ -156,6 +175,15 @@ export default defineComponent({
|
|
|
156
175
|
required: false,
|
|
157
176
|
default: undefined
|
|
158
177
|
},
|
|
178
|
+
pill: {
|
|
179
|
+
type: Boolean,
|
|
180
|
+
default: false
|
|
181
|
+
},
|
|
182
|
+
pillColor: {
|
|
183
|
+
type: String,
|
|
184
|
+
default: 'primary',
|
|
185
|
+
validator: (prop: string): boolean => pillColorVariants.includes(prop)
|
|
186
|
+
},
|
|
159
187
|
schema: {
|
|
160
188
|
type: Boolean,
|
|
161
189
|
default: false
|
|
@@ -163,6 +191,18 @@ export default defineComponent({
|
|
|
163
191
|
},
|
|
164
192
|
computed: {
|
|
165
193
|
...mapStores(useThemeStore),
|
|
194
|
+
hasTag(): boolean {
|
|
195
|
+
return this.topics?.length || this.label ? true : false
|
|
196
|
+
},
|
|
197
|
+
hasEyebrow(): boolean {
|
|
198
|
+
return this.hasTag || this.readTime ? true : false
|
|
199
|
+
},
|
|
200
|
+
hasByline(): boolean {
|
|
201
|
+
return this.authors?.length || this.publicationDate ? true : false
|
|
202
|
+
},
|
|
203
|
+
hasData(): boolean {
|
|
204
|
+
return this.title || this.hasEyebrow || this.hasByline ? true : false
|
|
205
|
+
},
|
|
166
206
|
pubDatetime(): string | undefined {
|
|
167
207
|
const currentTime = this.publicationTime || '00:00:00'
|
|
168
208
|
const returnDate = new Date(this.publicationDate + ' ' + currentTime)
|
|
@@ -181,6 +221,9 @@ export default defineComponent({
|
|
|
181
221
|
authors = [this.author] as AuthorObject[]
|
|
182
222
|
}
|
|
183
223
|
return authors
|
|
224
|
+
},
|
|
225
|
+
pillLabel(): string | undefined {
|
|
226
|
+
return this.label ? this.label : this.topics?.length ? this.topics[0].title : undefined
|
|
184
227
|
}
|
|
185
228
|
}
|
|
186
229
|
})
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
<script lang="ts">
|
|
39
39
|
import { defineComponent } from 'vue'
|
|
40
|
+
import { eventBus } from './../../utils/eventBus'
|
|
40
41
|
import { mapStores } from 'pinia'
|
|
41
42
|
import { useHeaderStore } from './../../store/header'
|
|
42
43
|
import NavDropdownToggle from './../NavDropdownToggle/NavDropdownToggle.vue'
|
|
@@ -80,10 +81,7 @@ export default defineComponent({
|
|
|
80
81
|
}
|
|
81
82
|
},
|
|
82
83
|
mounted() {
|
|
83
|
-
|
|
84
|
-
// TODO: find a cleaner way to do this w/o using mounted or root level events
|
|
85
|
-
// scoped slots? https://github.com/vuejs/vue/issues/4332
|
|
86
|
-
// this.$root?.$on('linkClicked', this.closeDropdown)
|
|
84
|
+
eventBus.on('linkClicked', () => this.closeDropdown())
|
|
87
85
|
},
|
|
88
86
|
methods: {
|
|
89
87
|
toggleDropdown() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<button
|
|
3
3
|
class="NavDropdownToggle group cursor-pointer"
|
|
4
|
-
:class="{ '-active': isActivePath }"
|
|
4
|
+
:class="{ '-active': isActivePath, '-invert': invert }"
|
|
5
5
|
:aria-expanded="ariaExpanded"
|
|
6
6
|
@click="clickEvent()"
|
|
7
7
|
@keydown.esc="escEvent()"
|
|
@@ -26,13 +26,18 @@ export default defineComponent({
|
|
|
26
26
|
props: {
|
|
27
27
|
path: {
|
|
28
28
|
type: String,
|
|
29
|
-
|
|
29
|
+
default: undefined
|
|
30
30
|
},
|
|
31
31
|
ariaExpanded: {
|
|
32
32
|
type: Boolean,
|
|
33
|
-
|
|
33
|
+
default: false
|
|
34
|
+
},
|
|
35
|
+
invert: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false
|
|
34
38
|
}
|
|
35
39
|
},
|
|
40
|
+
emits: ['closeDropdown', 'toggleClicked'],
|
|
36
41
|
computed: {
|
|
37
42
|
isActivePath() {
|
|
38
43
|
if (this.path) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import NavJumpMenu from './NavJumpMenu.vue'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Navigation/Jump Menu',
|
|
5
|
+
component: NavJumpMenu,
|
|
6
|
+
tags: ['!autodocs'],
|
|
7
|
+
excludeStories: /.*Data$/
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const JumpLinksData = [
|
|
11
|
+
{
|
|
12
|
+
title: 'Heading title',
|
|
13
|
+
path: '#heading_title'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
title: 'Heading title',
|
|
17
|
+
path: '#heading_title'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
title: 'Heading title',
|
|
21
|
+
path: '#heading_title'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
title: 'Heading title',
|
|
25
|
+
path: '#heading_title'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: 'Heading title',
|
|
29
|
+
path: '#heading_title'
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
export const BaseStory = {
|
|
33
|
+
name: 'Jump Menu',
|
|
34
|
+
args: {
|
|
35
|
+
title: 'Page Title',
|
|
36
|
+
jumpLinks: JumpLinksData,
|
|
37
|
+
invert: true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const Light = {
|
|
42
|
+
args: {
|
|
43
|
+
title: 'Page Title',
|
|
44
|
+
jumpLinks: JumpLinksData,
|
|
45
|
+
invert: false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NavSecondary
|
|
3
|
+
v-if="enabled"
|
|
4
|
+
id="JumpMenuTop"
|
|
5
|
+
ref="NavJumpMenuRef"
|
|
6
|
+
class="NavJumpMenu -hide-until-threshold"
|
|
7
|
+
:invert="invert"
|
|
8
|
+
>
|
|
9
|
+
<template v-for="(item, index) in theBreadcrumbs">
|
|
10
|
+
<template v-if="item.children && item.children.length > 0">
|
|
11
|
+
<NavSecondaryDropdown
|
|
12
|
+
:key="index"
|
|
13
|
+
:item="item"
|
|
14
|
+
:index="index"
|
|
15
|
+
:is-last="theBreadcrumbs && index === theBreadcrumbs.length - 1"
|
|
16
|
+
:invert="invert"
|
|
17
|
+
>
|
|
18
|
+
<NavJumpMenuContent
|
|
19
|
+
:key="index"
|
|
20
|
+
:item="item"
|
|
21
|
+
/>
|
|
22
|
+
</NavSecondaryDropdown>
|
|
23
|
+
</template>
|
|
24
|
+
<template v-else>
|
|
25
|
+
<NavSecondaryLink
|
|
26
|
+
:key="index"
|
|
27
|
+
:item="item"
|
|
28
|
+
:index="index"
|
|
29
|
+
:invert="invert"
|
|
30
|
+
/>
|
|
31
|
+
</template>
|
|
32
|
+
</template>
|
|
33
|
+
</NavSecondary>
|
|
34
|
+
</template>
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { computed, defineExpose, ref, onMounted, watch } from 'vue'
|
|
37
|
+
import { mixinUpdateSecondary } from './../../utils/mixins'
|
|
38
|
+
import { useRoute } from 'vue-router'
|
|
39
|
+
import NavSecondary from './../NavSecondary/NavSecondary.vue'
|
|
40
|
+
import NavSecondaryDropdown from './../NavSecondary/NavSecondaryDropdown.vue'
|
|
41
|
+
import NavSecondaryLink from './../NavSecondary/NavSecondaryLink.vue'
|
|
42
|
+
import NavJumpMenuContent from './../NavJumpMenu/NavJumpMenuContent.vue'
|
|
43
|
+
import type { BlockData, BreadcrumbPathObject } from './../../interfaces'
|
|
44
|
+
import { getHeadingId } from '../../utils/getHeadingId'
|
|
45
|
+
|
|
46
|
+
interface NavJumpMenuProps {
|
|
47
|
+
title?: string
|
|
48
|
+
jumpLinks?: BreadcrumbPathObject[]
|
|
49
|
+
blocks?: BlockData[]
|
|
50
|
+
headingLevel?: string
|
|
51
|
+
invert?: boolean
|
|
52
|
+
enabled?: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<NavJumpMenuProps>(), {
|
|
56
|
+
title: undefined,
|
|
57
|
+
jumpLinks: undefined,
|
|
58
|
+
blocks: undefined,
|
|
59
|
+
headingLevel: 'h2',
|
|
60
|
+
enabled: true,
|
|
61
|
+
invert: true,
|
|
62
|
+
hidden: false
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const NavJumpMenuRef = ref()
|
|
66
|
+
|
|
67
|
+
const theJumpLinks = computed(() => {
|
|
68
|
+
if (props.jumpLinks) {
|
|
69
|
+
return props.jumpLinks
|
|
70
|
+
} else if (props.blocks) {
|
|
71
|
+
const indexedBlocks = props.blocks.map((b, index) => {
|
|
72
|
+
return {
|
|
73
|
+
...b,
|
|
74
|
+
index: index
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
const filteredBlocks = indexedBlocks.filter((b) => {
|
|
78
|
+
return b.blockType === 'HeadingBlock' && b.level === props.headingLevel
|
|
79
|
+
})
|
|
80
|
+
// map to the correct data shape
|
|
81
|
+
const links: BreadcrumbPathObject[] = filteredBlocks.map((l) => {
|
|
82
|
+
return {
|
|
83
|
+
// @ts-expect-error using parameter that was added to BlockData
|
|
84
|
+
path: '#' + getHeadingId(l.heading, l.index),
|
|
85
|
+
title: l.heading
|
|
86
|
+
} as BreadcrumbPathObject
|
|
87
|
+
})
|
|
88
|
+
return links
|
|
89
|
+
}
|
|
90
|
+
return []
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const theBreadcrumbs = computed(() => {
|
|
94
|
+
let breadcrumb = undefined
|
|
95
|
+
const rootItem = props.title
|
|
96
|
+
? {
|
|
97
|
+
title: props.title,
|
|
98
|
+
path: '#JumpMenuTop'
|
|
99
|
+
}
|
|
100
|
+
: {
|
|
101
|
+
title: 'Back to top',
|
|
102
|
+
path: '#JumpMenuTop'
|
|
103
|
+
}
|
|
104
|
+
const jumpMenu: BreadcrumbPathObject = {
|
|
105
|
+
title: 'Jump to…',
|
|
106
|
+
path: '#',
|
|
107
|
+
children: theJumpLinks.value as BreadcrumbPathObject[]
|
|
108
|
+
}
|
|
109
|
+
if (theJumpLinks.value) {
|
|
110
|
+
breadcrumb = [rootItem, jumpMenu]
|
|
111
|
+
}
|
|
112
|
+
return breadcrumb as BreadcrumbPathObject[] | undefined
|
|
113
|
+
})
|
|
114
|
+
defineExpose({
|
|
115
|
+
NavJumpMenuRef
|
|
116
|
+
})
|
|
117
|
+
onMounted(() => {
|
|
118
|
+
mixinUpdateSecondary(theBreadcrumbs.value, true)
|
|
119
|
+
})
|
|
120
|
+
const route = useRoute()
|
|
121
|
+
|
|
122
|
+
// repopulate the store with the jump links since the store is cleared on route changes
|
|
123
|
+
watch(
|
|
124
|
+
route,
|
|
125
|
+
() => {
|
|
126
|
+
mixinUpdateSecondary(theBreadcrumbs.value, true)
|
|
127
|
+
}
|
|
128
|
+
// { flush: 'pre', immediate: true, deep: true }
|
|
129
|
+
)
|
|
130
|
+
</script>
|
|
131
|
+
<style lang="scss">
|
|
132
|
+
.NavJumpMenu {
|
|
133
|
+
&.-hide-until-threshold {
|
|
134
|
+
@apply opacity-0 h-0 transition-none overflow-visible;
|
|
135
|
+
&.-is-sticky,
|
|
136
|
+
&.-is-sticky-offset {
|
|
137
|
+
@apply opacity-100 transition-opacity;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ul class="NavJumpMenuContent bg-white">
|
|
3
|
+
<li
|
|
4
|
+
v-for="(child, index) in item.children"
|
|
5
|
+
:key="index"
|
|
6
|
+
class="text-base border-b border-gray-light"
|
|
7
|
+
>
|
|
8
|
+
<BaseLink
|
|
9
|
+
:href="child.path"
|
|
10
|
+
variant="none"
|
|
11
|
+
:link-class="linkClass(child)"
|
|
12
|
+
>
|
|
13
|
+
<span>
|
|
14
|
+
{{ child.title }}
|
|
15
|
+
</span>
|
|
16
|
+
</BaseLink>
|
|
17
|
+
</li>
|
|
18
|
+
</ul>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { defineComponent } from 'vue'
|
|
23
|
+
import BaseLink from './../BaseLink/BaseLink.vue'
|
|
24
|
+
|
|
25
|
+
interface NavItemObject {
|
|
26
|
+
path: String
|
|
27
|
+
title: String
|
|
28
|
+
children: [Object]
|
|
29
|
+
}
|
|
30
|
+
export default defineComponent({
|
|
31
|
+
name: 'NavJumpMenuContent',
|
|
32
|
+
components: {
|
|
33
|
+
BaseLink
|
|
34
|
+
},
|
|
35
|
+
props: {
|
|
36
|
+
// the tertiary nav item object that includes path, title, and children
|
|
37
|
+
item: {
|
|
38
|
+
type: Object,
|
|
39
|
+
required: true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
methods: {
|
|
43
|
+
linkClass(item?: NavItemObject) {
|
|
44
|
+
// default
|
|
45
|
+
let computedClass = 'py-2 lg:py-4'
|
|
46
|
+
if (!item) {
|
|
47
|
+
// if first (aka Overview)
|
|
48
|
+
computedClass = 'py-2 lg:pt-4 lg:pb-4'
|
|
49
|
+
} else if (item.children && item.children.length > 0) {
|
|
50
|
+
// if has children
|
|
51
|
+
computedClass = 'pt-2 pb-1 lg:pt-4 lg:pb-2'
|
|
52
|
+
}
|
|
53
|
+
return computedClass
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
</script>
|
|
58
|
+
<style lang="scss">
|
|
59
|
+
.NavJumpMenuContent {
|
|
60
|
+
a {
|
|
61
|
+
@apply block pl-18 pr-6 lg:pl-6 text-gray-dark;
|
|
62
|
+
|
|
63
|
+
> span {
|
|
64
|
+
@apply border-b border-transparent pb-2px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&:hover {
|
|
68
|
+
> span {
|
|
69
|
+
@apply border-gray-dark text-gray-dark;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -115,6 +115,7 @@
|
|
|
115
115
|
// @ts-nocheck
|
|
116
116
|
import { defineComponent } from 'vue'
|
|
117
117
|
import { mapStores } from 'pinia'
|
|
118
|
+
import { eventBus } from './../../utils/eventBus'
|
|
118
119
|
import LogoColor from '@explorer-1/common/src/images/svg/logo-tribrand-color.svg'
|
|
119
120
|
import LogoWhite from '@explorer-1/common/src/images/svg/logo-tribrand-white.svg'
|
|
120
121
|
import { useHeaderStore } from './../../store/header'
|
|
@@ -216,10 +217,7 @@ export default defineComponent({
|
|
|
216
217
|
}
|
|
217
218
|
},
|
|
218
219
|
mounted() {
|
|
219
|
-
|
|
220
|
-
// // TODO: find a cleaner way to do this w/o using mounted or root level events
|
|
221
|
-
// // scoped slots? https://github.com/vuejs/vue/issues/4332
|
|
222
|
-
// this.$root?.$on('linkClicked', this.closeMenu)
|
|
220
|
+
eventBus.on('linkClicked', () => this.closeMenu())
|
|
223
221
|
},
|
|
224
222
|
methods: {
|
|
225
223
|
toggleMenu() {
|