@iqual/nuxt-icms 1.6.1 → 1.6.2-rc.2
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/README.md +86 -0
- package/__tests__/anchors.spec.ts +43 -0
- package/__tests__/contract/blokkliComponents.spec.ts +59 -0
- package/__tests__/contract/componentFragments.spec.ts +24 -0
- package/__tests__/contract/languages.spec.ts +63 -0
- package/__tests__/contract/project.ts +144 -0
- package/__tests__/hreflang.spec.ts +39 -0
- package/{tests → __tests__}/pages.spec.ts +14 -5
- package/app.config.ts +15 -0
- package/app.vue +3 -21
- package/assets/css/backend.css +56 -0
- package/assets/css/base/base.css +2 -2
- package/assets/css/base/typography.css +2 -2
- package/assets/css/ckeditor.css +3 -4
- package/assets/css/components/decorations.css +1 -1
- package/assets/css/components/editor.css +1 -1
- package/assets/css/components/grid-container.css +19 -19
- package/assets/css/fe-fixes.css +6 -12
- package/assets/css/theme/colors/color-primary.css +33 -11
- package/assets/css/theme/colors/color-secondary.css +33 -11
- package/assets/css/utilities/utilities.css +1 -1
- package/assets/symbols/anchor.svg +3 -0
- package/blokkli.editAdapter.ts +56 -0
- package/bundles.ts +6 -6
- package/cache/__tests__/pageCache.spec.ts +540 -0
- package/cache/cacheTagRegistry.ts +124 -0
- package/cache/enabledForRequest.ts +21 -0
- package/cache/multiCache.serverOptions.ts +58 -0
- package/cache/pageCache.ts +360 -0
- package/components/Blokkli/Anchor/icon.svg +3 -0
- package/components/Blokkli/Anchor/index.vue +13 -0
- package/components/Blokkli/AnchorNavigation/icon.svg +8 -0
- package/components/Blokkli/AnchorNavigation/index.vue +9 -0
- package/components/Blokkli/Card/Element/index.vue +0 -1
- package/components/Blokkli/Collapsible/Element/index.vue +5 -0
- package/components/Blokkli/FAQ/Element/index.vue +0 -1
- package/components/Blokkli/Fragment/Separator/icon.svg +4 -0
- package/components/Blokkli/Fragment/Separator/index.vue +87 -0
- package/components/Blokkli/Gallery/index.vue +1 -3
- package/components/Blokkli/IconText/Element/index.vue +0 -1
- package/components/Blokkli/Introduction/index.vue +0 -1
- package/components/Blokkli/Listing/icon.svg +9 -0
- package/components/Blokkli/Listing/index.vue +24 -0
- package/components/Blokkli/Logo/Element/index.vue +0 -1
- package/components/Blokkli/Table/index.vue +0 -1
- package/components/Blokkli/Team/Element/index.vue +0 -1
- package/components/Blokkli/Team/Layout/index.vue +0 -1
- package/components/Blokkli/Testimonial/Element/index.vue +0 -1
- package/components/Blokkli/Text/index.vue +0 -1
- package/components/DesignSystem/Atoms/Button.vue +3 -0
- package/components/DesignSystem/Atoms/GridRow.vue +3 -1
- package/components/DesignSystem/Molecules/ButtonGroup.vue +5 -4
- package/components/DesignSystem/Molecules/IconText.vue +6 -6
- package/components/DesignSystem/Molecules/MediaLightbox.vue +8 -8
- package/components/DesignSystem/Molecules/MediaSlider.vue +9 -11
- package/components/DesignSystem/Molecules/Partnership.vue +7 -4
- package/components/DesignSystem/Molecules/Table.vue +35 -19
- package/components/DesignSystem/Molecules/TitleComposition.vue +46 -15
- package/components/DesignSystem/Molecules/iFrame.vue +2 -2
- package/components/DesignSystem/Organisms/CallToAction.vue +8 -7
- package/components/DesignSystem/Organisms/CallToActionFeature.vue +8 -7
- package/components/DesignSystem/Organisms/CallToActionFullwidth.vue +9 -8
- package/components/DesignSystem/Organisms/ContentBlock.vue +26 -16
- package/components/DesignSystem/Organisms/FacetedListing.vue +38 -17
- package/components/DesignSystem/Organisms/FactCard.vue +6 -7
- package/components/DesignSystem/Organisms/FeatureCard.vue +14 -14
- package/components/DesignSystem/Organisms/FlipTeaser.vue +19 -19
- package/components/DesignSystem/Organisms/HeaderHero.vue +11 -11
- package/components/DesignSystem/Organisms/HeaderTextImage.vue +10 -10
- package/components/DesignSystem/Organisms/LinkList.vue +20 -12
- package/components/DesignSystem/Organisms/LogoElement.vue +7 -4
- package/components/DesignSystem/Organisms/Map.vue +1 -1
- package/components/DesignSystem/Organisms/SearchResults.vue +15 -6
- package/components/DesignSystem/Organisms/SpotlightCard.vue +18 -18
- package/components/DesignSystem/Organisms/TeamElement.vue +19 -18
- package/components/DesignSystem/Organisms/TeaserCard.vue +16 -17
- package/components/DesignSystem/Organisms/TeaserSimple.vue +4 -31
- package/components/DesignSystem/Organisms/Testimonial.vue +13 -13
- package/components/DesignSystem/Organisms/TextCard.vue +10 -10
- package/components/DesignSystem/Organisms/TimelineElement.vue +20 -19
- package/components/DesignSystem/Organisms/TwoColumnTextElement.vue +5 -5
- package/components/DesignSystem/README.md +59 -40
- package/components/Drupal/Breadcrumb/index.vue +25 -12
- package/components/Drupal/Messages/index.vue +9 -4
- package/components/Drupal/Webform/index.vue +271 -51
- package/components/Drupal/Webform/mutation.graphql +17 -1
- package/components/Form/HiddenInput.vue +1 -4
- package/components/Form/SelectInput.vue +1 -1
- package/components/Form/SubmitButton.vue +2 -2
- package/components/Form/TextAreaInput.vue +3 -3
- package/components/Form/TextEmailInput.vue +1 -1
- package/components/Form/TextInput.vue +1 -1
- package/components/Form/TextPhoneInput.vue +1 -1
- package/components/Form/WebformMarkup.vue +1 -1
- package/components/Form/composables/useFormDefaultValue.ts +9 -13
- package/components/Form/composables/useFormElement.ts +5 -3
- package/components/Form/utils/types.ts +3 -3
- package/components/Media/Image/fragment.graphql +4 -0
- package/components/Media/Image/index.vue +27 -17
- package/components/Media/RemoteVideo/index.vue +3 -3
- package/components/Media/Video/index.vue +1 -1
- package/components/Node/Icms/Page/SearchResult/fragment.graphql +1 -1
- package/components/Node/Icms/Page/SearchResult/index.vue +2 -2
- package/components/Node/Icms/Page/Teaser/index.vue +1 -1
- package/components/Node/Icms/Page/index.vue +8 -2
- package/components/Paragraph/Anchor/fragment.graphql +4 -0
- package/components/Paragraph/Anchor/index.vue +48 -0
- package/components/Paragraph/AnchorNavigation/fragment.graphql +3 -0
- package/components/Paragraph/AnchorNavigation/index.vue +154 -0
- package/components/Paragraph/BlokkliFragment/fragment.graphql +5 -0
- package/components/Paragraph/Button/Element/fragment.graphql +1 -1
- package/components/Paragraph/Button/Element/index.vue +13 -8
- package/components/Paragraph/Card/Element/index.vue +1 -1
- package/components/Paragraph/Card/Layout/index.vue +9 -9
- package/components/Paragraph/Collapsible/Element/index.vue +2 -2
- package/components/Paragraph/Collapsible/Layout/index.vue +19 -18
- package/components/Paragraph/Contact/index.vue +17 -17
- package/components/Paragraph/Downloads/fragment.graphql +1 -1
- package/components/Paragraph/Downloads/index.vue +13 -13
- package/components/Paragraph/FAQ/Element/index.vue +4 -3
- package/components/Paragraph/FAQ/Layout/index.vue +42 -21
- package/components/Paragraph/FromLibrary/fragment.graphql +17 -0
- package/components/Paragraph/Gallery/index.vue +14 -14
- package/components/Paragraph/HeroImage/index.vue +26 -19
- package/components/Paragraph/IconText/Element/index.vue +4 -4
- package/components/Paragraph/IconText/Layout/index.vue +12 -12
- package/components/Paragraph/Introduction/index.vue +17 -17
- package/components/Paragraph/LinkLists/Element/fragment.graphql +1 -1
- package/components/Paragraph/LinkLists/Element/index.vue +5 -5
- package/components/Paragraph/LinkLists/Layout/index.vue +9 -9
- package/components/Paragraph/Listing/fragment.graphql +14 -0
- package/components/Paragraph/Listing/index.vue +91 -0
- package/components/Paragraph/Logo/Element/fragment.graphql +1 -1
- package/components/Paragraph/Logo/Element/index.vue +7 -4
- package/components/Paragraph/Media/index.vue +23 -21
- package/components/Paragraph/Partnerships/Element/fragment.graphql +1 -1
- package/components/Paragraph/Partnerships/Element/index.vue +10 -7
- package/components/Paragraph/Partnerships/Layout/index.vue +13 -13
- package/components/Paragraph/SingleTestimonial/index.vue +13 -13
- package/components/Paragraph/Table/index.vue +12 -12
- package/components/Paragraph/Team/Element/index.vue +16 -14
- package/components/Paragraph/Team/Layout/index.vue +9 -9
- package/components/Paragraph/Teasers/index.vue +14 -17
- package/components/Paragraph/Testimonial/Element/index.vue +19 -18
- package/components/Paragraph/Testimonial/Layout/index.vue +24 -18
- package/components/Paragraph/Text/fragment.graphql +1 -1
- package/components/Paragraph/Text/index.vue +8 -8
- package/components/Paragraph/TextMedia/index.vue +22 -22
- package/components/Paragraph/Timeline/Element/index.vue +19 -19
- package/components/Paragraph/Timeline/Layout/index.vue +51 -20
- package/components/Paragraph/TwoColumnText/Element/index.vue +13 -13
- package/components/Paragraph/TwoColumnText/Layout/index.vue +1 -1
- package/components/Paragraph/iFrame/index.vue +8 -8
- package/components/Search/Facets/FacetDropdownUI.vue +25 -31
- package/components/Search/Facets/FacetedSearchUI.vue +72 -48
- package/components/Search/Facets/fragment.graphql +20 -15
- package/components/Section/Footer/index.vue +55 -32
- package/components/Section/Header/Buttons.vue +4 -5
- package/components/Section/Header/DesktopMainMenu.vue +5 -4
- package/components/Section/Header/DesktopMainMenuDropdown.vue +5 -5
- package/components/Section/Header/LanguageDropdown.vue +2 -3
- package/components/Section/Header/Logo.vue +3 -3
- package/components/Section/Header/MainButtons.vue +4 -5
- package/components/Section/Header/MetaButtons.vue +5 -6
- package/components/Section/Header/MetaMenu.vue +1 -1
- package/components/Section/Header/MobileMainMenu.vue +1 -1
- package/components/Section/Header/MobileMainMenuItem.vue +2 -2
- package/components/Section/Header/index.vue +6 -13
- package/composables/__tests__/useImageSources.spec.ts +91 -0
- package/composables/breadcrumb.ts +12 -7
- package/composables/useAccordionScroll.ts +12 -4
- package/composables/useAllowedFragments.ts +14 -0
- package/composables/useAnchors.ts +48 -0
- package/composables/useChildTitleWeight.ts +1 -1
- package/composables/useCurrentLangcodeEnum.ts +1 -1
- package/composables/useCurrentNodeId.ts +8 -0
- package/composables/useDrupalUrl.ts +15 -0
- package/composables/useEnrichFacetLabels.ts +8 -6
- package/composables/useEntityRegistry.ts +3 -3
- package/composables/useGlobalConfig.ts +1 -2
- package/composables/useGraphqlCacheability.ts +12 -1
- package/composables/useHreflangLinks.ts +54 -0
- package/composables/useImageSources.ts +70 -28
- package/composables/useInitData.ts +1 -2
- package/composables/useIsHomepage.ts +1 -1
- package/composables/useLinkUrl.ts +30 -0
- package/composables/useMediaLightbox.ts +62 -50
- package/composables/useMenu.ts +1 -3
- package/composables/useMessageBus.ts +14 -7
- package/composables/useMobileMenuOverlayBus.ts +1 -1
- package/composables/useNavigationOffset.ts +49 -6
- package/composables/usePageCache.ts +33 -0
- package/composables/useSiteShell.ts +11 -0
- package/composables/useTheme.ts +17 -13
- package/config/__tests__/stubs/nuxt-multi-cache-config.ts +2 -0
- package/config/blokkli.ts +7 -13
- package/config/easyTexts.ts +1 -1
- package/config/graphqlMiddleware.ts +11 -4
- package/config/index.ts +5 -2
- package/config/languageNegotiation.ts +1 -1
- package/config/multiCache.ts +6 -1
- package/config/postcss-drupal/postcss.config.cjs +2 -2
- package/config/runtimeConfig.ts +7 -2
- package/config/svgIconSprite.ts +1 -2
- package/config/vuepal.ts +16 -18
- package/error.vue +1 -22
- package/layouts/default.vue +2 -6
- package/layouts/error.vue +7 -15
- package/modules/blokkliFragments.ts +76 -0
- package/modules/dynamicEntityComponentResolver.ts +47 -47
- package/modules/dynamicParagraphResolver.ts +43 -8
- package/modules/pageCache.ts +106 -0
- package/nuxt.config.ts +62 -33
- package/package.json +23 -22
- package/pages/[...slug]/index.vue +25 -4
- package/pages/index.vue +3 -3
- package/pages/node/[nid]/index.vue +2 -3
- package/patches/@blokkli+editor+2.0.0-alpha.13.patch +15 -1
- package/plugins/currentNode.ts +17 -0
- package/plugins/graphqlMiddleware.ts +10 -2
- package/plugins/initData.ts +15 -7
- package/plugins/registerNodeIcmsPageComponents.ts +6 -2
- package/plugins/siteShell.client.ts +94 -0
- package/queries/facetTermLabels.graphql +1 -5
- package/queries/fragment.linkUrl.graphql +12 -0
- package/queries/query.contentSearch.graphql +17 -2
- package/queries/query.initData.graphql +1 -1
- package/scripts/create-shade.ts +13 -4
- package/scripts/update-icms.ts +3 -1
- package/server/__tests__/graphqlMiddleware.serverOptions.spec.ts +2 -2
- package/server/__tests__/turnstile.spec.ts +119 -0
- package/server/api/icms/blokkli/last-changed.get.ts +24 -0
- package/server/api/icon/[...params].ts +3 -9
- package/server/graphqlMiddleware.serverOptions.ts +9 -8
- package/server/helpers/index.ts +0 -1
- package/server/middleware/turnstile.ts +51 -0
- package/server/plugins/frameAncestors.ts +20 -0
- package/server/plugins/pageCache.ts +59 -0
- package/server/utils/__tests__/accessDeniedHandover.spec.ts +49 -0
- package/server/utils/__tests__/backendRequestHeaders.spec.ts +115 -0
- package/server/utils/accessDeniedHandover.ts +48 -0
- package/server/utils/backendRequestHeaders.ts +51 -0
- package/server/utils/cacheability.ts +4 -4
- package/shared/__tests__/blokkliState.spec.ts +54 -0
- package/shared/__tests__/csp.spec.ts +53 -0
- package/shared/__tests__/drupalUrl.spec.ts +50 -0
- package/shared/blokkliState.ts +40 -0
- package/shared/csp.ts +47 -0
- package/shared/drupalUrl.ts +30 -0
- package/tailwind.config.ts +0 -5
- package/types/breadcrumb.ts +22 -0
- package/types/global.ts +15 -14
- package/types/langcode.ts +16 -0
- package/types/layerConfig.ts +84 -0
- package/utils/pageCache.ts +95 -0
- package/utils/turnstile.ts +35 -0
- package/composables/useWebformEnrichment.ts +0 -68
- package/server/__tests__/graphqlMiddleware.ts +0 -41
- package/server/multiCache.serverOptions.ts +0 -32
- package/vitest.config.mts +0 -28
|
@@ -4,31 +4,37 @@
|
|
|
4
4
|
v-if="imageSources.length === 1"
|
|
5
5
|
:srcset="imageSources[0]?.srcset"
|
|
6
6
|
:sizes="imageSources[0]?.sizes"
|
|
7
|
+
:width="imageSources[0]?.width"
|
|
8
|
+
:height="imageSources[0]?.height"
|
|
7
9
|
:alt="media?.alt?.alt"
|
|
8
10
|
:title="media?.title"
|
|
9
11
|
:src="placeHolderImage"
|
|
10
12
|
:loading="imageLoading"
|
|
11
13
|
:class="themedClass('elementClass', 'w-full')"
|
|
12
14
|
/>
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
<template v-if="imageSources.length > 1">
|
|
16
|
+
<source
|
|
17
|
+
v-for="(source, key) in imageSources"
|
|
18
|
+
:key="key"
|
|
19
|
+
:srcset="source.srcset"
|
|
20
|
+
:media="source.media"
|
|
21
|
+
:width="source.width"
|
|
22
|
+
:height="source.height"
|
|
23
|
+
/>
|
|
24
|
+
<img
|
|
25
|
+
:src="placeHolderImage"
|
|
26
|
+
:width="fallbackSource?.width"
|
|
27
|
+
:height="fallbackSource?.height"
|
|
28
|
+
:alt="media?.alt?.alt"
|
|
29
|
+
:title="media?.title"
|
|
30
|
+
:loading="imageLoading"
|
|
31
|
+
:class="themedClass('elementClass', 'w-full')"
|
|
32
|
+
/>
|
|
33
|
+
</template>
|
|
28
34
|
<figcaption
|
|
29
35
|
v-if="showCaption && media?.caption"
|
|
30
|
-
v-text="media?.caption"
|
|
31
36
|
:class="themedClass('figcaptionClass', 'mt-2 text-center italic')"
|
|
37
|
+
v-text="media?.caption"
|
|
32
38
|
/>
|
|
33
39
|
</picture>
|
|
34
40
|
</template>
|
|
@@ -73,7 +79,7 @@ const imageAspectRatios = computed(() => {
|
|
|
73
79
|
return ''
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
return
|
|
82
|
+
return ''
|
|
77
83
|
})
|
|
78
84
|
|
|
79
85
|
const { placeHolderImage, imageSources } = useImageSources({
|
|
@@ -81,4 +87,8 @@ const { placeHolderImage, imageSources } = useImageSources({
|
|
|
81
87
|
imageAspectRatios: imageAspectRatios,
|
|
82
88
|
imageSizes: toRef(props, 'imageSizes'),
|
|
83
89
|
})
|
|
90
|
+
|
|
91
|
+
// The <img> of a <picture> is used when no <source> matches: the last source
|
|
92
|
+
// carries no media query and covers the widest screens.
|
|
93
|
+
const fallbackSource = computed(() => imageSources.value.at(-1))
|
|
84
94
|
</script>
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
<button
|
|
18
18
|
v-if="!isPlaying"
|
|
19
|
-
@click.prevent="isPlaying = true"
|
|
20
19
|
:class="
|
|
21
20
|
themedClass(
|
|
22
21
|
'playButtonClass',
|
|
23
22
|
'relative flex aspect-video w-full cursor-pointer items-center justify-center',
|
|
24
23
|
)
|
|
25
24
|
"
|
|
25
|
+
@click.prevent="isPlaying = true"
|
|
26
26
|
>
|
|
27
27
|
<img
|
|
28
28
|
:src="thumbnailUrl"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
:class="
|
|
31
31
|
themedClass(
|
|
32
32
|
'thumbnailClass',
|
|
33
|
-
'absolute
|
|
33
|
+
'absolute top-0 left-0 aspect-video size-full object-cover',
|
|
34
34
|
)
|
|
35
35
|
"
|
|
36
36
|
/>
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
|
|
44
44
|
<figcaption
|
|
45
45
|
v-if="showCaption && media?.caption"
|
|
46
|
-
v-text="media?.caption"
|
|
47
46
|
:class="themedClass('figcaptionClass', 'mt-2 text-center italic')"
|
|
47
|
+
v-text="media?.caption"
|
|
48
48
|
/>
|
|
49
49
|
</figure>
|
|
50
50
|
</VuepalRemoteVideo>
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<main>
|
|
3
|
-
<BlokkliProvider v-
|
|
4
|
-
<BlokkliField
|
|
3
|
+
<BlokkliProvider v-bind="blokkliProps" :entity="props">
|
|
4
|
+
<BlokkliField
|
|
5
|
+
:list="paragraphs"
|
|
6
|
+
name="field_icms_paragraphs"
|
|
7
|
+
:allowed-fragments="allowedFragments"
|
|
8
|
+
/>
|
|
5
9
|
</BlokkliProvider>
|
|
6
10
|
</main>
|
|
7
11
|
</template>
|
|
@@ -11,6 +15,8 @@ import type { NodeIcmsPageFragment } from '#graphql-operations'
|
|
|
11
15
|
|
|
12
16
|
const props = defineProps<NodeIcmsPageFragment>()
|
|
13
17
|
|
|
18
|
+
const allowedFragments = useAllowedFragments()
|
|
19
|
+
|
|
14
20
|
// Pass the topics of the article to the context, to be used in Layouts
|
|
15
21
|
provide('topics', props.topics)
|
|
16
22
|
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="isEditing"
|
|
4
|
+
:class="
|
|
5
|
+
themedClass(
|
|
6
|
+
'placeholderClass',
|
|
7
|
+
'flex items-center gap-2 p-4 text-sm text-gray-600',
|
|
8
|
+
)
|
|
9
|
+
"
|
|
10
|
+
>
|
|
11
|
+
<SpriteSymbol
|
|
12
|
+
name="anchor"
|
|
13
|
+
:class="themedClass('iconClass', 'size-6 shrink-0')"
|
|
14
|
+
/>
|
|
15
|
+
<span>{{ label }}</span>
|
|
16
|
+
<span v-if="id" :class="themedClass('idClass', 'text-gray-400')">
|
|
17
|
+
#{{ id }}
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div v-else-if="id">
|
|
21
|
+
<!-- Wrapped, so the attributes blokkli sets on the root element can not
|
|
22
|
+
override the anchor's own id. -->
|
|
23
|
+
<span
|
|
24
|
+
:id="id"
|
|
25
|
+
:style="{ '--anchor-offset': `${anchorOffset}px` }"
|
|
26
|
+
:class="themedClass('anchorClass', 'scroll-mt-(--anchor-offset)')"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script lang="ts" setup>
|
|
32
|
+
import type { ParagraphIcmsLayoutAnchorFragment } from '#graphql-operations'
|
|
33
|
+
import type { DefineBlokkliContext } from '#blokkli/types'
|
|
34
|
+
import {
|
|
35
|
+
resolveAnchorId,
|
|
36
|
+
useAnchorOffset,
|
|
37
|
+
} from '@iqual/nuxt-icms/composables/useAnchors'
|
|
38
|
+
|
|
39
|
+
const props = defineProps<ParagraphIcmsLayoutAnchorFragment>()
|
|
40
|
+
|
|
41
|
+
const themedClass = useTheme('ParagraphAnchor')
|
|
42
|
+
|
|
43
|
+
const { isEditing } = useAttrs().blokkliData as DefineBlokkliContext
|
|
44
|
+
|
|
45
|
+
const { anchorOffset } = useAnchorOffset()
|
|
46
|
+
|
|
47
|
+
const id = computed(() => resolveAnchorId(props.label, props.anchor))
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DesignSystemAtomsContainer
|
|
3
|
+
v-if="anchors.length"
|
|
4
|
+
:spacing="{ top: 'none', bottom: 'none' }"
|
|
5
|
+
:style="{ '--anchor-navigation-top': `${headerOffset}px` }"
|
|
6
|
+
:class="containerClass"
|
|
7
|
+
>
|
|
8
|
+
<DesignSystemAtomsGridRow>
|
|
9
|
+
<nav
|
|
10
|
+
:aria-label="
|
|
11
|
+
$texts('anchor_navigation.nav_aria_label', 'Anchor navigation')
|
|
12
|
+
"
|
|
13
|
+
:class="themedClass('navClass', 'col-span-12')"
|
|
14
|
+
>
|
|
15
|
+
<ul
|
|
16
|
+
:class="
|
|
17
|
+
themedClass('listClass', 'flex flex-nowrap gap-6 overflow-x-auto')
|
|
18
|
+
"
|
|
19
|
+
>
|
|
20
|
+
<li
|
|
21
|
+
v-for="anchor in anchors"
|
|
22
|
+
:key="anchor.id"
|
|
23
|
+
:class="themedClass('listItemClass', 'shrink-0')"
|
|
24
|
+
>
|
|
25
|
+
<a
|
|
26
|
+
:href="`#${anchor.id}`"
|
|
27
|
+
:aria-current="anchor.id === activeId ? 'true' : undefined"
|
|
28
|
+
:class="linkClass(anchor.id === activeId)"
|
|
29
|
+
>
|
|
30
|
+
{{ anchor.label }}
|
|
31
|
+
</a>
|
|
32
|
+
</li>
|
|
33
|
+
</ul>
|
|
34
|
+
</nav>
|
|
35
|
+
</DesignSystemAtomsGridRow>
|
|
36
|
+
</DesignSystemAtomsContainer>
|
|
37
|
+
<div
|
|
38
|
+
v-else-if="isEditing"
|
|
39
|
+
:class="
|
|
40
|
+
themedClass(
|
|
41
|
+
'placeholderClass',
|
|
42
|
+
'flex items-center gap-2 p-4 text-sm text-gray-600',
|
|
43
|
+
)
|
|
44
|
+
"
|
|
45
|
+
>
|
|
46
|
+
<SpriteSymbol
|
|
47
|
+
name="anchor"
|
|
48
|
+
:class="themedClass('iconClass', 'size-5 shrink-0')"
|
|
49
|
+
/>
|
|
50
|
+
{{
|
|
51
|
+
$texts(
|
|
52
|
+
'anchor_navigation.editor_empty',
|
|
53
|
+
'Add anchor blocks to fill this navigation',
|
|
54
|
+
)
|
|
55
|
+
}}
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script lang="ts" setup>
|
|
60
|
+
import cn from '@iqual/nuxt-icms/composables/useCn'
|
|
61
|
+
import type { DefineBlokkliContext } from '#blokkli/types'
|
|
62
|
+
import {
|
|
63
|
+
resolveAnchorId,
|
|
64
|
+
useAnchorOffset,
|
|
65
|
+
type Anchor,
|
|
66
|
+
} from '@iqual/nuxt-icms/composables/useAnchors'
|
|
67
|
+
|
|
68
|
+
const themedClass = useTheme('ParagraphAnchorNavigation')
|
|
69
|
+
|
|
70
|
+
const { $texts } = useNuxtApp()
|
|
71
|
+
|
|
72
|
+
const { isEditing, rootBlocks } = useAttrs().blokkliData as DefineBlokkliContext
|
|
73
|
+
|
|
74
|
+
const { headerOffset, anchorOffset } = useAnchorOffset()
|
|
75
|
+
const activeId = ref('')
|
|
76
|
+
|
|
77
|
+
// Anchors retrieved from Blokkli's rootBlocks without querying the DOM.
|
|
78
|
+
// Already in the right order and available during SSR.
|
|
79
|
+
const anchors = computed<Anchor[]>(() =>
|
|
80
|
+
rootBlocks.value.flatMap((block) => {
|
|
81
|
+
if (block.bundle !== 'icms_layout_anchor') {
|
|
82
|
+
return []
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const label = block.props?.label?.trim() ?? ''
|
|
86
|
+
const id = resolveAnchorId(label, block.props?.anchor)
|
|
87
|
+
|
|
88
|
+
return id ? [{ id, label }] : []
|
|
89
|
+
}),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
// Highlight the last anchor that scrolled past the bottom edge of the sticky bar.
|
|
93
|
+
const updateActive = () => {
|
|
94
|
+
const line = anchorOffset.value + 1
|
|
95
|
+
|
|
96
|
+
activeId.value =
|
|
97
|
+
anchors.value.findLast((anchor) => {
|
|
98
|
+
const top = document
|
|
99
|
+
.getElementById(anchor.id)
|
|
100
|
+
?.getBoundingClientRect().top
|
|
101
|
+
|
|
102
|
+
return top !== undefined && top <= line
|
|
103
|
+
})?.id ?? ''
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Throttle to one check per frame.
|
|
107
|
+
let frame = 0
|
|
108
|
+
const scheduleUpdate = () => {
|
|
109
|
+
if (frame) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
frame = requestAnimationFrame(() => {
|
|
114
|
+
frame = 0
|
|
115
|
+
updateActive()
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Editing the page changes the anchors, so recheck which one is active.
|
|
120
|
+
watch(anchors, updateActive, { flush: 'post' })
|
|
121
|
+
|
|
122
|
+
onMounted(() => {
|
|
123
|
+
updateActive()
|
|
124
|
+
window.addEventListener('scroll', scheduleUpdate, { passive: true })
|
|
125
|
+
window.addEventListener('resize', scheduleUpdate, { passive: true })
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
onBeforeUnmount(() => {
|
|
129
|
+
window.removeEventListener('scroll', scheduleUpdate)
|
|
130
|
+
window.removeEventListener('resize', scheduleUpdate)
|
|
131
|
+
cancelAnimationFrame(frame)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Not sticky while editing, it would cover the blocks in the editor canvas.
|
|
135
|
+
const containerClass = computed(() =>
|
|
136
|
+
cn(
|
|
137
|
+
themedClass('navigationContainerClass', 'border-b border-gray-100'),
|
|
138
|
+
isEditing
|
|
139
|
+
? ''
|
|
140
|
+
: themedClass('stickyClass', 'sticky top-(--anchor-navigation-top) z-40'),
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const linkClass = (isActive: boolean) =>
|
|
145
|
+
cn(
|
|
146
|
+
themedClass(
|
|
147
|
+
'linkClass',
|
|
148
|
+
'hover:text-primary block py-4 text-sm whitespace-nowrap motion-safe:transition-colors',
|
|
149
|
+
),
|
|
150
|
+
isActive
|
|
151
|
+
? themedClass('linkActiveClass', 'text-primary font-semibold')
|
|
152
|
+
: themedClass('linkInactiveClass', 'text-gray-600'),
|
|
153
|
+
)
|
|
154
|
+
</script>
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
<DesignSystemAtomsButton
|
|
4
4
|
:label="label"
|
|
5
5
|
:title="title"
|
|
6
|
-
:link="
|
|
6
|
+
:link="buttonLink"
|
|
7
7
|
:params="params"
|
|
8
|
-
:
|
|
9
|
-
:
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
8
|
+
:external="isFileLink"
|
|
9
|
+
:target-blank="targetBlank || isFileLink"
|
|
10
|
+
:button-style="buttonStyle"
|
|
11
|
+
:color-scheme="colorScheme"
|
|
12
|
+
:button-class="themedClass('buttonClass')"
|
|
13
|
+
:label-class="themedClass('labelClass')"
|
|
13
14
|
/>
|
|
14
15
|
</div>
|
|
15
16
|
</template>
|
|
@@ -20,9 +21,13 @@ import type { ParagraphIcmsButtonElementFragment } from '#graphql-operations'
|
|
|
20
21
|
|
|
21
22
|
const themedClass = useTheme('ParagraphButtonElement')
|
|
22
23
|
|
|
23
|
-
const { options
|
|
24
|
+
const { options } = useAttrs().blokkliData as any
|
|
24
25
|
|
|
25
|
-
defineProps<ParagraphIcmsButtonElementFragment>()
|
|
26
|
+
const props = defineProps<ParagraphIcmsButtonElementFragment>()
|
|
27
|
+
|
|
28
|
+
const resolvedLink = computed(() => resolveLinkUrl(props.link?.uri))
|
|
29
|
+
const isFileLink = computed(() => resolvedLink.value.isFile)
|
|
30
|
+
const buttonLink = computed(() => resolvedLink.value.url)
|
|
26
31
|
|
|
27
32
|
const buttonStyle = computed(() => {
|
|
28
33
|
return options.value.buttonStyle
|
|
@@ -22,7 +22,7 @@ const getComponent = (variant: CardVariant) => {
|
|
|
22
22
|
spotlight: DesignSystemOrganismsSpotlightCard,
|
|
23
23
|
fact: DesignSystemOrganismsFactCard,
|
|
24
24
|
}
|
|
25
|
-
return components[variant] ||
|
|
25
|
+
return components[variant] || DesignSystemOrganismsFeatureCard
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const variant = inject<CardVariant>('variant')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<DesignSystemAtomsContainer
|
|
3
|
-
:
|
|
3
|
+
:color-scheme="colorScheme"
|
|
4
4
|
:spacing="spacing"
|
|
5
5
|
:class="themedClass('containerClass')"
|
|
6
6
|
>
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
>
|
|
20
20
|
<DesignSystemOrganismsContentBlock
|
|
21
21
|
:pretitle="pretitle"
|
|
22
|
-
:
|
|
22
|
+
:pretitle-class="themedClass('pretitleClass')"
|
|
23
23
|
:title="title"
|
|
24
|
-
:
|
|
25
|
-
:
|
|
26
|
-
:
|
|
27
|
-
:
|
|
24
|
+
:title-class="themedClass('titleClass', 'mb-0')"
|
|
25
|
+
:title-weight="titleWeight"
|
|
26
|
+
:title-style="titleStyle"
|
|
27
|
+
:color-scheme="colorScheme"
|
|
28
28
|
/>
|
|
29
29
|
</div>
|
|
30
30
|
<div
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
>
|
|
39
39
|
<DesignSystemOrganismsContentBlock
|
|
40
40
|
:subtitle="subtitle"
|
|
41
|
-
:
|
|
41
|
+
:subtitle-class="themedClass('subtitleClass')"
|
|
42
42
|
:text="text"
|
|
43
|
-
:
|
|
44
|
-
:
|
|
43
|
+
:text-class="themedClass('textClass')"
|
|
44
|
+
:color-scheme="colorScheme"
|
|
45
45
|
/>
|
|
46
46
|
</div>
|
|
47
47
|
</DesignSystemAtomsGridRow>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div v-if="isEditing" :class="title({ colorScheme })">
|
|
3
3
|
<DesignSystemOrganismsContentBlock
|
|
4
4
|
:text="collapsibleTitle"
|
|
5
|
-
:
|
|
5
|
+
:text-class="themedClass('textClass', 'text-sm mb-3')"
|
|
6
6
|
/>
|
|
7
7
|
<BlokkliField
|
|
8
8
|
name="field_icms_collapsible_layouts"
|
|
@@ -38,7 +38,7 @@ import type { ParagraphIcmsCollapsibleElementFragment } from '#graphql-operation
|
|
|
38
38
|
|
|
39
39
|
defineProps<ParagraphIcmsCollapsibleElementFragment>()
|
|
40
40
|
|
|
41
|
-
const {
|
|
41
|
+
const { isEditing } = useAttrs().blokkliData as any
|
|
42
42
|
|
|
43
43
|
const themedClass = useTheme('ParagraphCollapsibleElement')
|
|
44
44
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<DesignSystemAtomsContainer
|
|
3
3
|
:spacing="spacing"
|
|
4
|
-
:
|
|
4
|
+
:color-scheme="colorScheme"
|
|
5
5
|
:class="themedClass('containerClass')"
|
|
6
6
|
>
|
|
7
7
|
<DesignSystemAtomsGridRow :class="themedClass('firstRowClass')">
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<DesignSystemOrganismsContentBlock
|
|
18
18
|
:pretitle="pretitle"
|
|
19
19
|
:title="title"
|
|
20
|
-
:
|
|
21
|
-
:
|
|
22
|
-
:
|
|
20
|
+
:title-weight="titleWeight"
|
|
21
|
+
:title-style="titleStyle"
|
|
22
|
+
:title-class="themedClass('titleClass')"
|
|
23
23
|
:subtitle="subtitle"
|
|
24
24
|
:text="text"
|
|
25
25
|
/>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
})
|
|
33
33
|
"
|
|
34
34
|
>
|
|
35
|
-
<div class="relative col-span-12"
|
|
35
|
+
<div v-if="isEditing" class="relative col-span-12">
|
|
36
36
|
<BlokkliField
|
|
37
37
|
name="field_icms_collapsible_elements"
|
|
38
38
|
:list="collapsibleElements"
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
</div>
|
|
42
42
|
|
|
43
43
|
<div
|
|
44
|
-
:class="themedClass('secondRowColumnClass', 'relative col-span-12')"
|
|
45
44
|
v-else
|
|
45
|
+
:class="themedClass('secondRowColumnClass', 'relative col-span-12')"
|
|
46
46
|
>
|
|
47
47
|
<template v-if="collapsibleVariant === 'tabs'">
|
|
48
48
|
<UTabs
|
|
49
49
|
v-if="!isMobile"
|
|
50
50
|
:items="tabItems"
|
|
51
51
|
:default-index="0"
|
|
52
|
-
:
|
|
52
|
+
:unmount-on-hide="false"
|
|
53
53
|
variant="link"
|
|
54
54
|
:ui="{
|
|
55
55
|
root: themedClass('tabsClass', 'gap-0'),
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
indicator: tabIndicator({ colorScheme }),
|
|
59
59
|
}"
|
|
60
60
|
>
|
|
61
|
-
<template #content="{ item
|
|
61
|
+
<template #content="{ item }">
|
|
62
62
|
<div :class="themedClass('tabsContentInnerClass', 'py-6')">
|
|
63
63
|
<BlokkliField
|
|
64
64
|
name="field_icms_collapsible_elements"
|
|
@@ -91,9 +91,9 @@
|
|
|
91
91
|
content: themedClass('accordionContentClass'),
|
|
92
92
|
trailingIcon: accordionTrailingIcon({ colorScheme }),
|
|
93
93
|
}"
|
|
94
|
-
@update:
|
|
94
|
+
@update:model-value="handleAccordionClick"
|
|
95
95
|
>
|
|
96
|
-
<template #content="{ item
|
|
96
|
+
<template #content="{ item }">
|
|
97
97
|
<div :class="themedClass('accordionContentInnerClass', 'py-6')">
|
|
98
98
|
<BlokkliField
|
|
99
99
|
name="field_icms_collapsible_elements"
|
|
@@ -127,9 +127,9 @@
|
|
|
127
127
|
content: accordionContent({ colorScheme }),
|
|
128
128
|
trailingIcon: accordionTrailingIcon({ colorScheme }),
|
|
129
129
|
}"
|
|
130
|
-
@update:
|
|
130
|
+
@update:model-value="handleAccordionClick"
|
|
131
131
|
>
|
|
132
|
-
<template #content="{ item
|
|
132
|
+
<template #content="{ item }">
|
|
133
133
|
<div :class="themedClass('accordionContentInnerClass', 'py-6')">
|
|
134
134
|
<BlokkliField
|
|
135
135
|
name="field_icms_collapsible_elements"
|
|
@@ -154,12 +154,11 @@
|
|
|
154
154
|
<template v-if="collapsibleVariant === 'carousel'">
|
|
155
155
|
<div :class="carousel({ colorScheme })">
|
|
156
156
|
<UCarousel
|
|
157
|
-
v-model="carouselIndex"
|
|
158
157
|
:items="collapsibleElements || []"
|
|
159
158
|
:arrows="(collapsibleElements?.length || 0) > 1"
|
|
160
159
|
:watch-drag="(collapsibleElements?.length || 0) > 1"
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
next-icon="i-lucide-chevron-right"
|
|
161
|
+
prev-icon="i-lucide-chevron-left"
|
|
163
162
|
:class="themedClass('carouselClass')"
|
|
164
163
|
:ui="{
|
|
165
164
|
viewport: themedClass('carouselViewportClass', 'pt-8'),
|
|
@@ -188,7 +187,11 @@
|
|
|
188
187
|
:list="[item]"
|
|
189
188
|
proxy-mode
|
|
190
189
|
>
|
|
191
|
-
<BlokkliItem
|
|
190
|
+
<BlokkliItem
|
|
191
|
+
v-for="fieldItem in items"
|
|
192
|
+
:key="fieldItem.uuid"
|
|
193
|
+
v-bind="fieldItem"
|
|
194
|
+
/>
|
|
192
195
|
</BlokkliField>
|
|
193
196
|
</template>
|
|
194
197
|
</UCarousel>
|
|
@@ -238,8 +241,6 @@ const accordionItems = computed(() => {
|
|
|
238
241
|
const accordionRef = ref()
|
|
239
242
|
const { scrollToTrigger } = useAccordionScroll()
|
|
240
243
|
|
|
241
|
-
const carouselIndex = ref(0)
|
|
242
|
-
|
|
243
244
|
const handleAccordionClick = (
|
|
244
245
|
payload: string | string[] | number | undefined,
|
|
245
246
|
): void => {
|