@explorer-1/vue 0.2.38 → 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.38",
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>
@@ -5,13 +5,13 @@
5
5
  :to="theItem.url ? theItem.url : undefined"
6
6
  :href="theItem.externalLink ? theItem.externalLink : undefined"
7
7
  class="BlockLinkCard group"
8
- :link-class="`block ${small ? 'pb-3' : 'pb-5'} ${large ? 'sm:flex flex-row' : ''}`"
8
+ :link-class="`block ${small ? 'pb-3' : 'pb-5'} ${large ? 'sm:flex flex-row border-b border-gray-light-mid pb-5 mb-5' : ''}`"
9
9
  external-target-blank
10
10
  >
11
11
  <BaseImagePlaceholder
12
12
  :aspect-ratio="large ? '3:2' : '16:9'"
13
- class="bg-gray-dark relative mb-6 edu:lg:mb-8 overflow-hidden"
14
- :class="{ 'lg:mb-10': medium, 'sm:w-1/3': large }"
13
+ class="bg-gray-dark h-full relative overflow-hidden mb-6"
14
+ :class="{ 'lg:mb-10': medium, 'sm:w-1/3 lg:mb-0': large }"
15
15
  dark-mode
16
16
  no-logo
17
17
  >
@@ -97,7 +97,7 @@
97
97
  {{ theItem.title }}
98
98
  </component>
99
99
  <p
100
- v-if="theItem.date"
100
+ v-if="theItem.date && !themeStore.isEdu"
101
101
  class="text-gray-mid-dark mt-2"
102
102
  :class="{ 'mt-2': !large, 'mt-4': large }"
103
103
  >
@@ -106,9 +106,19 @@
106
106
  <p
107
107
  v-if="large && theItem.summary"
108
108
  class="mt-4 text-gray-mid-dark"
109
+ :class="{
110
+ 'line-clamp-2 sm:line-clamp-1 lg:line-clamp-2 xl:line-clamp-3': themeStore.isEdu
111
+ }"
109
112
  >
110
113
  {{ theItem.summary }}
111
114
  </p>
115
+ <p
116
+ v-if="theItem.date && themeStore.isEdu"
117
+ class="text-gray-mid-dark mt-2"
118
+ :class="{ 'mt-2': !large, 'mt-4': large }"
119
+ >
120
+ {{ theItem.date }}
121
+ </p>
112
122
  <div
113
123
  v-if="metadataAttrs"
114
124
  :class="{ 'mt-4': large, 'mt-2 mb-1': medium, 'mt-1 mb-0': small }"
@@ -117,13 +127,13 @@
117
127
  v-if="metadataType === 'EDUEventPage'"
118
128
  :event="theItem"
119
129
  :show-time="large"
120
- :show-location="false"
121
130
  compact
122
131
  />
123
132
  <MetadataEduResource
124
133
  v-else-if="metadataAttrs.type === 'resource'"
125
134
  :resource="theItem as EduResourceCardObject"
126
135
  :variant="metadataAttrs.icons"
136
+ :show-time="true"
127
137
  compact
128
138
  />
129
139
  </div>
@@ -16,8 +16,6 @@ const props = withDefaults(defineProps<BlockLinkCardListProps>(), {
16
16
  <BlockLinkCard
17
17
  v-for="(item, index) in props.items"
18
18
  :key="index"
19
- class="border-b border-gray-light-mid mb-5"
20
- :class="{ 'pt-3': index !== 0 }"
21
19
  :data="item"
22
20
  size="lg"
23
21
  show-calendar-chip
@@ -33,8 +33,11 @@ const splitDate = computed(() => {
33
33
  <div class="text-subtitle">Ongoing</div>
34
34
  </template>
35
35
  <template v-else-if="themeStore.isEdu && splitDate">
36
- <div class="font-extrabold text-6xl leading-tight tracking-wider uppercase">
37
- {{ splitDate.month }}
36
+ <div
37
+ v-if="splitDate.month"
38
+ class="font-extrabold text-6xl leading-tight tracking-wider uppercase"
39
+ >
40
+ {{ splitDate.month.substring(0, 3) }}
38
41
  </div>
39
42
  <div class="text-subtitle">
40
43
  {{ splitDate.year }}
@@ -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>
@@ -2,8 +2,8 @@
2
2
  <svg
3
3
  class="IconCalendar"
4
4
  width="20"
5
- height="19"
6
- viewBox="0 0 20 19"
5
+ height="20"
6
+ viewBox="0 0 20 20"
7
7
  xmlns="http://www.w3.org/2000/svg"
8
8
  aria-hidden="true"
9
9
  focusable="false"
@@ -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}`"
@@ -20,8 +20,11 @@
20
20
  endDate,
21
21
  customDate,
22
22
  location,
23
- eventType: eventType,
24
- ongoing
23
+ eventType,
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: {
@@ -15,12 +15,18 @@
15
15
  startDate,
16
16
  endTime,
17
17
  endDate,
18
+ customDate,
18
19
  location,
19
- eventType: eventType
20
+ eventType,
21
+ ongoing,
22
+ primarySubject,
23
+ gradeLevels,
24
+ time
20
25
  }
21
26
  }"
22
27
  :heading-level="headingLevel"
23
28
  size="sm"
29
+ show-calendar-chip
24
30
  />
25
31
  <BlockLinkCard
26
32
  v-else-if="typename === 'News'"
@@ -91,6 +97,7 @@
91
97
  import type { PropType } from 'vue'
92
98
  import { defineComponent } from 'vue'
93
99
  import { mapStores } from 'pinia'
100
+ import type { PrimarySubjectObject, GradeLevelsObject, EduResourcesTime } from './../../interfaces'
94
101
  import { useThemeStore } from '../../store/theme'
95
102
  import BaseLink from './../BaseLink/BaseLink.vue'
96
103
  import BaseImage from './../BaseImage/BaseImage.vue'
@@ -148,6 +155,11 @@ export default defineComponent({
148
155
  type: String,
149
156
  required: false
150
157
  },
158
+ customDate: {
159
+ type: String,
160
+ required: false,
161
+ default: undefined
162
+ },
151
163
  startTime: {
152
164
  type: String,
153
165
  required: false,
@@ -173,6 +185,18 @@ export default defineComponent({
173
185
  pageContentType: {
174
186
  type: String,
175
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
176
200
  }
177
201
  },
178
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"
@@ -45,10 +48,17 @@
45
48
  :topic="page.topic"
46
49
  :image="page.image"
47
50
  :date="page.date"
51
+ :custom-date="page.customDate"
48
52
  :start-date="page.startDate"
49
53
  :end-date="page.endDate"
50
54
  :start-time="page.startTime"
51
55
  :end-time="page.endTime"
56
+ :event-type="page.eventType"
57
+ :ongoing="page.ongoing"
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"
52
62
  :title="page.title"
53
63
  heading-level="h2"
54
64
  />
@@ -60,7 +70,7 @@
60
70
  <script lang="ts">
61
71
  import { defineComponent } from 'vue'
62
72
  import type { ElasticSearchPage } from '../../interfaces'
63
-
73
+ import type { PrimarySubjectObject, GradeLevelsObject, EduResourcesTime } from './../../interfaces'
64
74
  // @ts-ignore
65
75
  import dayjs from 'dayjs'
66
76
  import SearchResultCard from './../SearchResultCard/SearchResultCard.vue'
@@ -126,6 +136,10 @@ export default defineComponent({
126
136
  : page._source[handle + '__image']
127
137
  // date field is different for mission and event detail pages
128
138
  let date
139
+ let location
140
+ let primarySubject
141
+ let gradeLevels
142
+ let time
129
143
  let topic =
130
144
  handle === 'missions_mission'
131
145
  ? page._source[handle + '__status_filter']
@@ -137,6 +151,7 @@ export default defineComponent({
137
151
  date = 'Event date: ' + parseDate(page._source[handle + '__start_datetime'])
138
152
  } else if (handle === 'edu_events_edueventpage') {
139
153
  date = null
154
+ location = page._source[handle + '__location_filter']
140
155
  } else if (handle === 'missions_mission') {
141
156
  date = page._source.display_date_filter
142
157
  ? 'Launch date: ' + page._source.display_date_filter
@@ -146,6 +161,17 @@ export default defineComponent({
146
161
  } else if (handle === 'profiles_profilepage') {
147
162
  topic = page._source[handle + '__go_site_name']
148
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
149
175
  } else {
150
176
  date =
151
177
  typeof page._source.publication_date_filter !== 'undefined'
@@ -161,9 +187,7 @@ export default defineComponent({
161
187
  page.topic = topic
162
188
  // properties for event's page
163
189
  page.location =
164
- handle === 'events_eventpage' || handle === 'edu_events_edueventpage'
165
- ? page._source[handle + '__location'] | page._source[handle + '__location_name']
166
- : null
190
+ handle === 'events_eventpage' ? page._source[handle + '__location'] : location
167
191
  page.startDate =
168
192
  handle === 'events_eventpage' || handle === 'edu_events_edueventpage'
169
193
  ? page._source[handle + '__start_datetime']
@@ -192,6 +216,10 @@ export default defineComponent({
192
216
  handle === 'edu_events_edueventpage'
193
217
  ? page._source.edu_events_edueventpage__ongoing
194
218
  : undefined
219
+ // edu resources
220
+ page.gradeLevels = gradeLevels
221
+ page.time = time
222
+ page.primarySubject = primarySubject
195
223
  // properties that are different for profiles page
196
224
  page.summary =
197
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)) {
@@ -288,14 +290,16 @@ export const mixinLightboxGalleryItems = (items: object | any): object | false =
288
290
  // return event dates for the red box that appears in the corner of the hero and thumbnail images
289
291
  export const mixinFormatSplitEventDates = (
290
292
  startDatetime: string,
291
- endDatetime?: string
293
+ endDatetime?: string,
294
+ compact?: boolean
292
295
  ): EventDateObject => {
293
296
  const startDateDayjs = dayjs(startDatetime)
294
297
 
298
+ const monthFormat = compact ? 'MM' : 'MMM'
295
299
  let day = startDateDayjs.format('D')
296
- const month = startDateDayjs.format('MMM').replace('.', '')
300
+ const month = startDateDayjs.format(monthFormat).replace('.', '')
297
301
  const year = startDateDayjs.format('YYYY')
298
- const monthAndYear = startDateDayjs.format('MMM YYYY')
302
+ const monthAndYear = startDateDayjs.format(`${monthFormat} YYYY`)
299
303
 
300
304
  if (endDatetime) {
301
305
  const endDateDayjs = dayjs(endDatetime)