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,125 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-input
|
|
3
|
+
ref="numericBone"
|
|
4
|
+
class="bone-widget bone-widget-numeric bone-widget-numeric-default"
|
|
5
|
+
:class="[`bone-widget-numeric-${name}`]"
|
|
6
|
+
type="number"
|
|
7
|
+
:disabled="boneState.readonly"
|
|
8
|
+
:value="value"
|
|
9
|
+
:min="state.minAmount"
|
|
10
|
+
:max="state.maxAmount"
|
|
11
|
+
:step="state.precision"
|
|
12
|
+
:required="
|
|
13
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
14
|
+
"
|
|
15
|
+
:placeholder="state.placeholder"
|
|
16
|
+
:data-data-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
17
|
+
@change="changeEvent"
|
|
18
|
+
@keyup="changeEvent"
|
|
19
|
+
></wa-input>
|
|
20
|
+
<ul class="bone-widget-info">
|
|
21
|
+
<li v-if="state.minAmount !== -9223372036854776000 && state.minAmount">
|
|
22
|
+
{{ $t("bones.numeric.min", { val: state.minAmount }) }}
|
|
23
|
+
</li>
|
|
24
|
+
<li v-if="state.maxAmount !== 9223372036854776000 && state.maxAmount">
|
|
25
|
+
{{ $t("bones.numeric.max", { val: state.maxAmount }) }}
|
|
26
|
+
</li>
|
|
27
|
+
<li v-if="state.precision">{{ $t("bones.numeric.precision", { val: boneState.bonestructure["precision"] }) }}</li>
|
|
28
|
+
</ul>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { reactive, onMounted, computed, inject, ref, watchEffect } from "vue"
|
|
33
|
+
import { useTimeoutFn } from "@vueuse/core"
|
|
34
|
+
/**
|
|
35
|
+
* Numerisches Eingabefeld für Ganzzahlen und Dezimalwerte.
|
|
36
|
+
*
|
|
37
|
+
* Rendert ein `<wa-input type="number">` und zeigt optional Hinweise zu
|
|
38
|
+
* Mindest-/Höchstwert und Präzision an. Unterstützt Mehrsprachigkeit
|
|
39
|
+
* (`languages`) und mehrfache Werte (`multiple`). Der Schritt (`step`) wird
|
|
40
|
+
* aus `boneState.bonestructure.precision` berechnet.
|
|
41
|
+
*
|
|
42
|
+
* @displayName NumericBone
|
|
43
|
+
*
|
|
44
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
45
|
+
* @cssClass bone-widget-numeric - Kennzeichnet den Bone-Typ
|
|
46
|
+
* @cssClass bone-widget-numeric-default - Standard-Variante
|
|
47
|
+
* @cssClass bone-widget-numeric-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-numeric-amount`
|
|
48
|
+
*/
|
|
49
|
+
defineOptions({
|
|
50
|
+
inheritAttrs: false,
|
|
51
|
+
})
|
|
52
|
+
const props = defineProps({
|
|
53
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
54
|
+
name: String,
|
|
55
|
+
/** Aktueller Bone-Wert. */
|
|
56
|
+
value: [Object, String, Number, Boolean, Array],
|
|
57
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
58
|
+
index: Number,
|
|
59
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
60
|
+
lang: String,
|
|
61
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
62
|
+
bone: Object,
|
|
63
|
+
/** Setzt den Fokus nach 600 ms auf das Input-Element. */
|
|
64
|
+
autofocus: Boolean,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const emit = defineEmits([
|
|
68
|
+
/**
|
|
69
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
70
|
+
* @arg {string} name - Bone-Name
|
|
71
|
+
* @arg {string} value - Aktueller Eingabewert (als String)
|
|
72
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
73
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
74
|
+
* @arg {boolean} [valid] - Validierungsstatus; fehlt beim Init-Event
|
|
75
|
+
*/
|
|
76
|
+
"change",
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
const boneState = inject("boneState")
|
|
80
|
+
const state = reactive({
|
|
81
|
+
minAmount: computed(() => {
|
|
82
|
+
return boneState.bonestructure["minAmount"]
|
|
83
|
+
}),
|
|
84
|
+
maxAmount: computed(() => {
|
|
85
|
+
return boneState.bonestructure["maxAmount"]
|
|
86
|
+
}),
|
|
87
|
+
precision: computed(() => {
|
|
88
|
+
if (boneState.bonestructure["precision"] > 0) {
|
|
89
|
+
return parseFloat(`0.${"0".repeat(boneState.bonestructure["precision"] - 1)}1`)
|
|
90
|
+
}
|
|
91
|
+
return undefined
|
|
92
|
+
}),
|
|
93
|
+
placeholder: computed(() => {
|
|
94
|
+
if (boneState.label !== "placeholder") return undefined
|
|
95
|
+
let name = boneState?.bonestructure?.descr
|
|
96
|
+
if (boneState.bonestructure.required) {
|
|
97
|
+
name += "*"
|
|
98
|
+
}
|
|
99
|
+
return name
|
|
100
|
+
}),
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const numericBone = ref(null)
|
|
104
|
+
|
|
105
|
+
function changeEvent(event) {
|
|
106
|
+
let valid = numericBone.value.reportValidity()
|
|
107
|
+
emit("change", props.name, event.target.value, props.lang, props.index, valid)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
watchEffect(() => {
|
|
111
|
+
if (props.autofocus) {
|
|
112
|
+
if (numericBone.value && numericBone.value !== null && numericBone.value !== null) {
|
|
113
|
+
const { start } = useTimeoutFn(() => {
|
|
114
|
+
numericBone.value.focus()
|
|
115
|
+
}, 600)
|
|
116
|
+
|
|
117
|
+
start()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
onMounted(() => {
|
|
123
|
+
emit("change", props.name, "" + props.value, props.lang, props.index) //init
|
|
124
|
+
})
|
|
125
|
+
</script>
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="wa-stack wa-gap-2xs">
|
|
3
|
+
<wa-input
|
|
4
|
+
ref="passwordBone"
|
|
5
|
+
v-model="state.value1"
|
|
6
|
+
class="bone-widget bone-widget-password bone-widget-password-default"
|
|
7
|
+
:class="([`bone-widget-password-${name}`], { 'has-check': state.needsConfirm })"
|
|
8
|
+
:disabled="boneState.readonly"
|
|
9
|
+
type="password"
|
|
10
|
+
with-clear
|
|
11
|
+
password-toggle="true"
|
|
12
|
+
:placeholder="state.placeholder"
|
|
13
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
14
|
+
@change="changeEvent"
|
|
15
|
+
@wa-clear="state.value1 = ''"
|
|
16
|
+
@keyup="changeEvent"
|
|
17
|
+
>
|
|
18
|
+
<wa-icon
|
|
19
|
+
v-if="state.needsConfirm"
|
|
20
|
+
slot="end"
|
|
21
|
+
:name="state.equal && state.value1.length ? 'check' : 'xmark'"
|
|
22
|
+
></wa-icon>
|
|
23
|
+
</wa-input>
|
|
24
|
+
<wa-input
|
|
25
|
+
v-if="state.needsConfirm"
|
|
26
|
+
v-model="state.value2"
|
|
27
|
+
class="bone-widget bone-widget-boolean bone-widget-boolean-default bone-widget-boolean-repeat bone-widget-password-check"
|
|
28
|
+
:class="[`bone-widget-boolean-${name}`]"
|
|
29
|
+
type="password"
|
|
30
|
+
with-clear
|
|
31
|
+
password-toggle="true"
|
|
32
|
+
:placeholder="state.placeholder"
|
|
33
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
34
|
+
@change="changeEvent"
|
|
35
|
+
@wa-clear="state.value2 = ''"
|
|
36
|
+
@keyup="changeEvent"
|
|
37
|
+
>
|
|
38
|
+
<wa-icon
|
|
39
|
+
slot="end"
|
|
40
|
+
:name="state.equal && state.value1.length ? 'check' : 'xmark'"
|
|
41
|
+
></wa-icon>
|
|
42
|
+
</wa-input>
|
|
43
|
+
</div>
|
|
44
|
+
<ul class="bone-widget-errors">
|
|
45
|
+
<li
|
|
46
|
+
v-for="(error, index) in state.passwordInfo"
|
|
47
|
+
:key="index"
|
|
48
|
+
>
|
|
49
|
+
{{ error }}
|
|
50
|
+
</li>
|
|
51
|
+
<li
|
|
52
|
+
v-for="(error, index) in state.requiredPasswordInfo"
|
|
53
|
+
:key="index"
|
|
54
|
+
class="bone-widget-required-info"
|
|
55
|
+
>
|
|
56
|
+
{{ error }}
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup>
|
|
62
|
+
import { reactive, onMounted, computed, inject, watch, ref, watchEffect } from "vue"
|
|
63
|
+
import { useTimeoutFn } from "@vueuse/core"
|
|
64
|
+
/**
|
|
65
|
+
* Passwort-Eingabefeld mit Wiederholung und Stärke-Validierung.
|
|
66
|
+
*
|
|
67
|
+
* Rendert `<wa-input type="password">` — mit Wiederholungs-Feld, WENN der Bone
|
|
68
|
+
* überhaupt Passwort-Tests mitbringt (`bonestructure.tests`). Ein Login-Formular
|
|
69
|
+
* liefert serverseitig einen rohen Passwort-Bone ohne Tests
|
|
70
|
+
* (`PasswordBone(raw=True, tests=(), test_threshold=0)`); dort wäre eine
|
|
71
|
+
* Wiederholung falsch — man tippt ein bestehendes Passwort ein, man setzt keins.
|
|
72
|
+
* Im Readonly-Modus entfällt die Wiederholung ebenfalls. Passwort-Tests
|
|
73
|
+
* werden über `boneState.bonestructure.tests` (Array aus `[regex, message, required]`
|
|
74
|
+
* Tupeln) konfiguriert. `boneState.bonestructure.test_threshold` legt fest,
|
|
75
|
+
* wie viele optionale Tests fehlschlagen dürfen.
|
|
76
|
+
*
|
|
77
|
+
* @displayName PasswordBone
|
|
78
|
+
*
|
|
79
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
80
|
+
* @cssClass bone-widget-password - Kennzeichnet den Bone-Typ
|
|
81
|
+
* @cssClass bone-widget-password-default - Standard-Variante
|
|
82
|
+
* @cssClass bone-widget-password-{name} - Dynamisch gesetzt auf den Bone-Namen
|
|
83
|
+
* @cssClass has-check - Gesetzt wenn nicht readonly; entfernt border-bottom-right-radius für nahtlosen Übergang zum Wiederholungs-Feld
|
|
84
|
+
*/
|
|
85
|
+
defineOptions({
|
|
86
|
+
inheritAttrs: false,
|
|
87
|
+
})
|
|
88
|
+
const props = defineProps({
|
|
89
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
90
|
+
name: String,
|
|
91
|
+
/** Aktueller Bone-Wert (wird in `state.value1` gespiegelt). */
|
|
92
|
+
value: [Object, String, Number, Boolean, Array],
|
|
93
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
94
|
+
index: Number,
|
|
95
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
96
|
+
lang: String,
|
|
97
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
98
|
+
bone: Object,
|
|
99
|
+
/** Setzt den Fokus nach 600 ms auf das erste Passwort-Feld. */
|
|
100
|
+
autofocus: Boolean,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const emit = defineEmits([
|
|
104
|
+
/**
|
|
105
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
106
|
+
* @arg {string} name - Bone-Name
|
|
107
|
+
* @arg {string} value - Aktueller Passwortwert (nur wenn beide Felder übereinstimmen und Tests bestanden)
|
|
108
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
109
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
110
|
+
* @arg {boolean} valid - `true` wenn Passwort gültig (Tests bestanden + Felder gleich)
|
|
111
|
+
*/
|
|
112
|
+
"change",
|
|
113
|
+
])
|
|
114
|
+
|
|
115
|
+
const boneState = inject("boneState")
|
|
116
|
+
const state = reactive({
|
|
117
|
+
value1: "",
|
|
118
|
+
value2: null,
|
|
119
|
+
equal: false,
|
|
120
|
+
// Wiederholung nur, wenn der Bone Tests mitbringt (= ein Passwort wird
|
|
121
|
+
// GESETZT). Beim Login liefert der Server einen rohen Bone ohne Tests.
|
|
122
|
+
needsConfirm: computed(() => !boneState.readonly && (boneState?.bonestructure?.["tests"]?.length ?? 0) > 0),
|
|
123
|
+
passwordInfo: [],
|
|
124
|
+
requiredPasswordInfo: [],
|
|
125
|
+
placeholder: computed(() => {
|
|
126
|
+
if (boneState.label !== "placeholder") return undefined
|
|
127
|
+
let name = boneState?.bonestructure?.descr
|
|
128
|
+
if (boneState.bonestructure.required) {
|
|
129
|
+
name += "*"
|
|
130
|
+
}
|
|
131
|
+
return name
|
|
132
|
+
}),
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const passwordBone = ref(null)
|
|
136
|
+
|
|
137
|
+
function changeEvent(event) {
|
|
138
|
+
testPassword(state.value1)
|
|
139
|
+
|
|
140
|
+
if (!state.needsConfirm || state.value1 === state.value2) {
|
|
141
|
+
state.equal = true
|
|
142
|
+
} else {
|
|
143
|
+
state.equal = false
|
|
144
|
+
// dont try to update if not equal
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// boneState.bonestructure["test_threshold"] = 2 *needs to be removed by the look cuz overridees server settings
|
|
148
|
+
if (
|
|
149
|
+
state.requiredPasswordInfo.length === 0 &&
|
|
150
|
+
state.passwordInfo.length - state.requiredPasswordInfo.length <= boneState.bonestructure["test_threshold"]
|
|
151
|
+
) {
|
|
152
|
+
emit("change", props.name, state.value1, props.lang, props.index, true)
|
|
153
|
+
} else {
|
|
154
|
+
emit("change", props.name, state.value1, props.lang, props.index, false)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onMounted(() => {
|
|
159
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
function testPassword(password) {
|
|
163
|
+
state.passwordInfo = []
|
|
164
|
+
state.requiredPasswordInfo = []
|
|
165
|
+
for (const test of boneState.bonestructure["tests"]) {
|
|
166
|
+
if (!new RegExp(test[0]).test(password)) {
|
|
167
|
+
if (test[2]) {
|
|
168
|
+
state.requiredPasswordInfo.push(test[1])
|
|
169
|
+
} else {
|
|
170
|
+
state.passwordInfo.push(test[1])
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (state.needsConfirm && !state.equal) {
|
|
175
|
+
state.requiredPasswordInfo.push("Die eingegebenen Passwörter stimmen nicht überein.")
|
|
176
|
+
}
|
|
177
|
+
if (!state.value1) {
|
|
178
|
+
state.requiredPasswordInfo.push("Das eingegebene Passwort ist leer.")
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
watchEffect(() => {
|
|
183
|
+
if (props.autofocus) {
|
|
184
|
+
if (passwordBone.value && passwordBone.value !== null && passwordBone.value !== null) {
|
|
185
|
+
const { start } = useTimeoutFn(() => {
|
|
186
|
+
passwordBone.value.focus()
|
|
187
|
+
}, 600)
|
|
188
|
+
|
|
189
|
+
start()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
watch(
|
|
195
|
+
() => props.value,
|
|
196
|
+
(newVal, oldVal) => {
|
|
197
|
+
state.value1 = newVal
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<style scoped>
|
|
203
|
+
.bone-widget-errors {
|
|
204
|
+
display: grid;
|
|
205
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
206
|
+
grid-gap: 2px 7px;
|
|
207
|
+
margin-top: var(--wa-space-xs);
|
|
208
|
+
font-size: 0.7em;
|
|
209
|
+
font-weight: bold;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.bone-widget-required-info {
|
|
213
|
+
color: var(--wa-color-danger-50);
|
|
214
|
+
}
|
|
215
|
+
</style>
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-spinner v-if="state.loading"></wa-spinner>
|
|
3
|
+
<div
|
|
4
|
+
v-else
|
|
5
|
+
class="wa-flank wa-gap-xs bone-widget-input-wrapper"
|
|
6
|
+
>
|
|
7
|
+
<wa-select
|
|
8
|
+
:value="state.dialCode"
|
|
9
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
10
|
+
@change="handleSelect"
|
|
11
|
+
>
|
|
12
|
+
<wa-option
|
|
13
|
+
v-for="country in state.countryInfo"
|
|
14
|
+
:key="country.currentCode"
|
|
15
|
+
:value="country.dialCode"
|
|
16
|
+
>
|
|
17
|
+
{{ country.emoji }}
|
|
18
|
+
</wa-option>
|
|
19
|
+
</wa-select>
|
|
20
|
+
<wa-input
|
|
21
|
+
ref="phoneBone"
|
|
22
|
+
type="tel"
|
|
23
|
+
:disabled="boneState.readonly"
|
|
24
|
+
:value="Utils.unescape(state.value)"
|
|
25
|
+
:required="
|
|
26
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
27
|
+
"
|
|
28
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
29
|
+
@change="changeEvent"
|
|
30
|
+
></wa-input>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup>
|
|
35
|
+
import { reactive, onMounted, inject, computed, watchEffect, ref } from "vue"
|
|
36
|
+
import { useTimeoutFn } from "@vueuse/core"
|
|
37
|
+
import Utils from "../utils"
|
|
38
|
+
import jsonData from "../country_information.json"
|
|
39
|
+
import parsePhoneNumber from "libphonenumber-js/min"
|
|
40
|
+
import { AsYouType, validatePhoneNumberLength } from "libphonenumber-js/min"
|
|
41
|
+
/**
|
|
42
|
+
* Telefonnummer-Eingabefeld mit Ländervorwahl-Auswahl.
|
|
43
|
+
*
|
|
44
|
+
* Rendert ein `<wa-select>` für die Ländervorwahl (Flaggen-Emoji) und ein
|
|
45
|
+
* `<wa-input type="tel">` für die eigentliche Nummer. Die Nummer wird beim
|
|
46
|
+
* Tippen über `libphonenumber-js` formatiert und normalisiert. Der
|
|
47
|
+
* Standard-Ländercode kann über `boneState.bonestructure.default_country_code`
|
|
48
|
+
* gesteuert werden (Standard: `+49`).
|
|
49
|
+
*
|
|
50
|
+
* @displayName PhoneBone
|
|
51
|
+
*
|
|
52
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets (auf dem inneren `<wa-input>`)
|
|
53
|
+
* @cssClass bone-widget-phone - Kennzeichnet den Bone-Typ (auf dem inneren `<wa-input>`, via Wrapper-Klasse)
|
|
54
|
+
*/
|
|
55
|
+
defineOptions({
|
|
56
|
+
inheritAttrs: false,
|
|
57
|
+
})
|
|
58
|
+
const props = defineProps({
|
|
59
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
60
|
+
name: String,
|
|
61
|
+
/** Aktueller Bone-Wert als internationale Telefonnummer, z.B. `"+49 89 12345678"`. */
|
|
62
|
+
value: [Object, String, Number, Boolean, Array],
|
|
63
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
64
|
+
index: Number,
|
|
65
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
66
|
+
lang: String,
|
|
67
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
68
|
+
bone: Object,
|
|
69
|
+
/** Setzt den Fokus nach 600 ms auf das Tel-Input-Element. */
|
|
70
|
+
autofocus: Boolean,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const emit = defineEmits([
|
|
74
|
+
/**
|
|
75
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
76
|
+
* @arg {string} name - Bone-Name
|
|
77
|
+
* @arg {string} value - Formatierte internationale Telefonnummer
|
|
78
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
79
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
80
|
+
*/
|
|
81
|
+
"change",
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
const boneState = inject("boneState")
|
|
85
|
+
const state = reactive({
|
|
86
|
+
value: computed(() =>
|
|
87
|
+
props.value
|
|
88
|
+
? parsePhoneNumber(state.reference, getCountry(state.dialCode)).formatInternational()
|
|
89
|
+
: state.dialCode + ""
|
|
90
|
+
),
|
|
91
|
+
loading: false,
|
|
92
|
+
reference: computed(() => parsePhoneNumber(props.value, getCountry(state.dialCode)).formatNational()),
|
|
93
|
+
country: computed(() => getCountry(state.dialCode)),
|
|
94
|
+
dialCode: "",
|
|
95
|
+
countryInfo: [],
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const countryInformation = ref(jsonData)
|
|
99
|
+
const phoneBone = ref(null)
|
|
100
|
+
|
|
101
|
+
function changeEvent(event) {
|
|
102
|
+
if (!event.target.value) {
|
|
103
|
+
emit("change", props.name, "", props.lang, props.index)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
if (event.target.value.length > 4) {
|
|
107
|
+
const number = parsePhoneNumber(event.target.value, state.country)
|
|
108
|
+
const valueFormatter = new AsYouType()
|
|
109
|
+
|
|
110
|
+
let value = valueFormatter.input(number ? number.number : event.target.value)
|
|
111
|
+
state.dialCode = "+" + number?.countryCallingCode
|
|
112
|
+
emit("change", props.name, value, props.lang, props.index)
|
|
113
|
+
return
|
|
114
|
+
} else return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
watchEffect(() => {
|
|
118
|
+
if (phoneBone.value && phoneBone.value !== null && phoneBone.value !== null) {
|
|
119
|
+
if (props.autofocus) {
|
|
120
|
+
const { start } = useTimeoutFn(() => {
|
|
121
|
+
phoneBone.value.focus()
|
|
122
|
+
}, 600)
|
|
123
|
+
|
|
124
|
+
start()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
function handleSelect(e) {
|
|
130
|
+
state.dialCode = e.target.value
|
|
131
|
+
changeEvent({ target: { value: state.value } })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getCountry(dialCode) {
|
|
135
|
+
let result = state.countryInfo.filter((country) => country.dialCode === dialCode)
|
|
136
|
+
result = result[0] ? result[0].code : "DE"
|
|
137
|
+
return result
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getDefaultCode() {
|
|
141
|
+
let defaultDialCode = boneState.bonestructure.default_country_code
|
|
142
|
+
? boneState.bonestructure.default_country_code
|
|
143
|
+
: "+49"
|
|
144
|
+
|
|
145
|
+
state.dialCode = defaultDialCode
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getCode(value) {
|
|
149
|
+
if (!value) return boneState.bonestructure.default_country_code ? boneState.bonestructure.default_country_code : "+49"
|
|
150
|
+
return "+" + parsePhoneNumber(value).countryCallingCode
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
onMounted(() => {
|
|
154
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
155
|
+
state.loading = true
|
|
156
|
+
countryInformation.value.forEach((country) => {
|
|
157
|
+
let temp = {}
|
|
158
|
+
temp.name = country.name
|
|
159
|
+
temp.code = country.code
|
|
160
|
+
temp.emoji = country.emoji
|
|
161
|
+
temp.dialCode = country.dialCodes ? country.dialCodes[0] : ""
|
|
162
|
+
state.countryInfo.push(temp)
|
|
163
|
+
})
|
|
164
|
+
state.loading = false
|
|
165
|
+
state.dialCode = getCode(props.value)
|
|
166
|
+
// let available_countries = []
|
|
167
|
+
})
|
|
168
|
+
</script>
|
|
169
|
+
<style scoped>
|
|
170
|
+
wa-select {
|
|
171
|
+
width: 6rem;
|
|
172
|
+
}
|
|
173
|
+
</style>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wa-textarea
|
|
3
|
+
v-if="boneState.bonestructure.type === 'raw.json'"
|
|
4
|
+
class="bone-widget bone-widget-raw bone-widget-raw-default"
|
|
5
|
+
:class="[`bone-widget-raw-${name}`]"
|
|
6
|
+
:disabled="boneState?.readonly"
|
|
7
|
+
:value="typeof value === 'object' ? JSON.stringify(value) : value"
|
|
8
|
+
:required="
|
|
9
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
10
|
+
"
|
|
11
|
+
:placeholder="state.placeholder"
|
|
12
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
13
|
+
@input="changeEvent"
|
|
14
|
+
></wa-textarea>
|
|
15
|
+
<wa-textarea
|
|
16
|
+
v-else
|
|
17
|
+
class="bone-widget bone-widget-raw bone-widget-raw-default"
|
|
18
|
+
:class="[`bone-widget-raw-${name}`]"
|
|
19
|
+
:disabled="boneState?.readonly"
|
|
20
|
+
:value="value"
|
|
21
|
+
:required="
|
|
22
|
+
boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
|
|
23
|
+
"
|
|
24
|
+
:placeholder="state.placeholder"
|
|
25
|
+
:data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
|
|
26
|
+
@input="changeEvent"
|
|
27
|
+
></wa-textarea>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup>
|
|
31
|
+
import { reactive, onMounted, inject, computed } from "vue"
|
|
32
|
+
/**
|
|
33
|
+
* Mehrzeiliges Freitext- oder JSON-Eingabefeld für Rohdaten.
|
|
34
|
+
*
|
|
35
|
+
* Rendert ein `<wa-textarea>`. Wenn `boneState.bonestructure.type === 'raw.json'`
|
|
36
|
+
* wird der Wert beim Anzeigen per `JSON.stringify` serialisiert. Unterstützt
|
|
37
|
+
* Mehrsprachigkeit (`languages`) und Mehrfachwerte (`multiple`).
|
|
38
|
+
*
|
|
39
|
+
* @displayName RawBone
|
|
40
|
+
*
|
|
41
|
+
* @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
|
|
42
|
+
* @cssClass bone-widget-raw - Kennzeichnet den Bone-Typ
|
|
43
|
+
* @cssClass bone-widget-raw-default - Standard-Variante
|
|
44
|
+
* @cssClass bone-widget-raw-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-raw-content`
|
|
45
|
+
*/
|
|
46
|
+
defineOptions({
|
|
47
|
+
inheritAttrs: false,
|
|
48
|
+
})
|
|
49
|
+
const props = defineProps({
|
|
50
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
51
|
+
name: String,
|
|
52
|
+
/** Aktueller Bone-Wert. Bei `raw.json` wird ein Objekt per `JSON.stringify` dargestellt. */
|
|
53
|
+
value: [Object, String, Number, Boolean, Array],
|
|
54
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
55
|
+
index: Number,
|
|
56
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
57
|
+
lang: String,
|
|
58
|
+
/** Bone-Struktur (aktuell nicht aktiv genutzt). */
|
|
59
|
+
bone: Object,
|
|
60
|
+
/** Setzt den Fokus nach dem Mount auf das Textarea-Element. */
|
|
61
|
+
autofocus: Boolean,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const emit = defineEmits([
|
|
65
|
+
/**
|
|
66
|
+
* Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
|
|
67
|
+
* @arg {string} name - Bone-Name
|
|
68
|
+
* @arg {string} value - Aktueller Eingabewert
|
|
69
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
70
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
71
|
+
*/
|
|
72
|
+
"change",
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
const boneState = inject("boneState")
|
|
76
|
+
const state = reactive({
|
|
77
|
+
placeholder: computed(() => {
|
|
78
|
+
if (boneState.label !== "placeholder") return undefined
|
|
79
|
+
let name = boneState?.bonestructure?.descr
|
|
80
|
+
if (boneState.bonestructure.required) {
|
|
81
|
+
name += "*"
|
|
82
|
+
}
|
|
83
|
+
return name
|
|
84
|
+
}),
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
function changeEvent(event) {
|
|
88
|
+
emit("change", props.name, event.target.value, props.lang, props.index)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onMounted(() => {
|
|
92
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
93
|
+
})
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Wrapper_nested
|
|
3
|
+
:value="value"
|
|
4
|
+
:name="name"
|
|
5
|
+
:index="index"
|
|
6
|
+
:lang="lang"
|
|
7
|
+
:bone="bone"
|
|
8
|
+
:disabled="bone['readonly']"
|
|
9
|
+
@change="changeEvent"
|
|
10
|
+
></Wrapper_nested>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
import { reactive, onMounted, inject, computed, getCurrentInstance } from "vue"
|
|
15
|
+
import Wrapper_nested from "../BoneNestedWrapper.vue"
|
|
16
|
+
/**
|
|
17
|
+
* Record-Widget für Bones vom Typ `record`.
|
|
18
|
+
*
|
|
19
|
+
* Delegiert die gesamte Darstellung an `Wrapper_nested`, der die Sub-Bones
|
|
20
|
+
* aus `bone.using` rendert. Der Record-Wert ist ein Objekt dessen Schlüssel
|
|
21
|
+
* den Sub-Bone-Namen entsprechen. Bei `multiple: true` ist der Wert ein Array
|
|
22
|
+
* dieser Objekte.
|
|
23
|
+
*
|
|
24
|
+
* @displayName RecordBone
|
|
25
|
+
*/
|
|
26
|
+
defineOptions({
|
|
27
|
+
inheritAttrs: false,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const emit = defineEmits([
|
|
31
|
+
/**
|
|
32
|
+
* Wird weitergeleitet wenn `Wrapper_nested` einen Wert ändert oder beim Mount (Initialisierung).
|
|
33
|
+
* @arg {string} name - Bone-Name
|
|
34
|
+
* @arg {object} value - Aktueller Record-Wert (Objekt mit Sub-Bone-Werten)
|
|
35
|
+
* @arg {string|undefined} lang - Sprach-Key
|
|
36
|
+
* @arg {number|undefined} index - Index im Multiple-Array
|
|
37
|
+
*/
|
|
38
|
+
"change",
|
|
39
|
+
])
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
/** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
|
|
42
|
+
name: String,
|
|
43
|
+
/** Aktueller Record-Wert; Objekt mit Sub-Bone-Werten oder Array davon bei `multiple`. */
|
|
44
|
+
value: null,
|
|
45
|
+
/** Index im Multiple-Array; `undefined` bei Einzelwert. */
|
|
46
|
+
index: Number,
|
|
47
|
+
/** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
|
|
48
|
+
lang: String,
|
|
49
|
+
/** Bone-Struktur; `using` enthält die Sub-Bone-Definitionen. */
|
|
50
|
+
bone: Object,
|
|
51
|
+
/** Aktuell nicht genutzt. */
|
|
52
|
+
autofocus: Boolean,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const state = reactive({})
|
|
56
|
+
|
|
57
|
+
function changeEvent(data) {
|
|
58
|
+
emit("change", data["name"], data["value"], data["lang"], data["index"])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onMounted(() => {
|
|
62
|
+
emit("change", props.name, props.value, props.lang, props.index) //init
|
|
63
|
+
})
|
|
64
|
+
</script>
|