@explorer-1/vue 0.2.50 → 0.2.52

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.
Files changed (71) hide show
  1. package/components.d.ts +2 -0
  2. package/dist/explorer-1-vue.js +3228 -3167
  3. package/dist/explorer-1-vue.umd.cjs +10 -10
  4. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Artists.jpg +0 -0
  5. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Communicators.jpg +0 -0
  6. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Designers.jpg +0 -0
  7. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Disruptors.jpg +0 -0
  8. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Dreamers.jpg +0 -0
  9. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Educators.jpg +0 -0
  10. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Innovators.jpg +0 -0
  11. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Inventors.jpg +0 -0
  12. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Makers.jpg +0 -0
  13. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Problem_Solvers.jpg +0 -0
  14. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Robiticists.jpg +0 -0
  15. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Scientists.jpg +0 -0
  16. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Software_Engineers.jpg +0 -0
  17. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Thinkers.jpg +0 -0
  18. package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Visualizers.jpg +0 -0
  19. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Artists.jpg +0 -0
  20. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Communicators.jpg +0 -0
  21. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Designers.jpg +0 -0
  22. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Disruptors.jpg +0 -0
  23. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Dreamers.jpg +0 -0
  24. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Educators.jpg +0 -0
  25. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Innovators.jpg +0 -0
  26. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Inventors.jpg +0 -0
  27. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Makers.jpg +0 -0
  28. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Problem_Solvers.jpg +0 -0
  29. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Robiticists.jpg +0 -0
  30. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Scientists.jpg +0 -0
  31. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Software_Engineers.jpg +0 -0
  32. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Thinkers.jpg +0 -0
  33. package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Visualizers.jpg +0 -0
  34. package/dist/src/components/BaseButton/BaseButton.stories.d.ts +1 -1
  35. package/dist/src/components/BaseButton/BaseButton.vue.d.ts +1 -1
  36. package/dist/src/components/BaseModal/BaseModal.stories.d.ts +2 -2
  37. package/dist/src/components/BlockStreamfield/BlockStreamfield.vue.d.ts +12 -0
  38. package/dist/src/components/HomepageMissionsCarousel/HomepageMissionsCarousel.stories.d.ts +5 -1
  39. package/dist/src/components/HomepageMissionsCarousel/HomepageMissionsCarouselItem.vue.d.ts +5 -1
  40. package/dist/src/components/SearchFilterGroup/SearchFilterGroup.vue.d.ts +1 -0
  41. package/dist/src/components/SearchResultCard/SearchResultCard.vue.d.ts +9 -0
  42. package/dist/src/components/SearchResultGridItem/SearchResultGridItem.vue.d.ts +9 -0
  43. package/dist/src/components/TextInput/TextInput.vue.d.ts +5 -2
  44. package/dist/src/components/YearTicker/YearTicker.stories.d.ts +1 -0
  45. package/dist/src/components/YearTicker/YearTicker.vue.d.ts +1 -0
  46. package/dist/src/interfaces.d.ts +4 -2
  47. package/dist/src/templates/edu/PageEduLesson/PageEduLesson.stories.d.ts +3 -5
  48. package/dist/src/templates/edu/PageEduStudentProject/PageEduStudentProject.stories.d.ts +601 -0
  49. package/dist/src/utils/generateHash.d.ts +1 -0
  50. package/dist/style.css +1 -1
  51. package/package.json +2 -2
  52. package/src/components/BlockIframeEmbed/BlockIframeEmbed.vue +1 -1
  53. package/src/components/BlockImage/BlockImage.vue +4 -1
  54. package/src/components/BlockImage/BlockImageFullBleed.vue +5 -2
  55. package/src/components/BlockImage/BlockImageStandard.vue +5 -2
  56. package/src/components/BlockImageCarouselItem/BlockImageCarouselItem.vue +5 -2
  57. package/src/components/BlockImageComparison/BlockImageComparison.vue +5 -2
  58. package/src/components/BlockInlineImage/BlockInlineImage.vue +9 -2
  59. package/src/components/BlockStreamfield/BlockStreamfield.vue +46 -23
  60. package/src/components/BlockVideo/BlockVideo.vue +1 -1
  61. package/src/components/BlockVideoEmbed/BlockVideoEmbed.vue +1 -1
  62. package/src/components/HeroInlineMedia/HeroInlineMedia.vue +2 -21
  63. package/src/components/HeroMedia/HeroMedia.vue +1 -1
  64. package/src/components/MetaPanel/MetaPanel.vue +49 -44
  65. package/src/components/SearchResultsList/SearchResultsList.vue +4 -1
  66. package/src/templates/edu/PageEduLesson/PageEduLesson.stories.js +3 -3
  67. package/src/templates/edu/PageEduLesson/PageEduLesson.vue +9 -7
  68. package/src/templates/edu/PageEduLesson/PageEduLessonSection.vue +3 -8
  69. package/src/templates/edu/PageEduStudentProject/PageEduStudentProject.stories.js +253 -0
  70. package/src/templates/edu/PageEduStudentProject/PageEduStudentProject.vue +419 -0
  71. package/src/templates/edu/PageEduStudentProject/PageEduStudentProjectSection.vue +179 -0
@@ -0,0 +1,419 @@
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref } from 'vue'
3
+ import type {
4
+ ImageObject,
5
+ PageEduResourcesObject,
6
+ StreamfieldBlockData
7
+ } from './../../../interfaces'
8
+ import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
9
+ import BaseLink from './../../../components/BaseLink/BaseLink.vue'
10
+ import type { BlockHeadingObject } from '../../../components/BlockHeading/BlockHeading.vue'
11
+ import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
12
+ import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
13
+ import DetailHeadline from './../../../components/DetailHeadline/DetailHeadline.vue'
14
+ import ShareButtonsEdu from './../../../components/ShareButtonsEdu/ShareButtonsEdu.vue'
15
+ import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
16
+ import BlockRelatedLinks from '../../../components/BlockRelatedLinks/BlockRelatedLinks.vue'
17
+ import MetaPanel from '../../../components/MetaPanel/MetaPanel.vue'
18
+ import MetaPanelItems from '../../../components/MetaPanelItems/MetaPanelItems.vue'
19
+ import PageEduStudentProjectSection, {
20
+ type PageEduStudentProjectSectionProps
21
+ } from './PageEduStudentProjectSection.vue'
22
+ import NavJumpMenu from './../../../components/NavJumpMenu/NavJumpMenu.vue'
23
+ import HeroInlineMedia from './../../../components/HeroInlineMedia/HeroInlineMedia.vue'
24
+ import AboutTheAuthor from './../../../components/AboutTheAuthor/AboutTheAuthor.vue'
25
+
26
+ import { HeadingLevel } from '../../../components/BaseHeading/BaseHeading.vue'
27
+ import StudentProjectBadge from '@explorer-1/common/src/images/svg/student-project-badge.svg'
28
+
29
+ interface EduStudentProjectSectionObject extends PageEduStudentProjectSectionProps {
30
+ type?: string
31
+ }
32
+ export interface EduStudentProjectStep {
33
+ heading?: string
34
+ media?: StreamfieldBlockData[]
35
+ content?: StreamfieldBlockData[]
36
+ }
37
+
38
+ interface PageEduStudentProjectObject extends PageEduResourcesObject {
39
+ [key: string]: any
40
+ studentProject: {
41
+ title: string
42
+ urlPath: string
43
+ }
44
+ overview: StreamfieldBlockData[]
45
+ overviewHeading: string
46
+ overviewImage: ImageObject
47
+ materials: string
48
+ materialsHeading: string
49
+ materialsImage: ImageObject
50
+ steps: EduStudentProjectStep[]
51
+ stepsHeading: string
52
+ stepsStepsNumbering?: boolean
53
+ customSections: any
54
+ }
55
+ interface PageEduStudentProjectProps {
56
+ data?: PageEduStudentProjectObject
57
+ }
58
+
59
+ const props = withDefaults(defineProps<PageEduStudentProjectProps>(), {
60
+ data: undefined
61
+ })
62
+
63
+ const { data } = reactive(props)
64
+
65
+ const PageEduStudentProjectJumpMenu = ref()
66
+
67
+ defineExpose({
68
+ PageEduStudentProjectJumpMenu
69
+ })
70
+
71
+ const stringAsHeadingBlockData = (
72
+ heading: HeadingLevel,
73
+ overrideText?: string
74
+ ): BlockHeadingObject => {
75
+ return {
76
+ blockType: 'HeadingBlock',
77
+ heading: (overrideText || heading) as HeadingLevel,
78
+ level: 'h2'
79
+ }
80
+ }
81
+
82
+ const heroEmpty = computed((): boolean => {
83
+ return data?.hero?.length === 0
84
+ })
85
+
86
+ const heroInline = computed((): boolean => {
87
+ // heroes with interactive elements have special handling
88
+ if (!heroEmpty.value && data?.hero) {
89
+ // excludes VideoBlock as this will autoplay
90
+ if (data?.hero[0].blockType === 'VideoBlock') {
91
+ return false
92
+ } 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'
97
+ ) {
98
+ return true
99
+ }
100
+ }
101
+ return false
102
+ })
103
+
104
+ const sectionOrder = ['top', 'overview', 'materials', 'steps', 'bottom']
105
+
106
+ // mimic HeadingBlock data shape for defined section headings
107
+ const staticSectionHeadings = computed((): { [key: string]: BlockHeadingObject } | undefined => {
108
+ if (data) {
109
+ const result = sectionOrder.reduce<Record<string, BlockHeadingObject>>((acc, section) => {
110
+ // only include the heading if the section has content
111
+ if (data[section]?.length) {
112
+ const headingText =
113
+ section === 'steps' ? 'Project Steps' : section.charAt(0).toUpperCase() + section.slice(1)
114
+ acc[section] = stringAsHeadingBlockData(
115
+ (data[`${section}Heading`] as HeadingLevel) || headingText
116
+ )
117
+ }
118
+ return acc
119
+ }, {})
120
+ return result
121
+ }
122
+ return undefined
123
+ })
124
+
125
+ const keyedCustomSections = computed(
126
+ ():
127
+ | {
128
+ [key: string]: StreamfieldBlockData[]
129
+ }
130
+ | undefined => {
131
+ if (data) {
132
+ const result = data.customSections.reduce(
133
+ (
134
+ acc: { [key: string]: StreamfieldBlockData[] },
135
+ section: {
136
+ heading: StreamfieldBlockData
137
+ content: StreamfieldBlockData[]
138
+ position: string
139
+ }
140
+ ) => {
141
+ const position = section.position
142
+ if (!acc[position]) {
143
+ acc[position] = []
144
+ }
145
+ acc[position].push(section.heading)
146
+ acc[position].push(...section.content)
147
+ return acc
148
+ },
149
+ {}
150
+ )
151
+ return result
152
+ }
153
+ return undefined
154
+ }
155
+ )
156
+
157
+ const consolidatedBlocks = computed(() => {
158
+ // NavJumpMenu handles filtering for HeadingBlock, so we don't need to do that here
159
+ const blocks = []
160
+ // include custom top blocks
161
+ if (keyedCustomSections.value && keyedCustomSections.value['top']) {
162
+ blocks.push(...keyedCustomSections.value['top'])
163
+ }
164
+ // include predefined section blocks
165
+ sectionOrder.forEach((section) => {
166
+ if (data && data[section]) {
167
+ if (staticSectionHeadings.value && staticSectionHeadings.value[section]) {
168
+ blocks.push(staticSectionHeadings.value[section])
169
+ }
170
+ if (section !== 'materials' && section !== 'steps') {
171
+ blocks.push(...data[section])
172
+ } else if (section === 'steps' && data.steps?.length) {
173
+ // get blocks in nested steps
174
+ data.steps.forEach((item) => {
175
+ if (item.content?.length) {
176
+ blocks.push(...item.content)
177
+ }
178
+ })
179
+ }
180
+ }
181
+ // include custom "after_" blocks
182
+ if (keyedCustomSections.value && keyedCustomSections.value[`after_${section}`]) {
183
+ blocks.push(...keyedCustomSections.value[`after_${section}`])
184
+ }
185
+ })
186
+ // include custom bottom blocks
187
+ if (keyedCustomSections.value && keyedCustomSections.value['bottom']) {
188
+ blocks.push(...keyedCustomSections.value['bottom'])
189
+ }
190
+ // include body blocks
191
+ if (data?.body?.length) {
192
+ blocks.push(...data.body)
193
+ }
194
+
195
+ return blocks
196
+ })
197
+
198
+ // organize data to render with PageEduStudentProjectSection component
199
+ const consolidatedSections = computed((): EduStudentProjectSectionObject[] => {
200
+ const sections: EduStudentProjectSectionObject[] = []
201
+ // include custom top section
202
+ if (keyedCustomSections.value && keyedCustomSections.value['top']) {
203
+ sections.push({ type: 'streamfield', blocks: keyedCustomSections.value['top'] })
204
+ }
205
+ sectionOrder.forEach((section) => {
206
+ if (data && data[section]) {
207
+ sections.push({
208
+ heading: staticSectionHeadings.value ? staticSectionHeadings.value[section] : undefined,
209
+ blocks: section !== 'materials' && section !== 'steps' ? data[section] : undefined,
210
+ text: section === 'materials' ? data[section] : undefined,
211
+ steps: section === 'steps' ? data[section] : undefined
212
+ })
213
+ }
214
+ // include custom "after_" sections
215
+ if (keyedCustomSections.value && keyedCustomSections.value[`after_${section}`]) {
216
+ sections.push({ type: 'streamfield', blocks: keyedCustomSections.value[`after_${section}`] })
217
+ }
218
+ })
219
+ // include custom bottom section
220
+ if (keyedCustomSections.value && keyedCustomSections.value['bottom']) {
221
+ sections.push({ type: 'streamfield', blocks: keyedCustomSections.value['bottom'] })
222
+ }
223
+ const filteredSections = sections.filter(
224
+ (item) => item.text || item.blocks?.length || item.steps?.length
225
+ )
226
+
227
+ return filteredSections
228
+ })
229
+
230
+ const studentBadge = computed(() => {
231
+ return StudentProjectBadge
232
+ })
233
+ </script>
234
+ <template>
235
+ <div
236
+ v-if="data"
237
+ class="ThemeVariantLight pt-5 lg:pt-12"
238
+ >
239
+ <div class="BaseGrid hidden lg:block container relative mx-auto z-20 pointer-events-none">
240
+ <div class="absolute top-0 left-0 col-start-1 col-end-3 text-right lg:-ml-2 xl:ml-4">
241
+ <img
242
+ :src="studentBadge"
243
+ alt=""
244
+ width="200"
245
+ height="200"
246
+ class="block w-full h-auto"
247
+ />
248
+ </div>
249
+ </div>
250
+ <LayoutHelper
251
+ indent="col-3"
252
+ class="mb-10"
253
+ >
254
+ <DetailHeadline
255
+ v-if="data.title"
256
+ :title="data.title"
257
+ label="Student Project"
258
+ pill
259
+ pill-color="secondary"
260
+ />
261
+ <ShareButtonsEdu
262
+ v-if="data?.url"
263
+ class="mt-4"
264
+ :url="data.url"
265
+ :title="data.title"
266
+ :image="data.thumbnailImage?.original"
267
+ />
268
+ <div
269
+ v-if="data?.lesson?.url"
270
+ class="mt-8 font-bold text-body-lg"
271
+ >
272
+ Want to teach this?
273
+ <BaseLink
274
+ class="font-normal inline text-action underline hover:text-action-dark cursor-pointer"
275
+ variant="none"
276
+ :to="data.lesson.url"
277
+ >
278
+ View the Lesson Plan
279
+ </BaseLink>
280
+ </div>
281
+ </LayoutHelper>
282
+
283
+ <div class="container relative mx-auto z-20 pointer-events-none">
284
+ <img
285
+ :src="studentBadge"
286
+ alt=""
287
+ width="150"
288
+ height="150"
289
+ class="absolute -mt-16 sm:-mt-24 right-0 lg:hidden md:w-[185px] md:h-[185px]"
290
+ />
291
+ </div>
292
+ <!-- hero media -->
293
+ <HeroMedia
294
+ v-if="
295
+ !heroEmpty &&
296
+ !heroInline &&
297
+ data?.hero?.length &&
298
+ (data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
299
+ "
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"
305
+ :constrain="data.heroConstrain"
306
+ />
307
+ <LayoutHelper
308
+ v-else-if="!heroEmpty && heroInline && data.hero?.length"
309
+ class="lg:mb-14 mb-10"
310
+ >
311
+ <HeroInlineMedia
312
+ :hero-blocks="data.hero"
313
+ :constrain="data.heroConstrain"
314
+ />
315
+ </LayoutHelper>
316
+
317
+ <MetaPanel
318
+ button="Info for Teachers"
319
+ theme="stars"
320
+ :class="{ 'mb-10 lg:mb-14': true }"
321
+ :primary-subject="data.primarySubject"
322
+ :additional-subjects="data.additionalSubjects"
323
+ :time="data.customTime ? { time: data.customTime } : data.time"
324
+ :standards="data.standards"
325
+ :negative-top="!heroInline && !heroEmpty"
326
+ >
327
+ <template #metaInfo>
328
+ <div :class="data?.standards ? 'border-b border-gray-light-mid' : ''">
329
+ <div class="py-6 lg:py-8">
330
+ <MetaPanelItems :grade-levels="data?.gradeLevels" />
331
+ <div
332
+ v-if="data.lesson"
333
+ class="mt-8 font-bold text-body-s"
334
+ >
335
+ Want to teach this?
336
+ <BaseLink
337
+ class="font-normal inline text-action underline hover:text-action-dark cursor-pointer"
338
+ variant="none"
339
+ :to="data.lesson.url"
340
+ >
341
+ View the Lesson Plan
342
+ </BaseLink>
343
+ </div>
344
+ </div>
345
+ </div>
346
+ </template>
347
+ </MetaPanel>
348
+
349
+ <NavJumpMenu
350
+ ref="PageEduStudentProjectJumpMenu"
351
+ :title="data.title"
352
+ :blocks="consolidatedBlocks"
353
+ dropdown-text="In this project"
354
+ />
355
+
356
+ <template
357
+ v-for="(value, _key) in consolidatedSections"
358
+ :key="_key"
359
+ >
360
+ <BlockStreamfield
361
+ v-if="value.type === 'streamfield'"
362
+ :data="value.blocks"
363
+ />
364
+ <PageEduStudentProjectSection
365
+ v-else
366
+ :heading="value.heading"
367
+ :blocks="value.blocks"
368
+ :steps="value.steps"
369
+ :steps-numbering="data.stepsNumbering"
370
+ :text="value.text"
371
+ :image="value.image"
372
+ />
373
+ </template>
374
+
375
+ <!-- streamfield blocks -->
376
+ <BlockStreamfield
377
+ v-if="data.body?.length"
378
+ :data="data.body"
379
+ />
380
+
381
+ <!-- related links -->
382
+ <LayoutHelper
383
+ v-if="data.relatedLinks && data.relatedLinks.length"
384
+ indent="col-3"
385
+ class="lg:my-18 my-10"
386
+ >
387
+ <BlockRelatedLinks :data="data.relatedLinks[0]" />
388
+ </LayoutHelper>
389
+
390
+ <!-- related content -->
391
+ <BlockLinkCarousel
392
+ item-type="cards"
393
+ class="lg:my-24 my-12 print:px-4"
394
+ :heading="data.relatedContentHeading || 'Related Projects'"
395
+ :items="data.relatedContent"
396
+ />
397
+
398
+ <LayoutHelper
399
+ v-if="data.authors?.length"
400
+ indent="col-3"
401
+ >
402
+ <AboutTheAuthor :authors="data.authors" />
403
+ </LayoutHelper>
404
+
405
+ <LayoutHelper
406
+ v-if="data.lastPublishedAt"
407
+ indent="col-3"
408
+ class="lg:my-18 my-10"
409
+ >
410
+ <p class="border-t border-gray-light-mid pt-8">
411
+ <strong>Lesson Last Updated:</strong>
412
+ {{
413
+ // @ts-ignore
414
+ $filters.displayDate(data.lastPublishedAt)
415
+ }}
416
+ </p>
417
+ </LayoutHelper>
418
+ </div>
419
+ </template>
@@ -0,0 +1,179 @@
1
+ <script setup lang="ts">
2
+ import { reactive } from 'vue'
3
+ import type { ImageObject, StreamfieldBlockData } from './../../../interfaces'
4
+ import BlockHeading, {
5
+ type BlockHeadingObject
6
+ } from './../../../components/BlockHeading/BlockHeading.vue'
7
+ import type { EduStudentProjectStep } from './PageEduStudentProject.vue'
8
+ import BaseHeading from './../../../components/BaseHeading/BaseHeading.vue'
9
+ import HeroInlineMedia from './../../../components/HeroInlineMedia/HeroInlineMedia.vue'
10
+ import BlockText from './../../../components/BlockText/BlockText.vue'
11
+ import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
12
+ import BlockImageStandard from './../../../components/BlockImage/BlockImageStandard.vue'
13
+ import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
14
+
15
+ export interface PageEduStudentProjectSectionProps {
16
+ heading?: BlockHeadingObject
17
+ blocks?: StreamfieldBlockData[]
18
+ steps?: EduStudentProjectStep[]
19
+ stepsNumbering?: boolean
20
+ text?: string
21
+ image?: ImageObject
22
+ }
23
+
24
+ const props = withDefaults(defineProps<PageEduStudentProjectSectionProps>(), {
25
+ heading: undefined,
26
+ blocks: undefined,
27
+ steps: undefined,
28
+ stepsNumbering: false,
29
+ text: undefined,
30
+ image: undefined
31
+ })
32
+
33
+ const { heading, blocks, image, steps, stepsNumbering, text } = reactive(props)
34
+ </script>
35
+ <template>
36
+ <section
37
+ class="PageEduStudentProjectSection"
38
+ :aria-label="heading?.heading"
39
+ >
40
+ <LayoutHelper
41
+ v-if="heading"
42
+ indent="col-3"
43
+ class="lg:mb-8 mb-5"
44
+ >
45
+ <BlockHeading
46
+ :data="heading"
47
+ generate-id
48
+ />
49
+ </LayoutHelper>
50
+
51
+ <LayoutHelper
52
+ v-if="image"
53
+ indent="col-2"
54
+ class="lg:mb-8 mb-5"
55
+ >
56
+ <!-- image goes here -->
57
+ <BlockImageStandard :data="image" />
58
+ </LayoutHelper>
59
+
60
+ <!-- regular streamfield -->
61
+ <BlockStreamfield
62
+ v-if="blocks"
63
+ :data="blocks"
64
+ />
65
+
66
+ <!-- steps -->
67
+ <component
68
+ :is="stepsNumbering ? 'ol' : 'ul'"
69
+ v-else-if="steps?.length"
70
+ >
71
+ <li
72
+ v-for="(step, index) in steps"
73
+ :key="index"
74
+ class="PageEduStudentProjectStep lg:mb-10 mb-8 px-4 lg:px-0"
75
+ >
76
+ <LayoutHelper
77
+ class="ThemeVariantGray bg-gray-light py-6 lg:py-14 px-4 lg:px-0"
78
+ indent="col-2"
79
+ >
80
+ <template v-if="step.media?.length">
81
+ <!-- split 50/50 -->
82
+ <div class="lg:grid grid-cols-2 gap-6 lg:gap-10">
83
+ <div
84
+ :class="index % 2 === 0 ? 'order-1' : 'order-2'"
85
+ class="mb-6 lg:mb-0"
86
+ >
87
+ <BaseHeading
88
+ level="h3"
89
+ class="mb-5"
90
+ >
91
+ <span
92
+ v-if="stepsNumbering"
93
+ class="steps-numbering"
94
+ aria-hidden
95
+ >{{ `Step ${index + 1}:` }}</span
96
+ >
97
+ {{ step.heading }}
98
+ </BaseHeading>
99
+ <BlockStreamfield
100
+ v-if="step.content"
101
+ :data="step.content"
102
+ size="medium"
103
+ variant="fluid"
104
+ />
105
+ </div>
106
+ <HeroInlineMedia
107
+ :hero-blocks="step.media"
108
+ :class="index % 2 === 1 ? 'order-1' : 'order-2'"
109
+ constrain
110
+ />
111
+ </div>
112
+ </template>
113
+ <template v-else>
114
+ <BaseHeading
115
+ level="h3"
116
+ class="mb-5"
117
+ >
118
+ <span
119
+ v-if="stepsNumbering"
120
+ aria-hidden
121
+ class="steps-numbering"
122
+ >{{ `Step ${index + 1}:` }}</span
123
+ >
124
+ {{ step.heading }}
125
+ </BaseHeading>
126
+ <BlockStreamfield
127
+ v-if="step.content"
128
+ class="PageEduStudentProjectStep__fullWidth"
129
+ :data="step.content"
130
+ size="medium"
131
+ variant="fluid"
132
+ />
133
+ </template>
134
+ </LayoutHelper>
135
+ </li>
136
+ </component>
137
+
138
+ <!-- simple richtext -->
139
+ <LayoutHelper
140
+ v-else-if="text"
141
+ indent="col-3"
142
+ class="lg:mb-18 mb-10"
143
+ >
144
+ <BlockText :text="text" />
145
+ </LayoutHelper>
146
+ </section>
147
+ </template>
148
+ <style lang="scss">
149
+ @use 'sass:math';
150
+ @function pxToRem($pxValue) {
151
+ // Assumes font-size for body element is a constant 16px
152
+ @return math.div($pxValue, 16) * 1rem;
153
+ }
154
+ .PageEduStudentProjectStep {
155
+ .BlockStreamfield {
156
+ div:last-child {
157
+ @apply mb-0 #{!important};
158
+ }
159
+ }
160
+ .caption-area {
161
+ @apply px-0;
162
+ }
163
+ .steps-numbering {
164
+ // intentionally overriding correction that occurs within ThemeVariantGray
165
+ @apply text-jpl-red;
166
+ }
167
+
168
+ .PageEduStudentProjectStep__fullWidth {
169
+ .LayoutHelper > div > .BlockText {
170
+ @screen lg {
171
+ @apply mr-[10rem];
172
+ }
173
+ @screen xl {
174
+ @apply mr-[14rem];
175
+ }
176
+ }
177
+ }
178
+ }
179
+ </style>