|
| 1 | +<!-- |
| 2 | + @title MoonLight |
| 3 | + @file MultiInput.svelte |
| 4 | + @repo https://github.com/MoonModules/MoonLight, submit changes to this file as PRs |
| 5 | + @Authors https://github.com/MoonModules/MoonLight/commits/main |
| 6 | + @Copyright © 2025 Github MoonLight Commit Authors |
| 7 | + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 |
| 8 | + @license For non GPL-v3 usage, commercial licenses must be purchased. Contact moonmodules@icloud.com |
| 9 | +
|
| 10 | + Not w-full! |
| 11 | +--> |
| 12 | + |
| 13 | + |
| 14 | +<script lang="ts"> |
| 15 | + import FileEdit from '$lib/components/custom/FileEdit.svelte'; |
| 16 | + import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list'; |
| 17 | + import Add from '~icons/tabler/circle-plus'; |
| 18 | + import { user } from '$lib/stores/user'; |
| 19 | + import { page } from '$app/state'; |
| 20 | + import Router from '~icons/tabler/router'; |
| 21 | + import { slide } from 'svelte/transition'; |
| 22 | + import { cubicOut } from 'svelte/easing'; |
| 23 | + import Edit from '~icons/tabler/pencil'; |
| 24 | + import Delete from '~icons/tabler/trash'; |
| 25 | + import MultiInput from '$lib/components/custom/MultiInput.svelte'; |
| 26 | + import Array from '$lib/components/custom/Array.svelte'; |
| 27 | + import ArrayLight from '$lib/components/custom/ArrayLight.svelte'; |
| 28 | +
|
| 29 | + let { property, data, definition, showEditor = false, onChange, changeOnInput, value1=$bindable(), value2 } = $props(); |
| 30 | +
|
| 31 | + let dataEditable: any = $state({}); |
| 32 | +
|
| 33 | + let propertyEditable: string = $state(""); |
| 34 | +
|
| 35 | + //if no records added yet, add an empty array |
| 36 | + if (data[property.name] == undefined) { |
| 37 | + data[property.name] = []; |
| 38 | + } |
| 39 | +
|
| 40 | + console.log("Array property", property, data, definition, showEditor, changeOnInput, data[property.name], value1, value2); |
| 41 | + for (let i = 0; i < definition.length; i++) { |
| 42 | + // console.log("addItem def", propertyName, property) |
| 43 | + if (property.name == definition[i].name) { |
| 44 | + console.log("def[i]", property.name, definition[i].n) |
| 45 | + } |
| 46 | + } |
| 47 | +
|
| 48 | + function onDrop(propertyName: string, { detail: { from, to } }: CustomEvent<DropEvent>) { |
| 49 | + |
| 50 | + if (!to || from === to) { |
| 51 | + return; |
| 52 | + } |
| 53 | +
|
| 54 | + data[propertyName] = reorder(data[propertyName], from.index, to.index); |
| 55 | + onChange(); |
| 56 | + // console.log(onDrop, data[propertyName]); |
| 57 | + } |
| 58 | +
|
| 59 | + function addItem(propertyName: string) { |
| 60 | + propertyEditable = propertyName; |
| 61 | + //set the default values from the definition... |
| 62 | + dataEditable = {}; |
| 63 | +
|
| 64 | + //set properties with their defaults |
| 65 | + for (let i = 0; i < definition.length; i++) { |
| 66 | + let property = definition[i]; |
| 67 | + // console.log("addItem def", propertyName, property) |
| 68 | + if (property.name == propertyName) { |
| 69 | + for (let i=0; i < property.n.length; i++) { |
| 70 | + let propertyN = property.n[i]; |
| 71 | + // console.log("propertyN", propertyN) |
| 72 | + dataEditable[propertyN.name] = propertyN.default; |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | +
|
| 78 | + function handleEdit(propertyName: string, index: number) { |
| 79 | + console.log("handleEdit", propertyName, index) |
| 80 | + propertyEditable = propertyName; |
| 81 | + showEditor = true; |
| 82 | + dataEditable = data[propertyName][index]; |
| 83 | + } |
| 84 | +
|
| 85 | + function deleteItem(propertyName: string, index: number) { |
| 86 | + // Check if item is currently been edited and delete as well |
| 87 | + if (data[propertyName][index].animation === dataEditable.animation) { |
| 88 | + addItem(propertyName); |
| 89 | + } |
| 90 | + // Remove item from array |
| 91 | + data[propertyName].splice(index, 1); |
| 92 | + data[propertyName] = [...data[propertyName]]; //Trigger reactivity |
| 93 | + showEditor = false; |
| 94 | + onChange(); |
| 95 | + } |
| 96 | +
|
| 97 | +</script> |
| 98 | + |
| 99 | + <div class="divider mb-2 mt-0"></div> |
| 100 | + <div class="h-16 flex w-full items-center justify-between space-x-3 p-0 text-xl font-medium"> |
| 101 | + {property.name} |
| 102 | + </div> |
| 103 | + <div class="relative w-full overflow-visible"> |
| 104 | + <!-- <div class="mx-4 mb-4 flex flex-wrap justify-end gap-2"> --> |
| 105 | + <button |
| 106 | + class="btn btn-primary text-primary-content btn-md absolute -top-14 right-0" |
| 107 | + onclick={() => { |
| 108 | + addItem(property.name); |
| 109 | + onChange(); |
| 110 | + |
| 111 | + //add the new item to the data |
| 112 | + data[property.name].push(dataEditable); |
| 113 | + showEditor = true; |
| 114 | + }} |
| 115 | + > |
| 116 | + <Add class="h-6 w-6" /></button |
| 117 | + > |
| 118 | + </div> |
| 119 | + |
| 120 | + <div |
| 121 | + class="overflow-x-auto space-y-1" |
| 122 | + transition:slide|local={{ duration: 300, easing: cubicOut }} |
| 123 | + > |
| 124 | + <DragDropList |
| 125 | + id={property.name} |
| 126 | + type={VerticalDropZone} |
| 127 | + itemSize={60} |
| 128 | + itemCount={data[property.name].length} |
| 129 | + on:drop={(event) => { |
| 130 | + onDrop(property.name, event); |
| 131 | + }} |
| 132 | + |
| 133 | + > |
| 134 | + {#snippet children({ index })} |
| 135 | + <!-- svelte-ignore a11y_click_events_have_key_events --> |
| 136 | + <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2"> |
| 137 | + <div class="mask mask-hexagon bg-primary h-auto w-10 shrink-0"> |
| 138 | + <Router class="text-primary-content h-auto w-full scale-75" /> |
| 139 | + </div> |
| 140 | + {#each property.n as propertyN} |
| 141 | + {#if propertyN.type == "array"} |
| 142 | + <div> |
| 143 | + <div class="font-bold">{data[property.name][index][propertyN.name].length}</div> |
| 144 | + </div> |
| 145 | + {:else if propertyN.type != "password"} |
| 146 | + <div> |
| 147 | + <div class="font-bold">{data[property.name][index][propertyN.name]}</div> |
| 148 | + </div> |
| 149 | + {/if} |
| 150 | + {/each} |
| 151 | + {#if !page.data.features.security || $user.admin} |
| 152 | + <div class="flex-grow"></div> |
| 153 | + <div class="space-x-0 px-0 mx-0"> |
| 154 | + <button |
| 155 | + class="btn btn-ghost btn-sm" |
| 156 | + onclick={() => { |
| 157 | + handleEdit(property.name, index); |
| 158 | + }} |
| 159 | + > |
| 160 | + <Edit class="h-6 w-6" /></button |
| 161 | + > |
| 162 | + <button |
| 163 | + class="btn btn-ghost btn-sm" |
| 164 | + onclick={() => { |
| 165 | + deleteItem(property.name, index); |
| 166 | + }} |
| 167 | + > |
| 168 | + <Delete class="text-error h-6 w-6" /> |
| 169 | + </button> |
| 170 | + </div> |
| 171 | + {/if} |
| 172 | + </div> |
| 173 | + {/snippet} |
| 174 | + </DragDropList> |
| 175 | + </div> |
| 176 | + {#if showEditor && property.name == propertyEditable} |
| 177 | + <div class="divider my-0"></div> |
| 178 | + {#each property.n as propertyN} |
| 179 | + {#if propertyN.type != "array"} |
| 180 | + <div> |
| 181 | + <MultiInput property={propertyN} bind:value={dataEditable[propertyN.name]} onChange={onChange} changeOnInput={changeOnInput}></MultiInput> |
| 182 | + </div> |
| 183 | + {:else if propertyN.type == "array"} |
| 184 | + <label for="{propertyN.name}">{propertyN.name}</label> |
| 185 | + <Array property={propertyN} bind:value1={data[propertyN.name]} value2={data} data={dataEditable} definition={definition} onChange={onChange} changeOnInput={changeOnInput}></Array> |
| 186 | + {/if} |
| 187 | + {/each} |
| 188 | + {/if} |
0 commit comments