@explorer-1/vue 1.1.13 → 1.1.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @explorer-1/vue
2
2
 
3
+ ## 1.1.15
4
+
5
+ ### Patch Changes
6
+
7
+ - de2df9a: Updates to prepare for Explore JPL. Most updates are concerned with improving UX on AttractionCard
8
+ - Updated dependencies [de2df9a]
9
+ - @explorer-1/common@2.1.8
10
+
11
+ ## 1.1.14
12
+
13
+ ### Patch Changes
14
+
15
+ - 215a70a: Fixed audio scrubber in BaseAudio.vue
16
+
3
17
  ## 1.1.13
4
18
 
5
19
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorer-1/vue",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -34,7 +34,7 @@
34
34
  "vue-router": "^5.0.3",
35
35
  "vue3-compare-image": "^1.2.6",
36
36
  "vue3-observe-visibility": "^1.0.6",
37
- "@explorer-1/common": "2.1.7"
37
+ "@explorer-1/common": "2.1.8"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@vitejs/plugin-vue": "^6.0.5",
@@ -43,11 +43,26 @@ const props = defineProps({
43
43
  type: String,
44
44
  default: undefined
45
45
  },
46
+ /**
47
+ * The description for use as card text. Plain text only.
48
+ */
49
+ description: {
50
+ type: String,
51
+ default: undefined
52
+ },
53
+ /**
54
+ * The theme text. Plain text only.
55
+ */
56
+ theme: {
57
+ type: String,
58
+ default: undefined
59
+ },
46
60
  /**
47
61
  * The image for the listing card.
48
62
  */
49
63
  image: {
50
- type: Object
64
+ type: Object,
65
+ default: undefined
51
66
  },
52
67
  /**
53
68
  * Controls the visibility of the Bookmark icon and its associated metadata.
@@ -78,6 +93,12 @@ const props = defineProps({
78
93
  class="border-gray-light-mid lg:pb-10 lg:mb-10 relative grid grid-cols-12 gap-5 pb-6 mb-6 border-b"
79
94
  >
80
95
  <div class="col-span-9">
96
+ <div
97
+ v-if="theme"
98
+ class="text-subtitle-sm md:text-subtitle mb-3 md:mb-5 text-gray-mid-dark"
99
+ >
100
+ {{ props.theme }}
101
+ </div>
81
102
  <BaseHeading
82
103
  v-if="props.title"
83
104
  :level="props.headingLevel"
@@ -91,6 +112,22 @@ const props = defineProps({
91
112
  :show-my-list="props.showMyList"
92
113
  :location-icon="props.locationIcon"
93
114
  />
115
+ <div class="hidden md:block">
116
+ <div
117
+ v-if="props.description"
118
+ class="w-full mt-5 text-body-md"
119
+ >
120
+ {{ props.description }}
121
+ </div>
122
+ <BaseLink
123
+ v-if="props.url"
124
+ class="mt-5"
125
+ variant="primary"
126
+ :href="props.url"
127
+ >
128
+ Learn more
129
+ </BaseLink>
130
+ </div>
94
131
  </div>
95
132
  <div
96
133
  v-if="props.image"
@@ -108,6 +145,22 @@ const props = defineProps({
108
145
  />
109
146
  </BaseImagePlaceholder>
110
147
  </div>
148
+ <div class="col-span-full md:hidden">
149
+ <div
150
+ v-if="props.description"
151
+ class="w-full text-body-md"
152
+ >
153
+ {{ props.description }}
154
+ </div>
155
+ <BaseLink
156
+ v-if="props.url"
157
+ class="mt-5"
158
+ variant="primary"
159
+ :href="props.url"
160
+ >
161
+ Learn more
162
+ </BaseLink>
163
+ </div>
111
164
  </div>
112
165
  </BaseLink>
113
166
  </template>
@@ -215,6 +215,8 @@ export default defineComponent({
215
215
  this.audio.removeEventListener('loadeddata', this._handleLoaded)
216
216
  this.audio.removeEventListener('pause', this._handlePlayPause)
217
217
  this.audio.removeEventListener('play', this._handlePlayPause)
218
+ this.audio.removeEventListener('durationchange', this._handleDurationChange)
219
+ this.audio.removeEventListener('loadedmetadata', this._handleDurationChange)
218
220
  }
219
221
  },
220
222
  methods: {
@@ -259,6 +261,9 @@ export default defineComponent({
259
261
  if (this.playing && !this.paused) {
260
262
  return
261
263
  } else if (this.audio) {
264
+ if (this.totalDuration === 0 && !isNaN(this.audio.duration)) {
265
+ this.totalDuration = this.audio.duration
266
+ }
262
267
  this.paused = false
263
268
  this.audio.play()
264
269
  this.playing = true
@@ -295,7 +300,7 @@ export default defineComponent({
295
300
  if (this.autoPlay) this.play()
296
301
 
297
302
  this.loaded = true
298
- this.totalDuration = this.audio.duration
303
+ this.totalDuration = isNaN(this.audio.duration) ? 0 : this.audio.duration
299
304
  } else {
300
305
  throw new Error('Failed to load audio file')
301
306
  }
@@ -325,6 +330,11 @@ export default defineComponent({
325
330
  _handleEnded() {
326
331
  this.paused = this.playing = false
327
332
  },
333
+ _handleDurationChange() {
334
+ if (this.audio) {
335
+ this.totalDuration = isNaN(this.audio.duration) ? 0 : this.audio.duration
336
+ }
337
+ },
328
338
  init() {
329
339
  if (this.audio) {
330
340
  this.audio.addEventListener('timeupdate', this._handlePlayingUI)
@@ -332,6 +342,13 @@ export default defineComponent({
332
342
  this.audio.addEventListener('pause', this._handlePlayPause)
333
343
  this.audio.addEventListener('play', this._handlePlayPause)
334
344
  this.audio.addEventListener('ended', this._handleEnded)
345
+ this.audio.addEventListener('loadedmetadata', this._handleDurationChange)
346
+ this.audio.addEventListener('durationchange', this._handleDurationChange)
347
+
348
+ // Fallback: metadata already available before calling init() (e.g. cached audio)
349
+ if (this.totalDuration === 0 && !isNaN(this.audio.duration)) {
350
+ this.totalDuration = this.audio.duration
351
+ }
335
352
  }
336
353
  // TODO: VUE3: pass uuID to pauseOthers() method
337
354
  // eventBus.on('play', () => this.pauseOthers())
@@ -1,4 +1,5 @@
1
1
  import MetadataStacked from './MetadataStacked.vue'
2
+ import ExploreJplIconImage from '@explorer-1/common-storybook/src/story-data/images/2026-Explore-JPL-Site-Number-1-Missions-in-Flight.png'
2
3
 
3
4
  export default {
4
5
  title: 'Components/Utilities/MetadataStacked',
@@ -10,7 +11,7 @@ export default {
10
11
  // data
11
12
  export const MetadataStackedData = {
12
13
  location: 'Building 179, Highbay 1',
13
- locationIcon: '',
14
+ locationIcon: ExploreJplIconImage,
14
15
  wait: '45 minute wait',
15
16
  showMyList: true
16
17
  }
@@ -36,15 +36,15 @@ const props = defineProps({
36
36
  </script>
37
37
 
38
38
  <template>
39
- <div class="MetadataStacked inline-flex flex-col flex-start gap-2.5 text-body-md">
39
+ <div class="MetadataStacked inline-flex flex-col flex-start gap-2 md:gap-2.5 text-body-md">
40
40
  <div
41
41
  v-if="props.location"
42
- class="flex flex-row gap-2.5 items-center self-stretch"
42
+ class="flex flex-row gap-2 md:gap-2.5 items-start self-stretch"
43
43
  >
44
44
  <img
45
45
  v-if="props.locationIcon"
46
46
  :src="props.locationIcon"
47
- class="w-4 h-4"
47
+ class="mt-[0.1rem] lg:mt-[0.2rem] w-6 h-6"
48
48
  alt=""
49
49
  />
50
50
  <IconLocation
@@ -55,17 +55,25 @@ const props = defineProps({
55
55
  </div>
56
56
  <div
57
57
  v-if="props.wait"
58
- class="flex flex-row gap-2.5 items-center self-stretch"
58
+ class="flex flex-row gap-2 md:gap-2.5 items-center self-stretch"
59
59
  >
60
- <IconHourglass class="MetadataStackedIcon text-jpl-red" />
60
+ <div class="w-6">
61
+ <IconHourglass class="MetadataStackedIcon text-jpl-red block mx-auto" />
62
+ </div>
61
63
  <span>{{ props.wait }}</span>
62
64
  </div>
63
65
  <div
64
66
  v-if="props.showMyList"
65
- class="flex flex-row gap-2.5 items-center self-stretch"
67
+ class="flex flex-row gap-2 md:gap-2.5 items-center self-stretch"
66
68
  >
67
- <IconBookmark class="MetadataStackedIcon text-gray-mid" />
69
+ <div class="w-6">
70
+ <IconBookmark class="MetadataStackedIcon text-gray-mid" />
71
+ </div>
68
72
  <span class="italic">Added to my list</span>
69
73
  </div>
70
74
  </div>
71
75
  </template>
76
+
77
+ <style lang="scss">
78
+ @import '@explorer-1/common/src/scss/components/MetadataStacked';
79
+ </style>
@@ -1,4 +1,5 @@
1
1
  import SearchResultCard from './SearchResultCard.vue'
2
+ import ExploreJplIconImage from '@explorer-1/common-storybook/src/story-data/images/2026-Explore-JPL-Site-Number-1-Missions-in-Flight.png'
2
3
 
3
4
  export default {
4
5
  title: 'Components/Cards/SearchResultCards',
@@ -81,10 +82,13 @@ export const EduEventCard = {
81
82
  export const AttractionCard = {
82
83
  args: {
83
84
  ...SearchResultCardData,
85
+ theme: 'Missions that Changed the World',
84
86
  isAttraction: true,
85
- location: 'Building 179, Highbay 1',
86
- locationIcon: '',
87
+ location: 'Building 179, Highbay 1, Subfloor 204',
88
+ locationIcon: ExploreJplIconImage,
87
89
  wait: '45 minute wait',
88
- showMyList: true
90
+ showMyList: true,
91
+ searchDescription:
92
+ 'This is the card description lorem ipsum dolor sit amet. Consectatur adipscing this is the card description. Lorem ipsum dolor sit amet.'
89
93
  }
90
94
  }
@@ -70,6 +70,8 @@
70
70
  :wait="wait"
71
71
  :show-my-list="showMyList"
72
72
  :location-icon="locationIcon"
73
+ :theme="theme"
74
+ :description="searchDescription"
73
75
  />
74
76
  <BaseLink
75
77
  v-else
@@ -405,6 +407,17 @@ export default defineComponent({
405
407
  locationIcon: {
406
408
  type: String,
407
409
  default: ''
410
+ },
411
+ theme: {
412
+ type: String,
413
+ default: undefined
414
+ },
415
+ /**
416
+ * The search meta description for use as card text. Plain text only.
417
+ */
418
+ searchDescription: {
419
+ type: String,
420
+ default: undefined
408
421
  }
409
422
  },
410
423
  computed: {
@@ -1246,6 +1246,7 @@ export const SearchResultsListAttractionData = {
1246
1246
  },
1247
1247
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1248
1248
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1249
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1249
1250
  explore_jpl_sites_explorejplsite__wait_time_filter: '15',
1250
1251
  explore_jpl_sites_explorejplsite__wait_time_label_filter: '15 minute wait',
1251
1252
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -1373,6 +1374,7 @@ export const SearchResultsListAttractionData = {
1373
1374
  },
1374
1375
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1375
1376
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1377
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1376
1378
  explore_jpl_sites_explorejplsite__wait_time_filter: '',
1377
1379
  explore_jpl_sites_explorejplsite__wait_time_label_filter: '',
1378
1380
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -1495,6 +1497,7 @@ export const SearchResultsListAttractionData = {
1495
1497
  },
1496
1498
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1497
1499
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1500
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1498
1501
  explore_jpl_sites_explorejplsite__wait_time_filter: '',
1499
1502
  explore_jpl_sites_explorejplsite__wait_time_label_filter: '60 minute wait',
1500
1503
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -1617,6 +1620,7 @@ export const SearchResultsListAttractionData = {
1617
1620
  },
1618
1621
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1619
1622
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1623
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1620
1624
  explore_jpl_sites_explorejplsite__wait_time_filter: '0',
1621
1625
  explore_jpl_sites_explorejplsite__wait_time_label_filter: 'No wait',
1622
1626
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -1739,6 +1743,7 @@ export const SearchResultsListAttractionData = {
1739
1743
  },
1740
1744
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1741
1745
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1746
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1742
1747
  explore_jpl_sites_explorejplsite__wait_time_filter: '90',
1743
1748
  explore_jpl_sites_explorejplsite__wait_time_label_filter: '90 minutes wait',
1744
1749
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -1864,6 +1869,7 @@ export const SearchResultsListAttractionData = {
1864
1869
  },
1865
1870
  explore_jpl_sites_explorejplsite__attraction_type_filter: 'Exhibits',
1866
1871
  explore_jpl_sites_explorejplsite__attraction_type: 'Exhibits',
1872
+ explore_jpl_sites_explorejplsite__theme_type_filter: 'Moon to Mars',
1867
1873
  explore_jpl_sites_explorejplsite__wait_time_filter: '135',
1868
1874
  explore_jpl_sites_explorejplsite__wait_time_label_filter: 'more than 2 hour wait',
1869
1875
  explore_jpl_sites_explorejplsite__latitude: '34.2012848',
@@ -40,8 +40,10 @@
40
40
  :primary-subject="page.primarySubject as unknown as PrimarySubjectObject"
41
41
  :grade-levels="page.gradeLevels as unknown as GradeLevelsObject[]"
42
42
  :time="page.time as unknown as EduResourcesTime"
43
+ :theme="page.theme"
43
44
  :title="page.title"
44
45
  :summary="page.summary"
46
+ :search-description="page.searchDescription"
45
47
  :featured="featureFirstResult ? index === 0 && currentPage === 1 : false"
46
48
  heading-level="h2"
47
49
  />
@@ -275,6 +277,14 @@ export default defineComponent({
275
277
  handle === 'explore_jpl_sites_explorejplsite'
276
278
  ? page._source[handle + '__map_icon']?.thumbnail_image
277
279
  : undefined
280
+ page.searchDescription =
281
+ handle === 'explore_jpl_sites_explorejplsite'
282
+ ? page._source.search_description
283
+ : undefined
284
+ page.theme =
285
+ handle === 'explore_jpl_sites_explorejplsite'
286
+ ? page._source[handle + '__theme_type_filter']
287
+ : undefined
278
288
  if (image) {
279
289
  page.image = {
280
290
  src: {
package/src/interfaces.ts CHANGED
@@ -147,6 +147,7 @@ export interface ElasticSearchPage {
147
147
  location?: string
148
148
  locationIcon?: string
149
149
  wait?: string
150
+ theme?: string
150
151
  title: string
151
152
  summary?: string
152
153
  eventType?: string
@@ -155,6 +156,7 @@ export interface ElasticSearchPage {
155
156
  gradeLevels?: string
156
157
  time?: string
157
158
  targetAudience?: string
159
+ searchDescription?: string
158
160
  }
159
161
 
160
162
  export interface FormOption {
@@ -255,6 +257,10 @@ export interface FooterResponse {
255
257
  footer: any
256
258
  }
257
259
 
260
+ export interface BannerAdminResponse {
261
+ bannerAdmin: any
262
+ }
263
+
258
264
  export interface EDUSubjectArea {
259
265
  id: number
260
266
  primarySubject: EduResourcesSubject
@@ -1,5 +1,5 @@
1
1
  import { BlockLinkCardCarouselData } from './../../../components/BlockLinkCarousel/BlockLinkCarousel.stories.js'
2
-
2
+ import ExploreJplIconImage from '@explorer-1/common-storybook/src/story-data/images/2026-Explore-JPL-Site-Number-1-Missions-in-Flight.png'
3
3
  import PageSiteExploreApp from './PageSiteExploreApp.vue'
4
4
 
5
5
  export default {
@@ -39,7 +39,9 @@ export const ExploreAppSitePageData = {
39
39
  location: 'Cafe 186',
40
40
  waitTime: '60',
41
41
  waitTimeLabel: '60 minute wait',
42
- locationIcon: '',
42
+ mapIcon: {
43
+ url: ExploreJplIconImage
44
+ },
43
45
  body: [
44
46
  {
45
47
  blockType: 'RichTextBlock',
@@ -12,7 +12,6 @@ import BlockStreamfield from './../../../components/BlockStreamfield/BlockStream
12
12
  import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
13
13
  import BaseExploreApp from './../../explore-jpl/BaseExploreApp/BaseExploreApp.vue'
14
14
  import BaseButton from '../../../components/BaseButton/BaseButton.vue'
15
- import BaseLink from '../../../components/BaseLink/BaseLink.vue'
16
15
  import MetadataStacked from '../../../components/MetadataStacked/MetadataStacked.vue'
17
16
  import IconMap from '../../../components/Icons/IconMap.vue'
18
17
 
@@ -27,22 +26,6 @@ const props = defineProps({
27
26
  })
28
27
  const { data } = reactive(props)
29
28
 
30
- // Handle navigating back to Sites page
31
- const previousPath = computed(() => {
32
- try {
33
- const path = route.path
34
-
35
- // Split URL segments and remove last item
36
- const segements = path.split('/').filter(Boolean)
37
- segements.pop()
38
-
39
- // Return Sites path
40
- return '/' + segements.join('/')
41
- } catch (e) {
42
- return '/'
43
- }
44
- })
45
-
46
29
  // Handle opening map to focus on respective site
47
30
  const mapPath = computed(() => {
48
31
  try {
@@ -67,26 +50,11 @@ const mapPath = computed(() => {
67
50
  >
68
51
  <BaseExploreApp :nav-data="data.breadcrumb">
69
52
  <template #default>
70
- <!-- back nav -->
71
- <LayoutHelper
72
- indent="col-2"
73
- class="mb-5"
74
- >
75
- <BaseLink
76
- class="BackToSitesBtn"
77
- variant="primary"
78
- :caret-left="true"
79
- caret-margin-right="mr-1"
80
- :to="previousPath"
81
- >Back to Sites</BaseLink
82
- >
83
- </LayoutHelper>
84
-
85
53
  <!-- page title area -->
86
54
  <LayoutHelper indent="col-2">
87
55
  <DetailHeadline
88
56
  :title="data.title"
89
- class="mb-5"
57
+ class="my-5 lg:mt-10"
90
58
  />
91
59
  <MetadataStacked
92
60
  :location="data.location"
@@ -151,8 +119,3 @@ const mapPath = computed(() => {
151
119
  </BaseExploreApp>
152
120
  </div>
153
121
  </template>
154
- <style lang="scss" scoped>
155
- .BackToSitesBtn {
156
- margin-left: -10px;
157
- }
158
- </style>