@iqual/nuxt-icms 1.0.14 → 1.1.0

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 (32) hide show
  1. package/components/Blokkli/Timeline/Element/icms_timeline_element.svg +7 -0
  2. package/components/Blokkli/Timeline/Element/icon.svg +7 -0
  3. package/components/Blokkli/Timeline/Element/index.vue +14 -0
  4. package/components/Blokkli/Timeline/Layout/icms_layout_timeline.svg +16 -0
  5. package/components/Blokkli/Timeline/Layout/icon.svg +16 -0
  6. package/components/Blokkli/Timeline/Layout/index.vue +14 -0
  7. package/components/Blokkli/iFrame/icon-blokkli-option-large-spacing-bottom.svg +1 -0
  8. package/components/Blokkli/iFrame/icon-blokkli-option-large-spacing-top.svg +1 -0
  9. package/components/Blokkli/iFrame/icon-blokkli-option-no-spacing.svg +1 -0
  10. package/components/Blokkli/iFrame/icon-blokkli-option-small-spacing-bottom.svg +1 -0
  11. package/components/Blokkli/iFrame/icon-blokkli-option-small-spacing-top.svg +1 -0
  12. package/components/Blokkli/iFrame/{Layout/index.vue → index.vue} +2 -6
  13. package/components/DesignSystem/Organisms/TimelineElement.vue +92 -0
  14. package/components/Paragraph/Timeline/Element/fragment.graphql +16 -0
  15. package/components/Paragraph/Timeline/Element/index.vue +31 -0
  16. package/components/Paragraph/Timeline/Layout/fragment.graphql +14 -0
  17. package/components/Paragraph/Timeline/Layout/index.vue +325 -0
  18. package/components/Paragraph/iFrame/{Layout/fragment.graphql → fragment.graphql} +2 -6
  19. package/components/Paragraph/iFrame/index.vue +56 -0
  20. package/nuxt.config.ts +17 -10
  21. package/package.json +1 -1
  22. package/components/Blokkli/iFrame/Element/icon.svg +0 -5
  23. package/components/Blokkli/iFrame/Element/index.vue +0 -14
  24. package/components/Paragraph/iFrame/Element/fragment.graphql +0 -9
  25. package/components/Paragraph/iFrame/Element/index.vue +0 -16
  26. package/components/Paragraph/iFrame/Layout/index.vue +0 -161
  27. /package/components/Blokkli/{iFrame → Timeline}/Layout/icon-blokkli-option-large-spacing-bottom.svg +0 -0
  28. /package/components/Blokkli/{iFrame → Timeline}/Layout/icon-blokkli-option-large-spacing-top.svg +0 -0
  29. /package/components/Blokkli/{iFrame → Timeline}/Layout/icon-blokkli-option-no-spacing.svg +0 -0
  30. /package/components/Blokkli/{iFrame → Timeline}/Layout/icon-blokkli-option-small-spacing-bottom.svg +0 -0
  31. /package/components/Blokkli/{iFrame → Timeline}/Layout/icon-blokkli-option-small-spacing-top.svg +0 -0
  32. /package/components/Blokkli/iFrame/{Layout/icon.svg → icon.svg} +0 -0
@@ -0,0 +1,7 @@
1
+ <svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M9 8L9 22" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
3
+ <circle cx="9" cy="11" r="1" fill="black"/>
4
+ <circle cx="9" cy="19" r="1" fill="black"/>
5
+ <rect x="12.25" y="9.25" width="9.5" height="3.5" rx="1.15" stroke="black" stroke-width="1.5"/>
6
+ <rect x="12.25" y="17.25" width="9.5" height="3.5" rx="1.15" stroke="black" stroke-width="1.5"/>
7
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M9 8L9 22" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
3
+ <circle cx="9" cy="11" r="1" fill="none"/>
4
+ <circle cx="9" cy="19" r="1" fill="none"/>
5
+ <rect x="12.25" y="9.25" width="9.5" height="3.5" rx="1.15" stroke="currentColor" stroke-width="1.5" fill="none"/>
6
+ <rect x="12.25" y="17.25" width="9.5" height="3.5" rx="1.15" stroke="currentColor" stroke-width="1.5" fill="none"/>
7
+ </svg>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <ParagraphTimelineElement v-bind="props" :blokkliData="blokkliData" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import type { ParagraphIcmsTimelineElementFragment } from '#graphql-operations'
7
+
8
+ const props = defineProps<ParagraphIcmsTimelineElementFragment>()
9
+
10
+ const blokkliData = defineBlokkli({
11
+ bundle: 'icms_timeline_element',
12
+ globalOptions: ['reverseColumns'],
13
+ })
14
+ </script>
@@ -0,0 +1,16 @@
1
+ <svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_286_20)">
3
+ <rect x="0.75" y="4.75" width="24.5" height="24.5" rx="1.25" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
4
+ <path d="M26 27H27C28.1046 27 29 26.1046 29 25V3C29 1.89543 28.1046 1 27 1H5C3.89543 1 3 1.89543 3 3V4" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
5
+ <path d="M7 10L7 24" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
6
+ <circle cx="7" cy="13" r="1" fill="black"/>
7
+ <circle cx="7" cy="21" r="1" fill="black"/>
8
+ <rect x="10.25" y="11.25" width="9.5" height="3.5" rx="1.15" stroke="black" stroke-width="1.5"/>
9
+ <rect x="10.25" y="19.25" width="9.5" height="3.5" rx="1.15" stroke="black" stroke-width="1.5"/>
10
+ </g>
11
+ <defs>
12
+ <clipPath id="clip0_286_20">
13
+ <rect width="30" height="30" fill="white"/>
14
+ </clipPath>
15
+ </defs>
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_286_20)">
3
+ <rect x="0.75" y="4.75" width="24.5" height="24.5" rx="1.25" stroke="currentColor" fill="none" stroke-width="1.5" stroke-linejoin="round"/>
4
+ <path d="M26 27H27C28.1046 27 29 26.1046 29 25V3C29 1.89543 28.1046 1 27 1H5C3.89543 1 3 1.89543 3 3V4" stroke="currentColor" fill="none" stroke-width="1.5" stroke-linejoin="round"/>
5
+ <path d="M7 10L7 24" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
6
+ <circle cx="7" cy="13" r="1" fill="none"/>
7
+ <circle cx="7" cy="21" r="1" fill="none"/>
8
+ <rect x="10.25" y="11.25" width="9.5" height="3.5" rx="1.15" stroke="currentColor" fill="none" stroke-width="1.5"/>
9
+ <rect x="10.25" y="19.25" width="9.5" height="3.5" rx="1.15" stroke="currentColor" fill="none" stroke-width="1.5"/>
10
+ </g>
11
+ <defs>
12
+ <clipPath id="clip0_286_20">
13
+ <rect width="30" height="30" fill="none"/>
14
+ </clipPath>
15
+ </defs>
16
+ </svg>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <ParagraphTimelineLayout v-bind="props" :blokkliData="blokkliData" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import type { ParagraphIcmsLayoutTimelineFragment } from '#graphql-operations'
7
+
8
+ const props = defineProps<ParagraphIcmsLayoutTimelineFragment>()
9
+
10
+ const blokkliData = defineBlokkli({
11
+ bundle: 'icms_layout_timeline',
12
+ globalOptions: ['spacingBottom', 'spacingTop', 'colorScheme'],
13
+ })
14
+ </script>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.49 12.87H5.51c-.28 0-.51-.23-.51-.51v-.99c0-.28.23-.51.51-.51H18.5c.28 0 .51.23.51.51v.99c0 .28-.23.51-.51.51Z" class="bk-option-icon-fill-strong" style="fill-rule:evenodd;stroke-width:0"/><rect width="20.01" height="5.95" x="2" y="14.29" class="bk-option-icon-fill-strong" rx=".53" ry=".53" style="stroke-width:0"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.49 12.87H5.51c-.28 0-.51-.23-.51-.51v-.99c0-.28.23-.51.51-.51H18.5c.28 0 .51.23.51.51v.99c0 .28-.23.51-.51.51Z" class="bk-option-icon-fill-strong" style="fill-rule:evenodd;stroke-width:0"/><rect width="20.01" height="5.95" x="2" y="3.5" class="bk-option-icon-fill-strong" rx=".53" ry=".53" style="stroke-width:0"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.49 12.87H5.51c-.28 0-.51-.23-.51-.51v-.99c0-.28.23-.51.51-.51H18.5c.28 0 .51.23.51.51v.99c0 .28-.23.51-.51.51Z" class="bk-option-icon-fill-strong" style="fill-rule:evenodd;stroke-width:0"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.49 12.87H5.51c-.28 0-.51-.23-.51-.51v-.99c0-.28.23-.51.51-.51H18.5c.28 0 .51.23.51.51v.99c0 .28-.23.51-.51.51Z" class="bk-option-icon-fill-strong" style="fill-rule:evenodd;stroke-width:0"/><rect width="20.01" height="3.53" x="2" y="14.29" class="bk-option-icon-fill-strong" rx=".52" ry=".52" style="stroke-width:0"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.49 12.87H5.51c-.28 0-.51-.23-.51-.51v-.99c0-.28.23-.51.51-.51H18.5c.28 0 .51.23.51.51v.99c0 .28-.23.51-.51.51Z" class="bk-option-icon-fill-strong" style="fill-rule:evenodd;stroke-width:0"/><rect width="20.01" height="3.53" x="2" y="5.92" class="bk-option-icon-fill-strong" rx=".52" ry=".52" style="stroke-width:0"/></svg>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <ParagraphIFrameLayout v-bind="props" :blokkliData="blokkliData" />
2
+ <ParagraphIFrame v-bind="props" :blokkliData="blokkliData" />
3
3
  </template>
4
4
 
5
5
  <script lang="ts" setup>
@@ -9,10 +9,6 @@ const props = defineProps<ParagraphIcmsLayoutIframeFragment>()
9
9
 
10
10
  const blokkliData = defineBlokkli({
11
11
  bundle: 'icms_layout_iframe',
12
- globalOptions: [
13
- 'spacingBottom',
14
- 'spacingTop',
15
- ],
12
+ globalOptions: ['textWidth', 'spacingBottom', 'spacingTop', 'colorScheme'],
16
13
  })
17
-
18
14
  </script>
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <DesignSystemAtomsGridRow
3
+ class="flex w-full shrink-0 flex-col-reverse justify-end p-0 lg:grid lg:scroll-mt-32"
4
+ data-timeline-element
5
+ >
6
+ <!-- Text column -->
7
+ <div :class="textColumnClasses">
8
+ <DesignSystemOrganismsContentBlock
9
+ :pretitle="pretitle"
10
+ :title="title"
11
+ :titleStyle="titleStyle"
12
+ :titleWeight="titleWeight"
13
+ titleClass="mb-2"
14
+ :subtitle="subtitle"
15
+ subtitleClass="font-semibold"
16
+ :text="text"
17
+ :colorScheme="colorScheme"
18
+ :hasDecorativeLine="false"
19
+ />
20
+ <BlokkliField
21
+ name="field_icms_buttons"
22
+ :list="buttons"
23
+ class="mt-6 flex flex-wrap gap-6"
24
+ :class="[{ droptarget: isEditing }]"
25
+ />
26
+ </div>
27
+
28
+ <!-- Media column -->
29
+ <div :class="mediaColumnClasses">
30
+ <DesignSystemMoleculesMediaSlider
31
+ v-if="media"
32
+ :media="media"
33
+ :colorScheme="colorScheme"
34
+ sliderClass="my-0"
35
+ imageSizes="xs:750px sm:750px md:750px lg:750px"
36
+ mediaClass="my-0 w-full rounded-lg overflow-hidden"
37
+ />
38
+ </div>
39
+ </DesignSystemAtomsGridRow>
40
+ </template>
41
+
42
+ <script lang="ts" setup>
43
+ import type { MediaFragment } from '#graphql-operations'
44
+ const props = defineProps<{
45
+ pretitle?: string
46
+ title?: string
47
+ titleStyle?: string
48
+ titleWeight?: string
49
+ subtitle?: string
50
+ text?: string
51
+ media?: (MediaFragment | null)[] | undefined
52
+ buttons?: Array<object>
53
+ colorScheme?: string
54
+ hasDecorativeLine?: boolean
55
+ reverseColumns?: boolean
56
+ isEditing?: boolean
57
+ }>()
58
+
59
+ const reverseColumns = ref(false)
60
+
61
+ watch(
62
+ () => props.reverseColumns,
63
+ (newVal) => {
64
+ reverseColumns.value = Boolean(Number(newVal))
65
+ },
66
+ { immediate: true },
67
+ )
68
+
69
+ const textColumnClasses = computed(() => {
70
+ const classList = ['col-span-12', 'lg:col-span-5']
71
+
72
+ if (reverseColumns.value) {
73
+ classList.push('lg:order-last', 'lg:col-start-8')
74
+ } else {
75
+ classList.push('lg:order-first', 'lg:col-start-1')
76
+ }
77
+
78
+ return classList.join(' ')
79
+ })
80
+
81
+ const mediaColumnClasses = computed(() => {
82
+ const classList = ['col-span-12', 'lg:col-span-5']
83
+
84
+ if (reverseColumns.value) {
85
+ classList.push('lg:order-first', 'lg:col-start-1')
86
+ } else {
87
+ classList.push('lg:order-last', 'lg:col-start-8')
88
+ }
89
+
90
+ return classList.join(' ')
91
+ })
92
+ </script>
@@ -0,0 +1,16 @@
1
+ fragment paragraphIcmsTimelineElement on ParagraphIcmsTimelineElement {
2
+ pretitle: fieldIcmsPretitle
3
+ title: fieldIcmsTitle
4
+ titleStyle: fieldIcmsTitleStyle
5
+ subtitle: fieldIcmsSubtitle
6
+ text: fieldIcmsText
7
+ buttons: fieldIcmsButtons {
8
+ ...paragraphsFieldItem
9
+ props {
10
+ ...paragraphIcmsButtonElement
11
+ }
12
+ }
13
+ media: fieldIcmsMediaElements {
14
+ ...media
15
+ }
16
+ }
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <DesignSystemOrganismsTimelineElement
3
+ :pretitle="pretitle"
4
+ :title="title"
5
+ :title-style="titleStyle"
6
+ :title-weight="titleWeight"
7
+ :subtitle="subtitle"
8
+ :text="text"
9
+ :media="media"
10
+ :buttons="buttons"
11
+ :color-scheme="colorScheme"
12
+ :has-decorative-line="false"
13
+ :reverse-columns="reverseColumns"
14
+ :is-editing="isEditing"
15
+ />
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ import type { ParagraphIcmsTimelineElementFragment } from '#graphql-operations'
20
+
21
+ defineProps<ParagraphIcmsTimelineElementFragment>()
22
+
23
+ const { options, isEditing } = useAttrs().blokkliData
24
+
25
+ const reverseColumns = computed(() => {
26
+ return options.value.reverseColumns
27
+ })
28
+
29
+ const colorScheme = inject<string>('colorScheme')
30
+ const titleWeight = inject<string>('titleWeight')
31
+ </script>
@@ -0,0 +1,14 @@
1
+ fragment paragraphIcmsLayoutTimeline on ParagraphIcmsLayoutTimeline {
2
+ pretitle: fieldIcmsPretitle
3
+ title: fieldIcmsTitle
4
+ titleStyle: fieldIcmsTitleStyle
5
+ titleWeight: fieldIcmsTitleWeight
6
+ subtitle: fieldIcmsSubtitle
7
+ text: fieldIcmsText
8
+ timelineElements: fieldIcmsTimelineElements {
9
+ ...paragraphsFieldItem
10
+ props {
11
+ ...paragraphIcmsTimelineElement
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,325 @@
1
+ <template>
2
+ <DesignSystemAtomsContainer :color-scheme="colorScheme" :spacing="spacing">
3
+ <DesignSystemAtomsGridRow v-if="pretitle || title || subtitle || text">
4
+ <div class="col-span-12">
5
+ <DesignSystemOrganismsContentBlock
6
+ :pretitle="pretitle"
7
+ :title="title"
8
+ :titleStyle="titleStyle"
9
+ :titleWeight="titleWeight"
10
+ :subtitle="subtitle"
11
+ :text="text"
12
+ :colorScheme="colorScheme"
13
+ :hasDecorativeLine="Boolean(subtitle || text)"
14
+ />
15
+ </div>
16
+ </DesignSystemAtomsGridRow>
17
+
18
+ <DesignSystemAtomsGridRow class="mt-10 lg:mt-20">
19
+ <!-- Anchor navigation -->
20
+ <div class="relative col-span-12 mb-6 lg:col-span-4 lg:mb-0">
21
+ <ul
22
+ v-if="!isEditing"
23
+ class="sticky left-0 top-32 z-10 flex flex-col gap-2"
24
+ >
25
+ <li v-for="(item, index) in props.timelineElements" :key="index">
26
+ <button
27
+ @click="handleAnchorLinkClick(Number(index))"
28
+ class="cursor-pointer transition-colors duration-200 ease-in-out"
29
+ :class="{
30
+ 'font-semibold': currentIndex === Number(index),
31
+ }"
32
+ >
33
+ {{ item.props?.title }}
34
+ </button>
35
+ </li>
36
+ </ul>
37
+ </div>
38
+
39
+ <div class="relative col-span-12 lg:col-span-8">
40
+ <!-- Mobile slider navigation -->
41
+ <div
42
+ v-if="isMobile && !isEditing"
43
+ class="relative col-span-12 mb-10 lg:col-span-4"
44
+ >
45
+ <ul
46
+ :class="decorativeLine({ colorScheme })"
47
+ class="flex h-0.5 flex-nowrap items-center justify-between"
48
+ >
49
+ <li
50
+ v-for="(_, index) in props.timelineElements"
51
+ :key="index"
52
+ class="p-4 first:-translate-x-4 last:translate-x-4"
53
+ >
54
+ <button
55
+ @click="handleAnchorLinkClick(Number(index))"
56
+ :class="
57
+ carouselDot({
58
+ colorScheme,
59
+ active: currentIndex === Number(index),
60
+ })
61
+ "
62
+ ></button>
63
+ </li>
64
+ </ul>
65
+ </div>
66
+
67
+ <!-- Mobile slider -->
68
+ <Carousel
69
+ v-if="isMobile && !isEditing"
70
+ @init-api="setApi"
71
+ :class="carousel({ colorScheme })"
72
+ >
73
+ <BlokkliField
74
+ v-slot="{ items }"
75
+ name="field_icms_timeline_elements"
76
+ :list="timelineElements"
77
+ proxy-mode
78
+ >
79
+ <CarouselContent class="m-0 gap-8">
80
+ <BlokkliItem v-for="item in items" v-bind="item" />
81
+ </CarouselContent>
82
+ </BlokkliField>
83
+ <CarouselPrevious
84
+ class="-bottom-[90px] left-0 top-auto xl:bottom-auto xl:left-16 xl:top-1/2"
85
+ :class="carouselButton({ colorScheme })"
86
+ >
87
+ <ChevronLeft :size="24" :stroke-width="1" />
88
+ </CarouselPrevious>
89
+ <CarouselNext
90
+ class="-bottom-[90px] left-[66px] top-auto xl:bottom-auto xl:left-auto xl:right-16 xl:top-1/2"
91
+ :class="carouselButton({ colorScheme })"
92
+ >
93
+ <ChevronRight :size="24" :stroke-width="1" />
94
+ </CarouselNext>
95
+ </Carousel>
96
+
97
+ <!-- Desktop timeline -->
98
+ <BlokkliField
99
+ v-if="isEditing || !isMobile"
100
+ name="field_icms_timeline_elements"
101
+ :list="timelineElements"
102
+ class="flex flex-col gap-32"
103
+ :class="{ droptarget: isEditing }"
104
+ />
105
+
106
+ <!-- Desktop line -->
107
+ <template v-if="!isMobile && !isEditing">
108
+ <div :class="decorativeCircle({ colorScheme })" class="top-0"></div>
109
+ <div
110
+ :class="decorativeLine({ colorScheme })"
111
+ class="absolute left-1/2 top-0 h-full w-0.5 -translate-x-1/2"
112
+ ></div>
113
+ <div
114
+ :class="decorativeCircle({ colorScheme })"
115
+ class="bottom-0"
116
+ ></div>
117
+ </template>
118
+ </div>
119
+ </DesignSystemAtomsGridRow>
120
+ </DesignSystemAtomsContainer>
121
+ </template>
122
+
123
+ <script lang="ts" setup>
124
+ import type { ParagraphIcmsLayoutTimelineFragment } from '#graphql-operations'
125
+ import { tv } from 'tailwind-variants'
126
+ import type { CarouselApi } from '@iqual/nuxt-icms/components/ui/carousel'
127
+ import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
128
+
129
+ const props = defineProps<ParagraphIcmsLayoutTimelineFragment>()
130
+
131
+ const { options, isEditing } = useAttrs().blokkliData
132
+
133
+ const { isMobile } = useIsMobile()
134
+
135
+ const colorScheme = computed(() => {
136
+ return options.value.colorScheme
137
+ })
138
+
139
+ const spacing = computed(() => {
140
+ return { bottom: options.value.spacingBottom, top: options.value.spacingTop }
141
+ })
142
+
143
+ const childTitleWeight = useChildTitleWeight(props.titleWeight ?? '')
144
+
145
+ const api = ref<CarouselApi>()
146
+ function setApi(val: CarouselApi) {
147
+ api.value = val
148
+
149
+ val?.on('select', () => {
150
+ const selectedIndex = val.selectedScrollSnap()
151
+ setCurrentIndex(selectedIndex)
152
+ })
153
+ }
154
+
155
+ const currentIndex = ref(0)
156
+ const setCurrentIndex = (index: number) => {
157
+ if (index < 0 || index >= (props.timelineElements?.length ?? 0)) return
158
+ currentIndex.value = index
159
+ }
160
+
161
+ const scrollToCarousel = (index: number) => {
162
+ if (!index && !api.value) return
163
+ nextTick(() => {
164
+ api.value?.scrollTo(index)
165
+ })
166
+ }
167
+
168
+ const scrollToElement = (element: HTMLElement) => {
169
+ if (!element) return
170
+ nextTick(() => {
171
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' })
172
+ })
173
+ }
174
+
175
+ const getTimelineElements = () =>
176
+ document.querySelectorAll('[data-timeline-element]')
177
+
178
+ const handleAnchorLinkClick = (index: number) => {
179
+ if (isMobile.value) {
180
+ setCurrentIndex(index)
181
+ scrollToCarousel(index)
182
+ } else {
183
+ const elements = getTimelineElements()
184
+ scrollToElement(elements[index] as HTMLElement)
185
+ }
186
+ }
187
+
188
+ const timelineObserver = ref<IntersectionObserver | null>(null)
189
+
190
+ onMounted(() => {
191
+ if (isEditing || isMobile.value) return
192
+
193
+ const elements = getTimelineElements()
194
+
195
+ timelineObserver.value = new IntersectionObserver(
196
+ (entries) => {
197
+ entries.forEach((entry) => {
198
+ if (entry.isIntersecting) {
199
+ const index = Array.from(elements).indexOf(
200
+ entry.target as HTMLElement,
201
+ )
202
+ if (index !== -1) {
203
+ setCurrentIndex(index)
204
+ }
205
+ }
206
+ })
207
+ },
208
+ {
209
+ rootMargin: '-10% 0px -80% 0px',
210
+ threshold: 0,
211
+ },
212
+ )
213
+
214
+ elements.forEach((element) => {
215
+ timelineObserver.value?.observe(element)
216
+ })
217
+ })
218
+
219
+ onUnmounted(() => {
220
+ timelineObserver.value?.disconnect()
221
+ })
222
+
223
+ const carousel = tv({
224
+ base: 'rounded-lg',
225
+ variants: {
226
+ colorScheme: {
227
+ none: 'bg-white',
228
+ dark: 'bg-gray-950',
229
+ light: 'bg-gray-50',
230
+ accent: 'bg-primary-600',
231
+ },
232
+ },
233
+ defaultVariants: {
234
+ colorScheme: 'none',
235
+ },
236
+ })
237
+
238
+ const carouselButton = tv({
239
+ base: 'size-10 border-none disabled:opacity-40 @sm:size:12 @md:size-16 [&_svg]:size-auto',
240
+ variants: {
241
+ colorScheme: {
242
+ none: 'bg-gray-50 text-black hover:bg-black hover:text-white dark:bg-gray-50 dark:text-black hover:dark:bg-black hover:dark:text-white',
243
+ dark: 'bg-white text-black hover:bg-gray-100 hover:text-black dark:bg-white dark:text-black hover:dark:bg-gray-100 hover:dark:text-black',
244
+ light:
245
+ 'bg-gray-100 text-black hover:bg-black hover:text-white dark:bg-gray-100 dark:text-black hover:dark:bg-black hover:dark:text-white',
246
+ accent:
247
+ 'bg-primary-700 text-white hover:bg-primary-800 hover:text-white dark:bg-primary-700 dark:text-white hover:dark:bg-primary-800 hover:dark:text-white',
248
+ },
249
+ },
250
+ defaultVariants: {
251
+ colorScheme: 'none',
252
+ },
253
+ })
254
+
255
+ const decorativeLine = tv({
256
+ base: '',
257
+ variants: {
258
+ colorScheme: {
259
+ none: 'bg-gray-200',
260
+ dark: 'bg-white',
261
+ light: 'bg-gray-200',
262
+ accent: 'bg-white',
263
+ },
264
+ },
265
+ defaultVariants: {
266
+ colorScheme: 'none',
267
+ },
268
+ })
269
+
270
+ const decorativeCircle = tv({
271
+ base: 'absolute left-1/2 size-4 -translate-x-1/2 border-2 rounded-full z-10',
272
+ variants: {
273
+ colorScheme: {
274
+ none: 'border-gray-200 bg-white',
275
+ dark: 'border-white bg-gray-950',
276
+ light: 'border-gray-200 bg-gray-50',
277
+ accent: 'border-white bg-primary-600',
278
+ },
279
+ },
280
+ defaultVariants: {
281
+ colorScheme: 'none',
282
+ },
283
+ })
284
+
285
+ const carouselDot = tv({
286
+ base: 'cursor-pointer transition-colors duration-200 ease-in-out size-4 border-2 rounded-full',
287
+ variants: {
288
+ colorScheme: {
289
+ none: 'bg-white border-gray-200',
290
+ dark: 'bg-gray-950 border-white',
291
+ light: 'bg-gray-50 border-gray-200',
292
+ accent: 'bg-primary-600 border-white',
293
+ },
294
+ },
295
+ compoundVariants: [
296
+ {
297
+ colorScheme: 'none',
298
+ active: true,
299
+ class: 'bg-gray-200',
300
+ },
301
+ {
302
+ colorScheme: 'dark',
303
+ active: true,
304
+ class: 'bg-white',
305
+ },
306
+ {
307
+ colorScheme: 'light',
308
+ active: true,
309
+ class: 'bg-gray-200',
310
+ },
311
+ {
312
+ colorScheme: 'accent',
313
+ active: true,
314
+ class: 'bg-white',
315
+ },
316
+ ],
317
+ defaultVariants: {
318
+ colorScheme: 'none',
319
+ active: false,
320
+ },
321
+ })
322
+
323
+ provide('colorScheme', colorScheme)
324
+ provide('titleWeight', childTitleWeight)
325
+ </script>
@@ -4,10 +4,6 @@ fragment paragraphIcmsLayoutIframe on ParagraphIcmsLayoutIframe {
4
4
  titleWeight: fieldIcmsTitleWeight
5
5
  titleStyle: fieldIcmsTitleStyle
6
6
  text: fieldIcmsText
7
- iFrameElements: fieldIcmsIframeElements {
8
- ...paragraphsFieldItem
9
- props {
10
- ...paragraphIcmsIframeElement
11
- }
12
- }
7
+ embed: fieldIcmsEmbedCode
8
+ height: fieldIcmsIframeHeight
13
9
  }
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <DesignSystemAtomsContainer :colorScheme="colorScheme" :spacing="spacing">
3
+ <DesignSystemAtomsGridRow>
4
+ <div :class="textWrapperClassList">
5
+ <DesignSystemOrganismsContentBlock
6
+ :pretitle="pretitle"
7
+ :title="title"
8
+ :titleWeight="titleWeight"
9
+ :text="text"
10
+ :colorScheme="colorScheme"
11
+ />
12
+
13
+ <div
14
+ v-if="embed"
15
+ v-html="embed"
16
+ class="embed-wrapper mt-10"
17
+ :style="{ '--iframe-height': `${props.height || 600}px` }"
18
+ />
19
+ </div>
20
+ </DesignSystemAtomsGridRow>
21
+ </DesignSystemAtomsContainer>
22
+ </template>
23
+
24
+ <script lang="ts" setup>
25
+ import type { ParagraphIcmsLayoutIframeFragment } from '#graphql-operations'
26
+
27
+ const props = defineProps<ParagraphIcmsLayoutIframeFragment>()
28
+
29
+ const { options, isEditing } = useAttrs().blokkliData
30
+
31
+ const textWrapperClassList = computed(() => {
32
+ if (options.value.textWidth === 'medium') {
33
+ return ['col-span-8', 'col-start-3']
34
+ } else if (options.value.textWidth === 'narrow') {
35
+ return ['col-span-8', 'col-start-3', 'md:col-span-6', 'md:col-start-4']
36
+ }
37
+ return ['col-span-12']
38
+ })
39
+
40
+ const colorScheme = computed(() => {
41
+ return options.value.colorScheme
42
+ })
43
+
44
+ const spacing = computed(() => {
45
+ return { bottom: options.value.spacingBottom, top: options.value.spacingTop }
46
+ })
47
+
48
+ provide('isEditing', isEditing)
49
+ </script>
50
+
51
+ <style>
52
+ .embed-wrapper > iframe {
53
+ width: 100%;
54
+ height: var(--iframe-height, 600px);
55
+ }
56
+ </style>
package/nuxt.config.ts CHANGED
@@ -168,15 +168,21 @@ export default defineNuxtConfig({
168
168
  drupalRoute: {
169
169
  enabled: true,
170
170
  routeQueries: {
171
- nodeIcmsPage: { fragments: [
172
- 'nodeIcmsPage'
173
- ] },
174
- slug: { fragments: [
175
- 'nodeIcmsPage',
176
- ]},
177
- nodeCanonical: { fragments: [
178
- 'nodeIcmsPage',
179
- ]},
171
+ nodeIcmsPage: {
172
+ fragments: [
173
+ 'nodeIcmsPage'
174
+ ]
175
+ },
176
+ slug: {
177
+ fragments: [
178
+ 'nodeIcmsPage',
179
+ ]
180
+ },
181
+ nodeCanonical: {
182
+ fragments: [
183
+ 'nodeIcmsPage',
184
+ ]
185
+ },
180
186
  },
181
187
  },
182
188
  devMode: {
@@ -217,6 +223,7 @@ export default defineNuxtConfig({
217
223
  'paragraphIcmsLayoutMedia',
218
224
  'paragraphIcmsLayoutDownloads',
219
225
  'paragraphIcmsLayoutIframe',
226
+ 'paragraphIcmsLayoutTimeline',
220
227
  // Element components
221
228
  'paragraphIcmsButtonElement',
222
229
  'paragraphIcmsCardElement',
@@ -228,7 +235,7 @@ export default defineNuxtConfig({
228
235
  'paragraphIcms2ColTextElement',
229
236
  'paragraphIcmsLogoElement',
230
237
  'paragraphIcmsPartnershipElement',
231
- 'paragraphIcmsIframeElement',
238
+ 'paragraphIcmsTimelineElement',
232
239
  ]
233
240
  }
234
241
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iqual/nuxt-icms",
3
- "version": "1.0.14",
3
+ "version": "1.1.0",
4
4
  "description": "ICMS Base",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,5 +0,0 @@
1
- <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="fill:none!important;">
2
- <path d="M6 1L1.80023 6.03972C1.33667 6.59599 1.33667 7.40401 1.80023 7.96028L6 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
3
- <path d="M12 13L16.1998 7.96028C16.6633 7.40401 16.6633 6.59599 16.1998 6.03972L12 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
4
- <path d="M7.5 10.5L10.5 3.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
5
- </svg>
@@ -1,14 +0,0 @@
1
- <template>
2
- <ParagraphIFrameElement v-bind="props" :blokkliData="blokkliData" />
3
- </template>
4
-
5
- <script lang="ts" setup>
6
- import type { ParagraphIcmsIframeElementFragment } from '#graphql-operations'
7
-
8
- const props = defineProps<ParagraphIcmsIframeElementFragment>()
9
-
10
- const blokkliData = defineBlokkli({
11
- bundle: 'icms_iframe_element',
12
- })
13
-
14
- </script>
@@ -1,9 +0,0 @@
1
- fragment paragraphIcmsIframeElement on ParagraphIcmsIframeElement {
2
- title: fieldIcmsTitle
3
- link: fieldIcmsLink {
4
- uri {
5
- path
6
- }
7
- }
8
- height: fieldIcmsNumber
9
- }
@@ -1,16 +0,0 @@
1
- <template>
2
- <DesignSystemMoleculesIFrame
3
- :title="title"
4
- :link="link?.uri?.path"
5
- :height="height"
6
- :is-editing="isEditing"
7
- />
8
- </template>
9
-
10
- <script lang="ts" setup>
11
- import type { ParagraphIcmsIframeElementFragment } from '#graphql-operations'
12
-
13
- defineProps<ParagraphIcmsIframeElementFragment>()
14
-
15
- const isEditing = inject<boolean>('isEditing')
16
- </script>
@@ -1,161 +0,0 @@
1
- <template>
2
- <DesignSystemAtomsContainer :colorScheme="colorScheme" :spacing="spacing">
3
- <DesignSystemAtomsGridRow class="pb-14 md:pb-0">
4
- <div class="col-span-12 md:col-span-6" v-if="title">
5
- <DesignSystemOrganismsContentBlock
6
- :pretitle="pretitle"
7
- :title="title"
8
- :titleWeight="titleWeight"
9
- :text="text"
10
- :colorScheme="colorScheme"
11
- />
12
- </div>
13
-
14
- <div v-if="isEditing" class="col-span-12">
15
- <BlokkliField
16
- name="field_icms_iframe_elements"
17
- :list="iFrameElements"
18
- :class="{ droptarget: isEditing }"
19
- />
20
- </div>
21
-
22
- <div v-else class="col-span-12">
23
- <template v-if="iFrameElements && iFrameElements.length === 1">
24
- <DesignSystemMoleculesIFrame
25
- :title="iFrameElements[0].props?.title"
26
- :link="iFrameElements[0].props?.link?.uri?.path"
27
- :height="iFrameElements[0].props?.height"
28
- />
29
- </template>
30
-
31
- <Tabs
32
- v-if="!isMobile && iFrameElements && iFrameElements.length > 1"
33
- default-value="tab-1"
34
- class="rounded-lg shadow-custom"
35
- >
36
- <TabsList
37
- class="flex flex-col items-center justify-center bg-transparent p-0 lg:flex-row dark:bg-transparent"
38
- >
39
- <TabsTrigger
40
- v-for="(iFrameElement, key) in iFrameElements"
41
- :key="key"
42
- :value="`tab-${key + 1}`"
43
- :class="tabTrigger({ colorScheme })"
44
- >
45
- {{ iFrameElement?.props?.title }}
46
- </TabsTrigger>
47
- </TabsList>
48
- <TabsContent
49
- forceMount
50
- v-for="(iFrameElement, key) in iFrameElements"
51
- :key="key"
52
- :value="`tab-${key + 1}`"
53
- :class="tabContent({ colorScheme })"
54
- >
55
- <DesignSystemMoleculesIFrame
56
- :title="iFrameElement.props?.title"
57
- :link="iFrameElement.props?.link?.uri?.path"
58
- :height="iFrameElement.props?.height"
59
- />
60
- </TabsContent>
61
- </Tabs>
62
-
63
- <Accordion
64
- v-if="isMobile && iFrameElements && iFrameElements.length > 1"
65
- type="single"
66
- class="w-full"
67
- collapsible
68
- default-value="accordion-1"
69
- >
70
- <AccordionItem
71
- v-for="(iFrameElement, key) in iFrameElements"
72
- :key="key"
73
- :value="`accordion-${key + 1}`"
74
- class="mb-1 border-none [&>h3]:mb-0"
75
- >
76
- <AccordionTrigger
77
- :ref="(el: any) => (triggerRefs[key] = el)"
78
- :class="accordionTrigger({ colorScheme })"
79
- @click="handleClick(key)"
80
- >
81
- {{ iFrameElement?.props?.title }}
82
- </AccordionTrigger>
83
- <AccordionContent class="p-4 pb-6 text-base">
84
- <DesignSystemMoleculesIFrame
85
- :title="iFrameElement.props?.title"
86
- :link="iFrameElement.props?.link?.uri?.path"
87
- :height="iFrameElement.props?.height"
88
- />
89
- </AccordionContent>
90
- </AccordionItem>
91
- </Accordion>
92
- </div>
93
- </DesignSystemAtomsGridRow>
94
- </DesignSystemAtomsContainer>
95
- </template>
96
-
97
- <script lang="ts" setup>
98
- import type { ParagraphIcmsLayoutIframeFragment } from '#graphql-operations'
99
- import { tv } from 'tailwind-variants'
100
-
101
- defineProps<ParagraphIcmsLayoutIframeFragment>()
102
-
103
- const { isMobile } = useIsMobile()
104
-
105
- const { options, isEditing } = useAttrs().blokkliData
106
-
107
- const tabTrigger = tv({
108
- base: 'inline-flex w-full flex-1 items-center justify-center whitespace-nowrap rounded-none rounded-t-lg py-4 text-base shadow-custom-inner transition-all disabled:pointer-events-none lg:w-fit data-[state=active]:shadow-none',
109
- variants: {
110
- colorScheme: {
111
- none: 'bg-white dark:bg-white text-black dark:text-black data-[state=active]:bg-white dark:data-[state=active]:bg-white dark:data-[state=active]:text-black',
112
- dark: 'bg-gray-700 dark:bg-gray-700 text-white dark:text-white data-[state=active]:bg-gray-900 dark:data-[state=active]:bg-gray-900 data-[state=active]:text-white',
113
- light:
114
- 'bg-white dark:bg-white text-black dark:text-black data-[state=active]:bg-white dark:data-[state=active]:bg-white dark:data-[state=active]:text-black',
115
- accent:
116
- 'bg-primary-600 dark:bg-primary-600 text-white dark:text-white data-[state=active]:bg-primary-700 dark:data-[state=active]:bg-primary-700 data-[state=active]:text-white',
117
- },
118
- },
119
- })
120
-
121
- const tabContent = tv({
122
- base: 'm-0 rounded-b-lg p-8 text-black md:p-16',
123
- variants: {
124
- colorScheme: {
125
- none: 'bg-white text-black',
126
- dark: 'bg-gray-900 text-white',
127
- light: 'bg-white text-black',
128
- accent: 'bg-primary-700 text-white',
129
- },
130
- },
131
- })
132
-
133
- const accordionTrigger = tv({
134
- base: 'items-center rounded-lg px-6 py-4 shadow-custom-small hover:no-underline gap-4 text-left text-base',
135
- variants: {
136
- colorScheme: {
137
- none: 'bg-white text-black',
138
- dark: 'bg-gray-900 text-white',
139
- light: 'bg-white text-black',
140
- accent: 'bg-primary-700 text-white',
141
- },
142
- },
143
- })
144
-
145
- const triggerRefs = ref<Array<ComponentPublicInstance>>([])
146
-
147
- const handleClick = (index: number): void => {
148
- const { handleAccordionOpen } = useAccordionScroll(triggerRefs.value[index])
149
- handleAccordionOpen()
150
- }
151
-
152
- const colorScheme = computed(() => {
153
- return options.value.colorScheme
154
- })
155
-
156
- const spacing = computed(() => {
157
- return { bottom: options.value.spacingBottom, top: options.value.spacingTop }
158
- })
159
-
160
- provide('isEditing', isEditing)
161
- </script>