@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.
Files changed (80) hide show
  1. package/components.d.ts +7 -0
  2. package/dist/explorer-1-vue.js +2415 -2405
  3. package/dist/explorer-1-vue.umd.cjs +12 -12
  4. package/dist/src/components/BaseAccordionItem/BaseAccordionItem.stories.d.ts +99 -0
  5. package/dist/src/components/BaseButton/BaseButton.stories.d.ts +13 -0
  6. package/dist/src/components/BaseButton/BaseButton.vue.d.ts +15 -1
  7. package/dist/src/components/BaseImage/BaseImage.vue.d.ts +1 -1
  8. package/dist/src/components/BaseImagePlaceholder/BaseImagePlaceholder.vue.d.ts +1 -1
  9. package/dist/src/components/BaseLink/BaseLink.vue.d.ts +1 -1
  10. package/dist/src/components/BaseModal/BaseModal.stories.d.ts +26 -0
  11. package/dist/src/components/BaseSwimlane/BaseSwimlane.vue.d.ts +2 -2
  12. package/dist/src/components/BaseTimer/BaseTimer.vue.d.ts +2 -2
  13. package/dist/src/components/BlockAccordion/BlockAccordion.stories.d.ts +98 -0
  14. package/dist/src/components/BlockHeading/BlockHeading.stories.d.ts +1 -0
  15. package/dist/src/components/BlockHeading/BlockHeading.vue.d.ts +13 -11
  16. package/dist/src/components/BlockLinkCard/BlockLinkCard.vue.d.ts +1 -1
  17. package/dist/src/components/BlockLinkCarousel/BlockLinkCarousel.vue.d.ts +1 -1
  18. package/dist/src/components/BlockRelatedLinks/RelatedLink.vue.d.ts +1 -1
  19. package/dist/src/components/BlockStreamfield/BlockStreamfield.stories.d.ts +75 -40
  20. package/dist/src/components/BlockStreamfield/BlockStreamfield.vue.d.ts +2 -19
  21. package/dist/src/components/BlockText/BlockText.vue.d.ts +1 -1
  22. package/dist/src/components/HeroMedia/HeroMedia.vue.d.ts +7 -0
  23. package/dist/src/components/LayoutHelper/LayoutHelper.vue.d.ts +1 -1
  24. package/dist/src/components/MetaPanel/MetaPanel.stories.d.ts +51 -0
  25. package/dist/src/components/MetaPanelItems/MetaPanelItems.stories.d.ts +25 -0
  26. package/dist/src/components/MetadataEduResource/MetadataEduResource.stories.d.ts +1 -1
  27. package/dist/src/components/MixinCarousel/MixinCarousel.vue.d.ts +2 -2
  28. package/dist/src/components/NavSecondary/NavSecondary.vue.d.ts +4 -0
  29. package/dist/src/interfaces.d.ts +84 -7
  30. package/dist/src/templates/PageContent/PageContent.stories.d.ts +6 -5
  31. package/dist/src/templates/PageImageDetail/PageImageDetail.stories.d.ts +43 -16
  32. package/dist/src/templates/PageNewsDetail/PageNewsDetail.stories.d.ts +344 -128
  33. package/dist/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.stories.d.ts +44 -16
  34. package/dist/src/templates/edu/PageEduLesson/PageEduLesson.stories.d.ts +544 -0
  35. package/dist/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.d.ts +67 -63
  36. package/dist/src/templates/www/PageAsteroidWatchIndex/PageAsteroidWatchIndex.stories.d.ts +22 -21
  37. package/dist/src/templates/www/PageCuratedGallery/PageCuratedGallery.stories.d.ts +43 -16
  38. package/dist/src/templates/www/PageRoboticsDetail/PageRoboticsDetail.stories.d.ts +43 -16
  39. package/dist/src/utils/getHeadingId.d.ts +3 -1
  40. package/dist/style.css +1 -1
  41. package/package.json +3 -2
  42. package/src/components/BaseAccordionItem/BaseAccordionItem.stories.js +15 -0
  43. package/src/components/BaseAccordionItem/BaseAccordionItem.vue +108 -0
  44. package/src/components/BaseButton/BaseButton.vue +12 -1
  45. package/src/components/BaseImage/BaseImage.vue +1 -1
  46. package/src/components/BaseImagePlaceholder/BaseImagePlaceholder.vue +1 -1
  47. package/src/components/BaseLink/BaseLink.vue +1 -1
  48. package/src/components/BaseTimer/BaseTimer.vue +6 -3
  49. package/src/components/BlockAccordion/BlockAccordion.stories.js +29 -0
  50. package/src/components/BlockAccordion/BlockAccordion.vue +32 -0
  51. package/src/components/BlockHeading/BlockHeading.stories.js +2 -2
  52. package/src/components/BlockHeading/BlockHeading.vue +15 -7
  53. package/src/components/BlockLinkCarousel/BlockLinkCarousel.vue +1 -1
  54. package/src/components/BlockRelatedLinks/RelatedLink.vue +1 -1
  55. package/src/components/BlockStreamfield/BlockStreamfield.stories.js +23 -5
  56. package/src/components/BlockStreamfield/BlockStreamfield.vue +8 -26
  57. package/src/components/BlockText/BlockText.vue +1 -1
  58. package/src/components/HeroMedia/HeroMedia.vue +10 -1
  59. package/src/components/LayoutHelper/LayoutHelper.vue +1 -1
  60. package/src/components/MetaPanel/MetaPanel.stories.js +112 -0
  61. package/src/components/MetaPanel/MetaPanel.vue +237 -0
  62. package/src/components/MetaPanelAccordion/MetaPanelAccordion.vue +64 -0
  63. package/src/components/MetaPanelItems/MetaPanelItems.stories.js +27 -0
  64. package/src/components/MetaPanelItems/MetaPanelItems.vue +186 -0
  65. package/src/components/MetadataEduResource/MetadataEduResource.stories.js +2 -4
  66. package/src/components/MixinCarousel/MixinCarousel.vue +2 -2
  67. package/src/components/NavJumpMenu/NavJumpMenu.vue +10 -8
  68. package/src/components/NavSecondary/NavSecondary.vue +26 -15
  69. package/src/components/ShareButtonsEdu/ShareButtonsEdu.vue +1 -1
  70. package/src/interfaces.ts +91 -8
  71. package/src/main.ts +2 -0
  72. package/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.stories.js +1 -0
  73. package/src/templates/edu/PageEduExplainerArticle/PageEduExplainerArticle.vue +10 -1
  74. package/src/templates/edu/PageEduLesson/PageEduLesson.stories.js +303 -0
  75. package/src/templates/edu/PageEduLesson/PageEduLesson.vue +410 -0
  76. package/src/templates/edu/PageEduLesson/PageEduLessonSection.vue +98 -0
  77. package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.stories.js +1 -0
  78. package/src/templates/edu/PageEduNewsDetail/PageEduNewsDetail.vue +4 -21
  79. package/src/utils/dayjs.js +0 -6
  80. 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>
@@ -56,6 +56,7 @@ export const BaseStory = {
56
56
  heroPosition: 'full_bleed',
57
57
  heroImage: HeroMediaData.image,
58
58
  heroImageInline: HeroMediaData.imageInline,
59
+ showJumpMenu: true,
59
60
  ...BlockStreamfieldData
60
61
  }
61
62
  }
@@ -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 { StreamfieldBlockData } from './../../../components/BlockStreamfield/BlockStreamfield.vue'
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 PageResponseObject {
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="true"
74
+ :enabled="data.showJumpMenu"
92
75
  />
93
76
 
94
77
  <!-- schema.org -->
@@ -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: string, index?: number) => {
4
- return camelCase(heading + (index ? index : ''))
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
  }