diff --git a/.yarn/cache/@floating-ui-core-npm-1.5.0-3cdde0a88b-54b4fe26b3.zip b/.yarn/cache/@floating-ui-core-npm-1.5.0-3cdde0a88b-54b4fe26b3.zip new file mode 100644 index 0000000..b2e42dd Binary files /dev/null and b/.yarn/cache/@floating-ui-core-npm-1.5.0-3cdde0a88b-54b4fe26b3.zip differ diff --git a/.yarn/cache/@floating-ui-dom-npm-1.5.3-ac36b589ae-0005374206.zip b/.yarn/cache/@floating-ui-dom-npm-1.5.3-ac36b589ae-0005374206.zip new file mode 100644 index 0000000..5be7c81 Binary files /dev/null and b/.yarn/cache/@floating-ui-dom-npm-1.5.3-ac36b589ae-0005374206.zip differ diff --git a/.yarn/cache/@floating-ui-utils-npm-0.1.4-ec4958b0f8-e6195ded5b.zip b/.yarn/cache/@floating-ui-utils-npm-0.1.4-ec4958b0f8-e6195ded5b.zip new file mode 100644 index 0000000..8ee3ef8 Binary files /dev/null and b/.yarn/cache/@floating-ui-utils-npm-0.1.4-ec4958b0f8-e6195ded5b.zip differ diff --git a/.yarn/cache/svelte-floating-ui-npm-1.2.8-a056ad8ed6-884b3c5f7f.zip b/.yarn/cache/svelte-floating-ui-npm-1.2.8-a056ad8ed6-884b3c5f7f.zip new file mode 100644 index 0000000..84e2b50 Binary files /dev/null and b/.yarn/cache/svelte-floating-ui-npm-1.2.8-a056ad8ed6-884b3c5f7f.zip differ diff --git a/.yarn/cache/svelte-select-npm-5.7.0-ec57b7e455-85f5f57d59.zip b/.yarn/cache/svelte-select-npm-5.7.0-ec57b7e455-85f5f57d59.zip new file mode 100644 index 0000000..faa0e80 Binary files /dev/null and b/.yarn/cache/svelte-select-npm-5.7.0-ec57b7e455-85f5f57d59.zip differ diff --git a/api/collections/fields/pagebuilder.yml b/api/collections/fields/pagebuilder.yml index bec07a3..94948b1 100644 --- a/api/collections/fields/pagebuilder.yml +++ b/api/collections/fields/pagebuilder.yml @@ -695,6 +695,56 @@ subFields: - id: sunday name: Sonntag + - name: showMultiSelect + type: boolean + meta: + label: Mehrfachauswahl anzeigen + + - name: multiSelectEmailTitle + type: string + meta: + label: Mehrfachauswahl Email Titel + dependsOn: + eval: $parent.showMultiSelect + + - name: multiSelectNotRequired + type: boolean + meta: + label: nicht Notwendig + dependsOn: + eval: $parent.showMultiSelect + + - name: multiSelectPlaceholder + type: string + meta: + label: Mehrfachauswahl Platzhalter + dependsOn: + eval: $parent.showMultiSelect + + - name: multiSelectOptions + type: object[] + meta: + label: Mehrfachauswahl Optionen + dependsOn: + eval: $parent.showMultiSelect + subFields: + - name: name + type: string + meta: + label: Name + + - name: multiSelectProps + type: object + meta: + label: Mehrfachauswahl Eigenschaften + dependsOn: + eval: $parent.showMultiSelect + subFields: + - name: additionalAddableValues + type: boolean + meta: + label: Zusätzliche hinzufügbare Werte + - name: text type: object[] meta: diff --git a/frontend/src/lib/assets/css/svelte-select.less b/frontend/src/lib/assets/css/svelte-select.less new file mode 100644 index 0000000..4f8e895 --- /dev/null +++ b/frontend/src/lib/assets/css/svelte-select.less @@ -0,0 +1,65 @@ +.svelte-select { + height: 55px !important; + margin-bottom: 20px; + + margin: 5px 0px !important; + box-shadow: 0 0 25px 10px var(--opposite-bg-color-5) !important; + padding: 10px 20px !important; + border: 0px solid var(--opposite-bg-color) !important; + border-bottom: 3px solid var(--heading-font-color) !important; + outline: 0px solid var(--opposite-bg-color) !important; + color: var(--opposite-bg-color) !important; + background-color: var(--background-color) !important; + resize: none !important; + .value-container { + input { + padding-top: 20px; + } + .selected-item { + padding-top: 19px; + } + } + + .multi-item { + background-color: rgba(0, 0, 0, 0.1) !important; + border-radius: 3px !important; + } + + .selected-item { + margin-left: 3px; + height: auto; + line-height: initial; + } + &.focused { + border-color: var(--hover-color) !important; + } + + input { + height: 100%; + font-size: 1rem !important; + } + + .list-item { + .item { + &.active { + background-color: transparent !important; + color: var(--normal-font-color) !important; + } + } + } + + .indicators { + color: #ccc; + + & > * { + border-left: 1px solid #ccc !important; + height: 40px !important; + cursor: pointer !important; + } + + .clear-select { + color: var(--hover-color) !important; + font-weight: 700 !important; + } + } +} diff --git a/frontend/src/lib/components/pagebuilder/form/datepicker.svelte b/frontend/src/lib/components/pagebuilder/form/datepicker.svelte index 5d3c9e5..5a251c5 100644 --- a/frontend/src/lib/components/pagebuilder/form/datepicker.svelte +++ b/frontend/src/lib/components/pagebuilder/form/datepicker.svelte @@ -1,9 +1,11 @@ <script lang="ts"> import { CalendarView } from "fluent-svelte" + import type { Writable } from "svelte/store" + export let groupTitle: string export let datePickerProps: DatePickerProps - export let formValues: any + export let formValues: Writable<FormValues> export let rowNr: number export let formCol: FormColumn diff --git a/frontend/src/lib/components/pagebuilder/form/form.svelte b/frontend/src/lib/components/pagebuilder/form/form.svelte index c3641b7..3255c63 100644 --- a/frontend/src/lib/components/pagebuilder/form/form.svelte +++ b/frontend/src/lib/components/pagebuilder/form/form.svelte @@ -3,6 +3,7 @@ import Datepicker from "./datepicker.svelte" import FormLabelNumberBlock from "./formLabelNumberBlock.svelte" import type { Writable } from "svelte/store" + import Select from "./select.svelte" export let formRow: FormRow export let index: number @@ -135,9 +136,19 @@ formValues="{formValues}" rowNr="{index}" formCol="{column}" - /> {/if} + + {#if column.showMultiSelect} + <Select + groupTitle="{column.groupTitle}" + formValues="{formValues}" + rowNr="{index}" + formCol="{column}" + options="{column.multiSelectOptions}" + /> + {/if} + {#each column.text ?? [] as textField, textFieldIndex} <div> <h3 class="textTitle">{textField.textTitle || ""}</h3> diff --git a/frontend/src/lib/components/pagebuilder/form/mobileForm.svelte b/frontend/src/lib/components/pagebuilder/form/mobileForm.svelte index 92d0873..a42daff 100644 --- a/frontend/src/lib/components/pagebuilder/form/mobileForm.svelte +++ b/frontend/src/lib/components/pagebuilder/form/mobileForm.svelte @@ -3,6 +3,7 @@ import type { Writable } from "svelte/store" import CheckboxGroup from "./checkboxGroup.svelte" import Datepicker from "./datepicker.svelte" + import Select from "./select.svelte" export let formRow: FormRow export let formValues: Writable<FormValues> export let index: number @@ -147,6 +148,16 @@ formCol="{column}" /> {/if} + + {#if column.showMultiSelect} + <Select + groupTitle="{column.groupTitle}" + formValues="{formValues}" + rowNr="{index}" + formCol="{column}" + options="{column.multiSelectOptions}" + /> + {/if} {#each column.text ?? [] as textField, textFieldIndex} <div class="column-{columnIndex} position-{getPosition(column, 5 + textFieldIndex, textFieldIndex)}"> <h3 class="textTitle">{textField.textTitle || ""}</h3> diff --git a/frontend/src/lib/components/pagebuilder/form/select.svelte b/frontend/src/lib/components/pagebuilder/form/select.svelte new file mode 100644 index 0000000..37dc40e --- /dev/null +++ b/frontend/src/lib/components/pagebuilder/form/select.svelte @@ -0,0 +1,78 @@ +<script lang="ts"> + import Select from "svelte-select/Select.svelte" + import { onMount } from "svelte" + import type { Writable } from "svelte/store" + + export let options: MultiSelectOptions[] = [] + export let groupTitle: string + + export let formValues: Writable<FormValues> + export let rowNr: number + export let formCol: FormColumn + + let valueStorage: any[] = [] + let value: string[] = [] + let values = options.map((option) => ({ label: option.name, value: option.name })) + let lastCustomInput: string | null = null + + function setFormValues() { + // @ts-ignore + $formValues[`selectMultiple_${groupTitle}_${rowNr}_${formCol.multiSelectEmailTitle}`] = { + value: value.toString(), + required: !formCol.multiSelectNotRequired, + } + } + + function handleInputChange(e: Event) { + const newCustomInput = (e.target as HTMLInputElement).value + + // Remove the last custom input if it exists + if (lastCustomInput) { + values = values.filter((item) => item.label !== lastCustomInput) + } + + // Add the new custom input + if (newCustomInput && !values.some((item) => item.label === newCustomInput)) { + values = [...values, { label: newCustomInput, value: newCustomInput }] + } + + // Update lastCustomInput + lastCustomInput = newCustomInput + } + + $: { + if (value.length) { + setFormValues() + } + } + + onMount(() => { + const inputEl = document.querySelector(".svelte-select input") + if (inputEl) { + inputEl.addEventListener("input", handleInputChange) + } + return () => { + if (inputEl) { + inputEl.removeEventListener("input", handleInputChange) + } + } + }) + $: value = valueStorage.map((item) => item.value) +</script> + +<Select + bind:items="{values}" + inputAttributes="{{ autocomplete: 'on' }}" + placeholder="" + showChevron="{true}" + clearable="{true}" + hideEmptyState="{true}" + searchable="{true}" + multiple="{true}" + bind:value="{valueStorage}" +/> + +<style lang="less" global> + @import "../../../assets/css/variables.less"; + @import "../../../assets/css/svelte-select.less"; +</style> diff --git a/package.json b/package.json index c1430ce..9448779 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-preset-env": "^8.5.1", + "svelte-select": "^5.7.0", "swiper": "^9.2.0" } } diff --git a/types/global.d.ts b/types/global.d.ts index ea238ce..2ff9c18 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -216,6 +216,18 @@ interface FormColumn { datePickerNotRequired: boolean datePickerProps: DatePickerProps datePickerEmailTitle: string + showMultiSelect: boolean + multiSelectNotRequired: boolean + multiSelectPlaceholder: string + multiSelectEmailTitle: string + multiSelectOptions: MultiSelectOptions[] + multiSelectProps: { + additionalAddableValues: boolean + } +} + +interface MultiSelectOptions { + name: string } interface CustomHTMLElement extends HTMLElement { diff --git a/yarn.lock b/yarn.lock index 7e8762e..50677a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1948,6 +1948,32 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.1.0, @floating-ui/core@npm:^1.4.2": + version: 1.5.0 + resolution: "@floating-ui/core@npm:1.5.0" + dependencies: + "@floating-ui/utils": ^0.1.3 + checksum: 54b4fe26b3c228746ac5589f97303abf158b80aa5f8b99027259decd68d1c2030c4c637648ebd33dfe78a4212699453bc2bd7537fd5a594d3bd3e63d362f666f + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.1.0, @floating-ui/dom@npm:^1.2.1": + version: 1.5.3 + resolution: "@floating-ui/dom@npm:1.5.3" + dependencies: + "@floating-ui/core": ^1.4.2 + "@floating-ui/utils": ^0.1.3 + checksum: 00053742064aac70957f0bd5c1542caafb3bfe9716588bfe1d409fef72a67ed5e60450d08eb492a77f78c22ed1ce4f7955873cc72bf9f9caf2b0f43ae3561c21 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.1.3": + version: 0.1.4 + resolution: "@floating-ui/utils@npm:0.1.4" + checksum: e6195ded5b3a6fd38411a833605184c31f24609b08feab2615e90ccc063bf4d3965383d817642fc7e8ca5ab6a54c29c71103e874f3afb0518595c8bd3390ba16 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -6410,6 +6436,16 @@ __metadata: languageName: node linkType: hard +"svelte-floating-ui@npm:1.2.8": + version: 1.2.8 + resolution: "svelte-floating-ui@npm:1.2.8" + dependencies: + "@floating-ui/core": ^1.1.0 + "@floating-ui/dom": ^1.1.0 + checksum: 884b3c5f7f32e6b1937432e61e200800a215f63ec1e7b5a81ca43602727505824c53d95780574d9aed74f4486eac70628ab89b2f4c3f012175c1a3789a2a7d4a + languageName: node + linkType: hard + "svelte-hmr@npm:^0.15.3": version: 0.15.3 resolution: "svelte-hmr@npm:0.15.3" @@ -6493,6 +6529,16 @@ __metadata: languageName: node linkType: hard +"svelte-select@npm:^5.7.0": + version: 5.7.0 + resolution: "svelte-select@npm:5.7.0" + dependencies: + "@floating-ui/dom": ^1.2.1 + svelte-floating-ui: 1.2.8 + checksum: 85f5f57d592239874f88d7a48b9fa17d80efbf22ebed7a9848297dd693ac398e1dd9d12a8bd396c6c86597a895201cb333d0e83982a9d94ac0190e3cd387ee86 + languageName: node + linkType: hard + "svelte2tsx@npm:^0.1.157": version: 0.1.193 resolution: "svelte2tsx@npm:0.1.193" @@ -6613,6 +6659,7 @@ __metadata: svelte-preprocess: ^5.0.4 svelte-preprocess-esbuild: ^3.0.1 svelte-routing: ^1.6.0 + svelte-select: ^5.7.0 swiper: ^9.2.0 tslib: ^2.6.2 typescript: ^5.0.4