@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
package/config/blokkli.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import drupal from '@blokkli/editor/drupal'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { defineNuxtConfigProperty, currentLayerDir } from '.'
|
|
4
|
-
import tailwindConfig from '../tailwind.config'
|
|
5
|
-
// import resolveConfig from 'tailwindcss/resolveConfig'
|
|
6
4
|
|
|
7
|
-
// const fullTailwindConfig = resolveConfig(tailwindConfig)
|
|
8
|
-
// const tailwindColors = fullTailwindConfig.theme.colors as Record<string, any>
|
|
9
5
|
const IS_DEV = process.env.NODE_ENV === 'development'
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
const transformColor = (color: string): number[] => {
|
|
13
|
-
return color.substring(4, color.length - 1).replace(/ /g, '').split(',').map(Number)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default defineNuxtConfigProperty<'blokkli'>((ctx) => {
|
|
7
|
+
export default defineNuxtConfigProperty<'blokkli'>(() => {
|
|
17
8
|
return {
|
|
18
|
-
|
|
19
9
|
modules: [drupal()],
|
|
10
|
+
// The layer's adapter extends blökkli's Drupal adapter so that Drupal
|
|
11
|
+
// forms load from the backend domain. An app-level
|
|
12
|
+
// app/blokkli.editAdapter.ts still takes precedence.
|
|
13
|
+
editAdapterPath: join(currentLayerDir, 'blokkli.editAdapter.ts'),
|
|
20
14
|
theme: {
|
|
21
15
|
accent: {
|
|
22
16
|
'50': [254, 245, 238],
|
|
@@ -167,7 +161,7 @@ export default defineNuxtConfigProperty<'blokkli'>((ctx) => {
|
|
|
167
161
|
right: {
|
|
168
162
|
label: 'Right',
|
|
169
163
|
icon: 'icon-blokkli-option-align-h-right',
|
|
170
|
-
}
|
|
164
|
+
},
|
|
171
165
|
},
|
|
172
166
|
},
|
|
173
167
|
alignHorizontalReduced: {
|
|
@@ -183,7 +177,7 @@ export default defineNuxtConfigProperty<'blokkli'>((ctx) => {
|
|
|
183
177
|
right: {
|
|
184
178
|
label: 'Right',
|
|
185
179
|
icon: 'icon-blokkli-option-align-h-right',
|
|
186
|
-
}
|
|
180
|
+
},
|
|
187
181
|
},
|
|
188
182
|
},
|
|
189
183
|
alignVerticalReduced: {
|
package/config/easyTexts.ts
CHANGED
|
@@ -3,8 +3,15 @@ import { join } from 'path'
|
|
|
3
3
|
|
|
4
4
|
const NUXT_BACKEND_URL = process.env.NUXT_BACKEND_URL
|
|
5
5
|
const IS_DEV = process.env.NODE_ENV === 'development'
|
|
6
|
+
// A dev-mode prepare on CI (the linting workflow needs one) has no Drupal
|
|
7
|
+
// backend to introspect — the committed schema.graphql is the source there.
|
|
8
|
+
const IS_CI = !!process.env.CI
|
|
9
|
+
// `make frontend-schema` forces a download regardless of mode: locally to
|
|
10
|
+
// refresh the committed schema, in the testing workflow's build job (where a
|
|
11
|
+
// Drupal backend exists) to detect a stale one.
|
|
12
|
+
const FORCE_DOWNLOAD = process.env.NUXT_GRAPHQL_DOWNLOAD_SCHEMA === 'true'
|
|
6
13
|
|
|
7
|
-
export default defineNuxtConfigProperty<'graphqlMiddleware'>((
|
|
14
|
+
export default defineNuxtConfigProperty<'graphqlMiddleware'>(() => {
|
|
8
15
|
return {
|
|
9
16
|
serverApiPrefix: '/api/graphql',
|
|
10
17
|
autoImportPatterns: [
|
|
@@ -20,10 +27,10 @@ export default defineNuxtConfigProperty<'graphqlMiddleware'>((ctx) => {
|
|
|
20
27
|
'~/server/**/*.{gql,graphql}',
|
|
21
28
|
'~/sites/**/*.{gql,graphql}',
|
|
22
29
|
'!node_modules',
|
|
23
|
-
join(currentLayerDir, './**/*.{gql,graphql}')
|
|
30
|
+
join(currentLayerDir, './**/*.{gql,graphql}'),
|
|
24
31
|
],
|
|
25
32
|
documents: [],
|
|
26
|
-
downloadSchema: IS_DEV,
|
|
33
|
+
downloadSchema: FORCE_DOWNLOAD || (IS_DEV && !IS_CI),
|
|
27
34
|
graphqlEndpoint: `${NUXT_BACKEND_URL}/graphql`, // If we have Multisite Setup we need to use NUXT_HOST here.
|
|
28
35
|
codegenConfig: {
|
|
29
36
|
skipUnusedFragments: true,
|
|
@@ -36,4 +43,4 @@ export default defineNuxtConfigProperty<'graphqlMiddleware'>((ctx) => {
|
|
|
36
43
|
},
|
|
37
44
|
},
|
|
38
45
|
}
|
|
39
|
-
})
|
|
46
|
+
})
|
package/config/index.ts
CHANGED
|
@@ -3,7 +3,10 @@ import type { NuxtConfig } from '@nuxt/schema'
|
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { dirname, join } from 'node:path'
|
|
5
5
|
|
|
6
|
-
export const currentLayerDir = join(
|
|
6
|
+
export const currentLayerDir = join(
|
|
7
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
8
|
+
'..',
|
|
9
|
+
)
|
|
7
10
|
|
|
8
11
|
type ModuleConfigContext = {
|
|
9
12
|
DEV: boolean
|
|
@@ -21,4 +24,4 @@ export function defineNuxtConfigProperty<T extends keyof NuxtConfig>(
|
|
|
21
24
|
cb: (ctx: ModuleConfigContext) => NuxtConfig[T],
|
|
22
25
|
) {
|
|
23
26
|
return cb(ctx)
|
|
24
|
-
}
|
|
27
|
+
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
acceptLanguage,
|
|
6
6
|
} from 'nuxt-language-negotiation/negotiators'
|
|
7
7
|
|
|
8
|
-
export default defineNuxtConfigProperty<'languageNegotiation'>((
|
|
8
|
+
export default defineNuxtConfigProperty<'languageNegotiation'>(() => {
|
|
9
9
|
return {
|
|
10
10
|
negotiators: [
|
|
11
11
|
pathPrefix(),
|
package/config/multiCache.ts
CHANGED
|
@@ -8,8 +8,10 @@ export default defineNuxtConfigProperty<'multiCache'>((ctx) => {
|
|
|
8
8
|
data: {
|
|
9
9
|
enabled: true,
|
|
10
10
|
},
|
|
11
|
+
// Built in, but pages only land in it via usePageCache() and only in
|
|
12
|
+
// the default page cache mode (runtimeConfig.pageCache).
|
|
11
13
|
route: {
|
|
12
|
-
enabled:
|
|
14
|
+
enabled: true,
|
|
13
15
|
},
|
|
14
16
|
api: {
|
|
15
17
|
enabled: true,
|
|
@@ -17,6 +19,9 @@ export default defineNuxtConfigProperty<'multiCache'>((ctx) => {
|
|
|
17
19
|
prefix: '/api/multi-cache',
|
|
18
20
|
cacheTagInvalidationDelay: 5000,
|
|
19
21
|
},
|
|
22
|
+
// Collects the cacheability of every SSR response. The headers are only
|
|
23
|
+
// sent as-is in the `varnish` page cache mode; the default mode rewrites
|
|
24
|
+
// them to browser-only headers (server/plugins/pageCache.ts).
|
|
20
25
|
cdn: {
|
|
21
26
|
enabled: true,
|
|
22
27
|
cacheControlHeader: 'Cache-Control',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
plugins: {
|
|
3
|
-
|
|
3
|
+
'@tailwindcss/postcss': {},
|
|
4
4
|
'postcss-nesting': {},
|
|
5
5
|
'postcss-hexrgba': {
|
|
6
6
|
colorFunctionNotation: 'modern',
|
|
@@ -10,4 +10,4 @@ module.exports = {
|
|
|
10
10
|
preset: 'default',
|
|
11
11
|
},
|
|
12
12
|
},
|
|
13
|
-
}
|
|
13
|
+
}
|
package/config/runtimeConfig.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { defineNuxtConfigProperty } from '.'
|
|
2
|
+
import { pageCacheDefaults } from '../utils/pageCache'
|
|
2
3
|
|
|
3
|
-
export default defineNuxtConfigProperty<'runtimeConfig'>(() => {
|
|
4
|
+
export default defineNuxtConfigProperty<'runtimeConfig'>((ctx) => {
|
|
4
5
|
return {
|
|
5
6
|
backendUrl: '',
|
|
6
7
|
requestHost: '',
|
|
7
8
|
drupalGraphqlToken: process.env.DRUPAL_GRAPHQL_TOKEN || '',
|
|
8
9
|
elasticsearchUrl: '',
|
|
9
10
|
elasticsearchPrefix: '',
|
|
11
|
+
// Page cache mode and budgets (ICMS-268), overridable per project in
|
|
12
|
+
// nuxt.config.ts or via NUXT_PAGE_CACHE_* environment variables. See
|
|
13
|
+
// utils/pageCache.ts. The route cache is off in development.
|
|
14
|
+
pageCache: pageCacheDefaults(ctx.DEV),
|
|
10
15
|
public: {
|
|
11
|
-
imageBaseUrl: ''
|
|
16
|
+
imageBaseUrl: '',
|
|
12
17
|
},
|
|
13
18
|
}
|
|
14
19
|
})
|
package/config/svgIconSprite.ts
CHANGED
package/config/vuepal.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import { defineNuxtConfigProperty } from '.'
|
|
2
|
-
import { resolve, dirname } from 'path'
|
|
3
|
-
import { fileURLToPath } from 'url'
|
|
4
|
-
import { existsSync } from 'fs'
|
|
5
2
|
|
|
6
3
|
const NUXT_REQUEST_HOST = process.env.NUXT_REQUEST_HOST
|
|
7
4
|
const IS_DEV = process.env.NODE_ENV === 'development'
|
|
5
|
+
// A dev-mode prepare on CI (the linting workflow needs one) has no local
|
|
6
|
+
// Drupal checkout to write into.
|
|
7
|
+
const IS_CI = !!process.env.CI
|
|
8
8
|
|
|
9
|
-
export default defineNuxtConfigProperty<'vuepal'>((
|
|
9
|
+
export default defineNuxtConfigProperty<'vuepal'>(() => {
|
|
10
10
|
return {
|
|
11
11
|
frontendRouting: {
|
|
12
12
|
enabled: true,
|
|
13
|
-
outputPath:
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
outputPath:
|
|
14
|
+
IS_DEV && !IS_CI
|
|
15
|
+
? './../drupal/config/default/frontend_routing.settings.yml'
|
|
16
|
+
: './app/tmp.yml',
|
|
16
17
|
},
|
|
18
|
+
// The admin UI is Drupal's: editors browse the frontend inside the
|
|
19
|
+
// backend's site shell (docs/routing.md), so the frontend renders no
|
|
20
|
+
// toolbar or local tasks of its own.
|
|
17
21
|
adminToolbar: {
|
|
18
|
-
enabled:
|
|
22
|
+
enabled: false,
|
|
19
23
|
},
|
|
20
24
|
localTasks: {
|
|
21
|
-
enabled:
|
|
25
|
+
enabled: false,
|
|
22
26
|
},
|
|
23
27
|
languageSwitchLinks: {
|
|
24
28
|
enabled: true,
|
|
@@ -27,19 +31,13 @@ export default defineNuxtConfigProperty<'vuepal'>((ctx) => {
|
|
|
27
31
|
enabled: true,
|
|
28
32
|
routeQueries: {
|
|
29
33
|
nodeIcmsPage: {
|
|
30
|
-
fragments: [
|
|
31
|
-
'nodeIcmsPage'
|
|
32
|
-
]
|
|
34
|
+
fragments: ['nodeIcmsPage'],
|
|
33
35
|
},
|
|
34
36
|
slug: {
|
|
35
|
-
fragments: [
|
|
36
|
-
'nodeIcmsPage',
|
|
37
|
-
]
|
|
37
|
+
fragments: ['nodeIcmsPage'],
|
|
38
38
|
},
|
|
39
39
|
nodeCanonical: {
|
|
40
|
-
fragments: [
|
|
41
|
-
'nodeIcmsPage',
|
|
42
|
-
]
|
|
40
|
+
fragments: ['nodeIcmsPage'],
|
|
43
41
|
},
|
|
44
42
|
},
|
|
45
43
|
},
|
package/error.vue
CHANGED
|
@@ -1,34 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="bk-main-canvas relative">
|
|
3
|
-
<
|
|
4
|
-
<div
|
|
5
|
-
v-if="drupalUser.accessToolbar && !isEditing"
|
|
6
|
-
class="hidden border-b border-b-gray-100 bg-white 2xl:block"
|
|
7
|
-
>
|
|
8
|
-
<VuepalAdminToolbar :key="language" />
|
|
9
|
-
<div class="flex">
|
|
10
|
-
<div class="mx-auto w-auto bg-white py-8 xl:min-w-[1174px]">
|
|
11
|
-
<VuepalLocalTasks />
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|
|
14
|
-
</div>
|
|
15
|
-
</ClientOnly>
|
|
3
|
+
<NuxtLoadingIndicator color="var(--primary-500)" :height="3" />
|
|
16
4
|
<NuxtLayout name="error" />
|
|
17
5
|
</div>
|
|
18
6
|
</template>
|
|
19
7
|
|
|
20
8
|
<script setup lang="ts">
|
|
21
|
-
const route = useRoute()
|
|
22
|
-
const drupalUser = useDrupalUser()
|
|
23
9
|
const language = useCurrentLanguage()
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
const isEditing = computed(
|
|
27
|
-
() =>
|
|
28
|
-
!!(route.query.blokkliEditing || route.query.blokkliPreview) &&
|
|
29
|
-
drupalUser.value.accessToolbar,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
11
|
useHead({
|
|
33
12
|
htmlAttrs: {
|
|
34
13
|
lang: language.value,
|
package/layouts/default.vue
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<SectionHeader />
|
|
4
4
|
|
|
5
|
-
<!-- <NuxtPageDependency>
|
|
6
|
-
<DrupalBreadcrumb v-if="showBreadcrumb" :links="breadcrumb" />
|
|
7
|
-
</NuxtPageDependency> -->
|
|
8
|
-
|
|
9
5
|
<div>
|
|
10
6
|
<ClientOnly>
|
|
11
7
|
<DrupalMessages v-if="!isEditing" />
|
|
@@ -21,6 +17,7 @@
|
|
|
21
17
|
const route = useRoute()
|
|
22
18
|
const drupalUser = useDrupalUser()
|
|
23
19
|
const language = useCurrentLanguage()
|
|
20
|
+
const hreflangLinks = useHreflangLinks()
|
|
24
21
|
|
|
25
22
|
const node = route.meta.drupalFrontendRoute
|
|
26
23
|
? ref(null)
|
|
@@ -30,6 +27,7 @@ useHead({
|
|
|
30
27
|
htmlAttrs: {
|
|
31
28
|
lang: language.value,
|
|
32
29
|
},
|
|
30
|
+
link: hreflangLinks,
|
|
33
31
|
meta: [
|
|
34
32
|
{
|
|
35
33
|
name: 'robots',
|
|
@@ -48,6 +46,4 @@ const isEditing = computed(
|
|
|
48
46
|
!!(route.query.blokkliEditing || route.query.blokkliPreview) &&
|
|
49
47
|
drupalUser.value.accessToolbar,
|
|
50
48
|
)
|
|
51
|
-
|
|
52
|
-
const showBreadcrumb = computed(() => !route.meta.hideBreadcrumb)
|
|
53
49
|
</script>
|
package/layouts/error.vue
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<SectionHeader />
|
|
4
4
|
|
|
5
|
-
<!-- <NuxtPageDependency>
|
|
6
|
-
<DrupalBreadcrumb v-if="showBreadcrumb" :links="breadcrumb" />
|
|
7
|
-
</NuxtPageDependency> -->
|
|
8
|
-
|
|
9
5
|
<div>
|
|
10
6
|
<ClientOnly>
|
|
11
7
|
<DrupalMessages v-if="!isEditing" />
|
|
@@ -14,7 +10,7 @@
|
|
|
14
10
|
<div class="container mx-auto grid grid-cols-1 gap-6 py-12">
|
|
15
11
|
<div class="ck-editor text-center">
|
|
16
12
|
<h1>Whooops!</h1>
|
|
17
|
-
<p v-html="errorMessage.replace(/\n/g, '<br>')"
|
|
13
|
+
<p v-html="errorMessage.replace(/\n/g, '<br>')" />
|
|
18
14
|
<NuxtLink
|
|
19
15
|
class="mt-8 inline-block underline"
|
|
20
16
|
:to="`/${currentLanguage}`"
|
|
@@ -45,25 +41,21 @@ const isEditing = computed(
|
|
|
45
41
|
drupalUser.value.accessToolbar,
|
|
46
42
|
)
|
|
47
43
|
|
|
48
|
-
const showBreadcrumb = computed(() => !route.meta.hideBreadcrumb)
|
|
49
|
-
|
|
50
44
|
const currentLanguage = useCurrentLanguage()
|
|
51
45
|
|
|
52
46
|
const error = useError()
|
|
53
47
|
const errorCode = error.value?.statusCode || 'Unknown error'
|
|
54
48
|
|
|
55
|
-
const
|
|
49
|
+
const UNKNOWN_ERROR_MESSAGE = 'An unknown error occurred.'
|
|
50
|
+
|
|
51
|
+
const errorMessages: Record<string, string> = {
|
|
56
52
|
404: "We can't find the page \nyou are looking for.",
|
|
57
53
|
500: 'An error occurred on the server. Please try again later.',
|
|
58
|
-
'Unknown error': 'An unknown error occurred.',
|
|
59
54
|
}
|
|
60
55
|
|
|
61
|
-
const errorMessage = computed(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
return errorMessages['Unknown error']
|
|
66
|
-
})
|
|
56
|
+
const errorMessage = computed(
|
|
57
|
+
() => errorMessages[String(errorCode)] ?? UNKNOWN_ERROR_MESSAGE,
|
|
58
|
+
)
|
|
67
59
|
|
|
68
60
|
useHead({
|
|
69
61
|
title: errorMessage,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires up blökkli fragments for the layer stack.
|
|
3
|
+
*
|
|
4
|
+
* Fragments (`defineBlokkliFragment()`) are blocks whose content lives only in
|
|
5
|
+
* the frontend; Drupal stores nothing but the fragment's name, so *which*
|
|
6
|
+
* fragments editors may place is a frontend decision. This module
|
|
7
|
+
*
|
|
8
|
+
* 1. collects the fragments blökkli discovered in all layers and exposes their
|
|
9
|
+
* names as `icms.allowedFragments` in the app config, from where
|
|
10
|
+
* `useAllowedFragments()` passes them to the page-level `<BlokkliField>` —
|
|
11
|
+
* a fragment only needs to exist to be offered,
|
|
12
|
+
* 2. disables blökkli's `fragments` feature when a layer sets
|
|
13
|
+
* `icms.blokkliFragments: false` or no fragment exists — otherwise the
|
|
14
|
+
* editor shows an "Add fragment" button opening an empty dialog, because
|
|
15
|
+
* the feature itself only depends on Drupal exposing the
|
|
16
|
+
* `blokkli_fragment` paragraph type.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // In the nuxt.config.ts of a project that does not want fragments
|
|
20
|
+
* export default defineNuxtConfig({
|
|
21
|
+
* icms: {
|
|
22
|
+
* blokkliFragments: false,
|
|
23
|
+
* },
|
|
24
|
+
* })
|
|
25
|
+
*/
|
|
26
|
+
import { defineNuxtModule } from 'nuxt/kit'
|
|
27
|
+
import type { IcmsLayerConfig } from '../types/layerConfig'
|
|
28
|
+
|
|
29
|
+
export default defineNuxtModule({
|
|
30
|
+
meta: {
|
|
31
|
+
name: 'blokkliFragments',
|
|
32
|
+
},
|
|
33
|
+
setup(_options, nuxt) {
|
|
34
|
+
// The project layer comes first, so its setting wins.
|
|
35
|
+
const enabled =
|
|
36
|
+
nuxt.options._layers
|
|
37
|
+
.map(
|
|
38
|
+
(layer) =>
|
|
39
|
+
(layer.config?.icms as IcmsLayerConfig | undefined)
|
|
40
|
+
?.blokkliFragments,
|
|
41
|
+
)
|
|
42
|
+
.find((value) => typeof value === 'boolean') ?? true
|
|
43
|
+
|
|
44
|
+
// The features hook runs before the blocks hook, so the decision to
|
|
45
|
+
// disable is taken once the fragments are known.
|
|
46
|
+
let feature: { disable: () => void } | undefined
|
|
47
|
+
nuxt.hooks.hook('blokkli:alter-features', ({ features }) => {
|
|
48
|
+
feature = features.find((v) => v.getDefinition()?.id === 'fragments')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
nuxt.hooks.hook('blokkli:alter-blocks', ({ blocks }) => {
|
|
52
|
+
const allowedFragments = enabled
|
|
53
|
+
? [
|
|
54
|
+
...new Set(
|
|
55
|
+
blocks
|
|
56
|
+
.filter((block) => block.type === 'fragment')
|
|
57
|
+
.map((block) => block.definition?.name)
|
|
58
|
+
.filter((name): name is string => typeof name === 'string'),
|
|
59
|
+
),
|
|
60
|
+
].sort()
|
|
61
|
+
: []
|
|
62
|
+
|
|
63
|
+
// Cast: the type of `appConfig` is generated from the very value written
|
|
64
|
+
// here, so it narrows `allowedFragments` to whatever the last build had.
|
|
65
|
+
const appConfig = nuxt.options.appConfig as Record<string, unknown>
|
|
66
|
+
appConfig.icms = {
|
|
67
|
+
...(appConfig.icms as Record<string, unknown> | undefined),
|
|
68
|
+
allowedFragments,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!allowedFragments.length) {
|
|
72
|
+
feature?.disable()
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
})
|
|
@@ -6,100 +6,100 @@
|
|
|
6
6
|
* proper override priority (main app overrides layers).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { defineNuxtModule
|
|
9
|
+
import { defineNuxtModule } from '@nuxt/kit'
|
|
10
|
+
import type { IcmsLayerConfig } from '../types/layerConfig'
|
|
10
11
|
|
|
11
12
|
export default defineNuxtModule({
|
|
12
13
|
meta: { name: 'dynamicEntityComponentResolver' },
|
|
13
14
|
setup(_options, nuxt) {
|
|
14
|
-
|
|
15
15
|
// Private helper to extract unique fragment values from any routeQueries object.
|
|
16
|
-
function extractFragments(routeQueries:
|
|
17
|
-
const fragments = new Set<string>()
|
|
16
|
+
function extractFragments(routeQueries: unknown): string[] {
|
|
17
|
+
const fragments = new Set<string>()
|
|
18
18
|
|
|
19
|
-
function traverse(obj:
|
|
19
|
+
function traverse(obj: unknown) {
|
|
20
20
|
if (Array.isArray(obj)) {
|
|
21
|
-
obj.forEach(item => traverse(item))
|
|
21
|
+
obj.forEach((item) => traverse(item))
|
|
22
22
|
} else if (obj && typeof obj === 'object') {
|
|
23
|
-
for (const key
|
|
24
|
-
if (key === 'fragments' && Array.isArray(
|
|
25
|
-
|
|
26
|
-
if (typeof frag === 'string') fragments.add(frag)
|
|
27
|
-
})
|
|
23
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
24
|
+
if (key === 'fragments' && Array.isArray(value)) {
|
|
25
|
+
value.forEach((frag) => {
|
|
26
|
+
if (typeof frag === 'string') fragments.add(frag)
|
|
27
|
+
})
|
|
28
28
|
} else {
|
|
29
|
-
traverse(
|
|
29
|
+
traverse(value)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
traverse(routeQueries)
|
|
36
|
-
return Array.from(fragments)
|
|
35
|
+
traverse(routeQueries)
|
|
36
|
+
return Array.from(fragments)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const layerConfigs: IcmsLayerConfig[] = nuxt.options._layers
|
|
40
|
+
.map((layer) => layer.config?.icms)
|
|
41
|
+
.filter((config): config is IcmsLayerConfig => !!config)
|
|
42
|
+
|
|
39
43
|
// Creates graphQL fragments for all reoute queries.
|
|
40
44
|
const entityFragments = nuxt.options._layers
|
|
41
|
-
.map((layer
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
return
|
|
45
|
+
.map((layer) => {
|
|
46
|
+
const vuepal = layer.config?.vuepal
|
|
47
|
+
const queries = vuepal ? vuepal.drupalRoute?.routeQueries : undefined
|
|
48
|
+
return extractFragments(queries)
|
|
45
49
|
})
|
|
46
|
-
.filter(arr => Array.isArray(arr) && arr.length)
|
|
50
|
+
.filter((arr) => Array.isArray(arr) && arr.length)
|
|
47
51
|
.flat()
|
|
48
|
-
.filter(Boolean)
|
|
52
|
+
.filter(Boolean)
|
|
49
53
|
|
|
50
54
|
// Extend entity fragments (index)
|
|
51
|
-
const entityFragmentsExtend =
|
|
52
|
-
.map((
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
.filter(arr => Array.isArray(arr) && arr.length)
|
|
55
|
+
const entityFragmentsExtend = layerConfigs
|
|
56
|
+
.map((config) => config.extendFragments?.entity)
|
|
57
|
+
.filter((arr) => Array.isArray(arr) && arr.length)
|
|
56
58
|
.flat()
|
|
57
|
-
.filter(Boolean)
|
|
59
|
+
.filter(Boolean)
|
|
58
60
|
|
|
59
61
|
// Extend entity fragments (Teaser)
|
|
60
|
-
const entityTeaserFragmentsExtend =
|
|
61
|
-
.map((
|
|
62
|
-
|
|
63
|
-
})
|
|
64
|
-
.filter(arr => Array.isArray(arr) && arr.length)
|
|
62
|
+
const entityTeaserFragmentsExtend = layerConfigs
|
|
63
|
+
.map((config) => config.extendFragments?.teaser)
|
|
64
|
+
.filter((arr) => Array.isArray(arr) && arr.length)
|
|
65
65
|
.flat()
|
|
66
|
-
.filter(Boolean)
|
|
66
|
+
.filter(Boolean)
|
|
67
67
|
|
|
68
68
|
// Extend entity fragments (SearchResult)
|
|
69
|
-
const entitySearchResultFragmentsExtend =
|
|
70
|
-
.map((
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
.filter(arr => Array.isArray(arr) && arr.length)
|
|
69
|
+
const entitySearchResultFragmentsExtend = layerConfigs
|
|
70
|
+
.map((config) => config.extendFragments?.searchResult)
|
|
71
|
+
.filter((arr) => Array.isArray(arr) && arr.length)
|
|
74
72
|
.flat()
|
|
75
|
-
.filter(Boolean)
|
|
73
|
+
.filter(Boolean)
|
|
76
74
|
|
|
77
75
|
const entityFagment = `
|
|
78
76
|
fragment entity on Entity {
|
|
79
77
|
__typename
|
|
80
|
-
${entityFragments.map((fragmentName) => fragmentName ? `...${fragmentName}` : '').join('\n ')}
|
|
81
|
-
${entityFragmentsExtend.map((fragmentName) => fragmentName ? `...${fragmentName}` : '').join('\n ')}
|
|
78
|
+
${entityFragments.map((fragmentName) => (fragmentName ? `...${fragmentName}` : '')).join('\n ')}
|
|
79
|
+
${entityFragmentsExtend.map((fragmentName) => (fragmentName ? `...${fragmentName}` : '')).join('\n ')}
|
|
82
80
|
}
|
|
83
81
|
`
|
|
84
82
|
const entityTeaserFagment = `
|
|
85
83
|
fragment entityTeaser on Entity {
|
|
86
84
|
__typename
|
|
87
|
-
${entityFragments.map((fragmentName) => fragmentName ? `...${fragmentName}Teaser` : '').join('\n ')}
|
|
88
|
-
${entityTeaserFragmentsExtend.map((fragmentName) => fragmentName ? `...${fragmentName}` : '').join('\n ')}
|
|
85
|
+
${entityFragments.map((fragmentName) => (fragmentName ? `...${fragmentName}Teaser` : '')).join('\n ')}
|
|
86
|
+
${entityTeaserFragmentsExtend.map((fragmentName) => (fragmentName ? `...${fragmentName}` : '')).join('\n ')}
|
|
89
87
|
}
|
|
90
88
|
`
|
|
91
89
|
const entitySearchResultFagment = `
|
|
92
90
|
fragment entitySearchResult on Entity {
|
|
93
91
|
__typename
|
|
94
|
-
${entityFragments.map((fragmentName) => fragmentName ? `...${fragmentName}SearchResult` : '').join('\n ')}
|
|
95
|
-
${entitySearchResultFragmentsExtend.map((fragmentName) => fragmentName ? `...${fragmentName}` : '').join('\n ')}
|
|
92
|
+
${entityFragments.map((fragmentName) => (fragmentName ? `...${fragmentName}SearchResult` : '')).join('\n ')}
|
|
93
|
+
${entitySearchResultFragmentsExtend.map((fragmentName) => (fragmentName ? `...${fragmentName}` : '')).join('\n ')}
|
|
96
94
|
}
|
|
97
95
|
`
|
|
98
96
|
nuxt.hooks.hookOnce('nuxt-graphql-middleware:init', (context) => {
|
|
99
97
|
context.addDocument('fragment.entity.graphql', entityFagment)
|
|
100
98
|
context.addDocument('fragment.entityTeaser.graphql', entityTeaserFagment)
|
|
101
|
-
context.addDocument(
|
|
99
|
+
context.addDocument(
|
|
100
|
+
'fragment.entitySearchResult.graphql',
|
|
101
|
+
entitySearchResultFagment,
|
|
102
|
+
)
|
|
102
103
|
})
|
|
103
|
-
}
|
|
104
|
+
},
|
|
104
105
|
})
|
|
105
|
-
|