@explorer-1/vue 0.2.24 → 0.2.26

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.24",
3
+ "version": "0.2.26",
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.6"
33
+ "@explorer-1/common": "1.1.7"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@vitejs/plugin-vue": "^5.0.4",
package/src/constants.ts CHANGED
@@ -11,6 +11,11 @@ export const eduMetadataDictionary: PillDictionaryInterface = {
11
11
  variant: 'primary',
12
12
  type: 'resource'
13
13
  },
14
+ EDUTeachableMomentPage: {
15
+ label: 'Teachable Moment',
16
+ variant: 'primary',
17
+ type: 'resource'
18
+ },
14
19
  EDUEventPage: {
15
20
  variant: 'primary',
16
21
  type: 'event'
@@ -24,5 +29,6 @@ export const searchContentTypeToPageType: DictionaryInterface = {
24
29
  missions_mission: 'Mission',
25
30
  eduevents_edueventpage: 'EDUEventPage',
26
31
  eduresources_eduexplainerarticlepage: 'EDUExplainerArticlePage',
27
- eduresources_edulessonpage: 'EDULessonPage'
32
+ eduresources_edulessonpage: 'EDULessonPage',
33
+ eduresources_eduteachablemomentpage: 'EDUTeachableMomentPage'
28
34
  }
package/src/interfaces.ts CHANGED
@@ -214,11 +214,13 @@ export type MetaPanelTheme = 'primary' | 'secondary' | 'stars'
214
214
  export interface PageObject {
215
215
  __typename: string
216
216
  contentType: string
217
+ lastPublishedAt?: string
217
218
  breadcrumb?: string
218
219
  slug: string
219
220
  url: string
220
221
  title: string
221
222
  getTopicsForDisplay?: Topic[]
223
+ showJumpMenu?: boolean
222
224
  label?: string
223
225
  summary?: string
224
226
  topper?: string
@@ -44,6 +44,7 @@ export const BaseStory = {
44
44
  seoTitle: 'Test Lesson',
45
45
  slug: 'test-lesson',
46
46
  publicationDate: '2024-08-16',
47
+ lastPublishedAt: '2024-08-22T02:33:13.507206+00:00',
47
48
  thumbnailImage: {
48
49
  __typename: 'CustomImage',
49
50
  original: 'http://127.0.0.1:9000/media/original_images/imagessirtfsirtf-090303-16.jpg',
@@ -114,15 +114,6 @@ const heroInline = computed((): boolean => {
114
114
  return false
115
115
  })
116
116
 
117
- const computedClass = computed((): string => {
118
- if (heroInline.value || heroEmpty) {
119
- return 'pt-5 lg:pt-12'
120
- } else if (!heroInline.value) {
121
- return '-nav-offset'
122
- }
123
- return ''
124
- })
125
-
126
117
  const sectionOrder = [
127
118
  'top',
128
119
  'overview',
@@ -262,8 +253,7 @@ const consolidatedSections = computed((): EduLessonSectionObject[] => {
262
253
  <template>
263
254
  <div
264
255
  v-if="data"
265
- class="ThemeVariantLight"
266
- :class="computedClass"
256
+ class="ThemeVariantLight pt-5 lg:pt-12"
267
257
  >
268
258
  <LayoutHelper
269
259
  indent="col-2"
@@ -409,5 +399,19 @@ const consolidatedSections = computed((): EduLessonSectionObject[] => {
409
399
  :heading="data.relatedContentHeading"
410
400
  :items="data.relatedContent"
411
401
  />
402
+
403
+ <LayoutHelper
404
+ v-if="data.lastPublishedAt"
405
+ indent="col-3"
406
+ class="lg:my-18 my-10"
407
+ >
408
+ <p class="border-t border-gray-light-mid pt-8">
409
+ <strong>Lesson Last Updated:</strong>
410
+ {{
411
+ // @ts-ignore
412
+ $filters.displayDate(data.lastPublishedAt)
413
+ }}
414
+ </p>
415
+ </LayoutHelper>
412
416
  </div>
413
417
  </template>
@@ -40,6 +40,7 @@ const anchorId = computed(() => {
40
40
  <template>
41
41
  <section
42
42
  :id="anchorId"
43
+ class="PageEduLessonSection"
43
44
  :aria-label="heading.heading"
44
45
  >
45
46
  <LayoutHelper
@@ -79,10 +80,12 @@ const anchorId = computed(() => {
79
80
  {{ 'Section ' + (Number(index) + 1) }}
80
81
  </BaseHeading>
81
82
  </LayoutHelper>
82
- <BlockStreamfield
83
- v-if="item?.blocks"
84
- :data="item.blocks"
85
- />
83
+ <div class="PageEduProcedureSection">
84
+ <BlockStreamfield
85
+ v-if="item?.blocks"
86
+ :data="item.blocks"
87
+ />
88
+ </div>
86
89
  </template>
87
90
  </template>
88
91
  <LayoutHelper
@@ -94,3 +97,27 @@ const anchorId = computed(() => {
94
97
  </LayoutHelper>
95
98
  </section>
96
99
  </template>
100
+ <style lang="scss">
101
+ .PageEduProcedureSection {
102
+ counter-reset: listitem;
103
+ .BlockText ol {
104
+ list-style-type: none;
105
+ counter-reset: nestedlistitem;
106
+ > li {
107
+ counter-increment: listitem;
108
+ &::marker {
109
+ content: counter(listitem) '. ';
110
+ }
111
+ }
112
+ ol {
113
+ list-style-type: none;
114
+ > li {
115
+ counter-increment: nestedlistitem;
116
+ &::marker {
117
+ content: counter(nestedlistitem) '. ';
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ </style>
@@ -151,6 +151,7 @@ defineExpose({
151
151
  itemprop="articleBody"
152
152
  :data="data.body"
153
153
  />
154
+
154
155
  <div class="bg-stars bg-primary-darker">
155
156
  <div class="py-10 text-center text-white">
156
157
  <strong>Related News goes here</strong>
@@ -0,0 +1,142 @@
1
+ import { HeroMediaData } from './../../../components/HeroMedia/HeroMedia.stories'
2
+ import { BlockIframeEmbedData } from './../../../components/BlockIframeEmbed/BlockIframeEmbed.stories.js'
3
+ import { BlockImageCarouselData } from './../../../components/BlockImageCarousel/BlockImageCarousel.stories'
4
+ import { BlockImageData } from './../../../components/BlockImage/BlockImage.stories'
5
+ import { BlockHeadingData } from './../../../components/BlockHeading/BlockHeading.stories'
6
+ import { BlockImageComparisonData } from './../../../components/BlockImageComparison/BlockImageComparison.stories'
7
+ import { BaseVideoData } from './../../../components/BaseVideo/BaseVideo.stories'
8
+ import { BlockVideoEmbedData } from './../../../components/BlockVideoEmbed/BlockVideoEmbed.stories'
9
+ import { BlockRelatedLinksData } from './../../../components/BlockRelatedLinks/BlockRelatedLinks.stories.js'
10
+ import { BlockLinkCardCarouselData } from './../../../components/BlockLinkCarousel/BlockLinkCarousel.stories.js'
11
+ import {
12
+ BlockStreamfieldTruncatedData,
13
+ BlockStreamfieldMinimalData
14
+ } from './../../../components/BlockStreamfield/BlockStreamfield.stories'
15
+ import PageEduTeachableMoment from './PageEduTeachableMoment.vue'
16
+
17
+ export default {
18
+ title: 'Templates/EDU/PageEduTeachableMoment',
19
+ component: PageEduTeachableMoment,
20
+ tags: ['!autodocs'],
21
+ decorators: [
22
+ () => ({
23
+ template: `<div id="storyDecorator" class="disable-nav-offset"><story/></div>`
24
+ })
25
+ ],
26
+ parameters: {
27
+ layout: 'fullscreen',
28
+ html: {
29
+ root: '#storyDecorator'
30
+ }
31
+ },
32
+ excludeStories: /.*Data$/
33
+ }
34
+
35
+ export const BaseStory = {
36
+ args: {
37
+ data: {
38
+ __typename: 'EDUTeachableMomentPage',
39
+ title: 'A Teachable Moment',
40
+ lastPublishedAt: '2024-08-22T02:33:13.507206+00:00',
41
+ url: 'http://localhost:3000/edu/resources/teachable-moment',
42
+ pageType: 'EDUTeachableMomentPage',
43
+ contentType: 'edu_resources.EDUTeachableMomentPage',
44
+ showJumpMenu: true,
45
+ thumbnailImage: {
46
+ __typename: 'CustomImage',
47
+ original: 'http://127.0.0.1:9000/media/original_images/imagessirtfsirtf-090303-16.jpg',
48
+ alt: ''
49
+ },
50
+ hero: [
51
+ {
52
+ ...HeroMediaData,
53
+ blockType: 'HeroImageBlock'
54
+ }
55
+ ],
56
+ heroConstrain: true,
57
+
58
+ body: BlockStreamfieldTruncatedData.body,
59
+
60
+ relatedLinks: BlockRelatedLinksData.data,
61
+ relatedContentHeading: 'Related Content',
62
+ relatedContent: BlockLinkCardCarouselData
63
+ }
64
+ }
65
+ }
66
+
67
+ export const HeroCarousel = {
68
+ args: {
69
+ data: {
70
+ ...BaseStory.args.data,
71
+ hero: [{ blockType: 'CarouselBlock', blocks: BlockImageCarouselData }]
72
+ }
73
+ }
74
+ }
75
+
76
+ export const HeroImageComparison = {
77
+ args: {
78
+ data: {
79
+ ...BaseStory.args.data,
80
+ hero: [
81
+ {
82
+ ...BlockImageComparisonData
83
+ }
84
+ ]
85
+ }
86
+ }
87
+ }
88
+
89
+ export const HeroVideo = {
90
+ args: {
91
+ data: {
92
+ ...BaseStory.args.data,
93
+ hero: [
94
+ {
95
+ blockType: 'VideoBlock',
96
+ video: BaseVideoData,
97
+ caption: 'Lorem ipsum dolor sit amet',
98
+ credit: 'NASA/JPL'
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ }
104
+
105
+ export const HeroVideoEmbed = {
106
+ args: {
107
+ data: {
108
+ ...BaseStory.args.data,
109
+ hero: [
110
+ {
111
+ ...BlockVideoEmbedData.data,
112
+ embed: {
113
+ embed: `<iframe title="Meet NASA's Diana Trujillo - Embedded Hero" width="480" height="270" src="https://www.youtube.com/embed/vUuUyYqI83Q?feature=oembed" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`
114
+ },
115
+ blockType: 'VideoEmbedBlock'
116
+ }
117
+ ]
118
+ }
119
+ }
120
+ }
121
+
122
+ export const HeroIframeEmbed = {
123
+ args: {
124
+ data: {
125
+ ...BaseStory.args.data,
126
+ hero: [
127
+ {
128
+ ...BlockIframeEmbedData
129
+ }
130
+ ]
131
+ }
132
+ }
133
+ }
134
+
135
+ export const NoHero = {
136
+ args: {
137
+ data: {
138
+ ...BaseStory.args.data,
139
+ hero: []
140
+ }
141
+ }
142
+ }
@@ -0,0 +1,217 @@
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref } from 'vue'
3
+ import type { PageEduResourcesObject } from './../../../interfaces'
4
+ import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
5
+ import BaseImagePlaceholder from './../../../components/BaseImagePlaceholder/BaseImagePlaceholder.vue'
6
+ import BlockImageCarousel from './../../../components/BlockImageCarousel/BlockImageCarousel.vue'
7
+ import BlockImageComparison from './../../../components/BlockImageComparison/BlockImageComparison.vue'
8
+ import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
9
+ import BlockVideo from './../../../components/BlockVideo/BlockVideo.vue'
10
+ import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
11
+ import DetailHeadline from './../../../components/DetailHeadline/DetailHeadline.vue'
12
+ import BlockImageStandard from './../../../components/BlockImage/BlockImageStandard.vue'
13
+ import ShareButtonsEdu from './../../../components/ShareButtonsEdu/ShareButtonsEdu.vue'
14
+ import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
15
+ import BlockIframeEmbed from '../../../components/BlockIframeEmbed/BlockIframeEmbed.vue'
16
+ import BlockRelatedLinks from '../../../components/BlockRelatedLinks/BlockRelatedLinks.vue'
17
+ import NavJumpMenu from './../../../components/NavJumpMenu/NavJumpMenu.vue'
18
+
19
+ interface PageEduTeachableMomentProps {
20
+ data?: PageEduResourcesObject
21
+ }
22
+
23
+ const props = withDefaults(defineProps<PageEduTeachableMomentProps>(), {
24
+ data: undefined
25
+ })
26
+
27
+ const { data } = reactive(props)
28
+
29
+ const PageEduTeachableMomentJumpMenu = ref()
30
+
31
+ defineExpose({
32
+ PageEduTeachableMomentJumpMenu
33
+ })
34
+
35
+ const heroEmpty = computed((): boolean => {
36
+ return data?.hero?.length === 0
37
+ })
38
+
39
+ const heroInline = computed((): boolean => {
40
+ // heroes with interactive elements have special handling
41
+ if (!heroEmpty.value && data?.hero) {
42
+ // excludes VideoBlock as this will autoplay
43
+ if (data?.hero[0].blockType === 'VideoBlock') {
44
+ return false
45
+ } else if (
46
+ data?.hero[0].blockType === 'CarouselBlock' ||
47
+ data?.hero[0].blockType === 'IframeEmbedBlock' ||
48
+ data?.hero[0].blockType === 'VideoEmbedBlock' ||
49
+ data?.hero[0].blockType === 'ImageComparisonBlock'
50
+ ) {
51
+ return true
52
+ }
53
+ }
54
+ return false
55
+ })
56
+
57
+ const computedClass = computed((): string => {
58
+ if (heroInline.value || heroEmpty.value) {
59
+ return 'pt-5 lg:pt-12'
60
+ } else if (!heroInline.value) {
61
+ return '-nav-offset'
62
+ }
63
+ return ''
64
+ })
65
+ </script>
66
+ <template>
67
+ <div
68
+ v-if="data"
69
+ class="ThemeEdu ThemeVariantLight"
70
+ :class="computedClass"
71
+ >
72
+ <NavJumpMenu
73
+ ref="PageEduTeachableMomentJumpMenu"
74
+ :title="data.title"
75
+ :blocks="data.body"
76
+ :enabled="data.showJumpMenu"
77
+ dropdown-text="In this Teachable Moment"
78
+ />
79
+
80
+ <!-- hero media -->
81
+ <HeroMedia
82
+ v-if="
83
+ !heroEmpty &&
84
+ !heroInline &&
85
+ data?.hero?.length &&
86
+ (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
87
+ "
88
+ class="md:mb-12 lg:mb-18 mb-10"
89
+ :image="data.hero[0].image"
90
+ :video="data.hero[0].video"
91
+ :display-caption="data.hero[0].displayCaption"
92
+ :caption="data.hero[0].caption"
93
+ :credit="data.hero[0].credit"
94
+ :constrain="data.heroConstrain"
95
+ />
96
+
97
+ <LayoutHelper
98
+ indent="col-2"
99
+ class="mb-10"
100
+ >
101
+ <DetailHeadline
102
+ :title="data.title"
103
+ label="Teachable Moment"
104
+ pill
105
+ />
106
+ <ShareButtonsEdu
107
+ v-if="data?.url"
108
+ class="mt-4"
109
+ :url="data.url"
110
+ :title="data.title"
111
+ :image="data.thumbnailImage?.original"
112
+ />
113
+ </LayoutHelper>
114
+
115
+ <!-- TODO: put this in a component (exclude layout though) -->
116
+ <LayoutHelper
117
+ v-if="!heroEmpty && heroInline && data.hero?.length"
118
+ class="lg:mb-22 mb-10"
119
+ >
120
+ <BlockImageStandard
121
+ v-if="data.hero[0].blockType === 'HeroImageBlock'"
122
+ :data="data.hero[0].imageInline"
123
+ :display-caption="data.hero[0].displayCaption"
124
+ :caption="data.hero[0].caption"
125
+ :constrain="data.heroConstrain"
126
+ />
127
+ <BlockImageCarousel
128
+ v-else-if="data.hero[0].blockType === 'CarouselBlock'"
129
+ :items="data.hero[0].blocks"
130
+ :block-id="data.hero[0].id"
131
+ />
132
+ <BlockIframeEmbed
133
+ v-else-if="data.hero[0].blockType === 'IframeEmbedBlock'"
134
+ :data="data.hero[0]"
135
+ />
136
+ <BlockVideo
137
+ v-else-if="data.hero[0].blockType === 'VideoBlock'"
138
+ :data="data.hero[0]"
139
+ autoplay
140
+ />
141
+ <BaseImagePlaceholder
142
+ v-else-if="data.hero[0].blockType === 'VideoEmbedBlock'"
143
+ aspect-ratio="16:9"
144
+ dark-mode
145
+ >
146
+ <div v-html="data.hero[0].embed?.embed"></div>
147
+ </BaseImagePlaceholder>
148
+ <BlockImageComparison
149
+ v-else-if="data.hero[0].blockType === 'ImageComparisonBlock'"
150
+ :data="data.hero[0]"
151
+ />
152
+ </LayoutHelper>
153
+
154
+ <!-- summary and topper -->
155
+ <LayoutHelper
156
+ indent="col-3"
157
+ class="lg:mb-8 mb-5"
158
+ >
159
+ <BlockText
160
+ v-if="data.topper && data.topper.length > 2"
161
+ class="lg:mb-8 mb-5"
162
+ :text="data.topper"
163
+ />
164
+ </LayoutHelper>
165
+ <!-- streamfield blocks -->
166
+ <BlockStreamfield
167
+ itemprop="articleBody"
168
+ :data="data.body"
169
+ />
170
+
171
+ <!-- streamfield blocks -->
172
+ <BlockStreamfield :data="data.body" />
173
+
174
+ <!-- related links -->
175
+ <LayoutHelper
176
+ v-if="data.relatedLinks && data.relatedLinks.length"
177
+ indent="col-3"
178
+ class="lg:my-18 my-10"
179
+ >
180
+ <BlockRelatedLinks :data="data.relatedLinks[0]" />
181
+ </LayoutHelper>
182
+
183
+ <!-- related content -->
184
+ <BlockLinkCarousel
185
+ v-if="data.relatedContent?.length"
186
+ item-type="cards"
187
+ class="lg:my-24 my-12 print:px-4"
188
+ :heading="data.relatedContentHeading"
189
+ :items="data.relatedContent"
190
+ />
191
+
192
+ <LayoutHelper
193
+ v-if="data.lastPublishedAt"
194
+ indent="col-3"
195
+ class="lg:my-18 my-10"
196
+ >
197
+ <p class="border-t border-gray-light-mid pt-8">
198
+ <strong>Teachable Moment Last Updated:</strong>
199
+ {{
200
+ // @ts-ignore
201
+ $filters.displayDate(data.lastPublishedAt)
202
+ }}
203
+ </p>
204
+ </LayoutHelper>
205
+ <!-- explore more -->
206
+ <div
207
+ v-if="data.relatedContent?.length"
208
+ class="bg-stars bg-primary-darker pt-14 pb-20 ThemeVariantDark text-white"
209
+ >
210
+ <BlockLinkCarousel
211
+ item-type="cards"
212
+ heading="Explore more"
213
+ :items="data.relatedContent"
214
+ />
215
+ </div>
216
+ </div>
217
+ </template>