@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.
Files changed (61) hide show
  1. package/components.d.ts +3 -1
  2. package/dist/explorer-1-vue.js +4677 -4526
  3. package/dist/explorer-1-vue.umd.cjs +12 -12
  4. package/dist/src/components/BlockHeading/BlockHeading.vue.d.ts +25 -2
  5. package/dist/src/components/DetailHeadline/DetailHeadline.stories.d.ts +29 -1
  6. package/dist/src/components/DetailHeadline/DetailHeadline.vue.d.ts +26 -0
  7. package/dist/src/components/NavDesktop/NavDesktop.stories.d.ts +3 -0
  8. package/dist/src/components/NavDesktop/NavDesktop.vue.d.ts +1 -0
  9. package/dist/src/components/NavDesktop/NavDesktopDropdown.vue.d.ts +1 -0
  10. package/dist/src/components/NavDesktopEdu/NavDesktopEdu.stories.d.ts +1 -0
  11. package/dist/src/components/NavDropdownToggle/NavDropdownToggle.vue.d.ts +19 -6
  12. package/dist/src/components/NavJumpMenu/NavJumpMenu.stories.d.ts +31 -0
  13. package/dist/src/components/NavMobile/NavMobile.stories.d.ts +30 -3
  14. package/dist/src/components/NavMobile/NavMobile.vue.d.ts +1 -0
  15. package/dist/src/components/NavMobile/NavMobileDropdown.vue.d.ts +9 -1
  16. package/dist/src/components/NavMobile/NavMobileEdu.stories.d.ts +3 -0
  17. package/dist/src/components/NavMobile/NavMobileSecondaryDropdown.vue.d.ts +7 -0
  18. package/dist/src/components/NavSecondary/NavSecondary.stories.d.ts +8 -0
  19. package/dist/src/components/NavSecondary/NavSecondary.vue.d.ts +23 -1
  20. package/dist/src/components/NavSecondary/NavSecondaryDropdown.vue.d.ts +33 -2
  21. package/dist/src/components/NavSecondary/NavSecondaryDropdownContent.vue.d.ts +11 -1
  22. package/dist/src/components/NavSecondary/NavSecondaryLink.vue.d.ts +21 -3
  23. package/dist/src/interfaces.d.ts +7 -1
  24. package/dist/src/store/header.d.ts +2 -0
  25. package/dist/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.stories.d.ts → PageEduExplainerArticle/PageEduExplainerArticle.stories.d.ts} +3 -2
  26. package/dist/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.d.ts +1004 -6
  27. package/dist/src/utils/eventBus.d.ts +1 -0
  28. package/dist/src/utils/getHeadingId.d.ts +1 -0
  29. package/dist/src/utils/mixins.d.ts +1 -1
  30. package/dist/style.css +1 -1
  31. package/package.json +3 -2
  32. package/src/components/BaseAudio/BaseAudio.vue +3 -4
  33. package/src/components/BaseLink/BaseLink.vue +2 -0
  34. package/src/components/BaseTag/BaseTag.vue +4 -4
  35. package/src/components/BlockHeading/BlockHeading.vue +28 -0
  36. package/src/components/BlockStreamfield/BlockStreamfield.vue +5 -1
  37. package/src/components/DetailHeadline/DetailHeadline.stories.js +27 -2
  38. package/src/components/DetailHeadline/DetailHeadline.vue +76 -33
  39. package/src/components/NavDesktop/NavDesktopDropdown.vue +2 -4
  40. package/src/components/NavDropdownToggle/NavDropdownToggle.vue +8 -3
  41. package/src/components/NavJumpMenu/NavJumpMenu.stories.js +47 -0
  42. package/src/components/NavJumpMenu/NavJumpMenu.vue +141 -0
  43. package/src/components/NavJumpMenu/NavJumpMenuContent.vue +74 -0
  44. package/src/components/NavMobile/NavMobile.vue +2 -4
  45. package/src/components/NavMobile/NavMobileDropdown.vue +8 -4
  46. package/src/components/NavMobile/NavMobileSecondaryDropdown.vue +4 -1
  47. package/src/components/NavSecondary/NavSecondary.stories.js +8 -3
  48. package/src/components/NavSecondary/NavSecondary.vue +26 -6
  49. package/src/components/NavSecondary/NavSecondaryDropdown.vue +52 -17
  50. package/src/components/NavSecondary/NavSecondaryDropdownContent.vue +5 -1
  51. package/src/components/NavSecondary/NavSecondaryLink.vue +38 -11
  52. package/src/interfaces.ts +7 -1
  53. package/src/store/header.ts +6 -1
  54. package/src/templates/edu/PageEduEventDetail/PageEduEventDetail.vue +2 -2
  55. package/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.stories.js → PageEduExplainerArticle/PageEduExplainerArticle.stories.js} +6 -6
  56. package/src/templates/edu/{PageEduResourceArticle/PageEduResourceArticle.vue → PageEduExplainerArticle/PageEduExplainerArticle.vue} +4 -2
  57. package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.js +6 -4
  58. package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.vue +16 -2
  59. package/src/utils/eventBus.ts +3 -0
  60. package/src/utils/getHeadingId.ts +5 -0
  61. 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.4",
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.0"
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: find solution for emitting event from slot
330
- // TODO: find a cleaner way to do this w/o using mounted or root level events
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 uppercase border-t-2 py-1 px-2.5',
13
- md: 'text-base capitalize border-t py-1 px-3',
14
- lg: 'text-lg capitalize border-t py-1 px-3'
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 :data="block" />
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="title || label || topics || publicationDate || author">
2
+ <div v-if="hasData">
3
3
  <div
4
- v-if="label || (topics && topics.length) || readTime"
5
- class="flex flex-wrap items-start mb-3"
4
+ v-if="hasEyebrow"
5
+ class="flex flex-wrap items-center mb-3"
6
6
  >
7
- <div
8
- v-if="topics && topics.length"
9
- class="inline"
10
- >
11
- <BaseLink
12
- variant="secondary"
13
- :to="topics[0].url"
14
- class="py-3"
15
- :use-primary-color="themeStore.theme === 'ThemeEdu'"
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="labelLink"
23
+ :to="topics[0].url"
33
24
  class="py-3"
34
25
  :use-primary-color="themeStore.theme === 'ThemeEdu'"
35
26
  >
36
- {{ label }}
27
+ <span :itemprop="schema ? 'articleSection' : undefined">
28
+ {{ topics[0].title }}
29
+ </span>
37
30
  </BaseLink>
38
- </template>
39
- </span>
40
- <span class="sr-only">.</span>
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="`${(topics && topics.length) || label ? 'divide-gray-mid-dark border-l ml-3 pl-3 ' : ''} my-4 text-gray-mid-dark uppercase text-sm lg:text-base leading-none`"
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="authors?.length || publicationDate"
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
- // TODO: VUE3: find solution for emitting event from slot
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
- required: false
29
+ default: undefined
30
30
  },
31
31
  ariaExpanded: {
32
32
  type: Boolean,
33
- required: false
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
- // // TODO: VUE3: find solution for emitting event from slot
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() {