@iqual/nuxt-icms 1.3.1 → 1.4.1
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 +78 -20
- 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 +24 -0
- package/components/Section/Header/MainButtons.vue +31 -0
- package/components/Section/Header/MenuIcon.vue +61 -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 +183 -185
- package/composables/useMenu.ts +2 -2
- package/composables/useMobileMenuOverlayBus.ts +44 -0
- package/composables/useNavigationOffset.ts +2 -2
- package/composables/useTheme.ts +75 -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,12 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="container mb-[30px] h-6 pt-20">
|
|
3
|
-
<section v-if="links.length" class="relative mobile-only:overflow-hidden">
|
|
4
|
-
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
2
|
+
<div :class="themedClass('containerClass', 'container mb-[30px] h-6 pt-20')">
|
|
3
|
+
<section v-if="links.length" class="relative mobile-only:overflow-hidden" :class="themedClass('sectionClass')">
|
|
4
|
+
<nav class="breadcrumb" aria-label="breadcrumbs" :class="themedClass('navClass')">
|
|
5
5
|
<ol
|
|
6
6
|
ref="list"
|
|
7
7
|
itemscope
|
|
8
8
|
itemtype="http://schema.org/BreadcrumbList"
|
|
9
9
|
class="flex items-center whitespace-nowrap"
|
|
10
|
+
:class="themedClass('listClass')"
|
|
10
11
|
>
|
|
11
12
|
<li
|
|
12
13
|
v-for="(link, index) in linksComputed"
|
|
@@ -14,9 +15,10 @@
|
|
|
14
15
|
itemprop="itemListElement"
|
|
15
16
|
itemscope
|
|
16
17
|
itemtype="http://schema.org/ListItem"
|
|
18
|
+
:class="themedClass('listItemClass')"
|
|
17
19
|
>
|
|
18
|
-
<component :is="link.tag" v-bind="link.props" itemprop="item">
|
|
19
|
-
<span itemprop="name">
|
|
20
|
+
<component :is="link.tag" v-bind="link.props" itemprop="item" :class="themedClass('linkClass')">
|
|
21
|
+
<span itemprop="name" :class="themedClass('linkLabelClass')">
|
|
20
22
|
{{ link.title }}
|
|
21
23
|
</span>
|
|
22
24
|
</component>
|
|
@@ -32,10 +34,19 @@
|
|
|
32
34
|
import type { BreadcrumbFragment } from '#graphql-operations'
|
|
33
35
|
import type { Langcode } from '#nuxt-language-negotiation/config'
|
|
34
36
|
import { NuxtLink } from '#components'
|
|
37
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
38
|
+
const themedClass = useTheme('DesignSystemAtomsButton')
|
|
35
39
|
|
|
36
40
|
const props = defineProps<{
|
|
37
41
|
links: BreadcrumbFragment[]
|
|
38
42
|
language?: Langcode
|
|
43
|
+
containerClass?: string
|
|
44
|
+
sectionClass?: string
|
|
45
|
+
navClass?: string
|
|
46
|
+
listClass?: string
|
|
47
|
+
listItemClass?: string
|
|
48
|
+
linkClass?: string
|
|
49
|
+
linkLabelClass?: string
|
|
39
50
|
}>()
|
|
40
51
|
|
|
41
52
|
const list = ref<HTMLOListElement | null>(null)
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-if="messages.length"
|
|
4
|
-
class="fixed bottom-0 left-0 z-[9999999] w-full
|
|
4
|
+
class="fixed bottom-0 left-0 z-[9999999] w-full"
|
|
5
|
+
:class="themedClass('wrapperClass', 'text-white')"
|
|
5
6
|
>
|
|
6
7
|
<div
|
|
7
8
|
v-for="(message, i) in messages"
|
|
8
9
|
:key="i"
|
|
9
|
-
class="flex items-center justify-between
|
|
10
|
-
:class="classes[message.type]"
|
|
10
|
+
class="flex items-center justify-between "
|
|
11
|
+
:class="cn(themedClass('innerClass', 'rounded p-6 font-medium'), classes[message.type])"
|
|
11
12
|
@click="removeMessage(i)"
|
|
12
13
|
>
|
|
13
|
-
<div v-html="message.message" />
|
|
14
|
-
<SpriteSymbol name="close" class="size-6" />
|
|
14
|
+
<div v-html="message.message" :class="themedClass('messageClass')"/>
|
|
15
|
+
<SpriteSymbol name="close" :class="themedClass('iconClass', 'size-6')" />
|
|
15
16
|
</div>
|
|
16
17
|
</div>
|
|
17
18
|
</template>
|
|
18
19
|
|
|
19
20
|
<script lang="ts" setup>
|
|
20
21
|
import type { DrupalMessageType } from '@iqual/nuxt-icms/composables/useDrupalMessages'
|
|
22
|
+
import cn from '@iqual/nuxt-icms/composables/useCn'
|
|
23
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
24
|
+
const themedClass = useTheme('DrupalMessages')
|
|
25
|
+
|
|
26
|
+
const props = defineProps<{
|
|
27
|
+
wrapperClass?: string
|
|
28
|
+
innerClass?: string
|
|
29
|
+
messageClass?: string
|
|
30
|
+
iconClass?: string
|
|
31
|
+
}>()
|
|
21
32
|
|
|
22
33
|
const { messages, removeMessage } = useDrupalMessages()
|
|
23
34
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
fragment
|
|
1
|
+
fragment metadata on WebformElementMetadata {
|
|
2
2
|
key
|
|
3
3
|
type
|
|
4
4
|
title
|
|
5
5
|
description
|
|
6
6
|
multiple
|
|
7
|
-
required
|
|
8
|
-
|
|
9
|
-
}
|
|
7
|
+
required
|
|
8
|
+
requiredError
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
fragment webformElementActions on WebformElementWebformActions {
|
|
@@ -72,7 +71,11 @@ fragment webformElementCheckboxes on WebformElementCheckboxes {
|
|
|
72
71
|
fragment webformElementWebformFlexbox on WebformElementWebformFlexbox {
|
|
73
72
|
elements {
|
|
74
73
|
__typename
|
|
75
|
-
...
|
|
74
|
+
... on WebformElement {
|
|
75
|
+
metadata {
|
|
76
|
+
...metadata
|
|
77
|
+
}
|
|
78
|
+
}
|
|
76
79
|
...webformElementActions
|
|
77
80
|
...webformElementTextBase
|
|
78
81
|
...webformElementMarkupBase
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div>
|
|
2
|
+
<div :class="themedClass('wrapperClass')">
|
|
3
3
|
<Transition name="fade">
|
|
4
|
-
<div v-if="!submitted && !showThankYou && !showError">
|
|
4
|
+
<div v-if="!submitted && !showThankYou && !showError" :class="themedClass('formWrapperClass')">
|
|
5
5
|
<FormKit
|
|
6
6
|
type="form"
|
|
7
7
|
:actions="false"
|
|
8
|
-
:messages-class="'mt-2'"
|
|
8
|
+
:messages-class="themedClass('messagesClass', 'mt-2')"
|
|
9
9
|
:incomplete-message="$texts(
|
|
10
10
|
'webform.submission_validation_incomplete',
|
|
11
11
|
'Please fill in all required fields.',
|
|
@@ -15,17 +15,20 @@
|
|
|
15
15
|
>
|
|
16
16
|
<div
|
|
17
17
|
v-for="formElement in formDefinition?.elements"
|
|
18
|
-
:key="formElement
|
|
18
|
+
:key="formElement?.metadata?.key"
|
|
19
|
+
:class="themedClass('elementWrapperClass')"
|
|
19
20
|
>
|
|
20
21
|
|
|
21
22
|
<NuxtTurnstile
|
|
22
23
|
v-model="token"
|
|
23
|
-
v-if="formElement
|
|
24
|
+
v-if="formElement?.metadata?.key === 'actions'"
|
|
24
25
|
/>
|
|
25
26
|
|
|
26
27
|
<component
|
|
27
|
-
|
|
28
|
+
v-if="formElement?.metadata?.type"
|
|
29
|
+
:is="getComponent(formElement?.metadata?.type)"
|
|
28
30
|
:formElement="formElement"
|
|
31
|
+
:class="themedClass('elementClass')"
|
|
29
32
|
/>
|
|
30
33
|
</div>
|
|
31
34
|
</FormKit>
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
<Transition name="fade">
|
|
36
39
|
<div
|
|
37
40
|
v-if="showThankYou"
|
|
38
|
-
class="mb-4 rounded-lg border border-success-500 px-6 py-4 text-success-500"
|
|
41
|
+
:class="themedClass('successMessageClass', 'mb-4 rounded-lg border border-success-500 px-6 py-4 text-success-500')"
|
|
39
42
|
>
|
|
40
43
|
<p>
|
|
41
44
|
{{
|
|
@@ -51,7 +54,7 @@
|
|
|
51
54
|
<Transition name="fade">
|
|
52
55
|
<div
|
|
53
56
|
v-if="showError"
|
|
54
|
-
class="mb-4 rounded-lg border border-error-500 px-6 py-4 text-error-500"
|
|
57
|
+
:class="themedClass('errorMessageClass', 'mb-4 rounded-lg border border-error-500 px-6 py-4 text-error-500')"
|
|
55
58
|
>
|
|
56
59
|
<p>
|
|
57
60
|
{{
|
|
@@ -78,6 +81,7 @@ import TextEmailInput from '@iqual/nuxt-icms/components/Form/TextEmailInput.vue'
|
|
|
78
81
|
import TextPhoneInput from '@iqual/nuxt-icms/components/Form/TextPhoneInput.vue'
|
|
79
82
|
import WebformMarkup from '@iqual/nuxt-icms/components/Form/WebformMarkup.vue'
|
|
80
83
|
import SubmitButton from '@iqual/nuxt-icms/components/Form/SubmitButton.vue'
|
|
84
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
81
85
|
|
|
82
86
|
interface FormData {
|
|
83
87
|
[fieldName: string]:
|
|
@@ -90,8 +94,15 @@ interface FormData {
|
|
|
90
94
|
| undefined
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
const themedClass = useTheme('DrupalWebform')
|
|
98
|
+
|
|
93
99
|
const props = defineProps<{
|
|
94
100
|
formId: string
|
|
101
|
+
wrapperClass: string
|
|
102
|
+
formWrapperClass: string
|
|
103
|
+
messagesClass: string
|
|
104
|
+
elementWrapperClass: string
|
|
105
|
+
elementClass: string
|
|
95
106
|
}>()
|
|
96
107
|
|
|
97
108
|
const { $texts } = useNuxtApp()
|
|
@@ -103,7 +114,7 @@ const showError = ref(false)
|
|
|
103
114
|
const queryKey = `drupalWebform-${props?.formId || '0'}`;
|
|
104
115
|
const { data: formDefinition } = await useAsyncData(queryKey, async () => {
|
|
105
116
|
return await useGraphqlQuery('webformById', { id: props?.formId }).then(
|
|
106
|
-
(v) => v?.data?.
|
|
117
|
+
(v) => v?.data?.webformById || { elements: [] }
|
|
107
118
|
)
|
|
108
119
|
})
|
|
109
120
|
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
query webformById($id: ID!) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
...webformElementMarkupBase
|
|
11
|
-
...webformElementTextarea
|
|
12
|
-
...webformElementSelect
|
|
13
|
-
...webformElementManagedFile
|
|
14
|
-
...webformElementTermSelect
|
|
15
|
-
...webformElementWebformFlexbox
|
|
16
|
-
...webformElementRadions
|
|
17
|
-
...webformElementCheckboxes
|
|
2
|
+
webformById(id: "contact") {
|
|
3
|
+
label
|
|
4
|
+
id
|
|
5
|
+
elements {
|
|
6
|
+
... on WebformElement {
|
|
7
|
+
metadata {
|
|
8
|
+
...metadata
|
|
9
|
+
}
|
|
18
10
|
}
|
|
11
|
+
...webformElementActions
|
|
12
|
+
...webformElementTextBase
|
|
13
|
+
...webformElementMarkupBase
|
|
14
|
+
...webformElementTextarea
|
|
15
|
+
...webformElementSelect
|
|
16
|
+
...webformElementManagedFile
|
|
17
|
+
...webformElementTermSelect
|
|
18
|
+
...webformElementWebformFlexbox
|
|
19
|
+
...webformElementRadions
|
|
20
|
+
...webformElementCheckboxes
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -12,8 +12,8 @@ const { cleanDescription, getValidationData } = useFormElement(
|
|
|
12
12
|
<template>
|
|
13
13
|
<FormKit
|
|
14
14
|
type="checkbox"
|
|
15
|
-
:name="formElement
|
|
16
|
-
:label="formElement
|
|
15
|
+
:name="formElement?.metadata?.key"
|
|
16
|
+
:label="formElement?.metadata?.title"
|
|
17
17
|
:help="cleanDescription"
|
|
18
18
|
:validation="getValidationData.rules || ''"
|
|
19
19
|
:validationMessages="getValidationData.messages || {}"
|
|
@@ -21,13 +21,13 @@ const { cleanDescription, getValidationData } = useFormElement(
|
|
|
21
21
|
<template #wrapper="{ node, value }">
|
|
22
22
|
<div class="flex items-center gap-2">
|
|
23
23
|
<Checkbox
|
|
24
|
-
:id="formElement
|
|
24
|
+
:id="formElement?.metadata?.key"
|
|
25
25
|
:checked="value"
|
|
26
26
|
@update:checked="node.input($event)"
|
|
27
|
-
class="border-1
|
|
27
|
+
class="border-1 focus-visible:ring-primary data-[state=checked]:text-primary dark:focus-visible:ring-primary dark:data-[state=checked]:text-primary size-5 border-gray-200 bg-white focus-visible:ring-offset-0 data-[state=checked]:bg-white dark:border-gray-200 dark:bg-white dark:data-[state=checked]:bg-white"
|
|
28
28
|
/>
|
|
29
|
-
<Label :for="formElement
|
|
30
|
-
{{ `${formElement
|
|
29
|
+
<Label :for="formElement?.metadata?.key" class="text-base font-normal">
|
|
30
|
+
{{ `${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}` }}
|
|
31
31
|
</Label>
|
|
32
32
|
</div>
|
|
33
33
|
</template>
|
|
@@ -11,14 +11,14 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
11
11
|
<template>
|
|
12
12
|
<FormKit
|
|
13
13
|
type="checkbox"
|
|
14
|
-
:name="formElement
|
|
14
|
+
:name="formElement?.metadata?.key"
|
|
15
15
|
:help="cleanDescription"
|
|
16
16
|
:validation="getValidationData.rules || ''"
|
|
17
17
|
:validationMessages="getValidationData.messages || {}"
|
|
18
18
|
>
|
|
19
19
|
<template #wrapper>
|
|
20
20
|
<Label class="mb-2 block text-base font-normal">
|
|
21
|
-
{{ `${formElement
|
|
21
|
+
{{ `${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}` }}
|
|
22
22
|
</Label>
|
|
23
23
|
<div class="flex flex-col gap-2">
|
|
24
24
|
<div
|
|
@@ -28,11 +28,11 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
28
28
|
>
|
|
29
29
|
<Checkbox
|
|
30
30
|
:value="option.value"
|
|
31
|
-
:id="`${formElement
|
|
32
|
-
class="border-1
|
|
31
|
+
:id="`${formElement?.metadata?.key}-${index}`"
|
|
32
|
+
class="border-1 focus-visible:ring-primary data-[state=checked]:text-primary dark:focus-visible:ring-primary dark:data-[state=checked]:text-primary size-5 border-gray-200 bg-white focus-visible:ring-offset-0 data-[state=checked]:bg-white dark:border-gray-200 dark:bg-white dark:data-[state=checked]:bg-white"
|
|
33
33
|
/>
|
|
34
34
|
<Label
|
|
35
|
-
:for="`${formElement
|
|
35
|
+
:for="`${formElement?.metadata?.key}-${index}`"
|
|
36
36
|
class="text-base font-normal"
|
|
37
37
|
>
|
|
38
38
|
{{ option.label }}
|
|
@@ -11,22 +11,22 @@ const { cleanDescription, getValidationData, getFormattedFileExtensions } =
|
|
|
11
11
|
<template>
|
|
12
12
|
<FormKit
|
|
13
13
|
type="file"
|
|
14
|
-
:name="formElement?.key"
|
|
14
|
+
:name="formElement?.metadata?.key"
|
|
15
15
|
:accept="getFormattedFileExtensions"
|
|
16
16
|
:help="cleanDescription"
|
|
17
|
-
:multiple="Boolean(formElement
|
|
17
|
+
:multiple="Boolean(formElement?.metadata?.multiple)"
|
|
18
18
|
:validation="getValidationData.rules || ''"
|
|
19
19
|
:validationMessages="getValidationData.messages || {}"
|
|
20
20
|
>
|
|
21
21
|
<template #wrapper>
|
|
22
22
|
<div class="grid w-full items-center">
|
|
23
|
-
<Label :for="formElement?.key" class="mb-2 block text-base font-normal">
|
|
24
|
-
{{ `${formElement
|
|
23
|
+
<Label :for="formElement?.metadata?.key" class="mb-2 block text-base font-normal">
|
|
24
|
+
{{ `${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}` }}
|
|
25
25
|
</Label>
|
|
26
26
|
<Input
|
|
27
|
-
:id="formElement?.key"
|
|
27
|
+
:id="formElement?.metadata?.key"
|
|
28
28
|
type="file"
|
|
29
|
-
class="
|
|
29
|
+
class="file:bg-primary focus-visible:ring-primary dark:focus-visible:ring-primary h-auto cursor-pointer rounded-none border-gray-200 bg-white px-6 py-4 text-base font-normal !ring-offset-0 transition-all file:mr-6 file:cursor-pointer file:px-4 file:py-2 file:text-base file:font-bold file:text-white placeholder:text-gray-300 dark:border-gray-200 dark:bg-white dark:placeholder:text-gray-300"
|
|
30
30
|
/>
|
|
31
31
|
</div>
|
|
32
32
|
</template>
|
|
@@ -11,14 +11,14 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
11
11
|
<template>
|
|
12
12
|
<FormKit
|
|
13
13
|
type="radio"
|
|
14
|
-
:name="formElement
|
|
14
|
+
:name="formElement?.metadata?.key"
|
|
15
15
|
:help="cleanDescription"
|
|
16
16
|
:validation="getValidationData.rules || ''"
|
|
17
17
|
:validationMessages="getValidationData.messages || {}"
|
|
18
18
|
>
|
|
19
19
|
<template #wrapper>
|
|
20
20
|
<Label class="mb-2 block text-base font-normal">
|
|
21
|
-
{{ `${formElement
|
|
21
|
+
{{ `${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}` }}
|
|
22
22
|
</Label>
|
|
23
23
|
<RadioGroup>
|
|
24
24
|
<div
|
|
@@ -27,12 +27,12 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
27
27
|
class="flex items-center gap-2"
|
|
28
28
|
>
|
|
29
29
|
<RadioGroupItem
|
|
30
|
-
:id="`${formElement
|
|
30
|
+
:id="`${formElement?.metadata?.key}-${index}`"
|
|
31
31
|
:value="option.value"
|
|
32
|
-
class="border-1
|
|
32
|
+
class="border-1 focus-visible:ring-primary data-[state=checked]:text-primary dark:focus-visible:ring-primary dark:data-[state=checked]:text-primary size-5 border-gray-200 bg-white focus-visible:ring-offset-0 data-[state=checked]:bg-white dark:border-gray-200 dark:bg-white dark:data-[state=checked]:bg-white"
|
|
33
33
|
/>
|
|
34
34
|
<Label
|
|
35
|
-
:for="`${formElement
|
|
35
|
+
:for="`${formElement?.metadata?.key}-${index}`"
|
|
36
36
|
class="text-base font-normal"
|
|
37
37
|
>
|
|
38
38
|
{{ option.label }}
|
|
@@ -11,12 +11,12 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
11
11
|
<template>
|
|
12
12
|
<FormKit
|
|
13
13
|
type="select"
|
|
14
|
-
:name="formElement
|
|
15
|
-
:label="formElement
|
|
14
|
+
:name="formElement?.metadata?.key"
|
|
15
|
+
:label="formElement?.metadata?.title"
|
|
16
16
|
:help="cleanDescription"
|
|
17
17
|
:validation="getValidationData.rules"
|
|
18
18
|
:validationMessages="getValidationData.messages"
|
|
19
|
-
:multiple="Boolean(formElement
|
|
19
|
+
:multiple="Boolean(formElement?.metadata?.multiple)"
|
|
20
20
|
>
|
|
21
21
|
<template #wrapper="{ value, node }">
|
|
22
22
|
<Select
|
|
@@ -24,19 +24,19 @@ const { cleanDescription, getValidationData, getFormattedOptions } =
|
|
|
24
24
|
@update:model-value="node.input"
|
|
25
25
|
>
|
|
26
26
|
<SelectTrigger
|
|
27
|
-
class="[&[data-state=open]>svg]:transition-200 border-1
|
|
27
|
+
class="[&[data-state=open]>svg]:transition-200 border-1 focus:ring-primary dark:focus:ring-primary h-auto rounded-none border-gray-200 bg-white px-6 py-4 focus:outline-none focus:ring-offset-0 dark:border-gray-200 dark:bg-white dark:focus:outline-none dark:focus:ring-offset-0 [&[data-state=open]>svg]:rotate-180 [&[data-state=open]>svg]:transition-all"
|
|
28
28
|
>
|
|
29
29
|
<SelectValue
|
|
30
|
-
:placeholder="`${formElement
|
|
30
|
+
:placeholder="`${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}`"
|
|
31
31
|
class="text-medium text-base text-black"
|
|
32
32
|
>
|
|
33
33
|
{{
|
|
34
|
-
value || `${formElement
|
|
34
|
+
value || `${formElement?.metadata?.title}${formElement?.metadata?.required ? '*' : ''}`
|
|
35
35
|
}}
|
|
36
36
|
</SelectValue>
|
|
37
37
|
</SelectTrigger>
|
|
38
38
|
<SelectContent
|
|
39
|
-
class="border-1
|
|
39
|
+
class="border-1 rounded-none border-gray-200 bg-white dark:border-gray-200 dark:bg-white [&>div]:!px-0 [&>div]:!py-1"
|
|
40
40
|
>
|
|
41
41
|
<SelectItem
|
|
42
42
|
v-for="(option, index) in getFormattedOptions"
|
|
@@ -15,8 +15,8 @@ const isFocused = ref<boolean>(false)
|
|
|
15
15
|
<FormKit
|
|
16
16
|
type="textarea"
|
|
17
17
|
v-model="inputValue"
|
|
18
|
-
:name="formElement?.key"
|
|
19
|
-
:label="formElement?.title"
|
|
18
|
+
:name="formElement?.metadata?.key"
|
|
19
|
+
:label="formElement?.metadata?.title"
|
|
20
20
|
:help="cleanDescription"
|
|
21
21
|
:placeholder="isFocused && !inputValue ? formElement?.placeholder : ''"
|
|
22
22
|
:validation="getValidationData.rules"
|
|
@@ -16,8 +16,8 @@ const isFocused = ref<boolean>(false)
|
|
|
16
16
|
<FormKit
|
|
17
17
|
type="email"
|
|
18
18
|
v-model="inputValue"
|
|
19
|
-
:name="formElement?.key"
|
|
20
|
-
:label="formElement?.title"
|
|
19
|
+
:name="formElement?.metadata?.key"
|
|
20
|
+
:label="formElement?.metadata?.title"
|
|
21
21
|
:help="cleanDescription"
|
|
22
22
|
:placeholder="isFocused && !inputValue ? formElement?.placeholder : ''"
|
|
23
23
|
:validation="getValidationData.rules"
|
|
@@ -16,8 +16,8 @@ const isFocused = ref<boolean>(false)
|
|
|
16
16
|
<FormKit
|
|
17
17
|
type="text"
|
|
18
18
|
v-model="inputValue"
|
|
19
|
-
:name="formElement?.key"
|
|
20
|
-
:label="formElement?.title"
|
|
19
|
+
:name="formElement?.metadata?.key"
|
|
20
|
+
:label="formElement?.metadata?.title"
|
|
21
21
|
:help="cleanDescription"
|
|
22
22
|
:placeholder="isFocused && !inputValue ? formElement?.placeholder : ''"
|
|
23
23
|
:validation="getValidationData.rules"
|
|
@@ -16,8 +16,8 @@ const isFocused = ref<boolean>(false)
|
|
|
16
16
|
<FormKit
|
|
17
17
|
type="tel"
|
|
18
18
|
v-model="inputValue"
|
|
19
|
-
:name="formElement?.key"
|
|
20
|
-
:label="formElement?.title"
|
|
19
|
+
:name="formElement?.metadata?.key"
|
|
20
|
+
:label="formElement?.metadata?.title"
|
|
21
21
|
:help="cleanDescription"
|
|
22
22
|
:placeholder="isFocused && !inputValue ? formElement?.placeholder : ''"
|
|
23
23
|
:validation="getValidationData.rules"
|
|
@@ -3,25 +3,25 @@ import type { FormElement, TextInput, SelectInput, InputOption, CheckboxInputs,
|
|
|
3
3
|
export function useFormElement(formElement: FormElement) {
|
|
4
4
|
const { $texts } = useNuxtApp()
|
|
5
5
|
const cleanDescription = computed(() => {
|
|
6
|
-
if (!formElement
|
|
6
|
+
if (!formElement?.metadata?.description) return ''
|
|
7
7
|
|
|
8
|
-
return formElement
|
|
8
|
+
return formElement?.metadata?.description.replace(/<[^>]*>/g, '')
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
const getValidationData = computed(() => {
|
|
12
12
|
const rules: string[] = []
|
|
13
13
|
const messages: Record<string, string> = {}
|
|
14
14
|
|
|
15
|
-
if (formElement?.required) {
|
|
15
|
+
if (formElement?.metadata?.required) {
|
|
16
16
|
rules.push('required')
|
|
17
|
-
messages['required'] = formElement
|
|
17
|
+
messages['required'] = formElement?.metadata?.requiredError || $texts(
|
|
18
18
|
'webform.required_field_error',
|
|
19
19
|
'This field is required.',
|
|
20
20
|
)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const checkboxInput = formElement as CheckboxInput;
|
|
24
|
-
if (checkboxInput?.type === 'checkbox' && checkboxInput?.required) {
|
|
24
|
+
if (checkboxInput?.metadata?.type === 'checkbox' && checkboxInput?.metadata?.required) {
|
|
25
25
|
rules.push('accepted')
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export type FormKitTypes = 'text' | 'number' | 'meta' | 'url' | 'button' | 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'form' | 'group' | 'hidden' | 'list' | 'month' | 'password'
|
|
2
2
|
|
|
3
3
|
export interface FormElement {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
metadata: {
|
|
5
|
+
key: string
|
|
6
|
+
type: string
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
multiple?: number
|
|
10
|
+
required?: boolean
|
|
11
|
+
requiredError?: string
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<img
|
|
3
|
-
:src="media?.url?.path"
|
|
4
|
-
:alt="media?.alt?.alt"
|
|
5
|
-
:title="media?.title"
|
|
6
|
-
:class="mediaClass"
|
|
2
|
+
<img
|
|
3
|
+
:src="media?.url?.path"
|
|
4
|
+
:alt="media?.alt?.alt"
|
|
5
|
+
:title="media?.title"
|
|
6
|
+
:class="themedClass('mediaClass')"
|
|
7
7
|
/>
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script lang="ts" setup>
|
|
11
11
|
import type { MediaIconFragment } from '#graphql-operations'
|
|
12
|
-
|
|
12
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
13
|
+
|
|
14
|
+
const themedClass = useTheme('MediaIcon')
|
|
15
|
+
|
|
16
|
+
defineProps<{
|
|
13
17
|
media: MediaIconFragment
|
|
14
18
|
mediaClass?: string
|
|
15
19
|
}>()
|
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<picture :class="mediaClass">
|
|
3
|
-
<img
|
|
4
|
-
|
|
5
|
-
:srcset="imageSources[0].srcset"
|
|
6
|
-
:sizes="imageSources[0].sizes"
|
|
7
|
-
:alt="media?.alt?.alt"
|
|
2
|
+
<picture :class="themedClass('mediaClass')">
|
|
3
|
+
<img
|
|
4
|
+
v-if="imageSources.length === 1"
|
|
5
|
+
:srcset="imageSources[0].srcset"
|
|
6
|
+
:sizes="imageSources[0].sizes"
|
|
7
|
+
:alt="media?.alt?.alt"
|
|
8
8
|
:title="media?.title"
|
|
9
9
|
:src="placeHolderImage"
|
|
10
10
|
:loading="imageLoading"
|
|
11
|
+
:class="themedClass('elementClass', 'w-full')"
|
|
11
12
|
/>
|
|
12
|
-
<source
|
|
13
|
+
<source
|
|
14
|
+
v-if="imageSources.length > 1"
|
|
15
|
+
v-for="(source, key) in imageSources"
|
|
16
|
+
:key="key"
|
|
13
17
|
:srcset="source.srcset"
|
|
14
|
-
:media="source.media"
|
|
18
|
+
:media="source.media"
|
|
15
19
|
/>
|
|
16
|
-
<img
|
|
17
|
-
|
|
18
|
-
:
|
|
20
|
+
<img
|
|
21
|
+
v-if="imageSources.length > 1"
|
|
22
|
+
:src="placeHolderImage"
|
|
23
|
+
:alt="media?.alt?.alt"
|
|
19
24
|
:title="media?.title"
|
|
20
|
-
:class="elementClass"
|
|
21
25
|
:loading="imageLoading"
|
|
26
|
+
:class="themedClass('elementClass', 'w-full')"
|
|
27
|
+
/>
|
|
28
|
+
<figcaption
|
|
29
|
+
v-if="showCaption && media?.caption"
|
|
30
|
+
v-text="media?.caption"
|
|
31
|
+
:class="themedClass('figcaptionClass', 'mt-2 text-center italic')"
|
|
22
32
|
/>
|
|
23
|
-
<figcaption v-if="showCaption && media?.caption"
|
|
24
|
-
v-text="media?.caption"
|
|
25
|
-
:class="figcaptionClass" />
|
|
26
33
|
</picture>
|
|
27
34
|
</template>
|
|
28
35
|
|
|
29
36
|
<script lang="ts" setup>
|
|
30
37
|
import type { MediaImageFragment } from '#graphql-operations'
|
|
38
|
+
import { useTheme } from '@iqual/nuxt-icms/composables/useTheme'
|
|
39
|
+
|
|
40
|
+
const themedClass = useTheme('MediaImage')
|
|
31
41
|
|
|
32
42
|
const props = withDefaults(
|
|
33
43
|
defineProps<{
|
|
@@ -35,15 +45,13 @@ const props = withDefaults(
|
|
|
35
45
|
mediaClass?: string
|
|
36
46
|
imageAspectRatios?: string
|
|
37
47
|
imageSizes?: string
|
|
38
|
-
imageLoading?:
|
|
48
|
+
imageLoading?: 'lazy' | 'eager' | undefined
|
|
39
49
|
showCaption?: boolean
|
|
40
50
|
figcaptionClass?: string
|
|
41
51
|
elementClass?: string
|
|
42
52
|
}>(),
|
|
43
53
|
{
|
|
44
54
|
imageLoading: 'lazy',
|
|
45
|
-
figcaptionClass: 'mt-2 italic text-center',
|
|
46
|
-
elementClass: 'size-full object-cover object-center',
|
|
47
55
|
},
|
|
48
56
|
)
|
|
49
57
|
|
|
@@ -52,6 +60,4 @@ const { placeHolderImage, imageSources } = useImageSources({
|
|
|
52
60
|
imageAspectRatios: toRef(props, 'imageAspectRatios'),
|
|
53
61
|
imageSizes: toRef(props, 'imageSizes'),
|
|
54
62
|
})
|
|
55
|
-
|
|
56
63
|
</script>
|
|
57
|
-
|