@iqual/nuxt-icms 1.3.0 → 1.4.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.
- package/assets/symbols/chevron-right.svg +1 -1
- package/components/Blokkli/HeroImage/index.vue +1 -1
- package/components/Blokkli/Teasers/icon.svg +3 -3
- package/components/DesignSystem/Atoms/Button.vue +86 -36
- package/components/DesignSystem/Atoms/Caption.vue +10 -10
- package/components/DesignSystem/Atoms/Container.vue +10 -6
- package/components/DesignSystem/Atoms/GridRow.vue +4 -3
- package/components/DesignSystem/Atoms/LinkListItem.vue +31 -8
- package/components/DesignSystem/Molecules/ButtonGroup.vue +18 -3
- package/components/DesignSystem/Molecules/Card.vue +86 -31
- package/components/DesignSystem/Molecules/IconText.vue +37 -16
- package/components/DesignSystem/Molecules/MediaLightbox.vue +213 -58
- package/components/DesignSystem/Molecules/MediaSlider.vue +88 -25
- package/components/DesignSystem/Molecules/Partnership.vue +40 -9
- package/components/DesignSystem/Molecules/Table.vue +90 -25
- package/components/DesignSystem/Molecules/TitleComposition.vue +15 -12
- package/components/DesignSystem/Organisms/ContentBlock.vue +61 -15
- package/components/DesignSystem/Organisms/FacetedListing.vue +170 -0
- package/components/DesignSystem/Organisms/FlipTeaser.vue +119 -47
- package/components/DesignSystem/Organisms/HeaderHero.vue +167 -0
- package/components/DesignSystem/Organisms/HeaderTextImage.vue +71 -0
- package/components/DesignSystem/Organisms/LogoElement.vue +33 -10
- package/components/DesignSystem/Organisms/Map.vue +11 -6
- package/components/DesignSystem/Organisms/SearchResults.vue +41 -0
- package/components/DesignSystem/Organisms/TeamElement.vue +84 -20
- package/components/DesignSystem/Organisms/TeaserCard.vue +87 -34
- package/components/DesignSystem/Organisms/Testimonial.vue +82 -12
- package/components/DesignSystem/Organisms/TimelineElement.vue +42 -12
- package/components/DesignSystem/Organisms/TwoColumnTextElement.vue +44 -9
- package/components/DesignSystem/Pages/Search.vue +39 -0
- package/components/Drupal/Breadcrumb/index.vue +16 -5
- package/components/Drupal/Messages/index.vue +16 -5
- package/components/Drupal/Webform/fragment.graphql +8 -5
- package/components/Drupal/Webform/index.vue +20 -9
- package/components/Drupal/Webform/query.graphql +18 -16
- package/components/Form/CheckboxInput.vue +6 -6
- package/components/Form/CheckboxInputs.vue +5 -5
- package/components/Form/FileUploadInput.vue +6 -6
- package/components/Form/RadioButtonsInput.vue +5 -5
- package/components/Form/SelectInput.vue +7 -7
- package/components/Form/SubmitButton.vue +1 -1
- package/components/Form/TextAreaInput.vue +2 -2
- package/components/Form/TextEmailInput.vue +2 -2
- package/components/Form/TextInput.vue +2 -2
- package/components/Form/TextPhoneInput.vue +2 -2
- package/components/Form/composables/useFormElement.ts +5 -5
- package/components/Form/utils/types.ts +8 -7
- package/components/Media/Icon/index.vue +10 -6
- package/components/Media/Image/index.vue +26 -20
- package/components/Media/RemoteVideo/index.vue +34 -21
- package/components/Media/Video/index.vue +6 -5
- package/components/Node/Icms/Page/Teaser/index.vue +6 -4
- package/components/Node/Icms/Page/index.vue +2 -2
- package/components/Paragraph/Button/Element/index.vue +9 -2
- package/components/Paragraph/CallToAction/index.vue +43 -10
- package/components/Paragraph/Card/Element/index.vue +2 -1
- package/components/Paragraph/Card/Layout/index.vue +29 -10
- package/components/Paragraph/Contact/index.vue +41 -12
- package/components/Paragraph/Downloads/index.vue +82 -33
- package/components/Paragraph/FAQ/Element/index.vue +41 -12
- package/components/Paragraph/FAQ/Layout/index.vue +47 -13
- package/components/Paragraph/FlipTeasers/index.vue +26 -6
- package/components/Paragraph/Gallery/index.vue +42 -10
- package/components/Paragraph/HeroImage/index.vue +62 -16
- package/components/Paragraph/IconText/Element/index.vue +7 -1
- package/components/Paragraph/IconText/Layout/index.vue +39 -12
- package/components/Paragraph/Introduction/index.vue +50 -15
- package/components/Paragraph/Logo/Element/fragment.graphql +1 -0
- package/components/Paragraph/Logo/Element/index.vue +7 -2
- package/components/Paragraph/Media/index.vue +32 -31
- package/components/Paragraph/Partnerships/Element/fragment.graphql +1 -0
- package/components/Paragraph/Partnerships/Element/index.vue +7 -0
- package/components/Paragraph/Partnerships/Layout/index.vue +100 -27
- package/components/Paragraph/SingleTestimonial/index.vue +58 -15
- package/components/Paragraph/Table/index.vue +32 -9
- package/components/Paragraph/Tabs/Element/index.vue +44 -10
- package/components/Paragraph/Tabs/Layout/index.vue +123 -37
- package/components/Paragraph/Team/Element/index.vue +18 -1
- package/components/Paragraph/Team/Layout/index.vue +30 -7
- package/components/Paragraph/Teasers/index.vue +33 -7
- package/components/Paragraph/Testimonial/Element/index.vue +30 -5
- package/components/Paragraph/Testimonial/Layout/index.vue +78 -24
- package/components/Paragraph/Text/index.vue +32 -10
- package/components/Paragraph/TextMedia/index.vue +44 -13
- package/components/Paragraph/Timeline/Element/index.vue +23 -7
- package/components/Paragraph/Timeline/Layout/index.vue +232 -67
- package/components/Paragraph/TwoColumnText/Element/index.vue +20 -3
- package/components/Paragraph/TwoColumnText/Layout/index.vue +8 -1
- package/components/Paragraph/iFrame/index.vue +31 -11
- package/components/Search/Facets/FacetDropdownUI.vue +351 -77
- package/components/Search/Facets/FacetedSearchUI.vue +257 -101
- package/components/Search/Facets/fragment.graphql +5 -1
- package/components/Search/Input/index.vue +125 -0
- package/components/Section/Footer/index.vue +170 -30
- package/components/Section/Header/Buttons.vue +34 -0
- package/components/Section/Header/DesktopMainMenu.vue +160 -0
- package/components/Section/Header/DesktopMainMenuDropdown.vue +170 -0
- package/components/Section/Header/LanguageDropdown.vue +95 -64
- package/components/Section/Header/LanguageLinks.vue +39 -0
- package/components/Section/Header/Logo.vue +23 -0
- package/components/Section/Header/MainButtons.vue +31 -0
- package/components/Section/Header/MenuIcon.vue +60 -49
- package/components/Section/Header/MetaButtons.vue +31 -0
- package/components/Section/Header/MetaMenu.vue +55 -53
- package/components/Section/Header/MobileMainMenu.vue +23 -0
- package/components/Section/Header/MobileMainMenuItem.vue +191 -0
- package/components/Section/Header/index.vue +182 -186
- package/composables/useMenu.ts +2 -2
- package/composables/useMobileMenuOverlayBus.ts +44 -0
- package/composables/useNavigationOffset.ts +2 -2
- package/composables/useTheme.ts +107 -0
- package/formkit.theme.ts +541 -586
- package/layouts/default.vue +7 -35
- package/layouts/error.vue +7 -32
- package/nuxt.config.ts +13 -0
- package/package.json +1 -1
- package/plugins/initData.ts +8 -5
- package/queries/query.initData.graphql +4 -2
- package/tailwind.config.ts +11 -11
- package/types/global.ts +1 -0
- package/components/Section/Header/GlobalMenu.vue +0 -335
- package/components/Section/Header/LanguageSwitcher.vue +0 -37
|
@@ -1,69 +1,71 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<nav
|
|
2
|
+
<nav
|
|
3
|
+
:aria-label="
|
|
4
|
+
$texts('meta_navigation.nav_aria_label', 'Meta navigation menu')
|
|
5
|
+
"
|
|
6
|
+
:class="themedClass('containerClass')"
|
|
7
|
+
>
|
|
3
8
|
<ul
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
:class="
|
|
10
|
+
themedClass(
|
|
11
|
+
'listClass',
|
|
12
|
+
'flex flex-col gap-2 text-base 2xl:flex-row 2xl:flex-wrap 2xl:items-center 2xl:justify-end 2xl:gap-8',
|
|
13
|
+
)
|
|
14
|
+
"
|
|
6
15
|
>
|
|
7
16
|
<li
|
|
8
17
|
v-for="(menuItem, index) in menuItems"
|
|
9
18
|
:key="`meta_${index}`"
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
:class="
|
|
20
|
+
themedClass(
|
|
21
|
+
'listItemClass',
|
|
22
|
+
'group/main relative flex flex-col items-stretch 2xl:flex-row',
|
|
23
|
+
)
|
|
24
|
+
"
|
|
12
25
|
>
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
? '
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
<template v-if="menuItem?.href">
|
|
27
|
+
<NuxtLink
|
|
28
|
+
:to="menuItem.href"
|
|
29
|
+
:title="menuItem.label"
|
|
30
|
+
:aria-current="
|
|
31
|
+
menuItem?.href === menuItemActive?.href ? 'page' : undefined
|
|
32
|
+
"
|
|
33
|
+
:target="menuItem?.target"
|
|
34
|
+
:rel="menuItem?.rel"
|
|
35
|
+
@click="close()"
|
|
36
|
+
exact-active-class="text-primary"
|
|
37
|
+
:class="
|
|
38
|
+
cn(
|
|
39
|
+
themedClass(
|
|
40
|
+
'linkClass',
|
|
41
|
+
'2xl:group-hover/main:text-primary flex w-full items-center justify-between motion-safe:transition-colors motion-safe:duration-200 2xl:size-auto',
|
|
42
|
+
),
|
|
43
|
+
menuItem?.additionalClass,
|
|
44
|
+
)
|
|
45
|
+
"
|
|
46
|
+
>
|
|
47
|
+
{{ menuItem.label }}
|
|
48
|
+
</NuxtLink>
|
|
49
|
+
</template>
|
|
50
|
+
<template v-else>
|
|
51
|
+
<span
|
|
52
|
+
aria-disabled="true"
|
|
53
|
+
:class="themedClass('noLinkClass', 'cursor-default text-gray-400')"
|
|
54
|
+
>
|
|
55
|
+
{{ menuItem.label }}
|
|
56
|
+
</span>
|
|
57
|
+
</template>
|
|
25
58
|
</li>
|
|
26
59
|
</ul>
|
|
27
60
|
</nav>
|
|
28
|
-
<div
|
|
29
|
-
v-if="props.links?.length !== 0"
|
|
30
|
-
class="mb-8 flex gap-8 2xl:mb-0 2xl:ml-8"
|
|
31
|
-
>
|
|
32
|
-
<DesignSystemAtomsButton
|
|
33
|
-
v-for="(link, key) in links"
|
|
34
|
-
:key="key"
|
|
35
|
-
:label="link?.label"
|
|
36
|
-
:subline="link?.subline"
|
|
37
|
-
:title="link?.title"
|
|
38
|
-
:link="link?.link?.uri?.path"
|
|
39
|
-
:params="link?.params"
|
|
40
|
-
:target-blank="link?.targetBlank"
|
|
41
|
-
:style="link?.buttonStyle"
|
|
42
|
-
buttonClass="no-underline"
|
|
43
|
-
@click="onLangDropdownLinkClick"
|
|
44
|
-
/>
|
|
45
|
-
</div>
|
|
46
61
|
</template>
|
|
47
62
|
|
|
48
63
|
<script setup lang="ts">
|
|
49
|
-
import
|
|
64
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
65
|
+
import cn from '@iqual/nuxt-icms/composables/useCn'
|
|
50
66
|
|
|
51
67
|
const { menuItems, menuItemActive } = await useMenu('metaMenu')
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
}>()
|
|
56
|
-
|
|
57
|
-
const emit = defineEmits(['meta:empty', 'lang-dropdown:change'])
|
|
58
|
-
|
|
59
|
-
if (menuItems.value?.length === 0) {
|
|
60
|
-
emit('meta:empty')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const langSwitchDropdownOpen = ref(false)
|
|
64
|
-
|
|
65
|
-
const onLangDropdownLinkClick = () => {
|
|
66
|
-
emit('lang-dropdown:change')
|
|
67
|
-
langSwitchDropdownOpen.value = false
|
|
68
|
-
}
|
|
68
|
+
const themedClass = useTheme()
|
|
69
|
+
const { $texts } = useNuxtApp()
|
|
70
|
+
const { close } = useMobileMenuOverlayBus()
|
|
69
71
|
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav
|
|
3
|
+
:aria-label="$texts('main_menu.nav_aria_label', 'Main navigation')"
|
|
4
|
+
:class="themedClass('containerClass')"
|
|
5
|
+
>
|
|
6
|
+
<ul role="menu" :class="themedClass('listClass', 'flex flex-col gap-2')">
|
|
7
|
+
<MobileMainMenuItem
|
|
8
|
+
v-for="item in menuItems"
|
|
9
|
+
:key="item.id"
|
|
10
|
+
:item="item"
|
|
11
|
+
:menuItemActive="menuItemActive"
|
|
12
|
+
/>
|
|
13
|
+
</ul>
|
|
14
|
+
</nav>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import MobileMainMenuItem from './MobileMainMenuItem.vue'
|
|
19
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
20
|
+
const { menuItems, menuItemActive } = await useMenu('mainMenu')
|
|
21
|
+
|
|
22
|
+
const themedClass = useTheme()
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<li
|
|
3
|
+
role="menuitem"
|
|
4
|
+
:class="themedClass('listItemClass', 'relative flex flex-col gap-2')"
|
|
5
|
+
>
|
|
6
|
+
<div
|
|
7
|
+
:class="
|
|
8
|
+
themedClass('linkContainerClass', 'flex items-center justify-between')
|
|
9
|
+
"
|
|
10
|
+
>
|
|
11
|
+
<component
|
|
12
|
+
:is="elementType"
|
|
13
|
+
v-bind="componentAttrs"
|
|
14
|
+
@click="isLink && close()"
|
|
15
|
+
>
|
|
16
|
+
{{ item.label }}
|
|
17
|
+
</component>
|
|
18
|
+
<SpriteSymbol
|
|
19
|
+
v-if="hasSubmenu"
|
|
20
|
+
name="chevron-down"
|
|
21
|
+
:aria-label="
|
|
22
|
+
$texts('mobile_main_menu.submenu_toggle', 'Toggle submenu')
|
|
23
|
+
"
|
|
24
|
+
:aria-expanded="isOpen"
|
|
25
|
+
:aria-controls="submenuId"
|
|
26
|
+
role="button"
|
|
27
|
+
tabindex="0"
|
|
28
|
+
@click="isOpen = !isOpen"
|
|
29
|
+
:class="[
|
|
30
|
+
themedClass(
|
|
31
|
+
'submenuToggleClass',
|
|
32
|
+
'size-5 cursor-pointer motion-safe:transition-transform motion-safe:duration-300',
|
|
33
|
+
),
|
|
34
|
+
{ 'rotate-180': isOpen },
|
|
35
|
+
]"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
<Transition
|
|
39
|
+
v-if="hasSubmenu"
|
|
40
|
+
:enter-active-class="
|
|
41
|
+
themedClass(
|
|
42
|
+
'transitionEnterActiveClass',
|
|
43
|
+
'transition-[max-height,opacity] duration-300 ease-in-out overflow-hidden',
|
|
44
|
+
)
|
|
45
|
+
"
|
|
46
|
+
:enter-from-class="
|
|
47
|
+
themedClass('transitionEnterFromClass', 'max-h-0 opacity-0')
|
|
48
|
+
"
|
|
49
|
+
:enter-to-class="
|
|
50
|
+
themedClass('transitionEnterToClass', 'max-h-96 opacity-100')
|
|
51
|
+
"
|
|
52
|
+
:leave-active-class="
|
|
53
|
+
themedClass(
|
|
54
|
+
'transitionLeaveActiveClass',
|
|
55
|
+
'transition-[max-height,opacity] duration-300 ease-in-out overflow-hidden',
|
|
56
|
+
)
|
|
57
|
+
"
|
|
58
|
+
:leave-from-class="
|
|
59
|
+
themedClass('transitionLeaveFromClass', 'max-h-96 opacity-100')
|
|
60
|
+
"
|
|
61
|
+
:leave-to-class="
|
|
62
|
+
themedClass('transitionLeaveToClass', 'max-h-0 opacity-0')
|
|
63
|
+
"
|
|
64
|
+
>
|
|
65
|
+
<ul
|
|
66
|
+
v-show="isOpen"
|
|
67
|
+
:id="submenuId"
|
|
68
|
+
:class="themedClass('submenuListClass', 'flex flex-col gap-2 pl-4')"
|
|
69
|
+
>
|
|
70
|
+
<MobileMainMenuItem
|
|
71
|
+
v-for="item in item.children"
|
|
72
|
+
:key="`submenu_${item.id}`"
|
|
73
|
+
:item="item"
|
|
74
|
+
:menuItemActive="menuItemActive"
|
|
75
|
+
/>
|
|
76
|
+
</ul>
|
|
77
|
+
</Transition>
|
|
78
|
+
</li>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script lang="ts" setup>
|
|
82
|
+
import type { MappedMenuLink } from '@/packages/nuxt-icms/plugins/initData'
|
|
83
|
+
import { NuxtLink } from '#components'
|
|
84
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
85
|
+
import cn from '@iqual/nuxt-icms/composables/useCn'
|
|
86
|
+
|
|
87
|
+
const route = useRoute()
|
|
88
|
+
const themedClass = useTheme()
|
|
89
|
+
const { close } = useMobileMenuOverlayBus()
|
|
90
|
+
|
|
91
|
+
const props = defineProps<{
|
|
92
|
+
item: MappedMenuLink
|
|
93
|
+
menuItemActive?: MappedMenuLink | undefined
|
|
94
|
+
}>()
|
|
95
|
+
|
|
96
|
+
const { item, menuItemActive } = props
|
|
97
|
+
|
|
98
|
+
const isOpen = ref(false)
|
|
99
|
+
const isLink = computed(() => Boolean(item.href))
|
|
100
|
+
const hasSubmenu = computed(() => (item.children?.length ?? 0) > 0)
|
|
101
|
+
const submenuId = computed(() => `${item.id}-submenu`)
|
|
102
|
+
const hasActiveChild = computed(() => {
|
|
103
|
+
/**
|
|
104
|
+
* Determines whether the current menu item has any active descendant links.
|
|
105
|
+
*
|
|
106
|
+
* @remarks
|
|
107
|
+
* Recursively checks all nested children to see if any item's `href`
|
|
108
|
+
* matches the current route path. Used to keep parent menus open
|
|
109
|
+
* when navigating within their nested structure.
|
|
110
|
+
*
|
|
111
|
+
* @returns A computed boolean that is `true` if the current route
|
|
112
|
+
* matches any descendant link, otherwise `false`.
|
|
113
|
+
*/
|
|
114
|
+
const matchInTree = (children?: MappedMenuLink[]): boolean =>
|
|
115
|
+
!!children?.some(
|
|
116
|
+
(child) => child.href === route.fullPath || matchInTree(child.children),
|
|
117
|
+
)
|
|
118
|
+
return matchInTree(item.children)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
watch(
|
|
122
|
+
() => route.fullPath,
|
|
123
|
+
() => {
|
|
124
|
+
isOpen.value = hasActiveChild.value
|
|
125
|
+
},
|
|
126
|
+
{ immediate: true },
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Determines which element type to render for a menu item.
|
|
131
|
+
*
|
|
132
|
+
* @remarks
|
|
133
|
+
* - Returns `NuxtLink` if the item has a navigation link.
|
|
134
|
+
* - Returns `'button'` if the item opens a submenu but has no link.
|
|
135
|
+
* - Returns `'span'` if the item is non-interactive.
|
|
136
|
+
*
|
|
137
|
+
* @returns A computed reference containing either `NuxtLink`, `'button'`, or `'span'`.
|
|
138
|
+
*/
|
|
139
|
+
const elementType = computed(() => {
|
|
140
|
+
if (isLink.value) return NuxtLink
|
|
141
|
+
else if (hasSubmenu.value) return 'button'
|
|
142
|
+
else return 'span'
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Computes the appropriate HTML or component attributes for a menu item
|
|
147
|
+
* based on its computed element type.
|
|
148
|
+
*
|
|
149
|
+
* @returns A computed object containing attributes specific to the element type,
|
|
150
|
+
* including accessibility props and themed CSS classes.
|
|
151
|
+
*/
|
|
152
|
+
const componentAttrs = computed(() => {
|
|
153
|
+
switch (elementType.value) {
|
|
154
|
+
case NuxtLink:
|
|
155
|
+
return {
|
|
156
|
+
id: item.id,
|
|
157
|
+
to: item.href,
|
|
158
|
+
rel: item.rel,
|
|
159
|
+
target: item.target,
|
|
160
|
+
'aria-haspopup': hasSubmenu.value ? 'menu' : undefined,
|
|
161
|
+
'aria-expanded': hasSubmenu.value ? isOpen.value : undefined,
|
|
162
|
+
'aria-controls': hasSubmenu.value ? submenuId.value : undefined,
|
|
163
|
+
'aria-current': menuItemActive?.id === item.id ? 'page' : undefined,
|
|
164
|
+
tabindex: 0,
|
|
165
|
+
class: cn(themedClass('linkClass'), item.additionalClass),
|
|
166
|
+
'exact-active-class': 'text-primary',
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
case 'button':
|
|
170
|
+
return {
|
|
171
|
+
id: item.id,
|
|
172
|
+
type: 'button',
|
|
173
|
+
'aria-haspopup': 'menu',
|
|
174
|
+
'aria-expanded': isOpen.value,
|
|
175
|
+
'aria-controls': submenuId.value,
|
|
176
|
+
tabindex: 0,
|
|
177
|
+
class: themedClass('buttonClass'),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
case 'span':
|
|
181
|
+
return {
|
|
182
|
+
id: item.id,
|
|
183
|
+
'aria-disabled': true,
|
|
184
|
+
class: themedClass('nolinkClass', 'text-gray-400'),
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
default:
|
|
188
|
+
return {}
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
</script>
|