@explorer-1/vue 0.2.39 → 0.2.40

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorer-1/vue",
3
- "version": "0.2.39",
3
+ "version": "0.2.40",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -30,7 +30,7 @@
30
30
  "vue-bind-once": "^0.2.1",
31
31
  "vue-observe-visibility": "^1.0.0",
32
32
  "vue3-compare-image": "^1.2.5",
33
- "@explorer-1/common": "1.1.10"
33
+ "@explorer-1/common": "1.1.11"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@vitejs/plugin-vue": "^5.0.4",
@@ -0,0 +1,152 @@
1
+ <template>
2
+ <div class="BaseCarousel w-full overflow-hidden relative">
3
+ <div
4
+ ref="BaseCarousel"
5
+ class="swiper relative"
6
+ >
7
+ <div class="swiper-wrapper">
8
+ <slot />
9
+ </div>
10
+ <div class="swiper-nav lg:block absolute bottom-0 right-0 z-100">
11
+ <div class="relative z-10 flex">
12
+ <BaseButton
13
+ class="swiper-prev xl:text-xl border-collapse"
14
+ aria-label="Previous slide"
15
+ >
16
+ <template #icon>
17
+ <IconPrev />
18
+ </template>
19
+ </BaseButton>
20
+ <BaseButton
21
+ class="swiper-next xl:text-xl border-collapse"
22
+ aria-label="Next slide"
23
+ >
24
+ <template #icon>
25
+ <IconNext />
26
+ </template>
27
+ </BaseButton>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </template>
33
+ <script lang="ts">
34
+ import { defineComponent } from 'vue'
35
+ import Swiper from 'swiper'
36
+ import { A11y, Navigation } from 'swiper/modules'
37
+ import type { SwiperOptions } from 'swiper/types'
38
+ import swiperOptions from '@explorer-1/common/src/js/_swiperOptions'
39
+ import IconPrev from './../Icons/IconPrev.vue'
40
+ import IconNext from './../Icons/IconNext.vue'
41
+ import BaseButton from './../BaseButton/BaseButton.vue'
42
+ const BaseCarouselOptions = swiperOptions.BlockImageCarousel
43
+
44
+ Swiper.use([Navigation, A11y])
45
+
46
+ export default defineComponent({
47
+ name: 'BaseCarousel',
48
+ components: {
49
+ BaseButton,
50
+ IconPrev,
51
+ IconNext
52
+ },
53
+ props: {
54
+ loop: {
55
+ type: Boolean,
56
+ default: false
57
+ },
58
+ itemRole: {
59
+ type: String,
60
+ default: undefined
61
+ }
62
+ },
63
+ data(): {
64
+ currentIndex: number
65
+ slider: Swiper | null
66
+ currentCaption: string
67
+ sliderOptions: SwiperOptions
68
+ } {
69
+ return {
70
+ currentIndex: 0,
71
+ currentCaption: '',
72
+ slider: null,
73
+ sliderOptions: {
74
+ ...BaseCarouselOptions,
75
+ loop: this.loop,
76
+ // this component has custom pagination
77
+ pagination: false,
78
+ a11y: {
79
+ slideRole: this.itemRole as string | undefined
80
+ }
81
+ }
82
+ }
83
+ },
84
+ computed: {},
85
+ watch: {
86
+ slide(value) {
87
+ this.slideTo(value)
88
+ }
89
+ },
90
+ mounted() {
91
+ this.init()
92
+ },
93
+ methods: {
94
+ init() {
95
+ this.slider = new Swiper(this.$refs.BaseCarousel as HTMLElement, this.sliderOptions)
96
+ this.currentIndex = this.slider.realIndex
97
+ },
98
+ updateIndex(val: number) {
99
+ this.currentIndex = val
100
+ },
101
+ slideTo(val: number) {
102
+ if (this.slider) {
103
+ this.slider.slideTo(val)
104
+ }
105
+ }
106
+ }
107
+ })
108
+ </script>
109
+ <style lang="scss">
110
+ @import 'swiper/swiper-bundle.css';
111
+ .BaseCarousel {
112
+ .swiper {
113
+ .swiper-prev {
114
+ @apply mr-px;
115
+ }
116
+
117
+ .swiper-prev,
118
+ .swiper-next {
119
+ &.swiper-button-disabled {
120
+ @apply opacity-75 cursor-default bg-none;
121
+ }
122
+ }
123
+ }
124
+
125
+ .swiper-nav {
126
+ padding-top: 56.25%;
127
+ }
128
+
129
+ // .swiper-dots {
130
+ // padding-top: 56.25%;
131
+ // @apply pointer-events-none;
132
+
133
+ // .swiper-dot {
134
+ // @apply inline-block px-1 py-3 cursor-pointer pointer-events-auto;
135
+
136
+ // &:focus {
137
+ // @apply outline-none ring-1;
138
+ // }
139
+
140
+ // > span {
141
+ // @apply inline-block w-3 h-3 rounded-full bg-gray-light-mid;
142
+ // }
143
+
144
+ // &.swiper-dot-active {
145
+ // > span {
146
+ // @apply bg-primary;
147
+ // }
148
+ // }
149
+ // }
150
+ // }
151
+ }
152
+ </style>
@@ -133,6 +133,7 @@
133
133
  v-else-if="metadataAttrs.type === 'resource'"
134
134
  :resource="theItem as EduResourceCardObject"
135
135
  :variant="metadataAttrs.icons"
136
+ :show-time="true"
136
137
  compact
137
138
  />
138
139
  </div>
@@ -11,26 +11,24 @@ export default {
11
11
 
12
12
  // data
13
13
  export const HeroListingIndexData = {
14
- listingPage: {
15
- featured: {
16
- __typename: 'EDUExplainerArticlePage',
17
- topicLabel: 'Mars',
18
- url: '/news/new-maps-open-roads-to-research/',
19
- title: 'Creating Robots to go Where Humans Can’t',
20
- image: {
21
- src: {
22
- url: 'https://picsum.photos/id/973/1800/1200',
23
- width: 1800,
24
- height: 1200
25
- },
26
- srcSet:
27
- 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
28
- screenMd: {
29
- url: 'https://picsum.photos/id/921/800/640'
30
- },
31
- screenSm: {
32
- url: 'https://picsum.photos/id/247/640/900'
33
- }
14
+ featuredPage: {
15
+ __typename: 'EDUExplainerArticlePage',
16
+ topicLabel: 'Mars',
17
+ url: '/news/new-maps-open-roads-to-research/',
18
+ title: 'Creating Robots to go Where Humans Can’t',
19
+ image: {
20
+ src: {
21
+ url: 'https://picsum.photos/id/973/1800/1200',
22
+ width: 1800,
23
+ height: 1200
24
+ },
25
+ srcSet:
26
+ 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
27
+ screenMd: {
28
+ url: 'https://picsum.photos/id/921/800/640'
29
+ },
30
+ screenSm: {
31
+ url: 'https://picsum.photos/id/247/640/900'
34
32
  }
35
33
  }
36
34
  }
@@ -42,7 +40,7 @@ export const BaseStory = {
42
40
  name: 'HeroListingIndex',
43
41
  args: {
44
42
  customLabel: 'Featured',
45
- pageData: HeroListingIndexData.listingPage
43
+ pageData: HeroListingIndexData.featuredPage
46
44
  }
47
45
  }
48
46
 
@@ -50,31 +48,29 @@ export const NewsImageHero = {
50
48
  args: {
51
49
  customLabel: 'Featured',
52
50
  pageData: {
53
- featured: {
54
- topicLabel: 'Mars',
55
- url: '/news/new-maps-open-roads-to-research/',
56
- title: 'Creating Robots to go Where Humans Can’t',
57
- heroBlocks: [
58
- {
59
- blockType: 'ImageChooserBlock',
60
- listingPageHeroImage: {
61
- src: {
62
- url: 'https://picsum.photos/id/973/1800/1200',
63
- width: 1800,
64
- height: 1200
65
- },
66
- srcSet:
67
- 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
68
- screenMd: {
69
- url: 'https://picsum.photos/id/921/800/640'
70
- },
71
- screenSm: {
72
- url: 'https://picsum.photos/id/247/640/900'
73
- }
51
+ topicLabel: 'Mars',
52
+ url: '/news/new-maps-open-roads-to-research/',
53
+ title: 'Creating Robots to go Where Humans Can’t',
54
+ heroBlocks: [
55
+ {
56
+ blockType: 'ImageChooserBlock',
57
+ listingPageHeroImage: {
58
+ src: {
59
+ url: 'https://picsum.photos/id/973/1800/1200',
60
+ width: 1800,
61
+ height: 1200
62
+ },
63
+ srcSet:
64
+ 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
65
+ screenMd: {
66
+ url: 'https://picsum.photos/id/921/800/640'
67
+ },
68
+ screenSm: {
69
+ url: 'https://picsum.photos/id/247/640/900'
74
70
  }
75
71
  }
76
- ]
77
- }
72
+ }
73
+ ]
78
74
  }
79
75
  }
80
76
  }
@@ -83,20 +79,18 @@ export const NewsVideoHero = {
83
79
  args: {
84
80
  customLabel: 'Featured',
85
81
  pageData: {
86
- featured: {
87
- topicLabel: 'Mars',
88
- url: '/news/new-maps-open-roads-to-research/',
89
- title: 'Creating Robots to go Where Humans Can’t',
90
- heroBlocks: [
91
- {
92
- blockType: 'VideoBlock',
93
- video: {
94
- file: '/videos/NASA-Mars-Helicopter-IngenuityAnimations-7sec.mp4',
95
- fileWebm: '/videos/NASA-Mars-Helicopter-IngenuityAnimations-7sec.webm'
96
- }
82
+ topicLabel: 'Mars',
83
+ url: '/news/new-maps-open-roads-to-research/',
84
+ title: 'Creating Robots to go Where Humans Can’t',
85
+ heroBlocks: [
86
+ {
87
+ blockType: 'VideoBlock',
88
+ video: {
89
+ file: '/videos/NASA-Mars-Helicopter-IngenuityAnimations-7sec.mp4',
90
+ fileWebm: '/videos/NASA-Mars-Helicopter-IngenuityAnimations-7sec.webm'
97
91
  }
98
- ]
99
- }
92
+ }
93
+ ]
100
94
  }
101
95
  }
102
96
  }
@@ -105,52 +99,50 @@ export const NewsCarouselHero = {
105
99
  args: {
106
100
  customLabel: 'Featured',
107
101
  pageData: {
108
- featured: {
109
- topicLabel: 'Mars',
110
- url: '/news/new-maps-open-roads-to-research/',
111
- title: 'Creating Robots to go Where Humans Can’t',
112
- heroBlocks: [
113
- {
114
- blockType: 'CarouselBlock',
115
- blocks: [
116
- {
117
- listingPageHeroImage: {
118
- src: {
119
- url: 'https://picsum.photos/id/973/1800/1200',
120
- width: 1800,
121
- height: 1200
122
- },
123
- srcSet:
124
- 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
125
- screenMd: {
126
- url: 'https://picsum.photos/id/921/800/640'
127
- },
128
- screenSm: {
129
- url: 'https://picsum.photos/id/247/640/900'
130
- }
102
+ topicLabel: 'Mars',
103
+ url: '/news/new-maps-open-roads-to-research/',
104
+ title: 'Creating Robots to go Where Humans Can’t',
105
+ heroBlocks: [
106
+ {
107
+ blockType: 'CarouselBlock',
108
+ blocks: [
109
+ {
110
+ listingPageHeroImage: {
111
+ src: {
112
+ url: 'https://picsum.photos/id/973/1800/1200',
113
+ width: 1800,
114
+ height: 1200
115
+ },
116
+ srcSet:
117
+ 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
118
+ screenMd: {
119
+ url: 'https://picsum.photos/id/921/800/640'
120
+ },
121
+ screenSm: {
122
+ url: 'https://picsum.photos/id/247/640/900'
131
123
  }
132
- },
133
- {
134
- listingPageHeroImage: {
135
- src: {
136
- url: 'https://picsum.photos/id/973/1800/1200',
137
- width: 1800,
138
- height: 1200
139
- },
140
- srcSet:
141
- 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
142
- screenMd: {
143
- url: 'https://picsum.photos/id/921/800/640'
144
- },
145
- screenSm: {
146
- url: 'https://picsum.photos/id/247/640/900'
147
- }
124
+ }
125
+ },
126
+ {
127
+ listingPageHeroImage: {
128
+ src: {
129
+ url: 'https://picsum.photos/id/973/1800/1200',
130
+ width: 1800,
131
+ height: 1200
132
+ },
133
+ srcSet:
134
+ 'https://picsum.photos/id/865/768/548 768w, https://picsum.photos/id/865/1024/684 1024w, https://picsum.photos/id/865/1440/770 1440w, https://picsum.photos/id/865/1800/963 1800w',
135
+ screenMd: {
136
+ url: 'https://picsum.photos/id/921/800/640'
137
+ },
138
+ screenSm: {
139
+ url: 'https://picsum.photos/id/247/640/900'
148
140
  }
149
141
  }
150
- ]
151
- }
152
- ]
153
- }
142
+ }
143
+ ]
144
+ }
145
+ ]
154
146
  }
155
147
  }
156
148
  }
@@ -1,20 +1,18 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="pageData"
4
- class="max-w-screen-3xl mx-auto"
5
- :class="{ '-nav-offset': pageData.featured }"
4
+ class="HeroListingIndex max-w-screen-3xl mx-auto"
5
+ :class="{ '-nav-offset': pageData }"
6
6
  >
7
7
  <HeroMedium
8
- v-if="pageData.featured"
8
+ v-if="pageData"
9
9
  class="md:mb-12 lg:mb-18 mb-10"
10
10
  :custom-pill="themeStore.theme === 'ThemeEdu' && customLabel ? customLabel : undefined"
11
11
  :custom-pill-type="
12
- themeStore.theme === 'ThemeEdu' && pageData.featured?.__typename
13
- ? pageData.featured.__typename
14
- : undefined
12
+ themeStore.theme === 'ThemeEdu' && pageData.__typename ? pageData.__typename : undefined
15
13
  "
16
- :custom-label="themeStore.theme === 'ThemeEdu' ? pageData.featured.topicLabel : customLabel"
17
- :feature="pageData.featured"
14
+ :custom-label="themeStore.theme === 'ThemeEdu' ? pageData.topicLabel : customLabel"
15
+ :feature="pageData"
18
16
  :custom-video="customVideo"
19
17
  :custom-image="customImage"
20
18
  :cta="cta"
@@ -54,17 +52,17 @@ export default defineComponent({
54
52
  ...mapStores(useThemeStore),
55
53
  // parses a hero streamfield block for a video (newsDetailPage model)
56
54
  customVideo(): object | undefined {
57
- if (this.pageData && this.pageData.featured?.heroBlocks?.length > 0) {
58
- if (this.pageData.featured.heroBlocks[0].blockType === 'VideoBlock') {
59
- return this.pageData.featured.heroBlocks[0].video
55
+ if (this.pageData && this.pageData?.heroBlocks?.length > 0) {
56
+ if (this.pageData.heroBlocks[0].blockType === 'VideoBlock') {
57
+ return this.pageData.heroBlocks[0].video
60
58
  }
61
59
  }
62
60
  return undefined
63
61
  },
64
62
  customImage(): object | undefined {
65
63
  // parse hero streamfield block for the first usable image (newsDetailPage model)
66
- if (this.pageData && this.pageData.featured?.heroBlocks?.length > 0) {
67
- const block = this.pageData.featured.heroBlocks[0]
64
+ if (this.pageData?.heroBlocks?.length > 0) {
65
+ const block = this.pageData?.heroBlocks[0]
68
66
  if (block.blockType === 'ImageChooserBlock' || block.blockType === 'HeroImageBlock') {
69
67
  return block.listingPageHeroImage
70
68
  } else if (block.blockType === 'CarouselBlock') {
@@ -75,8 +73,8 @@ export default defineComponent({
75
73
  }
76
74
  }
77
75
  // else use heroImage
78
- else if (this.pageData?.featured?.listingPageHeroImage) {
79
- return this.pageData.featured.listingPageHeroImage
76
+ else if (this.pageData?.listingPageHeroImage) {
77
+ return this.pageData.listingPageHeroImage
80
78
  }
81
79
  return undefined
82
80
  }
@@ -74,7 +74,7 @@
74
74
  <p class="text-h3 font-semibold mb-0">
75
75
  <span class="mr-2">{{ feature.title }}</span>
76
76
  <span
77
- class="text-jpl-red-light lg:hidden can-hover:group-hover:ml-2 ml-0 text-4xl transition-all duration-200 ease-in"
77
+ class="text-action-light lg:hidden can-hover:group-hover:ml-2 ml-0 text-4xl transition-all duration-200 ease-in"
78
78
  >
79
79
  <IconArrow class="inline" />
80
80
  </span>
@@ -11,12 +11,14 @@ interface MetadataEduResourceProps {
11
11
  resource: EduResourceCardObject
12
12
  compact?: boolean
13
13
  variant?: string
14
+ showTime: boolean
14
15
  }
15
16
 
16
17
  // define props
17
18
  const props = withDefaults(defineProps<MetadataEduResourceProps>(), {
18
19
  resource: undefined,
19
20
  compact: false,
21
+ showTime: false,
20
22
  variant: 'primary'
21
23
  })
22
24
 
@@ -30,7 +32,11 @@ const audience = computed(() => {
30
32
  return rangeifyGrades(props.resource?.gradeLevels)
31
33
  })
32
34
  const time = computed(() => {
33
- return props.resource?.time?.time
35
+ let time = props.resource?.time?.time
36
+ if (time && props.compact) {
37
+ time = time.replace('Under ', '<')
38
+ }
39
+ return time
34
40
  })
35
41
  </script>
36
42
  <template>
@@ -61,7 +67,7 @@ const time = computed(() => {
61
67
  <span>{{ audience }}</span>
62
68
  </div>
63
69
  <div
64
- v-if="time && !compact"
70
+ v-if="time && showTime"
65
71
  class="MetadataEduResourceItem"
66
72
  >
67
73
  <IconTime
@@ -20,6 +20,7 @@ export const MetadataEventData = {
20
20
  endTime: '22:00:00',
21
21
  ongoing: false,
22
22
  isVirtualEvent: false,
23
+ isInPersonEvent: true,
23
24
  locationName: 'Webcast',
24
25
  locationLink: 'https://www.nasa.gov',
25
26
  customDate: undefined
@@ -52,8 +52,24 @@ const displayTime = computed((): string => {
52
52
  }
53
53
  return ''
54
54
  })
55
- const locationName = computed(() => {
56
- return props.event?.locationName || props.event?.location
55
+ const location = computed(() => {
56
+ if (props.event?.location) {
57
+ return props.event?.location
58
+ } else if (props.compact) {
59
+ let text = 'Hybrid'
60
+ let virtual = props.event.isVirtualEvent
61
+ let inPerson = props.event.isInPersonEvent
62
+ if (props.event?.isVirtualEvent && props) {
63
+ if (virtual && !inPerson) {
64
+ text = 'Online'
65
+ } else if (!virtual && inPerson) {
66
+ text = 'In-person'
67
+ }
68
+ }
69
+ return text
70
+ } else {
71
+ return props.event?.locationName
72
+ }
57
73
  })
58
74
  </script>
59
75
  <template>
@@ -96,7 +112,7 @@ const locationName = computed(() => {
96
112
  />
97
113
  <meta
98
114
  itemprop="name"
99
- :content="locationName"
115
+ :content="location"
100
116
  />
101
117
  <IconLocation class="MetadataEventIcon text-[1.1em]" />
102
118
  <BaseLink
@@ -105,18 +121,18 @@ const locationName = computed(() => {
105
121
  :href="props.event.locationLink"
106
122
  external-target-blank
107
123
  >
108
- {{ locationName }}
124
+ {{ location }}
109
125
  </BaseLink>
110
126
  </div>
111
127
  <!-- Normal location -->
112
128
  <div
113
- v-else-if="locationName"
129
+ v-else-if="location"
114
130
  class="MetadataEventItem"
115
131
  >
116
132
  <meta
117
133
  v-if="!props.compact"
118
134
  itemprop="location"
119
- :content="locationName"
135
+ :content="location"
120
136
  />
121
137
  <IconLocation class="MetadataEventIcon text-[1.2em]" />
122
138
  <BaseLink
@@ -126,9 +142,9 @@ const locationName = computed(() => {
126
142
  :href="props.event.locationLink"
127
143
  external-target-blank
128
144
  >
129
- {{ locationName }}
145
+ {{ location }}
130
146
  </BaseLink>
131
- <span v-else>{{ locationName }}</span>
147
+ <span v-else>{{ location }}</span>
132
148
  </div>
133
149
  </template>
134
150
  </div>
@@ -250,9 +250,9 @@ export default defineComponent({
250
250
  // key into the breadcrumbs for each section
251
251
  const sectionLinks = this.breadcrumb.menu_links[urlKey]
252
252
  // check if any of the paths contained in the array are active
253
- const isActive = sectionLinks.some((link: BreadcrumbPathObject) =>
254
- mixinIsActivePath(link.path)
255
- )
253
+ const isActive = sectionLinks?.length
254
+ ? sectionLinks.some((link: BreadcrumbPathObject) => mixinIsActivePath(link.path))
255
+ : undefined
256
256
  if (isActive) {
257
257
  mixinUpdateGlobalChildren(sectionLinks)
258
258
  }
@@ -265,7 +265,9 @@ export default defineComponent({
265
265
  // get the more menu array
266
266
  const arr = this.breadcrumb.more
267
267
  // check if array contains current path
268
- const isActive = arr.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
268
+ const isActive = arr?.length
269
+ ? arr.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
270
+ : undefined
269
271
  if (isActive) {
270
272
  // clear the secondary nav store when visiting a breadcrumb page
271
273
  // ensures blank secondary nav unless explicitly set via content page "Promote" settings
@@ -254,7 +254,9 @@ export default defineComponent({
254
254
  // key into the breadcrumbs for each section
255
255
  const objArray = this.breadcrumb.menu_links[urlKey]
256
256
  // check if any of the paths contained in the array are active
257
- const isActive = objArray.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
257
+ const isActive = objArray?.length
258
+ ? objArray.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
259
+ : undefined
258
260
  if (isActive) {
259
261
  mixinUpdateGlobalChildren(this.breadcrumb.menu_links[urlKey])
260
262
  }
@@ -267,7 +269,9 @@ export default defineComponent({
267
269
  // get the more menu array
268
270
  const arr = this.breadcrumb.more
269
271
  // check if array contains current path
270
- const isActive = arr.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
272
+ const isActive = arr?.length
273
+ ? arr.some((el: BreadcrumbPathObject) => mixinIsActivePath(el.path))
274
+ : undefined
271
275
  if (isActive) {
272
276
  // clear the secondary nav store when visiting a breadcrumb page
273
277
  // ensures blank secondary nav unless explicitly set via content page "Promote" settings
@@ -300,7 +304,7 @@ export default defineComponent({
300
304
  </script>
301
305
  <style lang="scss">
302
306
  .NavDesktopEdu {
303
- @apply border-b border-transparent;
307
+ @apply border-none;
304
308
 
305
309
  > .header-bg {
306
310
  @apply bg-gradient-to-r from-black to-primary bg-transparent to-90%;
@@ -280,9 +280,11 @@ export default defineComponent({
280
280
  // key into the breadcrumbs for each section
281
281
  const objArray = this.breadcrumb.menu_links[urlKey]
282
282
  // check if any of the paths contained in the array are active
283
- return objArray.some((el: BreadcrumbPathObject) => {
284
- return mixinIsActivePath(el.path)
285
- })
283
+ return objArray?.length
284
+ ? objArray.some((el: BreadcrumbPathObject) => {
285
+ return mixinIsActivePath(el.path)
286
+ })
287
+ : undefined
286
288
  }
287
289
  return false
288
290
  }
@@ -42,9 +42,11 @@ export default defineComponent({
42
42
  },
43
43
  computed: {
44
44
  hasContent() {
45
- return this.contacts.some(
46
- (c) => c.contact.name || c.contact.address || c.contact.phone || c.contact.email
47
- )
45
+ return this.contacts?.length
46
+ ? this.contacts.some(
47
+ (c) => c.contact.name || c.contact.address || c.contact.phone || c.contact.email
48
+ )
49
+ : undefined
48
50
  }
49
51
  },
50
52
  methods: {
@@ -13,32 +13,34 @@
13
13
  <legend class="md:mb-3 text-body-md mb-2 font-bold leading-normal tracking-wide">
14
14
  {{ groupTitle }}
15
15
  </legend>
16
- <div
17
- v-for="(bucket, index) in buckets"
18
- :key="bucket.key"
19
- ref="buckets"
20
- class="form-group form-check"
21
- >
22
- <!-- correct for zero based index -->
16
+ <div class="buckets">
23
17
  <div
24
- v-if="!truncateFilters || index <= checkbox.checkboxLimit - 1"
25
- class="flex my-2"
18
+ v-for="(bucket, index) in buckets"
19
+ :key="bucket.key"
20
+ ref="buckets"
21
+ class="form-group form-check"
26
22
  >
27
- <input
28
- :id="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
29
- v-model="filterByHandler"
30
- type="checkbox"
31
- :value="bucket.key_as_string ? bucket.key_as_string : bucket.key"
32
- class="text-primary focus:ring-2 focus:ring-primary flex-shrink-0 w-5 h-5 mt-px mr-1 align-middle border rounded-none"
33
- />
34
- <!-- 'key_as_string' exists for dates to have a human readable version -->
35
- <label
36
- :for="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
37
- class="form-check-label pl-2 tracking-normal align-middle"
23
+ <!-- correct for zero based index -->
24
+ <div
25
+ v-if="!truncateFilters || index <= checkbox.checkboxLimit - 1"
26
+ class="flex my-2"
38
27
  >
39
- {{ prettyFilterNames(bucket.key_as_string ? bucket.key_as_string : bucket.key) }}
40
- <span class="text-gray-mid-dark"> ({{ bucket.doc_count.toLocaleString() }}) </span>
41
- </label>
28
+ <input
29
+ :id="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
30
+ v-model="filterByHandler"
31
+ type="checkbox"
32
+ :value="bucket.key_as_string ? bucket.key_as_string : bucket.key"
33
+ class="text-primary focus:ring-2 focus:ring-primary flex-shrink-0 w-5 h-5 mt-px mr-1 align-middle border rounded-none"
34
+ />
35
+ <!-- 'key_as_string' exists for dates to have a human readable version -->
36
+ <label
37
+ :for="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
38
+ class="form-check-label pl-2 tracking-normal align-middle"
39
+ >
40
+ {{ prettyFilterNames(bucket.key_as_string ? bucket.key_as_string : bucket.key) }}
41
+ <span class="text-gray-mid-dark"> ({{ bucket.doc_count.toLocaleString() }}) </span>
42
+ </label>
43
+ </div>
42
44
  </div>
43
45
  </div>
44
46
  <!--
@@ -49,7 +51,7 @@
49
51
 
50
52
  <div v-show="truncateFilters && bucketsLength > checkbox.initialLimit">
51
53
  <button
52
- class="can-hover:hover:underline text-primary mt-2"
54
+ class="can-hover:hover:underline text-action mt-2"
53
55
  :aria-expanded="!checkbox.showMore ? 'true' : 'false'"
54
56
  aria-haspopup="true"
55
57
  :aria-controls="`filterGroup_${groupKey}`"
@@ -21,7 +21,10 @@
21
21
  customDate,
22
22
  location,
23
23
  eventType,
24
- ongoing
24
+ ongoing,
25
+ primarySubject,
26
+ gradeLevels,
27
+ time
25
28
  }
26
29
  }"
27
30
  show-calendar-chip
@@ -211,6 +214,7 @@
211
214
  import type { PropType } from 'vue'
212
215
  import { defineComponent } from 'vue'
213
216
  import { mapStores } from 'pinia'
217
+ import type { PrimarySubjectObject, GradeLevelsObject, EduResourcesTime } from './../../interfaces'
214
218
  import { useThemeStore } from '../../store/theme'
215
219
  import PodcastEpisodeCard from './../PodcastEpisodeCard/PodcastEpisodeCard.vue'
216
220
  import BaseLink from './../BaseLink/BaseLink.vue'
@@ -338,6 +342,18 @@ export default defineComponent({
338
342
  ongoing: {
339
343
  type: Boolean,
340
344
  default: false
345
+ },
346
+ primarySubject: {
347
+ type: Object as PropType<PrimarySubjectObject>,
348
+ default: undefined
349
+ },
350
+ gradeLevels: {
351
+ type: Array as PropType<GradeLevelsObject[]>,
352
+ default: undefined
353
+ },
354
+ time: {
355
+ type: Object as PropType<EduResourcesTime>,
356
+ default: undefined
341
357
  }
342
358
  },
343
359
  computed: {
@@ -18,7 +18,10 @@
18
18
  customDate,
19
19
  location,
20
20
  eventType,
21
- ongoing
21
+ ongoing,
22
+ primarySubject,
23
+ gradeLevels,
24
+ time
22
25
  }
23
26
  }"
24
27
  :heading-level="headingLevel"
@@ -94,6 +97,7 @@
94
97
  import type { PropType } from 'vue'
95
98
  import { defineComponent } from 'vue'
96
99
  import { mapStores } from 'pinia'
100
+ import type { PrimarySubjectObject, GradeLevelsObject, EduResourcesTime } from './../../interfaces'
97
101
  import { useThemeStore } from '../../store/theme'
98
102
  import BaseLink from './../BaseLink/BaseLink.vue'
99
103
  import BaseImage from './../BaseImage/BaseImage.vue'
@@ -181,6 +185,18 @@ export default defineComponent({
181
185
  pageContentType: {
182
186
  type: String,
183
187
  required: false
188
+ },
189
+ primarySubject: {
190
+ type: Object as PropType<PrimarySubjectObject>,
191
+ default: undefined
192
+ },
193
+ gradeLevels: {
194
+ type: Array as PropType<GradeLevelsObject[]>,
195
+ default: undefined
196
+ },
197
+ time: {
198
+ type: Object as PropType<EduResourcesTime>,
199
+ default: undefined
184
200
  }
185
201
  },
186
202
  computed: {
@@ -31,6 +31,9 @@
31
31
  :event-type="page.eventType"
32
32
  :ongoing="page.ongoing"
33
33
  :location="page.location"
34
+ :primary-subject="page.primarySubject as unknown as PrimarySubjectObject"
35
+ :grade-levels="page.gradeLevels as unknown as GradeLevelsObject[]"
36
+ :time="page.time as unknown as EduResourcesTime"
34
37
  :title="page.title"
35
38
  :summary="page.summary"
36
39
  :featured="featureFirstResult ? index === 0 && currentPage === 1 : false"
@@ -53,6 +56,9 @@
53
56
  :event-type="page.eventType"
54
57
  :ongoing="page.ongoing"
55
58
  :location="page.location"
59
+ :primary-subject="page.primarySubject as unknown as PrimarySubjectObject"
60
+ :grade-levels="page.gradeLevels as unknown as GradeLevelsObject[]"
61
+ :time="page.time as unknown as EduResourcesTime"
56
62
  :title="page.title"
57
63
  heading-level="h2"
58
64
  />
@@ -64,7 +70,7 @@
64
70
  <script lang="ts">
65
71
  import { defineComponent } from 'vue'
66
72
  import type { ElasticSearchPage } from '../../interfaces'
67
-
73
+ import type { PrimarySubjectObject, GradeLevelsObject, EduResourcesTime } from './../../interfaces'
68
74
  // @ts-ignore
69
75
  import dayjs from 'dayjs'
70
76
  import SearchResultCard from './../SearchResultCard/SearchResultCard.vue'
@@ -131,6 +137,9 @@ export default defineComponent({
131
137
  // date field is different for mission and event detail pages
132
138
  let date
133
139
  let location
140
+ let primarySubject
141
+ let gradeLevels
142
+ let time
134
143
  let topic =
135
144
  handle === 'missions_mission'
136
145
  ? page._source[handle + '__status_filter']
@@ -152,6 +161,17 @@ export default defineComponent({
152
161
  } else if (handle === 'profiles_profilepage') {
153
162
  topic = page._source[handle + '__go_site_name']
154
163
  date = null
164
+ } else if (handle.startsWith('edu_resources')) {
165
+ date = null
166
+ primarySubject = page._source[handle + '__primary_subject'] as PrimarySubjectObject
167
+ if (page._source[handle + '__grade_levels']) {
168
+ gradeLevels = [] as GradeLevelsObject[]
169
+ // @ts-expect-error
170
+ page._source[handle + '__grade_levels'].forEach((level) => {
171
+ gradeLevels.push({ gradeLevel: level.grade_level })
172
+ })
173
+ }
174
+ time = { time: page._source.activity_time_label_filter } as EduResourcesTime
155
175
  } else {
156
176
  date =
157
177
  typeof page._source.publication_date_filter !== 'undefined'
@@ -196,6 +216,10 @@ export default defineComponent({
196
216
  handle === 'edu_events_edueventpage'
197
217
  ? page._source.edu_events_edueventpage__ongoing
198
218
  : undefined
219
+ // edu resources
220
+ page.gradeLevels = gradeLevels
221
+ page.time = time
222
+ page.primarySubject = primarySubject
199
223
  // properties that are different for profiles page
200
224
  page.summary =
201
225
  handle === 'profiles_profilepage'
package/src/interfaces.ts CHANGED
@@ -92,6 +92,9 @@ export interface ElasticSearchPage {
92
92
  summary?: string
93
93
  eventType?: string
94
94
  ongoing?: boolean
95
+ primarySubject?: string
96
+ gradeLevels: string
97
+ time: string
95
98
  }
96
99
 
97
100
  export interface FormOption {
@@ -129,6 +132,7 @@ export interface EventCardObject extends Card {
129
132
  eventType?: string
130
133
  ongoing?: boolean
131
134
  isVirtualEvent?: boolean
135
+ isInPersonEvent?: boolean
132
136
  locationName?: string
133
137
  location?: string
134
138
  locationLink?: string
@@ -209,7 +209,11 @@ export default defineComponent({
209
209
  },
210
210
  created() {
211
211
  const sortByParam = this.$route?.query.sortBy
212
- if (sortByParam && sortByOptions.some((option) => option.value === sortByParam)) {
212
+ if (
213
+ sortByParam &&
214
+ sortByOptions &&
215
+ sortByOptions.some((option) => option.value === sortByParam)
216
+ ) {
213
217
  this.sortBy = sortByParam as SortBy
214
218
  }
215
219
 
@@ -194,12 +194,14 @@ export const mixinIsActivePath = (itemPath: string): Boolean => {
194
194
  */
195
195
  export const mixinGetSrcSet = (srcSetObject: Partial<ImageObject>): string => {
196
196
  let srcSet = ''
197
- const valid = Object.keys(srcSetObject).some(function (key) {
198
- if (key.startsWith('screen')) {
199
- return true
200
- }
201
- return false
202
- })
197
+ const valid = Object.keys(srcSetObject)?.length
198
+ ? Object.keys(srcSetObject).some(function (key) {
199
+ if (key.startsWith('screen')) {
200
+ return true
201
+ }
202
+ return false
203
+ })
204
+ : false
203
205
  if (valid) {
204
206
  const srcSetArray: string[] = []
205
207
  for (const [key, value] of Object.entries(srcSetObject)) {