@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.
- package/components.d.ts +2 -0
- package/dist/explorer-1-vue.js +3228 -3167
- package/dist/explorer-1-vue.umd.cjs +10 -10
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Artists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Communicators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Designers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Disruptors.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Dreamers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Educators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Innovators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Inventors.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Makers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Problem_Solvers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Robiticists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Scientists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Software_Engineers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Thinkers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImages/JPL_is__Visualizers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Artists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Communicators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Designers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Disruptors.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Dreamers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Educators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Innovators.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Inventors.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Makers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Problem_Solvers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Robiticists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Scientists.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Software_Engineers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Thinkers.jpg +0 -0
- package/dist/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Visualizers.jpg +0 -0
- package/dist/src/components/BaseButton/BaseButton.stories.d.ts +1 -1
- package/dist/src/components/BaseButton/BaseButton.vue.d.ts +1 -1
- package/dist/src/components/BaseModal/BaseModal.stories.d.ts +2 -2
- package/dist/src/components/BlockStreamfield/BlockStreamfield.vue.d.ts +12 -0
- package/dist/src/components/HomepageMissionsCarousel/HomepageMissionsCarousel.stories.d.ts +5 -1
- package/dist/src/components/HomepageMissionsCarousel/HomepageMissionsCarouselItem.vue.d.ts +5 -1
- package/dist/src/components/SearchFilterGroup/SearchFilterGroup.vue.d.ts +1 -0
- package/dist/src/components/SearchResultCard/SearchResultCard.vue.d.ts +9 -0
- package/dist/src/components/SearchResultGridItem/SearchResultGridItem.vue.d.ts +9 -0
- package/dist/src/components/TextInput/TextInput.vue.d.ts +5 -2
- package/dist/src/components/YearTicker/YearTicker.stories.d.ts +1 -0
- package/dist/src/components/YearTicker/YearTicker.vue.d.ts +1 -0
- package/dist/src/interfaces.d.ts +4 -2
- package/dist/src/templates/edu/PageEduLesson/PageEduLesson.stories.d.ts +3 -5
- package/dist/src/templates/edu/PageEduStudentProject/PageEduStudentProject.stories.d.ts +601 -0
- package/dist/src/utils/generateHash.d.ts +1 -0
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/BlockIframeEmbed/BlockIframeEmbed.vue +1 -1
- package/src/components/BlockImage/BlockImage.vue +4 -1
- package/src/components/BlockImage/BlockImageFullBleed.vue +5 -2
- package/src/components/BlockImage/BlockImageStandard.vue +5 -2
- package/src/components/BlockImageCarouselItem/BlockImageCarouselItem.vue +5 -2
- package/src/components/BlockImageComparison/BlockImageComparison.vue +5 -2
- package/src/components/BlockInlineImage/BlockInlineImage.vue +9 -2
- package/src/components/BlockStreamfield/BlockStreamfield.vue +46 -23
- package/src/components/BlockVideo/BlockVideo.vue +1 -1
- package/src/components/BlockVideoEmbed/BlockVideoEmbed.vue +1 -1
- package/src/components/HeroInlineMedia/HeroInlineMedia.vue +2 -21
- package/src/components/HeroMedia/HeroMedia.vue +1 -1
- package/src/components/MetaPanel/MetaPanel.vue +49 -44
- package/src/components/SearchResultsList/SearchResultsList.vue +4 -1
- package/src/templates/edu/PageEduLesson/PageEduLesson.stories.js +3 -3
- package/src/templates/edu/PageEduLesson/PageEduLesson.vue +9 -7
- package/src/templates/edu/PageEduLesson/PageEduLessonSection.vue +3 -8
- package/src/templates/edu/PageEduStudentProject/PageEduStudentProject.stories.js +253 -0
- package/src/templates/edu/PageEduStudentProject/PageEduStudentProject.vue +419 -0
- 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>
|