@explorer-1/vue 0.2.52 → 0.2.54

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.52",
3
+ "version": "0.2.54",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -10,9 +10,9 @@ export default {
10
10
  argTypes: {
11
11
  size: {
12
12
  control: {
13
- type: 'select',
14
- options: ['sm', 'md', 'lg']
15
- }
13
+ type: 'select'
14
+ },
15
+ options: ['sm', 'md', 'lg']
16
16
  },
17
17
  headingLevel: {
18
18
  description:
@@ -152,6 +152,29 @@ export const EventItemWithChip = {
152
152
  }
153
153
  }
154
154
  }
155
+ export const EventWithDateNoTime = {
156
+ decorators: [
157
+ () => ({
158
+ template: `<div id="storyDecorator" class="relative grid grid-cols-2 gap-3"><story/></div>`
159
+ })
160
+ ],
161
+ args: {
162
+ ...BlockLinkCardData,
163
+ showCalendarChip: true,
164
+ data: {
165
+ page: {
166
+ ...BlockLinkCardData.data,
167
+ __typename: 'EDUEventPage',
168
+ startDate: '2021-11-11',
169
+ startDatetime: null,
170
+ endDatetime: null,
171
+ endDate: '2021-11-12',
172
+ ongoing: false,
173
+ eventType: 'Workshop'
174
+ }
175
+ }
176
+ }
177
+ }
155
178
  export const EduLesson = {
156
179
  decorators: [
157
180
  () => ({
@@ -160,6 +183,7 @@ export const EduLesson = {
160
183
  ],
161
184
  args: {
162
185
  ...BlockLinkCardData,
186
+ size: 'sm',
163
187
  data: {
164
188
  page: {
165
189
  __typename: 'EDULessonPage',
@@ -172,7 +196,10 @@ export const EduLesson = {
172
196
  { gradeLevel: '1' },
173
197
  { gradeLevel: '2' },
174
198
  { gradeLevel: '8' }
175
- ]
199
+ ],
200
+ time: {
201
+ time: '1-2 hrs'
202
+ }
176
203
  }
177
204
  }
178
205
  }
@@ -98,7 +98,7 @@
98
98
  </component>
99
99
  <p
100
100
  v-if="(theItem as EventCardObject).targetAudience"
101
- :class="{ 'mt-2': !large, 'mt-4': large }"
101
+ :class="{ 'mt-1': !large, 'mt-4': large }"
102
102
  >
103
103
  <strong>Target Audience:</strong> {{ (theItem as EventCardObject).targetAudience }}
104
104
  </p>
@@ -109,7 +109,7 @@ export default defineComponent({
109
109
  computed: {
110
110
  ...mapStores(useThemeStore),
111
111
  theImageCaption(): string | undefined {
112
- if (this.image && this.caption && this.caption.length > 2 && this.displayCaption) {
112
+ if (this.displayCaption && this.image && this.caption && this.caption.length > 2) {
113
113
  return this.caption
114
114
  } else if (
115
115
  this.image &&
@@ -135,10 +135,12 @@ export default defineComponent({
135
135
  },
136
136
  // to handle captions for videos
137
137
  customCaption(): Partial<ImageObject> | undefined {
138
- if ((this.caption && this.caption.length > 2) || this.credit) {
139
- return {
140
- caption: this.caption,
141
- credit: this.credit
138
+ if (this.displayCaption) {
139
+ if ((this.caption && this.caption.length > 2) || this.credit) {
140
+ return {
141
+ caption: this.caption,
142
+ credit: this.credit
143
+ }
142
144
  }
143
145
  }
144
146
  return undefined
@@ -42,7 +42,7 @@ const time = computed(() => {
42
42
  <template>
43
43
  <div
44
44
  class="MetadataEduResource"
45
- :class="{ '-compact text-body-sm': props.compact, 'text-body-lg': !props.compact }"
45
+ :class="{ '-compact text-sm xl:text-base': props.compact, 'text-body-lg': !props.compact }"
46
46
  >
47
47
  <div
48
48
  v-if="primarySubject"
@@ -69,6 +69,7 @@ const time = computed(() => {
69
69
  <div
70
70
  v-if="time && showTime"
71
71
  class="MetadataEduResourceItem"
72
+ :class="primarySubject && audience && time ? '-xlScreensOnly' : ''"
72
73
  >
73
74
  <IconTime
74
75
  class="MetadataEduResourceIcon text-[1.15em]"
@@ -102,10 +103,14 @@ const time = computed(() => {
102
103
  }
103
104
 
104
105
  &.-compact {
105
- @apply flex flex-grow;
106
+ @apply flex flex-grow flex-wrap;
106
107
  .MetadataEduResourceItem {
108
+ @apply whitespace-nowrap;
107
109
  @apply max-w-none min-w-[4em];
108
110
  @apply mr-6 mb-0;
111
+ &.-xlScreensOnly {
112
+ @apply hidden xl:flex;
113
+ }
109
114
  }
110
115
  }
111
116
  }
@@ -76,7 +76,7 @@ const location = computed(() => {
76
76
  <div
77
77
  class="MetadataEvent"
78
78
  :class="{
79
- '-compact text-body-sm': props.compact,
79
+ '-compact text-sm xl:text-base': props.compact,
80
80
  'text-body-lg': !props.compact,
81
81
  '-allow-break': props.allowBreak
82
82
  }"
@@ -173,8 +173,9 @@ const location = computed(() => {
173
173
  }
174
174
 
175
175
  &.-compact {
176
- @apply flex flex-grow;
176
+ @apply flex flex-grow flex-wrap;
177
177
  .MetadataEventItem {
178
+ @apply whitespace-nowrap;
178
179
  @apply max-w-none min-w-[4em];
179
180
  @apply mr-6 mb-0;
180
181
  }
@@ -139,6 +139,7 @@ export default {
139
139
  if (newVal.length > 0) {
140
140
  query = {
141
141
  ...this.$route.query,
142
+ page: 1,
142
143
  [this.groupKey]: newVal.toString()
143
144
  }
144
145
  } else {
@@ -66,9 +66,20 @@ export const PodcastEpisodeCard = {
66
66
  export const EduEventCard = {
67
67
  args: {
68
68
  ...SearchResultCardData,
69
- pageContentType: 'eduevents_edueventpage',
69
+ __typename: 'EDUEventPage',
70
+ pageType: 'EDUEventPage',
71
+ contentType: 'EDUEventPage',
72
+ topic: null,
73
+ date: null,
74
+ isEvents: true,
75
+ type: 'Event',
70
76
  startDate: '2021-11-11',
71
77
  endDate: '2021-11-11',
72
- eventType: 'Internship'
78
+ eventType: 'Internship',
79
+ startTime: '00:00:00-08:00',
80
+ endTime: '23:59:59.999999-08:00',
81
+ location: 'Hybrid',
82
+ targetAudience: 'All ages',
83
+ pageContentType: 'edu_events_edueventpage'
73
84
  }
74
85
  }
@@ -150,20 +150,25 @@ export default defineComponent({
150
150
  : null
151
151
  const pageType = page._source[handle + '__label']
152
152
  if (handle === 'events_eventpage') {
153
+ // WWW Events
153
154
  date = 'Event date: ' + parseDate(page._source[handle + '__start_datetime'])
154
155
  } else if (handle === 'edu_events_edueventpage') {
156
+ // EDU Events
155
157
  date = null
156
158
  location = page._source[handle + '__location_filter']
157
159
  } else if (handle === 'missions_mission') {
160
+ // WWW Missions
158
161
  date = page._source.display_date_filter
159
162
  ? 'Launch date: ' + page._source.display_date_filter
160
163
  : typeof page._source.publication_date_filter !== 'undefined'
161
164
  ? 'Published: ' + parseDate(page._source.publication_date_filter)
162
165
  : 'Published: ' + parseDate(page._source.first_published_at_filter)
163
166
  } else if (handle === 'profiles_profilepage') {
167
+ // WWW Profiles
164
168
  topic = page._source[handle + '__go_site_name']
165
169
  date = null
166
170
  } else if (handle.startsWith('edu_resources')) {
171
+ // EDU Resources
167
172
  date = null
168
173
  primarySubject = page._source[handle + '__primary_subject'] as PrimarySubjectObject
169
174
  if (
@@ -178,6 +183,7 @@ export default defineComponent({
178
183
  }
179
184
  time = { time: page._source.activity_time_label_filter } as EduResourcesTime
180
185
  } else {
186
+ // Fallback publication date
181
187
  date =
182
188
  typeof page._source.publication_date_filter !== 'undefined'
183
189
  ? parseDate(page._source.publication_date_filter)
@@ -195,11 +201,11 @@ export default defineComponent({
195
201
  handle === 'events_eventpage' ? page._source[handle + '__location'] : location
196
202
  page.startDate =
197
203
  handle === 'events_eventpage' || handle === 'edu_events_edueventpage'
198
- ? page._source[handle + '__start_datetime']
204
+ ? page._source[handle + '__start_date']
199
205
  : null
200
206
  page.endDate =
201
207
  handle === 'events_eventpage' || handle === 'edu_events_edueventpage'
202
- ? page._source[handle + '__end_datetime']
208
+ ? page._source[handle + '__end_date']
203
209
  : null
204
210
  page.startTime =
205
211
  handle === 'events_eventpage' || handle === 'edu_events_edueventpage'
package/src/interfaces.ts CHANGED
@@ -38,6 +38,7 @@ export interface StreamfieldBlockData extends BlockData {
38
38
  caption?: string
39
39
  credit?: string
40
40
  imageInline?: ImageObject
41
+ heroSummary?: string
41
42
  }
42
43
 
43
44
  export interface ImageSrcObject {
@@ -109,6 +109,22 @@ export const BaseStory = {
109
109
  }
110
110
  }
111
111
  }
112
+
113
+ export const HeroTitle = {
114
+ args: {
115
+ data: {
116
+ ...BaseStory.args.data,
117
+ hero: [
118
+ {
119
+ ...HeroMediaData,
120
+ heroSummary: 'Text appears below the title',
121
+ blockType: 'HeroTitleBlock'
122
+ }
123
+ ]
124
+ }
125
+ }
126
+ }
127
+
112
128
  export const InlineHero = {
113
129
  args: {
114
130
  data: {
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { defineComponent } from 'vue'
3
3
  import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
4
+ import HeroLarge from './../../../components/HeroLarge/HeroLarge.vue'
4
5
  import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
5
6
  import BlockText from './../../../components/BlockText/BlockText.vue'
6
7
  import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
@@ -25,7 +26,8 @@ export default defineComponent({
25
26
  BlockLinkCarousel,
26
27
  BlockRelatedLinks,
27
28
  BlockText,
28
- NavJumpMenu
29
+ NavJumpMenu,
30
+ HeroLarge
29
31
  },
30
32
  props: {
31
33
  data: {
@@ -36,16 +38,35 @@ export default defineComponent({
36
38
  },
37
39
  computed: {
38
40
  heroEmpty(): boolean {
39
- return (this.data?.hero || []).length === 0
41
+ return this.data?.hero?.length === 0
42
+ },
43
+ theHero() {
44
+ if (this.data?.hero?.length) {
45
+ return this.data.hero[0]
46
+ }
47
+ return undefined
48
+ },
49
+ heroTitle(): boolean {
50
+ if (this.theHero) {
51
+ // excludes VideoBlock as this will autoplay
52
+ if (this.theHero.blockType === 'HeroTitleBlock') {
53
+ return true
54
+ }
55
+ }
56
+ return false
40
57
  },
41
58
  heroInline(): boolean {
42
- if (!this.heroEmpty) {
43
- if (this.data?.hero[0].blockType === 'VideoBlock') {
59
+ // heroes with interactive elements have special handling
60
+ if (this.theHero && !this.heroTitle) {
61
+ // excludes VideoBlock as this will autoplay
62
+ if (this.theHero.blockType === 'VideoBlock') {
44
63
  return false
45
64
  } else if (
46
65
  this.data?.heroPosition === 'inline' ||
47
- this.data?.hero[0].blockType === 'CarouselBlock' ||
48
- this.data?.hero[0].blockType === 'VideoEmbedBlock'
66
+ this.theHero.blockType === 'CarouselBlock' ||
67
+ this.theHero.blockType === 'IframeEmbedBlock' ||
68
+ this.theHero.blockType === 'VideoEmbedBlock' ||
69
+ this.theHero.blockType === 'ImageComparisonBlock'
49
70
  ) {
50
71
  return true
51
72
  }
@@ -77,20 +98,29 @@ export default defineComponent({
77
98
  itemprop="image"
78
99
  :content="data.thumbnailImage.original"
79
100
  />
80
-
101
+ <!-- hero title -->
102
+ <HeroLarge
103
+ v-if="heroTitle && theHero"
104
+ :title="data.title"
105
+ :image="theHero.image"
106
+ :summary="theHero.heroSummary"
107
+ :custom-pill-type="data.__typename"
108
+ />
81
109
  <!-- hero image -->
82
110
  <HeroMedia
83
111
  v-if="
84
112
  !heroEmpty &&
113
+ !heroTitle &&
85
114
  !heroInline &&
86
- (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
115
+ theHero &&
116
+ (theHero.blockType === 'HeroImageBlock' || theHero.blockType === 'VideoBlock')
87
117
  "
88
118
  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"
119
+ :image="theHero.image"
120
+ :video="theHero.video"
121
+ :display-caption="theHero.displayCaption"
122
+ :caption="theHero.caption"
123
+ :credit="theHero.credit"
94
124
  :constrain="data.heroConstrain"
95
125
  />
96
126
 
@@ -100,6 +130,7 @@ export default defineComponent({
100
130
  class="mb-10"
101
131
  >
102
132
  <DetailHeadline
133
+ v-if="!heroTitle"
103
134
  :title="data.title"
104
135
  :read-time="data.readTime"
105
136
  :publication-date="data.publicationDate"
@@ -112,7 +143,7 @@ export default defineComponent({
112
143
  />
113
144
  <ShareButtonsEdu
114
145
  v-if="data?.url"
115
- class="mt-4"
146
+ :class="heroTitle ? 'mt-10' : 'mt-4'"
116
147
  :url="data.url"
117
148
  :title="data.title"
118
149
  :image="data.thumbnailImage?.original"
@@ -58,7 +58,8 @@ export const BaseStory = {
58
58
  blockType: 'HeroImageBlock'
59
59
  }
60
60
  ],
61
- heroConstrain: true,
61
+ heroConstrain: false,
62
+ heroPosition: 'full_bleed',
62
63
 
63
64
  studentProject: {
64
65
  title: 'Student Project',
@@ -275,6 +276,30 @@ export const BaseStory = {
275
276
  }
276
277
  }
277
278
 
279
+ export const HeroTitle = {
280
+ args: {
281
+ data: {
282
+ ...BaseStory.args.data,
283
+ hero: [
284
+ {
285
+ ...HeroMediaData,
286
+ heroSummary: 'Text appears below the title',
287
+ blockType: 'HeroTitleBlock'
288
+ }
289
+ ]
290
+ }
291
+ }
292
+ }
293
+
294
+ export const InlineHero = {
295
+ args: {
296
+ data: {
297
+ ...BaseStory.args.data,
298
+ heroPosition: 'inline'
299
+ }
300
+ }
301
+ }
302
+
278
303
  export const HeroCarousel = {
279
304
  args: {
280
305
  data: {
@@ -6,6 +6,7 @@ import type {
6
6
  StreamfieldBlockData
7
7
  } from './../../../interfaces'
8
8
  import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
9
+ import HeroLarge from './../../../components/HeroLarge/HeroLarge.vue'
9
10
  import BaseLink from './../../../components/BaseLink/BaseLink.vue'
10
11
  import type { BlockHeadingObject } from '../../../components/BlockHeading/BlockHeading.vue'
11
12
  import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
@@ -94,17 +95,35 @@ const heroEmpty = computed((): boolean => {
94
95
  return data?.hero?.length === 0
95
96
  })
96
97
 
98
+ const theHero = computed(() => {
99
+ if (data?.hero?.length) {
100
+ return data.hero[0]
101
+ }
102
+ return undefined
103
+ })
104
+
105
+ const heroTitle = computed((): boolean => {
106
+ if (theHero.value) {
107
+ // excludes VideoBlock as this will autoplay
108
+ if (theHero.value.blockType === 'HeroTitleBlock') {
109
+ return true
110
+ }
111
+ }
112
+ return false
113
+ })
114
+
97
115
  const heroInline = computed((): boolean => {
98
116
  // heroes with interactive elements have special handling
99
- if (!heroEmpty.value && data?.hero) {
117
+ if (theHero.value && !heroTitle.value) {
100
118
  // excludes VideoBlock as this will autoplay
101
- if (data?.hero[0].blockType === 'VideoBlock') {
119
+ if (theHero.value.blockType === 'VideoBlock') {
102
120
  return false
103
121
  } else if (
104
- data?.hero[0].blockType === 'CarouselBlock' ||
105
- data?.hero[0].blockType === 'IframeEmbedBlock' ||
106
- data?.hero[0].blockType === 'VideoEmbedBlock' ||
107
- data?.hero[0].blockType === 'ImageComparisonBlock'
122
+ data?.heroPosition === 'inline' ||
123
+ theHero.value.blockType === 'CarouselBlock' ||
124
+ theHero.value.blockType === 'IframeEmbedBlock' ||
125
+ theHero.value.blockType === 'VideoEmbedBlock' ||
126
+ theHero.value.blockType === 'ImageComparisonBlock'
108
127
  ) {
109
128
  return true
110
129
  }
@@ -251,24 +270,43 @@ const consolidatedSections = computed((): EduLessonSectionObject[] => {
251
270
 
252
271
  return filteredSections
253
272
  })
273
+ const computedClass = computed((): string => {
274
+ if (heroInline.value || heroEmpty.value) {
275
+ return 'pt-5 lg:pt-12'
276
+ } else if (!heroInline.value) {
277
+ return '-nav-offset'
278
+ }
279
+ return ''
280
+ })
254
281
  </script>
255
282
  <template>
256
283
  <div
257
284
  v-if="data"
258
- class="ThemeVariantLight pt-5 lg:pt-12"
285
+ class="ThemeVariantLight"
286
+ :class="computedClass"
259
287
  >
288
+ <!-- hero title -->
289
+ <HeroLarge
290
+ v-if="heroTitle && theHero"
291
+ :title="data.title"
292
+ :image="theHero.image"
293
+ :summary="theHero.heroSummary"
294
+ :custom-pill-type="data.__typename"
295
+ />
296
+
260
297
  <LayoutHelper
261
298
  indent="col-2"
262
299
  class="mb-10"
263
300
  >
264
301
  <DetailHeadline
302
+ v-if="data.title && !heroTitle"
265
303
  :title="data.title"
266
304
  label="Lesson"
267
305
  pill
268
306
  />
269
307
  <ShareButtonsEdu
270
308
  v-if="data?.url"
271
- class="mt-4"
309
+ :class="heroTitle ? 'mt-10' : 'mt-4'"
272
310
  :url="data.url"
273
311
  :title="data.title"
274
312
  :image="data.thumbnailImage?.original"
@@ -281,6 +319,7 @@ const consolidatedSections = computed((): EduLessonSectionObject[] => {
281
319
  <BaseLink
282
320
  class="font-normal inline text-action underline hover:text-action-dark cursor-pointer"
283
321
  variant="none"
322
+ :to="data.studentProject?.url"
284
323
  >
285
324
  View the Project Steps
286
325
  </BaseLink>
@@ -289,29 +328,30 @@ const consolidatedSections = computed((): EduLessonSectionObject[] => {
289
328
  <MetaPanel
290
329
  button="View Standards"
291
330
  theme="primary"
292
- :class="{ 'mb-10 lg:mb-14': heroInline || data?.hero?.length === 0 }"
331
+ :class="{ 'mb-10 lg:mb-14': heroTitle || heroInline || data?.hero?.length === 0 }"
293
332
  :primary-subject="data.primarySubject"
294
333
  :additional-subjects="data.additionalSubjects"
295
334
  :time="data.customTime ? { time: data.customTime } : data.time"
296
335
  :grade-levels="data.gradeLevels"
297
336
  :standards="data.standards"
298
- :negative-bottom="heroInline || data?.hero?.length !== 0"
337
+ :negative-bottom="(heroInline || data?.hero?.length !== 0) && !heroTitle"
299
338
  />
300
339
 
301
340
  <!-- hero media -->
302
341
  <HeroMedia
303
342
  v-if="
304
343
  !heroEmpty &&
344
+ !heroTitle &&
305
345
  !heroInline &&
306
- data?.hero?.length &&
307
- (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
346
+ theHero &&
347
+ (theHero.blockType === 'HeroImageBlock' || theHero.blockType === 'VideoBlock')
308
348
  "
309
349
  class="md:mb-12 lg:mb-18 mb-10"
310
- :image="data.hero[0].image"
311
- :video="data.hero[0].video"
312
- :display-caption="data.hero[0].displayCaption"
313
- :caption="data.hero[0].caption"
314
- :credit="data.hero[0].credit"
350
+ :image="theHero.image"
351
+ :video="theHero.video"
352
+ :display-caption="theHero.displayCaption"
353
+ :caption="theHero.caption"
354
+ :credit="theHero.credit"
315
355
  :constrain="data.heroConstrain"
316
356
  />
317
357
 
@@ -56,9 +56,12 @@ export const BaseStory = {
56
56
  hero: [
57
57
  {
58
58
  ...HeroMediaData,
59
- blockType: 'HeroImageBlock'
59
+ blockType: 'HeroImageBlock',
60
+ displayCaption: false
60
61
  }
61
62
  ],
63
+ heroConstrain: false,
64
+ heroPosition: 'full_bleed',
62
65
 
63
66
  lesson: {
64
67
  title: 'Lesson',
@@ -130,7 +133,7 @@ export const BaseStory = {
130
133
  materialsHeading: 'Custom Materials Heading',
131
134
  materialsImage: BlockImageData.image,
132
135
 
133
- stepsNumbering: false,
136
+ stepsNumbering: true,
134
137
 
135
138
  steps: [
136
139
  {
@@ -175,6 +178,29 @@ export const BaseStory = {
175
178
  }
176
179
  }
177
180
 
181
+ export const HeroTitle = {
182
+ args: {
183
+ data: {
184
+ ...BaseStory.args.data,
185
+ hero: [
186
+ {
187
+ ...HeroMediaData,
188
+ heroSummary: 'Text appears below the title',
189
+ blockType: 'HeroTitleBlock'
190
+ }
191
+ ]
192
+ }
193
+ }
194
+ }
195
+
196
+ export const InlineHero = {
197
+ args: {
198
+ data: {
199
+ ...BaseStory.args.data,
200
+ heroPosition: 'inline'
201
+ }
202
+ }
203
+ }
178
204
  export const HeroCarousel = {
179
205
  args: {
180
206
  data: {
@@ -223,7 +249,8 @@ export const HeroVideoEmbed = {
223
249
  embed: {
224
250
  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>`
225
251
  },
226
- blockType: 'VideoEmbedBlock'
252
+ blockType: 'VideoEmbedBlock',
253
+ displayCaption: false
227
254
  }
228
255
  ]
229
256
  }
@@ -6,6 +6,7 @@ import type {
6
6
  StreamfieldBlockData
7
7
  } from './../../../interfaces'
8
8
  import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
9
+ import HeroLarge from './../../../components/HeroLarge/HeroLarge.vue'
9
10
  import BaseLink from './../../../components/BaseLink/BaseLink.vue'
10
11
  import type { BlockHeadingObject } from '../../../components/BlockHeading/BlockHeading.vue'
11
12
  import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
@@ -83,17 +84,35 @@ const heroEmpty = computed((): boolean => {
83
84
  return data?.hero?.length === 0
84
85
  })
85
86
 
87
+ const theHero = computed(() => {
88
+ if (data?.hero?.length) {
89
+ return data.hero[0]
90
+ }
91
+ return undefined
92
+ })
93
+
94
+ const heroTitle = computed((): boolean => {
95
+ if (theHero.value) {
96
+ // excludes VideoBlock as this will autoplay
97
+ if (theHero.value.blockType === 'HeroTitleBlock') {
98
+ return true
99
+ }
100
+ }
101
+ return false
102
+ })
103
+
86
104
  const heroInline = computed((): boolean => {
87
105
  // heroes with interactive elements have special handling
88
- if (!heroEmpty.value && data?.hero) {
106
+ if (theHero.value && !heroTitle.value) {
89
107
  // excludes VideoBlock as this will autoplay
90
- if (data?.hero[0].blockType === 'VideoBlock') {
108
+ if (theHero.value.blockType === 'VideoBlock') {
91
109
  return false
92
110
  } else if (
93
- data?.hero[0].blockType === 'CarouselBlock' ||
94
- data?.hero[0].blockType === 'IframeEmbedBlock' ||
95
- data?.hero[0].blockType === 'VideoEmbedBlock' ||
96
- data?.hero[0].blockType === 'ImageComparisonBlock'
111
+ data?.heroPosition === 'inline' ||
112
+ theHero.value.blockType === 'CarouselBlock' ||
113
+ theHero.value.blockType === 'IframeEmbedBlock' ||
114
+ theHero.value.blockType === 'VideoEmbedBlock' ||
115
+ theHero.value.blockType === 'ImageComparisonBlock'
97
116
  ) {
98
117
  return true
99
118
  }
@@ -230,13 +249,34 @@ const consolidatedSections = computed((): EduStudentProjectSectionObject[] => {
230
249
  const studentBadge = computed(() => {
231
250
  return StudentProjectBadge
232
251
  })
252
+
253
+ const computedClass = computed((): string => {
254
+ if (heroInline.value || heroEmpty.value) {
255
+ return 'pt-5 lg:pt-12'
256
+ } else if (!heroInline.value) {
257
+ return '-nav-offset'
258
+ }
259
+ return ''
260
+ })
233
261
  </script>
234
262
  <template>
235
263
  <div
236
264
  v-if="data"
237
- class="ThemeVariantLight pt-5 lg:pt-12"
265
+ class="ThemeVariantLight"
266
+ :class="computedClass"
238
267
  >
239
- <div class="BaseGrid hidden lg:block container relative mx-auto z-20 pointer-events-none">
268
+ <!-- hero title -->
269
+ <HeroLarge
270
+ v-if="heroTitle && theHero"
271
+ :title="data.title"
272
+ :image="theHero.image"
273
+ :summary="theHero.heroSummary"
274
+ :custom-pill-type="data.__typename"
275
+ />
276
+ <div
277
+ v-if="!heroTitle"
278
+ class="BaseGrid hidden lg:block container relative mx-auto z-20 pointer-events-none"
279
+ >
240
280
  <div class="absolute top-0 left-0 col-start-1 col-end-3 text-right lg:-ml-2 xl:ml-4">
241
281
  <img
242
282
  :src="studentBadge"
@@ -252,7 +292,7 @@ const studentBadge = computed(() => {
252
292
  class="mb-10"
253
293
  >
254
294
  <DetailHeadline
255
- v-if="data.title"
295
+ v-if="data.title && !heroTitle"
256
296
  :title="data.title"
257
297
  label="Student Project"
258
298
  pill
@@ -260,7 +300,7 @@ const studentBadge = computed(() => {
260
300
  />
261
301
  <ShareButtonsEdu
262
302
  v-if="data?.url"
263
- class="mt-4"
303
+ :class="heroTitle ? 'mt-10' : 'mt-4'"
264
304
  :url="data.url"
265
305
  :title="data.title"
266
306
  :image="data.thumbnailImage?.original"
@@ -280,7 +320,10 @@ const studentBadge = computed(() => {
280
320
  </div>
281
321
  </LayoutHelper>
282
322
 
283
- <div class="container relative mx-auto z-20 pointer-events-none">
323
+ <div
324
+ v-if="!heroTitle"
325
+ class="container relative mx-auto z-20 pointer-events-none"
326
+ >
284
327
  <img
285
328
  :src="studentBadge"
286
329
  alt=""
@@ -293,15 +336,16 @@ const studentBadge = computed(() => {
293
336
  <HeroMedia
294
337
  v-if="
295
338
  !heroEmpty &&
339
+ !heroTitle &&
296
340
  !heroInline &&
297
- data?.hero?.length &&
298
- (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
341
+ theHero &&
342
+ (theHero.blockType === 'HeroImageBlock' || theHero.blockType === 'VideoBlock')
299
343
  "
300
- :image="data.hero[0].image"
301
- :video="data.hero[0].video"
302
- :display-caption="false"
303
- :caption="data.hero[0].caption"
304
- :credit="data.hero[0].credit"
344
+ :image="theHero.image"
345
+ :video="theHero.video"
346
+ :display-caption="theHero.displayCaption"
347
+ :caption="theHero.caption"
348
+ :credit="theHero.credit"
305
349
  :constrain="data.heroConstrain"
306
350
  />
307
351
  <LayoutHelper
@@ -322,7 +366,7 @@ const studentBadge = computed(() => {
322
366
  :additional-subjects="data.additionalSubjects"
323
367
  :time="data.customTime ? { time: data.customTime } : data.time"
324
368
  :standards="data.standards"
325
- :negative-top="!heroInline && !heroEmpty"
369
+ :negative-top="!heroInline && !heroEmpty && !heroTitle"
326
370
  >
327
371
  <template #metaInfo>
328
372
  <div :class="data?.standards ? 'border-b border-gray-light-mid' : ''">
@@ -56,7 +56,8 @@ export const BaseStory = {
56
56
  blockType: 'HeroImageBlock'
57
57
  }
58
58
  ],
59
- heroConstrain: true,
59
+ heroConstrain: false,
60
+ heroPosition: 'full_bleed',
60
61
 
61
62
  body: BlockStreamfieldTruncatedData.body,
62
63
 
@@ -67,6 +68,30 @@ export const BaseStory = {
67
68
  }
68
69
  }
69
70
 
71
+ export const HeroTitle = {
72
+ args: {
73
+ data: {
74
+ ...BaseStory.args.data,
75
+ hero: [
76
+ {
77
+ ...HeroMediaData,
78
+ heroSummary: 'Text appears below the title',
79
+ blockType: 'HeroTitleBlock'
80
+ }
81
+ ]
82
+ }
83
+ }
84
+ }
85
+
86
+ export const InlineHero = {
87
+ args: {
88
+ data: {
89
+ ...BaseStory.args.data,
90
+ heroPosition: 'inline'
91
+ }
92
+ }
93
+ }
94
+
70
95
  export const HeroCarousel = {
71
96
  args: {
72
97
  data: {
@@ -2,6 +2,7 @@
2
2
  import { computed, reactive, ref } from 'vue'
3
3
  import type { PageEduResourcesObject } from './../../../interfaces'
4
4
  import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
5
+ import HeroLarge from './../../../components/HeroLarge/HeroLarge.vue'
5
6
  import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
6
7
  import BlockText from './../../../components/BlockText/BlockText.vue'
7
8
  import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
@@ -33,17 +34,35 @@ const heroEmpty = computed((): boolean => {
33
34
  return data?.hero?.length === 0
34
35
  })
35
36
 
37
+ const theHero = computed(() => {
38
+ if (data?.hero?.length) {
39
+ return data.hero[0]
40
+ }
41
+ return undefined
42
+ })
43
+
44
+ const heroTitle = computed((): boolean => {
45
+ if (theHero.value) {
46
+ // excludes VideoBlock as this will autoplay
47
+ if (theHero.value.blockType === 'HeroTitleBlock') {
48
+ return true
49
+ }
50
+ }
51
+ return false
52
+ })
53
+
36
54
  const heroInline = computed((): boolean => {
37
55
  // heroes with interactive elements have special handling
38
- if (!heroEmpty.value && data?.hero) {
56
+ if (theHero.value && !heroTitle.value) {
39
57
  // excludes VideoBlock as this will autoplay
40
- if (data?.hero[0].blockType === 'VideoBlock') {
58
+ if (theHero.value.blockType === 'VideoBlock') {
41
59
  return false
42
60
  } else if (
43
- data?.hero[0].blockType === 'CarouselBlock' ||
44
- data?.hero[0].blockType === 'IframeEmbedBlock' ||
45
- data?.hero[0].blockType === 'VideoEmbedBlock' ||
46
- data?.hero[0].blockType === 'ImageComparisonBlock'
61
+ data?.heroPosition === 'inline' ||
62
+ theHero.value.blockType === 'CarouselBlock' ||
63
+ theHero.value.blockType === 'IframeEmbedBlock' ||
64
+ theHero.value.blockType === 'VideoEmbedBlock' ||
65
+ theHero.value.blockType === 'ImageComparisonBlock'
47
66
  ) {
48
67
  return true
49
68
  }
@@ -73,21 +92,29 @@ const computedClass = computed((): string => {
73
92
  :blocks="data.body"
74
93
  dropdown-text="In this Teachable Moment"
75
94
  />
76
-
95
+ <!-- hero title -->
96
+ <HeroLarge
97
+ v-if="heroTitle && theHero"
98
+ :title="data.title"
99
+ :image="theHero.image"
100
+ :summary="theHero.heroSummary"
101
+ :custom-pill-type="data.__typename"
102
+ />
77
103
  <!-- hero media -->
78
104
  <HeroMedia
79
105
  v-if="
80
106
  !heroEmpty &&
107
+ !heroTitle &&
81
108
  !heroInline &&
82
- data?.hero?.length &&
83
- (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
109
+ theHero &&
110
+ (theHero.blockType === 'HeroImageBlock' || theHero.blockType === 'VideoBlock')
84
111
  "
85
112
  class="md:mb-12 lg:mb-18 mb-10"
86
- :image="data.hero[0].image"
87
- :video="data.hero[0].video"
88
- :display-caption="data.hero[0].displayCaption"
89
- :caption="data.hero[0].caption"
90
- :credit="data.hero[0].credit"
113
+ :image="theHero.image"
114
+ :video="theHero.video"
115
+ :display-caption="theHero.displayCaption"
116
+ :caption="theHero.caption"
117
+ :credit="theHero.credit"
91
118
  :constrain="data.heroConstrain"
92
119
  />
93
120
 
@@ -96,6 +123,7 @@ const computedClass = computed((): string => {
96
123
  class="mb-10"
97
124
  >
98
125
  <DetailHeadline
126
+ v-if="!heroTitle"
99
127
  :title="data.title"
100
128
  :read-time="data.readTime"
101
129
  label="Teachable Moment"
@@ -103,7 +131,7 @@ const computedClass = computed((): string => {
103
131
  />
104
132
  <ShareButtonsEdu
105
133
  v-if="data?.url"
106
- class="mt-4"
134
+ :class="heroTitle ? 'mt-10' : 'mt-4'"
107
135
  :url="data.url"
108
136
  :title="data.title"
109
137
  :image="data.thumbnailImage?.original"
@@ -122,11 +150,11 @@ const computedClass = computed((): string => {
122
150
 
123
151
  <!-- summary and topper -->
124
152
  <LayoutHelper
153
+ v-if="data.topper && data.topper.length > 2"
125
154
  indent="col-3"
126
155
  class="lg:mb-8 mb-5"
127
156
  >
128
157
  <BlockText
129
- v-if="data.topper && data.topper.length > 2"
130
158
  class="lg:mb-8 mb-5"
131
159
  :text="data.topper"
132
160
  />