viur-ui 0.0.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/README.md +79 -0
- package/index.d.ts +351 -0
- package/index.js +46 -0
- package/package.json +84 -0
- package/src/components/Browser/Browser.vue +496 -0
- package/src/components/Browser/TreeNav.vue +408 -0
- package/src/components/Browser/ViurBrowser.d.vue.ts +64 -0
- package/src/components/Browser/ViurBrowser.vue +271 -0
- package/src/components/Browser/dragSession.ts +134 -0
- package/src/components/Browser/index.ts +14 -0
- package/src/components/Browser/types.ts +30 -0
- package/src/components/DataTable/ColumnFilterPanel.vue +225 -0
- package/src/components/DataTable/DataTable.vue +3155 -0
- package/src/components/DataTable/ViurDataTable.d.vue.ts +72 -0
- package/src/components/DataTable/ViurDataTable.vue +331 -0
- package/src/components/DataTable/columns.ts +400 -0
- package/src/components/DataTable/index.ts +60 -0
- package/src/components/DataTable/rows.ts +446 -0
- package/src/components/DataTable/types.ts +267 -0
- package/src/components/DataTable/useColumnLayout.ts +294 -0
- package/src/components/DataTable/useGridNavigation.ts +193 -0
- package/src/components/DataTable/useVirtualRows.ts +126 -0
- package/src/components/DataTable/viurColumns.ts +330 -0
- package/src/forms/ViForm.d.vue.ts +106 -0
- package/src/forms/ViForm.vue +592 -0
- package/src/forms/ViFormCategory.vue +57 -0
- package/src/forms/bones/Bone.vue +1023 -0
- package/src/forms/bones/BoneActions.vue +127 -0
- package/src/forms/bones/BoneLabel.vue +122 -0
- package/src/forms/bones/BoneMultipleList.vue +91 -0
- package/src/forms/bones/BoneMultipleWrapper.vue +105 -0
- package/src/forms/bones/BoneNestedWrapper.vue +155 -0
- package/src/forms/bones/BoneSingleEntry.vue +51 -0
- package/src/forms/bones/FilterBone.vue +694 -0
- package/src/forms/bones/actionbar/defaultBar.vue +98 -0
- package/src/forms/bones/actionbar/fileBar.vue +165 -0
- package/src/forms/bones/actionbar/relationalBar.vue +125 -0
- package/src/forms/bones/actions/ActionDialog.vue +70 -0
- package/src/forms/bones/actions/Debug.vue +106 -0
- package/src/forms/bones/actions/Delete.vue +13 -0
- package/src/forms/bones/actions/DescribeImage.vue +201 -0
- package/src/forms/bones/actions/Download.vue +27 -0
- package/src/forms/bones/actions/Placeholder.vue +14 -0
- package/src/forms/bones/actions/Translation.vue +238 -0
- package/src/forms/bones/actions/index.js +280 -0
- package/src/forms/bones/bone.d.ts +45 -0
- package/src/forms/bones/country_information.json +779 -0
- package/src/forms/bones/index.js +225 -0
- package/src/forms/bones/uploadAdapter.js +46 -0
- package/src/forms/bones/utils.js +284 -0
- package/src/forms/bones/view/base_item.vue +35 -0
- package/src/forms/bones/view/boneLogic.js +202 -0
- package/src/forms/bones/view/index.js +5 -0
- package/src/forms/bones/widgets/booleanBone.vue +83 -0
- package/src/forms/bones/widgets/booleanBoneChoose.vue +57 -0
- package/src/forms/bones/widgets/booleanBoneSelect.vue +58 -0
- package/src/forms/bones/widgets/codeBone.vue +214 -0
- package/src/forms/bones/widgets/colorBone.vue +75 -0
- package/src/forms/bones/widgets/dateBone.vue +114 -0
- package/src/forms/bones/widgets/emailBone.vue +100 -0
- package/src/forms/bones/widgets/fileBone.vue +273 -0
- package/src/forms/bones/widgets/jsonBone.vue +63 -0
- package/src/forms/bones/widgets/keyBone.vue +71 -0
- package/src/forms/bones/widgets/numericBone.vue +125 -0
- package/src/forms/bones/widgets/passwordBone.vue +215 -0
- package/src/forms/bones/widgets/phoneBone.vue +173 -0
- package/src/forms/bones/widgets/rawBone.vue +94 -0
- package/src/forms/bones/widgets/recordBone.vue +64 -0
- package/src/forms/bones/widgets/relationalBone.vue +164 -0
- package/src/forms/bones/widgets/relationalBoneSelect.vue +120 -0
- package/src/forms/bones/widgets/selectBone.vue +122 -0
- package/src/forms/bones/widgets/selectBoneChoose.vue +104 -0
- package/src/forms/bones/widgets/spatialBone.vue +111 -0
- package/src/forms/bones/widgets/stringBone.vue +139 -0
- package/src/forms/bones/widgets/textBone.vue +641 -0
- package/src/forms/install.d.ts +4 -0
- package/src/forms/install.js +43 -0
- package/src/forms/layouts/LayloutList2Columns.vue +57 -0
- package/src/forms/layouts/LayoutCategory.vue +38 -0
- package/src/forms/layouts/LayoutList.vue +31 -0
- package/src/forms/utils.js +601 -0
- package/src/public/assets/icons/fontawesome-manifest.json +8 -0
- package/src/public/assets/icons/regular/file.svg +1 -0
- package/src/public/assets/icons/solid/arrows-left-right.svg +1 -0
- package/src/public/assets/icons/solid/bug.svg +1 -0
- package/src/public/assets/icons/solid/check.svg +1 -0
- package/src/public/assets/icons/solid/circle-info.svg +1 -0
- package/src/public/assets/icons/solid/compress.svg +1 -0
- package/src/public/assets/icons/solid/download.svg +1 -0
- package/src/public/assets/icons/solid/expand.svg +1 -0
- package/src/public/assets/icons/solid/grip-vertical.svg +1 -0
- package/src/public/assets/icons/solid/plus.svg +1 -0
- package/src/public/assets/icons/solid/question.svg +1 -0
- package/src/public/assets/icons/solid/triangle-exclamation.svg +1 -0
- package/src/public/assets/icons/solid/upload.svg +1 -0
- package/src/public/assets/icons/solid/xmark.svg +1 -0
- package/src/request/cache.js +183 -0
- package/src/request/configStore.js +516 -0
- package/src/request/envelope.js +211 -0
- package/src/request/index.js +35 -0
- package/src/request/messageStore.js +201 -0
- package/src/request/request.js +752 -0
- package/src/request/skel.js +90 -0
- package/src/theme/colorStore.js +168 -0
- package/src/translations/de.js +178 -0
- package/src/translations/en.js +177 -0
- package/src/user/LoginScreen.d.vue.ts +54 -0
- package/src/user/LoginScreen.vue +627 -0
- package/src/user/google.js +75 -0
- package/src/user/index.js +3 -0
- package/src/user/store.d.ts +59 -0
- package/src/user/store.js +183 -0
- package/src/webawesomeCompat.css +72 -0
- package/src/webawesomeConfig.js +61 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-input
|
|
3
|
+
class="bone-widget bone-widget-date bone-widget-date-default"
|
|
4
|
+
:class="[`bone-widget-date-${name}`]"
|
|
5
|
+
:disabled="boneState.readonly"
|
|
6
|
+
:type="state.typeString"
|
|
7
|
+
:name="name"
|
|
8
|
+
:value="state.value"
|
|
9
|
+
:required="
|
|
10
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
11
|
+
"
|
|
12
|
+
:placeholder="state.placeholder"
|
|
13
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
14
|
+
step="1"
|
|
15
|
+
@change="changeEvent"
|
|
16
|
+
></wa-input>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
import { reactive, onMounted, computed, inject } from "vue"
|
|
21
|
+
/**
|
|
22
|
+
* Datums- und/oder Zeitfeld, gesteuert durch `bonestructure.date` und `bonestructure.time`.
|
|
23
|
+
*
|
|
24
|
+
* Rendert ein `<wa-input>` mit dem passenden `type`-Attribut:
|
|
25
|
+
* - `date` wenn nur `date: true`
|
|
26
|
+
* - `time` wenn nur `time: true`
|
|
27
|
+
* - `datetime-local` wenn beide gesetzt sind
|
|
28
|
+
*
|
|
29
|
+
* Bereinigt Zeitzonendaten aus dem Wert, bevor er ans Input weitergegeben wird.
|
|
30
|
+
* Unterstützt mehrfache Werte (`multiple`), aber keine Mehrsprachigkeit.
|
|
31
|
+
*
|
|
32
|
+
* @displayName DateBone
|
|
33
|
+
*
|
|
34
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
35
|
+
* @cssClass bone-widget-date - Kennzeichnet den Bone-Typ
|
|
36
|
+
* @cssClass bone-widget-date-default - Standard-Variante
|
|
37
|
+
* @cssClass bone-widget-date-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-date-publishedAt`
|
|
38
|
+
*/
|
|
39
|
+
defineOptions({
|
|
40
|
+
inheritAttrs: false,
|
|
41
|
+
})
|
|
42
|
+
const props = defineProps({
|
|
43
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
44
|
+
name: String,
|
|
45
|
+
/** Aktueller Bone-Wert als ISO-String, z.B. `"2026-03-28"` oder `"10:30:00"`. */
|
|
46
|
+
value: [Object, String, Number, Boolean, Array],
|
|
47
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
48
|
+
index: Number,
|
|
49
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
50
|
+
lang: String,
|
|
51
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
52
|
+
bone: Object,
|
|
53
|
+
/** Setzt den Fokus nach 600 ms auf das Input-Element. */
|
|
54
|
+
autofocus: Boolean,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const emit = defineEmits([
|
|
58
|
+
/**
|
|
59
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
60
|
+
* @arg {string} name - Bone-Name
|
|
61
|
+
* @arg {string} value - Aktueller Eingabewert als ISO-String
|
|
62
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
63
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
64
|
+
*/
|
|
65
|
+
"change",
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
const boneState = inject("boneState")
|
|
69
|
+
const state = reactive({
|
|
70
|
+
value: computed(() => {
|
|
71
|
+
// remove timezone data if timed
|
|
72
|
+
|
|
73
|
+
let boneValue = props.value
|
|
74
|
+
if (props.value && boneState.bonestructure["time"] && boneState.bonestructure["date"]) {
|
|
75
|
+
boneValue = props.value?.split("+")[0]
|
|
76
|
+
} else if (props.value && boneState.bonestructure["time"] && props.value.includes("T")) {
|
|
77
|
+
boneValue = props.value?.split("+")[0]?.split("T")[1]
|
|
78
|
+
} else if (props.value && props.value.includes("T")) {
|
|
79
|
+
//convert to readable value
|
|
80
|
+
boneValue = new Date(props.value).toISOString().substr(0, 10)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return boneValue
|
|
84
|
+
}),
|
|
85
|
+
typeString: computed(() => {
|
|
86
|
+
// Calculate the correct datetype String
|
|
87
|
+
let typeString = "datetime-local"
|
|
88
|
+
if (!boneState.bonestructure["time"]) {
|
|
89
|
+
typeString = "date"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!boneState.bonestructure["date"]) {
|
|
93
|
+
typeString = "time"
|
|
94
|
+
}
|
|
95
|
+
return typeString
|
|
96
|
+
}),
|
|
97
|
+
placeholder: computed(() => {
|
|
98
|
+
if (boneState.label !== "placeholder") return undefined
|
|
99
|
+
let name = boneState?.bonestructure?.descr
|
|
100
|
+
if (boneState.bonestructure.required) {
|
|
101
|
+
name += "*"
|
|
102
|
+
}
|
|
103
|
+
return name
|
|
104
|
+
}),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
function changeEvent(event) {
|
|
108
|
+
emit("change", props.name, event.target.value, props.lang, props.index)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onMounted(() => {
|
|
112
|
+
emit("change", props.name, state.value, props.lang, props.index) //init
|
|
113
|
+
})
|
|
114
|
+
</script>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-input
|
|
3
|
+
ref="emailBone"
|
|
4
|
+
class="bone-widget bone-widget-email bone-widget-email-default"
|
|
5
|
+
:class="[`bone-widget-email-${name}`]"
|
|
6
|
+
:name="name"
|
|
7
|
+
:disabled="boneState.readonly"
|
|
8
|
+
type="email"
|
|
9
|
+
:value="value"
|
|
10
|
+
:required="
|
|
11
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
12
|
+
"
|
|
13
|
+
:placeholder="state.placeholder"
|
|
14
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
15
|
+
@change="changeEvent"
|
|
16
|
+
@keyup="changeEvent"
|
|
17
|
+
></wa-input>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
import { reactive, onMounted, inject, ref, watchEffect, computed } from "vue"
|
|
22
|
+
import { useTimeoutFn } from "@vueuse/core"
|
|
23
|
+
/**
|
|
24
|
+
* Einzeiliges E-Mail-Eingabefeld mit nativer Browser-Validierung.
|
|
25
|
+
*
|
|
26
|
+
* Rendert ein `<wa-input type="email">` und nutzt die native Constraint
|
|
27
|
+
* Validation API des Browsers zur Format-Prüfung. Bei
|
|
28
|
+
* `boneState.label === "placeholder"` wird die Bone-Beschreibung als
|
|
29
|
+
* Placeholder angezeigt statt als Label darüber.
|
|
30
|
+
*
|
|
31
|
+
* @displayName EmailBone
|
|
32
|
+
*
|
|
33
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
34
|
+
* @cssClass bone-widget-email - Kennzeichnet den Bone-Typ
|
|
35
|
+
* @cssClass bone-widget-email-default - Standard-Variante
|
|
36
|
+
* @cssClass bone-widget-email-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-email-email`
|
|
37
|
+
*/
|
|
38
|
+
defineOptions({
|
|
39
|
+
inheritAttrs: false,
|
|
40
|
+
})
|
|
41
|
+
const props = defineProps({
|
|
42
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
43
|
+
name: String,
|
|
44
|
+
/** Aktueller Bone-Wert. */
|
|
45
|
+
value: [Object, String, Number, Boolean, Array],
|
|
46
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
47
|
+
index: Number,
|
|
48
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
49
|
+
lang: String,
|
|
50
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
51
|
+
bone: Object,
|
|
52
|
+
/** Setzt den Fokus nach 600 ms auf das Input-Element. */
|
|
53
|
+
autofocus: Boolean,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const emit = defineEmits([
|
|
57
|
+
/**
|
|
58
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
59
|
+
* @arg {string} name - Bone-Name
|
|
60
|
+
* @arg {string} value - Aktueller Eingabewert
|
|
61
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
62
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
63
|
+
*/
|
|
64
|
+
"change",
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
const boneState = inject("boneState")
|
|
68
|
+
const state = reactive({
|
|
69
|
+
placeholder: computed(() => {
|
|
70
|
+
if (boneState.label !== "placeholder") return undefined
|
|
71
|
+
let name = boneState?.bonestructure?.descr
|
|
72
|
+
if (boneState.bonestructure.required) {
|
|
73
|
+
name += "*"
|
|
74
|
+
}
|
|
75
|
+
return name
|
|
76
|
+
}),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const emailBone = ref(null)
|
|
80
|
+
|
|
81
|
+
function changeEvent(event) {
|
|
82
|
+
emit("change", props.name, event.target.value, props.lang, props.index)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
watchEffect(() => {
|
|
86
|
+
if (props.autofocus) {
|
|
87
|
+
if (emailBone.value && emailBone.value !== null && emailBone.value !== null) {
|
|
88
|
+
const { start } = useTimeoutFn(() => {
|
|
89
|
+
emailBone.value.focus()
|
|
90
|
+
}, 600)
|
|
91
|
+
|
|
92
|
+
start()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
onMounted(() => {
|
|
98
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
99
|
+
})
|
|
100
|
+
</script>
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="bone-widget-file-area"
|
|
4
|
+
:class="{
|
|
5
|
+
'is-dragover': state.droparea,
|
|
6
|
+
'has-value': !!value,
|
|
7
|
+
'is-loading': state.loading,
|
|
8
|
+
'is-readonly': boneState.readonly,
|
|
9
|
+
}"
|
|
10
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
11
|
+
@click="openPicker"
|
|
12
|
+
@dragover.prevent="state.droparea = true"
|
|
13
|
+
@dragleave.prevent="state.droparea = false"
|
|
14
|
+
@drop.prevent="handleDrop"
|
|
15
|
+
>
|
|
16
|
+
<input
|
|
17
|
+
ref="uploadinput"
|
|
18
|
+
style="display: none"
|
|
19
|
+
type="file"
|
|
20
|
+
:multiple="boneState.multiple"
|
|
21
|
+
@change="handleUpload"
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
v-if="!value"
|
|
26
|
+
class="wa-cluster wa-gap-s wa-align-items-center bone-widget-prompt"
|
|
27
|
+
>
|
|
28
|
+
<wa-icon name="upload"></wa-icon>
|
|
29
|
+
<span>{{ $t("bone.dropzone") }}</span>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
v-else
|
|
34
|
+
class="wa-flank wa-gap-xs wa-align-items-center"
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
v-if="value?.['dest']?.['mimetype']?.includes('image')"
|
|
38
|
+
class="bone-widget-preview has-preview"
|
|
39
|
+
@click.stop="state.previewopen = !state.previewopen"
|
|
40
|
+
>
|
|
41
|
+
<wa-avatar
|
|
42
|
+
style="--size: round(calc(var(--wa-form-control-height) - 0.09em), 1px)"
|
|
43
|
+
class="bone-widget-preview-img"
|
|
44
|
+
shape="rounded"
|
|
45
|
+
:image="createBackgroundImage()"
|
|
46
|
+
alt=""
|
|
47
|
+
></wa-avatar>
|
|
48
|
+
<wa-dialog
|
|
49
|
+
:label="decodeURIComponent(value?.['dest']?.['name'])"
|
|
50
|
+
class="bone-widget-preview-overlay"
|
|
51
|
+
:open="state.previewopen"
|
|
52
|
+
>
|
|
53
|
+
<img
|
|
54
|
+
:src="createBackgroundImage()"
|
|
55
|
+
alt=""
|
|
56
|
+
/>
|
|
57
|
+
</wa-dialog>
|
|
58
|
+
</div>
|
|
59
|
+
<div
|
|
60
|
+
v-else
|
|
61
|
+
class="bone-widget-preview"
|
|
62
|
+
>
|
|
63
|
+
<wa-avatar
|
|
64
|
+
v-if="value?.['dest']?.['name']"
|
|
65
|
+
shape="rounded"
|
|
66
|
+
style="--size: round(calc(var(--wa-form-control-height) - 0.09em), 1px)"
|
|
67
|
+
>
|
|
68
|
+
<wa-icon
|
|
69
|
+
slot="icon"
|
|
70
|
+
name="file"
|
|
71
|
+
></wa-icon>
|
|
72
|
+
</wa-avatar>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="wa-flank:end wa-gap-xs wa-align-items-center bone-widget-file-row">
|
|
75
|
+
<div
|
|
76
|
+
v-if="value?.['dest']?.['name']"
|
|
77
|
+
class="bone-widget-filename"
|
|
78
|
+
:title="decodeURIComponent(value?.['dest']?.['name'])"
|
|
79
|
+
>
|
|
80
|
+
{{ decodeURIComponent(value?.["dest"]?.["name"]) }}
|
|
81
|
+
</div>
|
|
82
|
+
<div @click.stop>
|
|
83
|
+
<slot></slot>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div
|
|
89
|
+
v-if="state.loading"
|
|
90
|
+
class="wa-cluster wa-align-items-center bone-widget-loader"
|
|
91
|
+
>
|
|
92
|
+
<wa-spinner></wa-spinner>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<script setup>
|
|
98
|
+
import { reactive, onMounted, inject, ref } from "vue"
|
|
99
|
+
import { downloadUrlFor, uploadFile } from "../../../request/index.js"
|
|
100
|
+
/**
|
|
101
|
+
* Datei-Upload-Widget für Bones vom Typ `relational.tree.leaf.file.file`.
|
|
102
|
+
*
|
|
103
|
+
* Stellt eine kombinierte Klick-/Drop-Fläche bereit: ein Klick öffnet den File-Picker,
|
|
104
|
+
* ein Drop lädt die fallengelassene Datei hoch. Bei Bilddateien wird eine klickbare
|
|
105
|
+
* Vorschau angezeigt, die einen Dialog öffnet. Action-Buttons (Download, Delete)
|
|
106
|
+
* werden über `<bone-actions>` per Default-Slot eingebracht.
|
|
107
|
+
*
|
|
108
|
+
* @displayName FileBone
|
|
109
|
+
*
|
|
110
|
+
* @cssClass bone-widget-file-area - Äußerer klickbarer Container (Drop-Zone)
|
|
111
|
+
* @cssClass bone-widget-prompt - Empty-State (Icon + Text)
|
|
112
|
+
* @cssClass bone-widget-loader - Während des Uploads sichtbar
|
|
113
|
+
* @cssClass bone-widget-preview - Container für das Vorschaubild oder das Datei-Icon
|
|
114
|
+
* @cssClass has-preview - Zusatzklasse wenn ein Bild-Vorschau vorliegt
|
|
115
|
+
* @cssClass is-dragover - Wenn eine Datei über die Fläche gezogen wird
|
|
116
|
+
* @cssClass has-value - Wenn ein Wert gesetzt ist
|
|
117
|
+
* @cssClass is-loading - Während ein Upload läuft
|
|
118
|
+
* @cssClass is-readonly - Wenn der Bone readonly ist
|
|
119
|
+
*/
|
|
120
|
+
defineOptions({
|
|
121
|
+
inheritAttrs: false,
|
|
122
|
+
})
|
|
123
|
+
const props = defineProps({
|
|
124
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
125
|
+
name: String,
|
|
126
|
+
/** Aktueller Bone-Wert; Objekt mit `dest`- und `rel`-Schlüsseln oder `null`. */
|
|
127
|
+
value: [Object, String, Number, Boolean, Array],
|
|
128
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
129
|
+
index: Number,
|
|
130
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
131
|
+
lang: String,
|
|
132
|
+
/** Bone-Struktur; `public`-Flag steuert ob der Upload öffentlich ist. */
|
|
133
|
+
bone: Object,
|
|
134
|
+
/** Aktuell nicht genutzt. */
|
|
135
|
+
autofocus: Boolean,
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const emit = defineEmits([
|
|
139
|
+
/**
|
|
140
|
+
* Wird bei Datei-Upload, Datei-Drop, Löschen und beim Mount (Initialisierung) gefeuert.
|
|
141
|
+
* @arg {string} name - Bone-Name
|
|
142
|
+
* @arg {{ dest: object, rel: null }|string} value - Hochgeladenes Datei-Objekt oder `""` beim Löschen
|
|
143
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
144
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
145
|
+
*/
|
|
146
|
+
"change",
|
|
147
|
+
])
|
|
148
|
+
|
|
149
|
+
const boneState = inject("boneState")
|
|
150
|
+
const uploadinput = ref()
|
|
151
|
+
const state = reactive({
|
|
152
|
+
loading: false,
|
|
153
|
+
droparea: false,
|
|
154
|
+
previewopen: false,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
onMounted(() => {
|
|
158
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Triggert den nativen File-Picker des hidden Inputs.
|
|
163
|
+
* Nur im Empty-State aktiv — ist bereits ein Wert gesetzt, muss zuerst per
|
|
164
|
+
* Delete-Action geleert oder per Drag-and-Drop ersetzt werden.
|
|
165
|
+
*/
|
|
166
|
+
function openPicker() {
|
|
167
|
+
if (state.loading || boneState.readonly || props.value) return
|
|
168
|
+
uploadinput.value?.click()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function createBackgroundImage() {
|
|
172
|
+
console.log(props.value)
|
|
173
|
+
return downloadUrlFor(props.value, false)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function handleUpload(event) {
|
|
177
|
+
state.loading = true
|
|
178
|
+
try {
|
|
179
|
+
for (let file of event.target.files) {
|
|
180
|
+
let fileresult = await uploadFile(file, { publicUpload: !!boneState?.bonestructure["public"] })
|
|
181
|
+
console.log(fileresult)
|
|
182
|
+
uploadinput.value.value = ""
|
|
183
|
+
emit("change", props.name, { dest: fileresult, rel: null }, props.lang, props.index)
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
state.loading = false
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function handleDrop(event) {
|
|
191
|
+
state.loading = true
|
|
192
|
+
state.droparea = false
|
|
193
|
+
try {
|
|
194
|
+
for (let file of event.dataTransfer.files) {
|
|
195
|
+
let fileresult = await uploadFile(file, { publicUpload: !!boneState?.bonestructure["public"] })
|
|
196
|
+
uploadinput.value.value = ""
|
|
197
|
+
emit("change", props.name, { dest: fileresult, rel: null }, props.lang, props.index)
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
} finally {
|
|
201
|
+
state.loading = false
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
</script>
|
|
205
|
+
|
|
206
|
+
<style scoped>
|
|
207
|
+
.bone-widget-file-area {
|
|
208
|
+
position: relative;
|
|
209
|
+
padding: calc(var(--wa-space-xs) - 1px);
|
|
210
|
+
border: 2px dashed var(--wa-color-surface-border);
|
|
211
|
+
border-radius: var(--wa-border-radius-m);
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
transition: all 0.15s ease;
|
|
214
|
+
|
|
215
|
+
&.has-value {
|
|
216
|
+
border-style: none;
|
|
217
|
+
padding: 0;
|
|
218
|
+
background-color: var(--wa-color-neutral-95);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
&.is-dragover {
|
|
222
|
+
border-color: var(--wa-color-brand-50);
|
|
223
|
+
background-color: var(--wa-color-brand-95);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
&.is-readonly,
|
|
227
|
+
&.is-loading,
|
|
228
|
+
&.has-value {
|
|
229
|
+
cursor: default;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
&:hover:not(.is-readonly):not(.is-loading):not(.has-value) {
|
|
233
|
+
border-color: var(--wa-color-brand-60);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.bone-widget-prompt {
|
|
238
|
+
justify-content: center;
|
|
239
|
+
color: var(--wa-color-on-quiet);
|
|
240
|
+
/* Klicks fallen auf die outer Drop-Zone durch */
|
|
241
|
+
pointer-events: none;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Dateiname-Zeile darf schrumpfen; lange Namen werden mit Ellipsis abgekürzt */
|
|
245
|
+
.bone-widget-file-row {
|
|
246
|
+
min-width: 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.bone-widget-filename {
|
|
250
|
+
min-width: 0;
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
white-space: nowrap;
|
|
253
|
+
text-overflow: ellipsis;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.bone-widget-loader {
|
|
257
|
+
position: absolute;
|
|
258
|
+
inset: 0;
|
|
259
|
+
justify-content: center;
|
|
260
|
+
border-radius: inherit;
|
|
261
|
+
background-color: color-mix(in srgb, var(--wa-color-surface-default) 70%, transparent);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Klickbare Bildvorschau: Zoom-Cursor + leichte Skalierung beim Hover */
|
|
265
|
+
.bone-widget-preview.has-preview {
|
|
266
|
+
cursor: zoom-in;
|
|
267
|
+
transition: transform 0.15s ease;
|
|
268
|
+
|
|
269
|
+
&:hover {
|
|
270
|
+
transform: scale(1.08);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<JsonEditorVue
|
|
3
|
+
ref="editorRef"
|
|
4
|
+
v-model="value_ref"
|
|
5
|
+
:on-change="changeEvent"
|
|
6
|
+
:read-only="boneState.readonly"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
import JsonEditorVue from "json-editor-vue"
|
|
12
|
+
import { onMounted, inject, ref, watch } from "vue"
|
|
13
|
+
import { useTimeoutFn } from "@vueuse/core"
|
|
14
|
+
const value_ref = ref()
|
|
15
|
+
defineOptions({
|
|
16
|
+
inheritAttrs: false,
|
|
17
|
+
})
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
name: String,
|
|
20
|
+
value: [Object, String, Number, Boolean, Array],
|
|
21
|
+
index: Number,
|
|
22
|
+
lang: String,
|
|
23
|
+
readonly: Boolean,
|
|
24
|
+
params: Object,
|
|
25
|
+
bone: Object,
|
|
26
|
+
autofocus: Boolean,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const editorRef = ref(null)
|
|
30
|
+
const emit = defineEmits(["change"])
|
|
31
|
+
const boneState = inject("boneState")
|
|
32
|
+
function changeEvent(newVal) {
|
|
33
|
+
emit("change", props.name, newVal.json, props.lang, props.index)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onMounted(() => {
|
|
37
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
38
|
+
value_ref.value = props.value
|
|
39
|
+
})
|
|
40
|
+
watch(value_ref, (newVal, oldVal) => {
|
|
41
|
+
if (!oldVal && newVal) {
|
|
42
|
+
useTimeoutFn(() => {
|
|
43
|
+
editorRef.value.jsonEditor.collapse([], true)
|
|
44
|
+
}, 300)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style scoped>
|
|
50
|
+
.bone-widget-box {
|
|
51
|
+
width: 100%;
|
|
52
|
+
border: 1px solid var(--wa-color-neutral-60);
|
|
53
|
+
border-radius: 5px;
|
|
54
|
+
min-height: 40px;
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
57
|
+
<style>
|
|
58
|
+
:root {
|
|
59
|
+
--jse-theme-color: var(--wa-color-surface-default);
|
|
60
|
+
--jse-menu-color: var(--wa-color-neutral-30);
|
|
61
|
+
--jse-theme-color-highlight: var(--wa-color-neutral-90);
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-input
|
|
3
|
+
class="bone-widget bone-widget-key bone-widget-key-default"
|
|
4
|
+
:class="[`bone-widget-key-${name}`]"
|
|
5
|
+
disabled
|
|
6
|
+
:value="value"
|
|
7
|
+
:data-data-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
8
|
+
@change="changeEvent"
|
|
9
|
+
></wa-input>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import { reactive, onMounted, inject } from "vue"
|
|
14
|
+
/**
|
|
15
|
+
* Anzeige-Widget für den internen Datenbankschlüssel eines Eintrags (Typ `key`).
|
|
16
|
+
*
|
|
17
|
+
* Rendert ein immer deaktiviertes `<wa-input>`, da der Schlüssel nicht manuell
|
|
18
|
+
* editiert werden kann. Der Wert wird direkt aus dem Skel übernommen und dient
|
|
19
|
+
* ausschließlich zur Information. Unterstützt kein multiple und kein multilang.
|
|
20
|
+
*
|
|
21
|
+
* @displayName KeyBone
|
|
22
|
+
*
|
|
23
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
24
|
+
* @cssClass bone-widget-key - Kennzeichnet den Bone-Typ
|
|
25
|
+
* @cssClass bone-widget-key-default - Standard-Variante
|
|
26
|
+
* @cssClass bone-widget-key-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-key-key`
|
|
27
|
+
*/
|
|
28
|
+
defineOptions({
|
|
29
|
+
inheritAttrs: false,
|
|
30
|
+
})
|
|
31
|
+
const props = defineProps({
|
|
32
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
33
|
+
name: String,
|
|
34
|
+
/** Aktueller Schlüsselwert (URL-safe Base64-String). */
|
|
35
|
+
value: [Object, String, Number, Boolean, Array],
|
|
36
|
+
/** Index im Multiple-Array; nicht genutzt, da multiple nicht unterstützt wird. */
|
|
37
|
+
index: Number,
|
|
38
|
+
/** Sprach-Key; nicht genutzt, da multilang nicht unterstützt wird. */
|
|
39
|
+
lang: String,
|
|
40
|
+
/** Readonly-Flag (wird nicht ausgewertet – der Bone ist immer disabled). */
|
|
41
|
+
readonly: Boolean,
|
|
42
|
+
/** Zusätzliche Bone-Parameter. */
|
|
43
|
+
params: Object,
|
|
44
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
45
|
+
bone: Object,
|
|
46
|
+
/** Setzt den Fokus (ohne Effekt, da das Element immer disabled ist). */
|
|
47
|
+
autofocus: Boolean,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const emit = defineEmits([
|
|
51
|
+
/**
|
|
52
|
+
* Wird beim Mount (Initialisierung) und bei change gefeuert.
|
|
53
|
+
* @arg {string} name - Bone-Name
|
|
54
|
+
* @arg {string} value - Aktueller Schlüsselwert
|
|
55
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
56
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
57
|
+
*/
|
|
58
|
+
"change",
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
const boneState = inject("boneState")
|
|
62
|
+
const state = reactive({})
|
|
63
|
+
|
|
64
|
+
function changeEvent(event) {
|
|
65
|
+
emit("change", props.name, event.target.value, props.lang, props.index)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
onMounted(() => {
|
|
69
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
70
|
+
})
|
|
71
|
+
</script>
|