@explorer-1/vue 0.2.51 → 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 +48 -43
  65. package/src/components/SearchResultsList/SearchResultsList.vue +4 -1
  66. package/src/templates/edu/PageEduLesson/PageEduLesson.stories.js +1 -1
  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
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, reactive } from 'vue'
3
- import { camelCase } from 'lodash'
2
+ import { reactive } from 'vue'
4
3
  import type { ImageObject, StreamfieldBlockData } from './../../../interfaces'
5
4
  import BlockHeading, {
6
5
  type BlockHeadingObject
@@ -28,19 +27,15 @@ const props = withDefaults(defineProps<PageEduLessonSectionProps>(), {
28
27
  image: undefined
29
28
  })
30
29
 
31
- const { heading, blocks, image } = reactive(props)
32
-
33
- const anchorId = computed(() => {
34
- return 'lesson_' + camelCase(heading?.heading)
35
- })
30
+ const { heading, blocks, image, procedures, text } = reactive(props)
36
31
  </script>
37
32
  <template>
38
33
  <section
39
- :id="anchorId"
40
34
  class="PageEduLessonSection"
41
35
  :aria-label="heading?.heading"
42
36
  >
43
37
  <LayoutHelper
38
+ v-if="heading"
44
39
  indent="col-3"
45
40
  class="lg:mb-8 mb-5"
46
41
  >
@@ -0,0 +1,253 @@
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 { BlockInlineImageData } from './../../../components/BlockInlineImage/BlockInlineImage.stories'
6
+ import { BlockHeadingData } from './../../../components/BlockHeading/BlockHeading.stories'
7
+ import { BlockImageComparisonData } from './../../../components/BlockImageComparison/BlockImageComparison.stories'
8
+ import { BaseVideoData } from './../../../components/BaseVideo/BaseVideo.stories'
9
+ import { BlockVideoEmbedData } from './../../../components/BlockVideoEmbed/BlockVideoEmbed.stories'
10
+ import { BlockRelatedLinksData } from './../../../components/BlockRelatedLinks/BlockRelatedLinks.stories.js'
11
+ import { BlockLinkCardCarouselData } from './../../../components/BlockLinkCarousel/BlockLinkCarousel.stories.js'
12
+ import {
13
+ BlockStreamfieldTruncatedData,
14
+ BlockStreamfieldMinimalData
15
+ } from './../../../components/BlockStreamfield/BlockStreamfield.stories'
16
+ import PageEduStudentProject from './PageEduStudentProject.vue'
17
+ import { AboutTheAuthorData } from './../../../components/AboutTheAuthor/AboutTheAuthor.stories'
18
+
19
+ export default {
20
+ title: 'Templates/EDU/PageEduStudentProject',
21
+ component: PageEduStudentProject,
22
+ tags: ['!autodocs'],
23
+ decorators: [
24
+ () => ({
25
+ template: `<div id="storyDecorator" class="disable-nav-offset"><story/></div>`
26
+ })
27
+ ],
28
+ parameters: {
29
+ layout: 'fullscreen',
30
+ html: {
31
+ root: '#storyDecorator'
32
+ }
33
+ },
34
+ excludeStories: /.*Data$/
35
+ }
36
+
37
+ export const BaseStory = {
38
+ args: {
39
+ data: {
40
+ __typename: 'EDUStudentProjectPage',
41
+ title: 'Test Student Project',
42
+ url: '/edu/resources/test-student-project',
43
+ pageType: 'EDUStudentProjectPage',
44
+ contentType: 'edu_resources.EDUStudentProjectPage',
45
+ searchDescription: '',
46
+ seoTitle: 'Test Student Project',
47
+ slug: 'test-student-project',
48
+ publicationDate: '2024-08-16',
49
+ lastPublishedAt: '2024-08-22T02:33:13.507206+00:00',
50
+ thumbnailImage: {
51
+ __typename: 'CustomImage',
52
+ original: 'http://127.0.0.1:9000/media/original_images/imagessirtfsirtf-090303-16.jpg',
53
+ alt: ''
54
+ },
55
+ authors: AboutTheAuthorData,
56
+ hero: [
57
+ {
58
+ ...HeroMediaData,
59
+ blockType: 'HeroImageBlock'
60
+ }
61
+ ],
62
+
63
+ lesson: {
64
+ title: 'Lesson',
65
+ url: '/path-to-student-project'
66
+ },
67
+
68
+ primarySubject: {
69
+ subject: 'Arts'
70
+ },
71
+ additionalSubjects: [
72
+ {
73
+ subject: 'Science'
74
+ }
75
+ ],
76
+ gradeLevels: [
77
+ {
78
+ gradeLevel: 'K'
79
+ },
80
+ {
81
+ gradeLevel: '1'
82
+ }
83
+ ],
84
+ time: {
85
+ time: 'Under 30 mins'
86
+ },
87
+ customTime: undefined,
88
+ standards: [
89
+ {
90
+ standard: {
91
+ code: 'CCRA.R.1',
92
+ definition:
93
+ 'Read closely to determine what the text says explicitly and to make logical inferences from it; cite specific textual evidence when writing or speaking to support conclusions drawn from the text.',
94
+ domain: {
95
+ domain: 'College and Career Readiness Anchor Standards for Reading'
96
+ },
97
+ type: 'ccss_english_language_arts'
98
+ }
99
+ },
100
+ {
101
+ standard: {
102
+ code: 'RL.1.5',
103
+ definition:
104
+ 'Explain major differences between books that tell stories and books that give information, drawing on a wide reading of a range of text types.',
105
+ domain: {
106
+ domain: 'Reading Standards for Literature'
107
+ },
108
+ type: 'ccss_english_language_arts'
109
+ }
110
+ },
111
+ {
112
+ standard: {
113
+ code: 'K-PS2-1',
114
+ definition:
115
+ 'Plan and conduct an investigation to compare the effects of different strengths or different directions of pushes and pulls on the motion of an object.',
116
+ domain: {
117
+ domain: 'Physical Sciences'
118
+ },
119
+ type: 'ngss'
120
+ }
121
+ }
122
+ ],
123
+
124
+ overview: BlockStreamfieldMinimalData.body,
125
+ overviewHeading: undefined,
126
+ overviewImage: BlockImageData.image,
127
+
128
+ materials:
129
+ '<ul><li data-block-key="nvq4l">list item one</li><li data-block-key="efmt7">list item two</li><li data-block-key="d0f66">list item three this one is really long and the text just keeps on going lorem ipsum dolor sit amet consectatur</li></ul><p data-block-key="bksrq">Paragraph to appear below.</p>',
130
+ materialsHeading: 'Custom Materials Heading',
131
+ materialsImage: BlockImageData.image,
132
+
133
+ stepsNumbering: false,
134
+
135
+ steps: [
136
+ {
137
+ heading: 'Lorem ipsum dolor no media',
138
+ media: [],
139
+ content: [BlockImageData, ...BlockStreamfieldMinimalData.body, BlockInlineImageData.block]
140
+ },
141
+ {
142
+ heading: 'Sit amet',
143
+ media: [
144
+ {
145
+ blockType: 'VideoBlock',
146
+ video: BaseVideoData,
147
+ caption: 'Lorem ipsum dolor sit amet',
148
+ credit: 'NASA/JPL'
149
+ }
150
+ ],
151
+ content: BlockStreamfieldMinimalData.body
152
+ },
153
+ {
154
+ heading: 'Consectatur adipscing',
155
+ media: [BlockImageComparisonData],
156
+ content: BlockStreamfieldMinimalData.body
157
+ }
158
+ ],
159
+
160
+ customSections: [
161
+ {
162
+ blockType: 'EDUStudentProjectCustomSectionBlock',
163
+ content: BlockStreamfieldTruncatedData.body,
164
+ heading: BlockHeadingData,
165
+ position: 'after_overview'
166
+ }
167
+ ],
168
+
169
+ body: BlockStreamfieldTruncatedData.body,
170
+
171
+ relatedLinks: BlockRelatedLinksData.data,
172
+ relatedContentHeading: 'Related Content',
173
+ relatedContent: BlockLinkCardCarouselData
174
+ }
175
+ }
176
+ }
177
+
178
+ export const HeroCarousel = {
179
+ args: {
180
+ data: {
181
+ ...BaseStory.args.data,
182
+ hero: [{ blockType: 'CarouselBlock', blocks: BlockImageCarouselData }]
183
+ }
184
+ }
185
+ }
186
+
187
+ export const HeroImageComparison = {
188
+ args: {
189
+ data: {
190
+ ...BaseStory.args.data,
191
+ hero: [
192
+ {
193
+ ...BlockImageComparisonData
194
+ }
195
+ ]
196
+ }
197
+ }
198
+ }
199
+
200
+ export const HeroVideo = {
201
+ args: {
202
+ data: {
203
+ ...BaseStory.args.data,
204
+ hero: [
205
+ {
206
+ blockType: 'VideoBlock',
207
+ video: BaseVideoData,
208
+ caption: 'Lorem ipsum dolor sit amet',
209
+ credit: 'NASA/JPL'
210
+ }
211
+ ]
212
+ }
213
+ }
214
+ }
215
+
216
+ export const HeroVideoEmbed = {
217
+ args: {
218
+ data: {
219
+ ...BaseStory.args.data,
220
+ hero: [
221
+ {
222
+ ...BlockVideoEmbedData.data,
223
+ embed: {
224
+ 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
+ },
226
+ blockType: 'VideoEmbedBlock'
227
+ }
228
+ ]
229
+ }
230
+ }
231
+ }
232
+
233
+ export const HeroIframeEmbed = {
234
+ args: {
235
+ data: {
236
+ ...BaseStory.args.data,
237
+ hero: [
238
+ {
239
+ ...BlockIframeEmbedData
240
+ }
241
+ ]
242
+ }
243
+ }
244
+ }
245
+
246
+ export const NoHero = {
247
+ args: {
248
+ data: {
249
+ ...BaseStory.args.data,
250
+ hero: []
251
+ }
252
+ }
253
+ }
@@ -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>