@explorer-1/vue 0.2.14 → 0.2.16
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 +7 -0
- package/dist/explorer-1-vue.js +2415 -2405
- package/dist/explorer-1-vue.umd.cjs +12 -12
- package/dist/src/components/BaseAccordionItem/BaseAccordionItem.stories.d.ts +99 -0
- package/dist/src/components/BaseButton/BaseButton.stories.d.ts +13 -0
- package/dist/src/components/BaseButton/BaseButton.vue.d.ts +15 -1
- package/dist/src/components/BaseImage/BaseImage.vue.d.ts +1 -1
- package/dist/src/components/BaseImagePlaceholder/BaseImagePlaceholder.vue.d.ts +1 -1
- package/dist/src/components/BaseLink/BaseLink.vue.d.ts +1 -1
- package/dist/src/components/BaseModal/BaseModal.stories.d.ts +26 -0
- package/dist/src/components/BaseSwimlane/BaseSwimlane.vue.d.ts +2 -2
- package/dist/src/components/BaseTimer/BaseTimer.vue.d.ts +2 -2
- package/dist/src/components/BlockAccordion/BlockAccordion.stories.d.ts +98 -0
- package/dist/src/components/BlockHeading/BlockHeading.stories.d.ts +1 -0
- package/dist/src/components/BlockHeading/BlockHeading.vue.d.ts +13 -11
- package/dist/src/components/BlockLinkCard/BlockLinkCard.vue.d.ts +1 -1
- package/dist/src/components/BlockLinkCarousel/BlockLinkCarousel.vue.d.ts +1 -1
- package/dist/src/components/BlockRelatedLinks/RelatedLink.vue.d.ts +1 -1
- package/dist/src/components/BlockStreamfield/BlockStreamfield.stories.d.ts +75 -40
- package/dist/src/components/BlockStreamfield/BlockStreamfield.vue.d.ts +2 -19
- package/dist/src/components/BlockText/BlockText.vue.d.ts +1 -1
- package/dist/src/components/HeroMedia/HeroMedia.vue.d.ts +7 -0
- package/dist/src/components/LayoutHelper/LayoutHelper.vue.d.ts +1 -1
- package/dist/src/components/MetaPanel/MetaPanel.stories.d.ts +51 -0
- package/dist/src/components/MetaPanelItems/MetaPanelItems.stories.d.ts +25 -0
- package/dist/src/components/MetadataEduResource/MetadataEduResource.stories.d.ts +1 -1
- package/dist/src/components/MixinCarousel/MixinCarousel.vue.d.ts +2 -2
- package/dist/src/components/NavSecondary/NavSecondary.vue.d.ts +4 -0
- package/dist/src/interfaces.d.ts +84 -7
- package/dist/src/templates/PageContent/PageContent.stories.d.ts +6 -5
- package/dist/src/templates/PageImageDetail/PageImageDetail.stories.d.ts +43 -16
- package/dist/src/templates/PageNewsDetail/PageNewsDetail.stories.d.ts +344 -128
- package/dist/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.stories.d.ts +44 -16
- package/dist/src/templates/edu/PageEduLesson/PageEduLesson.stories.d.ts +544 -0
- package/dist/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.d.ts +67 -63
- package/dist/src/templates/www/PageAsteroidWatchIndex/PageAsteroidWatchIndex.stories.d.ts +22 -21
- package/dist/src/templates/www/PageCuratedGallery/PageCuratedGallery.stories.d.ts +43 -16
- package/dist/src/templates/www/PageRoboticsDetail/PageRoboticsDetail.stories.d.ts +43 -16
- package/dist/src/utils/getHeadingId.d.ts +3 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/components/BaseAccordionItem/BaseAccordionItem.stories.js +15 -0
- package/src/components/BaseAccordionItem/BaseAccordionItem.vue +108 -0
- package/src/components/BaseButton/BaseButton.vue +12 -1
- package/src/components/BaseImage/BaseImage.vue +1 -1
- package/src/components/BaseImagePlaceholder/BaseImagePlaceholder.vue +1 -1
- package/src/components/BaseLink/BaseLink.vue +1 -1
- package/src/components/BaseTimer/BaseTimer.vue +6 -3
- package/src/components/BlockAccordion/BlockAccordion.stories.js +29 -0
- package/src/components/BlockAccordion/BlockAccordion.vue +32 -0
- package/src/components/BlockHeading/BlockHeading.stories.js +2 -2
- package/src/components/BlockHeading/BlockHeading.vue +15 -7
- package/src/components/BlockLinkCarousel/BlockLinkCarousel.vue +1 -1
- package/src/components/BlockRelatedLinks/RelatedLink.vue +1 -1
- package/src/components/BlockStreamfield/BlockStreamfield.stories.js +23 -5
- package/src/components/BlockStreamfield/BlockStreamfield.vue +8 -26
- package/src/components/BlockText/BlockText.vue +1 -1
- package/src/components/HeroMedia/HeroMedia.vue +10 -1
- package/src/components/LayoutHelper/LayoutHelper.vue +1 -1
- package/src/components/MetaPanel/MetaPanel.stories.js +112 -0
- package/src/components/MetaPanel/MetaPanel.vue +237 -0
- package/src/components/MetaPanelAccordion/MetaPanelAccordion.vue +64 -0
- package/src/components/MetaPanelItems/MetaPanelItems.stories.js +27 -0
- package/src/components/MetaPanelItems/MetaPanelItems.vue +186 -0
- package/src/components/MetadataEduResource/MetadataEduResource.stories.js +2 -4
- package/src/components/MixinCarousel/MixinCarousel.vue +2 -2
- package/src/components/NavJumpMenu/NavJumpMenu.vue +10 -8
- package/src/components/NavSecondary/NavSecondary.vue +26 -15
- package/src/components/ShareButtonsEdu/ShareButtonsEdu.vue +1 -1
- package/src/interfaces.ts +91 -8
- package/src/main.ts +2 -0
- package/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.stories.js +1 -0
- package/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.vue +10 -1
- package/src/templates/edu/PageEduLesson/PageEduLesson.stories.js +303 -0
- package/src/templates/edu/PageEduLesson/PageEduLesson.vue +410 -0
- package/src/templates/edu/PageEduLesson/PageEduLessonSection.vue +98 -0
- package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.js +1 -0
- package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.vue +4 -21
- package/src/utils/dayjs.js +0 -6
- package/src/utils/getHeadingId.ts +3 -3
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, reactive, ref } from 'vue'
|
|
3
|
+
import type {
|
|
4
|
+
BlockData,
|
|
5
|
+
ImageObject,
|
|
6
|
+
PageEduResourcesObject,
|
|
7
|
+
StreamfieldBlockData
|
|
8
|
+
} from './../../../interfaces'
|
|
9
|
+
import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
|
|
10
|
+
import BaseLink from './../../../components/BaseLink/BaseLink.vue'
|
|
11
|
+
import BaseImagePlaceholder from './../../../components/BaseImagePlaceholder/BaseImagePlaceholder.vue'
|
|
12
|
+
import type { BlockHeadingObject } from '../../../components/BlockHeading/BlockHeading.vue'
|
|
13
|
+
import BlockImageCarousel from './../../../components/BlockImageCarousel/BlockImageCarousel.vue'
|
|
14
|
+
import BlockImageComparison from './../../../components/BlockImageComparison/BlockImageComparison.vue'
|
|
15
|
+
import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
|
|
16
|
+
import BlockVideo from './../../../components/BlockVideo/BlockVideo.vue'
|
|
17
|
+
import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
|
|
18
|
+
import DetailHeadline from './../../../components/DetailHeadline/DetailHeadline.vue'
|
|
19
|
+
import BlockImageStandard from './../../../components/BlockImage/BlockImageStandard.vue'
|
|
20
|
+
import ShareButtonsEdu from './../../../components/ShareButtonsEdu/ShareButtonsEdu.vue'
|
|
21
|
+
import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
|
|
22
|
+
import BlockIframeEmbed from '../../../components/BlockIframeEmbed/BlockIframeEmbed.vue'
|
|
23
|
+
import BlockRelatedLinks from '../../../components/BlockRelatedLinks/BlockRelatedLinks.vue'
|
|
24
|
+
import MetaPanel from '../../../components/MetaPanel/MetaPanel.vue'
|
|
25
|
+
import PageEduLessonSection, { type PageEduLessonSectionProps } from './PageEduLessonSection.vue'
|
|
26
|
+
import NavJumpMenu from './../../../components/NavJumpMenu/NavJumpMenu.vue'
|
|
27
|
+
import { HeadingLevel } from '../../../components/BaseHeading/BaseHeading.vue'
|
|
28
|
+
|
|
29
|
+
interface EduLessonSectionObject extends PageEduLessonSectionProps {
|
|
30
|
+
type?: 'streamfield'
|
|
31
|
+
}
|
|
32
|
+
interface EduLessonProcedureBlocks {
|
|
33
|
+
blocks: StreamfieldBlockData[]
|
|
34
|
+
}
|
|
35
|
+
interface EduLessonProcedure {
|
|
36
|
+
procedure: EduLessonProcedureBlocks
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface PageEduLessonObject extends PageEduResourcesObject {
|
|
40
|
+
[key: string]: any
|
|
41
|
+
studentProject: {
|
|
42
|
+
title: string
|
|
43
|
+
urlPath: string
|
|
44
|
+
}
|
|
45
|
+
overview: StreamfieldBlockData[]
|
|
46
|
+
overviewHeading: string
|
|
47
|
+
overviewImage: ImageObject
|
|
48
|
+
materials: string
|
|
49
|
+
materialsHeading: string
|
|
50
|
+
materialsImage: ImageObject
|
|
51
|
+
management: StreamfieldBlockData[]
|
|
52
|
+
managementHeading: string
|
|
53
|
+
background: StreamfieldBlockData[]
|
|
54
|
+
backgroundHeading: string
|
|
55
|
+
procedures: EduLessonProcedure[]
|
|
56
|
+
proceduresHeading: string
|
|
57
|
+
proceduresStepsNumbering: boolean
|
|
58
|
+
discussion: StreamfieldBlockData[]
|
|
59
|
+
discussionHeading: string
|
|
60
|
+
assessment: StreamfieldBlockData[]
|
|
61
|
+
assessmentHeading: string
|
|
62
|
+
extensions: StreamfieldBlockData[]
|
|
63
|
+
extensionsHeading: string
|
|
64
|
+
techAddons: StreamfieldBlockData[]
|
|
65
|
+
techAddonsHeading: string
|
|
66
|
+
customSections: any
|
|
67
|
+
}
|
|
68
|
+
interface PageEduLessonProps {
|
|
69
|
+
data?: PageEduLessonObject
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const props = withDefaults(defineProps<PageEduLessonProps>(), {
|
|
73
|
+
data: undefined
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const { data } = reactive(props)
|
|
77
|
+
|
|
78
|
+
const PageEduLessonJumpMenu = ref()
|
|
79
|
+
|
|
80
|
+
defineExpose({
|
|
81
|
+
PageEduLessonJumpMenu
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const stringAsHeadingBlockData = (
|
|
85
|
+
heading: HeadingLevel,
|
|
86
|
+
overrideText?: string
|
|
87
|
+
): BlockHeadingObject => {
|
|
88
|
+
return {
|
|
89
|
+
blockType: 'HeadingBlock',
|
|
90
|
+
heading: (overrideText || heading) as HeadingLevel,
|
|
91
|
+
level: 'h2'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const heroEmpty = computed((): boolean => {
|
|
96
|
+
return !data?.hero?.length
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const heroInline = computed((): boolean => {
|
|
100
|
+
if (!heroEmpty.value && data?.hero) {
|
|
101
|
+
if (data?.hero[0].blockType === 'VideoBlock') {
|
|
102
|
+
return false
|
|
103
|
+
} 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'
|
|
108
|
+
) {
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return false
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const computedClass = computed((): string => {
|
|
116
|
+
if (heroInline.value || heroEmpty) {
|
|
117
|
+
return 'pt-5 lg:pt-12'
|
|
118
|
+
} else if (!heroInline.value) {
|
|
119
|
+
return '-nav-offset'
|
|
120
|
+
}
|
|
121
|
+
return ''
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const sectionOrder = [
|
|
125
|
+
'top',
|
|
126
|
+
'overview',
|
|
127
|
+
'materials',
|
|
128
|
+
'management',
|
|
129
|
+
'background',
|
|
130
|
+
'procedures',
|
|
131
|
+
'discussion',
|
|
132
|
+
'assessment',
|
|
133
|
+
'extensions',
|
|
134
|
+
'techAddons',
|
|
135
|
+
'bottom'
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
// mimic HeadingBlock data shape for defined section headings
|
|
139
|
+
const staticSectionHeadings = computed((): { [key: string]: BlockHeadingObject } | undefined => {
|
|
140
|
+
if (data) {
|
|
141
|
+
const result = sectionOrder.reduce<Record<string, BlockHeadingObject>>((acc, section) => {
|
|
142
|
+
const headingText =
|
|
143
|
+
section === 'techAddons'
|
|
144
|
+
? 'Tech Add-ons'
|
|
145
|
+
: section.charAt(0).toUpperCase() + section.slice(1)
|
|
146
|
+
acc[section] = stringAsHeadingBlockData(
|
|
147
|
+
(data[`${section}Heading`] as HeadingLevel) || headingText
|
|
148
|
+
)
|
|
149
|
+
return acc
|
|
150
|
+
}, {})
|
|
151
|
+
return result
|
|
152
|
+
}
|
|
153
|
+
return undefined
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const keyedCustomSections = computed(
|
|
157
|
+
():
|
|
158
|
+
| {
|
|
159
|
+
[key: string]: StreamfieldBlockData[]
|
|
160
|
+
}
|
|
161
|
+
| undefined => {
|
|
162
|
+
if (data) {
|
|
163
|
+
const result = data.customSections.reduce(
|
|
164
|
+
(
|
|
165
|
+
acc: { [key: string]: StreamfieldBlockData[] },
|
|
166
|
+
section: {
|
|
167
|
+
heading: StreamfieldBlockData
|
|
168
|
+
content: StreamfieldBlockData[]
|
|
169
|
+
position: string
|
|
170
|
+
}
|
|
171
|
+
) => {
|
|
172
|
+
const position = section.position
|
|
173
|
+
if (!acc[position]) {
|
|
174
|
+
acc[position] = []
|
|
175
|
+
}
|
|
176
|
+
acc[position].push(section.heading)
|
|
177
|
+
acc[position].push(...section.content)
|
|
178
|
+
return acc
|
|
179
|
+
},
|
|
180
|
+
{}
|
|
181
|
+
)
|
|
182
|
+
return result
|
|
183
|
+
}
|
|
184
|
+
return undefined
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
const consolidatedBlocks = computed(() => {
|
|
189
|
+
// NavJumpMenu handles filtering for HeadingBlock, so we don't need to do that here
|
|
190
|
+
const blocks = []
|
|
191
|
+
// include custom top blocks
|
|
192
|
+
if (keyedCustomSections.value && keyedCustomSections.value['top']) {
|
|
193
|
+
blocks.push(...keyedCustomSections.value['top'])
|
|
194
|
+
}
|
|
195
|
+
// include predefined section blocks
|
|
196
|
+
sectionOrder.forEach((section) => {
|
|
197
|
+
if (data && data[section]) {
|
|
198
|
+
if (staticSectionHeadings.value && staticSectionHeadings.value[section]) {
|
|
199
|
+
blocks.push(staticSectionHeadings.value[section])
|
|
200
|
+
}
|
|
201
|
+
if (section !== 'materials' && section !== 'procedures') {
|
|
202
|
+
blocks.push(...data[section])
|
|
203
|
+
} else if (section === 'procedures' && data.procedures?.length) {
|
|
204
|
+
// get blocks in nested procedures
|
|
205
|
+
data.procedures.forEach((item) => {
|
|
206
|
+
if (item.procedure?.blocks?.length) {
|
|
207
|
+
blocks.push(...item.procedure.blocks)
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// include custom "after_" blocks
|
|
213
|
+
if (keyedCustomSections.value && keyedCustomSections.value[`after_${section}`]) {
|
|
214
|
+
blocks.push(...keyedCustomSections.value[`after_${section}`])
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
// include custom bottom blocks
|
|
218
|
+
if (keyedCustomSections.value && keyedCustomSections.value['bottom']) {
|
|
219
|
+
blocks.push(...keyedCustomSections.value['bottom'])
|
|
220
|
+
}
|
|
221
|
+
// include body blocks
|
|
222
|
+
if (data?.body?.length) {
|
|
223
|
+
blocks.push(...data.body)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return blocks
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// organize data to render with PageEduLessonSection component
|
|
230
|
+
const consolidatedSections = computed((): EduLessonSectionObject[] => {
|
|
231
|
+
const sections = []
|
|
232
|
+
// include custom top section
|
|
233
|
+
if (keyedCustomSections.value && keyedCustomSections.value['top']) {
|
|
234
|
+
sections.push({ type: 'streamfield', blocks: keyedCustomSections.value['top'] })
|
|
235
|
+
}
|
|
236
|
+
sectionOrder.forEach((section) => {
|
|
237
|
+
if (data && data[section]) {
|
|
238
|
+
sections.push({
|
|
239
|
+
heading: staticSectionHeadings.value
|
|
240
|
+
? (staticSectionHeadings.value[section] as BlockData)
|
|
241
|
+
: undefined,
|
|
242
|
+
blocks: section !== 'materials' && section !== 'procedures' ? data[section] : undefined,
|
|
243
|
+
text: section === 'materials' ? data[section] : undefined,
|
|
244
|
+
procedures: section === 'procedures' ? data[section] : undefined,
|
|
245
|
+
procedureSteps: section === 'procedures' ? data.proceduresStepsNumbering : false
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
// include custom "after_" sections
|
|
249
|
+
if (keyedCustomSections.value && keyedCustomSections.value[`after_${section}`]) {
|
|
250
|
+
sections.push({ type: 'streamfield', blocks: keyedCustomSections.value[`after_${section}`] })
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
// include custom bottom section
|
|
254
|
+
if (keyedCustomSections.value && keyedCustomSections.value['bottom']) {
|
|
255
|
+
sections.push({ type: 'streamfield', blocks: keyedCustomSections.value['bottom'] })
|
|
256
|
+
}
|
|
257
|
+
return sections as EduLessonSectionObject[]
|
|
258
|
+
})
|
|
259
|
+
</script>
|
|
260
|
+
<template>
|
|
261
|
+
<div
|
|
262
|
+
v-if="data"
|
|
263
|
+
class="ThemeVariantLight"
|
|
264
|
+
:class="computedClass"
|
|
265
|
+
>
|
|
266
|
+
<LayoutHelper
|
|
267
|
+
indent="col-3"
|
|
268
|
+
class="mb-10"
|
|
269
|
+
>
|
|
270
|
+
<DetailHeadline
|
|
271
|
+
:title="data.title"
|
|
272
|
+
label="Lesson"
|
|
273
|
+
pill
|
|
274
|
+
/>
|
|
275
|
+
<ShareButtonsEdu
|
|
276
|
+
v-if="data?.url"
|
|
277
|
+
class="mt-4"
|
|
278
|
+
:url="data.url"
|
|
279
|
+
:title="data.title"
|
|
280
|
+
:image="data.thumbnailImage?.original"
|
|
281
|
+
/>
|
|
282
|
+
<p
|
|
283
|
+
v-if="data.studentProject"
|
|
284
|
+
class="mt-8 font-bold text-body-lg"
|
|
285
|
+
>
|
|
286
|
+
Find out what’s involved for students:
|
|
287
|
+
<BaseLink
|
|
288
|
+
class="font-normal inline text-action underline hover:text-action-dark cursor-pointer"
|
|
289
|
+
variant="none"
|
|
290
|
+
:to="data.studentProject.urlPath"
|
|
291
|
+
>
|
|
292
|
+
View the Project Steps
|
|
293
|
+
</BaseLink>
|
|
294
|
+
</p>
|
|
295
|
+
</LayoutHelper>
|
|
296
|
+
<MetaPanel
|
|
297
|
+
button="View Standards"
|
|
298
|
+
theme="stars"
|
|
299
|
+
:primary-subject="data.primarySubject"
|
|
300
|
+
:additional-subjects="data.additionalSubjects"
|
|
301
|
+
:time="data.time"
|
|
302
|
+
:grade-levels="data.gradeLevels"
|
|
303
|
+
:standards="data.standards"
|
|
304
|
+
negative-bottom
|
|
305
|
+
/>
|
|
306
|
+
|
|
307
|
+
<!-- hero media -->
|
|
308
|
+
<HeroMedia
|
|
309
|
+
v-if="
|
|
310
|
+
!heroEmpty &&
|
|
311
|
+
!heroInline &&
|
|
312
|
+
data?.hero?.length &&
|
|
313
|
+
(data.hero[0].blockType === 'HeroImageBlock' || data.hero[0].blockType === 'VideoBlock')
|
|
314
|
+
"
|
|
315
|
+
class="md:mb-12 lg:mb-18 mb-10"
|
|
316
|
+
:image="data.hero[0].image"
|
|
317
|
+
:video="data.hero[0].video"
|
|
318
|
+
:display-caption="data.hero[0].displayCaption"
|
|
319
|
+
:caption="data.hero[0].caption"
|
|
320
|
+
:credit="data.hero[0].credit"
|
|
321
|
+
:constrain="data.heroConstrain"
|
|
322
|
+
/>
|
|
323
|
+
|
|
324
|
+
<!-- TODO: put this in a component (exclude layout though) -->
|
|
325
|
+
<LayoutHelper
|
|
326
|
+
v-if="!heroEmpty && heroInline && data.hero?.length"
|
|
327
|
+
class="lg:mb-22 mb-10"
|
|
328
|
+
>
|
|
329
|
+
<BlockImageStandard
|
|
330
|
+
v-if="data.hero[0].blockType === 'HeroImageBlock'"
|
|
331
|
+
:data="data.hero[0].imageInline"
|
|
332
|
+
:display-caption="data.hero[0].displayCaption"
|
|
333
|
+
:caption="data.hero[0].caption"
|
|
334
|
+
:constrain="data.heroConstrain"
|
|
335
|
+
/>
|
|
336
|
+
<BlockImageCarousel
|
|
337
|
+
v-else-if="data.hero[0].blockType === 'CarouselBlock'"
|
|
338
|
+
:items="data.hero[0].blocks"
|
|
339
|
+
:block-id="data.hero[0].id"
|
|
340
|
+
/>
|
|
341
|
+
<BlockIframeEmbed
|
|
342
|
+
v-else-if="data.hero[0].blockType === 'IframeEmbedBlock'"
|
|
343
|
+
:data="data.hero[0]"
|
|
344
|
+
/>
|
|
345
|
+
<BlockVideo
|
|
346
|
+
v-else-if="data.hero[0].blockType === 'VideoBlock'"
|
|
347
|
+
:data="data.hero[0]"
|
|
348
|
+
autoplay
|
|
349
|
+
/>
|
|
350
|
+
<BaseImagePlaceholder
|
|
351
|
+
v-else-if="data.hero[0].blockType === 'VideoEmbedBlock'"
|
|
352
|
+
aspect-ratio="16:9"
|
|
353
|
+
dark-mode
|
|
354
|
+
>
|
|
355
|
+
<div v-html="data.hero[0].embed?.embed"></div>
|
|
356
|
+
</BaseImagePlaceholder>
|
|
357
|
+
<BlockImageComparison
|
|
358
|
+
v-else-if="data.hero[0].blockType === 'ImageComparisonBlock'"
|
|
359
|
+
:data="data.hero[0]"
|
|
360
|
+
/>
|
|
361
|
+
</LayoutHelper>
|
|
362
|
+
|
|
363
|
+
<NavJumpMenu
|
|
364
|
+
ref="PageEduLessonJumpMenu"
|
|
365
|
+
:title="data.title"
|
|
366
|
+
:blocks="consolidatedBlocks"
|
|
367
|
+
:enabled="true"
|
|
368
|
+
/>
|
|
369
|
+
<div id="NavJumpMenuFrame">
|
|
370
|
+
<template
|
|
371
|
+
v-for="(value, _key) in consolidatedSections"
|
|
372
|
+
:key="_key"
|
|
373
|
+
>
|
|
374
|
+
<BlockStreamfield
|
|
375
|
+
v-if="value.type === 'streamfield'"
|
|
376
|
+
:data="value.blocks"
|
|
377
|
+
/>
|
|
378
|
+
<PageEduLessonSection
|
|
379
|
+
v-else
|
|
380
|
+
:heading="value.heading"
|
|
381
|
+
:blocks="value.blocks"
|
|
382
|
+
:procedures="value.procedures"
|
|
383
|
+
:procedure-steps="value.procedureSteps"
|
|
384
|
+
:text="value.text"
|
|
385
|
+
:image="value.image"
|
|
386
|
+
/>
|
|
387
|
+
</template>
|
|
388
|
+
|
|
389
|
+
<!-- streamfield blocks -->
|
|
390
|
+
<BlockStreamfield :data="data.body" />
|
|
391
|
+
|
|
392
|
+
<!-- related links -->
|
|
393
|
+
<LayoutHelper
|
|
394
|
+
v-if="data.relatedLinks && data.relatedLinks.length"
|
|
395
|
+
indent="col-3"
|
|
396
|
+
class="lg:my-18 my-10"
|
|
397
|
+
>
|
|
398
|
+
<BlockRelatedLinks :data="data.relatedLinks[0]" />
|
|
399
|
+
</LayoutHelper>
|
|
400
|
+
|
|
401
|
+
<!-- related content -->
|
|
402
|
+
<BlockLinkCarousel
|
|
403
|
+
item-type="cards"
|
|
404
|
+
class="lg:my-24 my-12 print:px-4"
|
|
405
|
+
:heading="data.relatedContentHeading"
|
|
406
|
+
:items="data.relatedContent"
|
|
407
|
+
/>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</template>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, reactive } from 'vue'
|
|
3
|
+
import { camelCase } from 'lodash'
|
|
4
|
+
import type { ImageObject, StreamfieldBlockData } from './../../../interfaces'
|
|
5
|
+
import BlockHeading, {
|
|
6
|
+
type BlockHeadingObject
|
|
7
|
+
} from './../../../components/BlockHeading/BlockHeading.vue'
|
|
8
|
+
import BaseHeading from './../../../components/BaseHeading/BaseHeading.vue'
|
|
9
|
+
import BlockText from './../../../components/BlockText/BlockText.vue'
|
|
10
|
+
import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
|
|
11
|
+
import BlockImageStandard from './../../../components/BlockImage/BlockImageStandard.vue'
|
|
12
|
+
import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
|
|
13
|
+
|
|
14
|
+
export interface PageEduLessonSectionProps {
|
|
15
|
+
heading: BlockHeadingObject
|
|
16
|
+
blocks?: StreamfieldBlockData[]
|
|
17
|
+
procedures?: {
|
|
18
|
+
procedure: {
|
|
19
|
+
blocks: StreamfieldBlockData[]
|
|
20
|
+
}
|
|
21
|
+
}[]
|
|
22
|
+
procedureSteps?: boolean
|
|
23
|
+
text?: string
|
|
24
|
+
image?: ImageObject
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const props = withDefaults(defineProps<PageEduLessonSectionProps>(), {
|
|
28
|
+
heading: undefined,
|
|
29
|
+
blocks: undefined,
|
|
30
|
+
procedures: undefined,
|
|
31
|
+
procedureSteps: false,
|
|
32
|
+
text: undefined,
|
|
33
|
+
image: undefined
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const { heading, blocks, image } = reactive(props)
|
|
37
|
+
|
|
38
|
+
const anchorId = computed(() => {
|
|
39
|
+
return 'lesson_' + camelCase(heading.heading)
|
|
40
|
+
})
|
|
41
|
+
</script>
|
|
42
|
+
<template>
|
|
43
|
+
<section
|
|
44
|
+
:id="anchorId"
|
|
45
|
+
:aria-label="heading.heading"
|
|
46
|
+
>
|
|
47
|
+
<LayoutHelper
|
|
48
|
+
indent="col-3"
|
|
49
|
+
class="lg:mb-8 mb-5"
|
|
50
|
+
>
|
|
51
|
+
<BlockHeading
|
|
52
|
+
:data="heading"
|
|
53
|
+
generate-id
|
|
54
|
+
/>
|
|
55
|
+
</LayoutHelper>
|
|
56
|
+
|
|
57
|
+
<LayoutHelper
|
|
58
|
+
v-if="image"
|
|
59
|
+
indent="col-2"
|
|
60
|
+
class="lg:mb-8 mb-5"
|
|
61
|
+
>
|
|
62
|
+
<!-- image goes here -->
|
|
63
|
+
<BlockImageStandard :data="image" />
|
|
64
|
+
</LayoutHelper>
|
|
65
|
+
|
|
66
|
+
<BlockStreamfield
|
|
67
|
+
v-if="blocks"
|
|
68
|
+
:data="blocks"
|
|
69
|
+
/>
|
|
70
|
+
<template v-else-if="procedures?.length">
|
|
71
|
+
<template
|
|
72
|
+
v-for="(item, index) in procedures"
|
|
73
|
+
:key="index"
|
|
74
|
+
>
|
|
75
|
+
<LayoutHelper
|
|
76
|
+
v-if="procedureSteps"
|
|
77
|
+
indent="col-3"
|
|
78
|
+
class="lg:mb-8 mb-5"
|
|
79
|
+
>
|
|
80
|
+
<BaseHeading level="h3">
|
|
81
|
+
{{ 'Section ' + (Number(index) + 1) }}
|
|
82
|
+
</BaseHeading>
|
|
83
|
+
</LayoutHelper>
|
|
84
|
+
<BlockStreamfield
|
|
85
|
+
v-if="item?.procedure?.blocks"
|
|
86
|
+
:data="item.procedure.blocks"
|
|
87
|
+
/>
|
|
88
|
+
</template>
|
|
89
|
+
</template>
|
|
90
|
+
<LayoutHelper
|
|
91
|
+
v-else-if="text"
|
|
92
|
+
indent="col-3"
|
|
93
|
+
class="lg:mb-18 mb-10"
|
|
94
|
+
>
|
|
95
|
+
<BlockText :text="text" />
|
|
96
|
+
</LayoutHelper>
|
|
97
|
+
</section>
|
|
98
|
+
</template>
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, defineExpose } from 'vue'
|
|
3
3
|
import isEmpty from 'lodash/isEmpty.js'
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
6
|
-
AuthorObject,
|
|
7
|
-
ImageObject,
|
|
8
|
-
PageResponseObject,
|
|
9
|
-
RelatedLinkObject,
|
|
10
|
-
Topic,
|
|
11
|
-
ThumbnailObject
|
|
12
|
-
} from './../../../interfaces'
|
|
4
|
+
import type { AuthorObject, ImageObject, PageObject } from './../../../interfaces'
|
|
13
5
|
import HeroMedia from './../../../components/HeroMedia/HeroMedia.vue'
|
|
14
6
|
import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
|
|
15
7
|
import DetailHeadline from './../../../components/DetailHeadline/DetailHeadline.vue'
|
|
@@ -19,23 +11,14 @@ import BlockText from './../../../components/BlockText/BlockText.vue'
|
|
|
19
11
|
import BlockStreamfield from './../../../components/BlockStreamfield/BlockStreamfield.vue'
|
|
20
12
|
import NavJumpMenu from './../../../components/NavJumpMenu/NavJumpMenu.vue'
|
|
21
13
|
|
|
22
|
-
interface PageEduNewsDetailObject extends
|
|
14
|
+
interface PageEduNewsDetailObject extends PageObject {
|
|
23
15
|
readTime: string
|
|
24
16
|
url: string
|
|
25
17
|
heroImage: ImageObject
|
|
26
18
|
heroImageInline: ImageObject
|
|
27
|
-
thumbnailImage: ThumbnailObject
|
|
28
|
-
heroPosition: string
|
|
29
19
|
heroConstrain: boolean
|
|
30
20
|
heroImageCaption: string
|
|
31
21
|
authors: AuthorObject[]
|
|
32
|
-
publicationDate: string
|
|
33
|
-
title: string
|
|
34
|
-
getTopicsForDisplay: Topic[]
|
|
35
|
-
summary: string
|
|
36
|
-
topper: string
|
|
37
|
-
relatedLinks: RelatedLinkObject[]
|
|
38
|
-
body: StreamfieldBlockData[]
|
|
39
22
|
showJumpMenu: boolean
|
|
40
23
|
}
|
|
41
24
|
|
|
@@ -69,7 +52,7 @@ const computedClass = computed(() => {
|
|
|
69
52
|
})
|
|
70
53
|
|
|
71
54
|
const dateTimeArray = computed(() => {
|
|
72
|
-
return props.data.publicationDate.split(' ')
|
|
55
|
+
return props.data.publicationDate ? props.data.publicationDate.split(' ') : undefined
|
|
73
56
|
})
|
|
74
57
|
|
|
75
58
|
defineExpose({
|
|
@@ -88,7 +71,7 @@ defineExpose({
|
|
|
88
71
|
ref="PageEduNewsDetailJumpMenu"
|
|
89
72
|
:title="data.title"
|
|
90
73
|
:blocks="data.body"
|
|
91
|
-
:enabled="
|
|
74
|
+
:enabled="data.showJumpMenu"
|
|
92
75
|
/>
|
|
93
76
|
|
|
94
77
|
<!-- schema.org -->
|
package/src/utils/dayjs.js
CHANGED
|
@@ -3,9 +3,6 @@ import updateLocale from 'dayjs/plugin/updateLocale.js'
|
|
|
3
3
|
import localizedFormat from 'dayjs/plugin/localizedFormat.js'
|
|
4
4
|
import timezone from 'dayjs/plugin/timezone.js'
|
|
5
5
|
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
|
|
6
|
-
// for BaseTimer
|
|
7
|
-
import duration from 'dayjs/plugin/duration.js'
|
|
8
|
-
import minMax from 'dayjs/plugin/minMax.js'
|
|
9
6
|
|
|
10
7
|
// Locales must be imported manually
|
|
11
8
|
// see https://github.com/iamkun/dayjs/tree/dev/src/locale
|
|
@@ -31,8 +28,5 @@ dayjs.updateLocale('en', {
|
|
|
31
28
|
})
|
|
32
29
|
dayjs.extend(timezone)
|
|
33
30
|
dayjs.extend(advancedFormat)
|
|
34
|
-
// for BaseTimer
|
|
35
|
-
dayjs.extend(duration)
|
|
36
|
-
dayjs.extend(minMax)
|
|
37
31
|
|
|
38
32
|
export default dayjs
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { camelCase } from 'lodash'
|
|
2
|
-
|
|
3
|
-
export const getHeadingId = (heading:
|
|
4
|
-
return camelCase(heading + (
|
|
2
|
+
import { type HeadingLevel } from '../components/BaseHeading/BaseHeading.vue'
|
|
3
|
+
export const getHeadingId = (heading: HeadingLevel, blockId?: string) => {
|
|
4
|
+
return 'anchor_' + camelCase(heading) + (blockId ? '_' + camelCase(blockId) : '')
|
|
5
5
|
}
|