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.
Files changed (114) hide show
  1. package/README.md +79 -0
  2. package/index.d.ts +351 -0
  3. package/index.js +46 -0
  4. package/package.json +84 -0
  5. package/src/components/Browser/Browser.vue +496 -0
  6. package/src/components/Browser/TreeNav.vue +408 -0
  7. package/src/components/Browser/ViurBrowser.d.vue.ts +64 -0
  8. package/src/components/Browser/ViurBrowser.vue +271 -0
  9. package/src/components/Browser/dragSession.ts +134 -0
  10. package/src/components/Browser/index.ts +14 -0
  11. package/src/components/Browser/types.ts +30 -0
  12. package/src/components/DataTable/ColumnFilterPanel.vue +225 -0
  13. package/src/components/DataTable/DataTable.vue +3155 -0
  14. package/src/components/DataTable/ViurDataTable.d.vue.ts +72 -0
  15. package/src/components/DataTable/ViurDataTable.vue +331 -0
  16. package/src/components/DataTable/columns.ts +400 -0
  17. package/src/components/DataTable/index.ts +60 -0
  18. package/src/components/DataTable/rows.ts +446 -0
  19. package/src/components/DataTable/types.ts +267 -0
  20. package/src/components/DataTable/useColumnLayout.ts +294 -0
  21. package/src/components/DataTable/useGridNavigation.ts +193 -0
  22. package/src/components/DataTable/useVirtualRows.ts +126 -0
  23. package/src/components/DataTable/viurColumns.ts +330 -0
  24. package/src/forms/ViForm.d.vue.ts +106 -0
  25. package/src/forms/ViForm.vue +592 -0
  26. package/src/forms/ViFormCategory.vue +57 -0
  27. package/src/forms/bones/Bone.vue +1023 -0
  28. package/src/forms/bones/BoneActions.vue +127 -0
  29. package/src/forms/bones/BoneLabel.vue +122 -0
  30. package/src/forms/bones/BoneMultipleList.vue +91 -0
  31. package/src/forms/bones/BoneMultipleWrapper.vue +105 -0
  32. package/src/forms/bones/BoneNestedWrapper.vue +155 -0
  33. package/src/forms/bones/BoneSingleEntry.vue +51 -0
  34. package/src/forms/bones/FilterBone.vue +694 -0
  35. package/src/forms/bones/actionbar/defaultBar.vue +98 -0
  36. package/src/forms/bones/actionbar/fileBar.vue +165 -0
  37. package/src/forms/bones/actionbar/relationalBar.vue +125 -0
  38. package/src/forms/bones/actions/ActionDialog.vue +70 -0
  39. package/src/forms/bones/actions/Debug.vue +106 -0
  40. package/src/forms/bones/actions/Delete.vue +13 -0
  41. package/src/forms/bones/actions/DescribeImage.vue +201 -0
  42. package/src/forms/bones/actions/Download.vue +27 -0
  43. package/src/forms/bones/actions/Placeholder.vue +14 -0
  44. package/src/forms/bones/actions/Translation.vue +238 -0
  45. package/src/forms/bones/actions/index.js +280 -0
  46. package/src/forms/bones/bone.d.ts +45 -0
  47. package/src/forms/bones/country_information.json +779 -0
  48. package/src/forms/bones/index.js +225 -0
  49. package/src/forms/bones/uploadAdapter.js +46 -0
  50. package/src/forms/bones/utils.js +284 -0
  51. package/src/forms/bones/view/base_item.vue +35 -0
  52. package/src/forms/bones/view/boneLogic.js +202 -0
  53. package/src/forms/bones/view/index.js +5 -0
  54. package/src/forms/bones/widgets/booleanBone.vue +83 -0
  55. package/src/forms/bones/widgets/booleanBoneChoose.vue +57 -0
  56. package/src/forms/bones/widgets/booleanBoneSelect.vue +58 -0
  57. package/src/forms/bones/widgets/codeBone.vue +214 -0
  58. package/src/forms/bones/widgets/colorBone.vue +75 -0
  59. package/src/forms/bones/widgets/dateBone.vue +114 -0
  60. package/src/forms/bones/widgets/emailBone.vue +100 -0
  61. package/src/forms/bones/widgets/fileBone.vue +273 -0
  62. package/src/forms/bones/widgets/jsonBone.vue +63 -0
  63. package/src/forms/bones/widgets/keyBone.vue +71 -0
  64. package/src/forms/bones/widgets/numericBone.vue +125 -0
  65. package/src/forms/bones/widgets/passwordBone.vue +215 -0
  66. package/src/forms/bones/widgets/phoneBone.vue +173 -0
  67. package/src/forms/bones/widgets/rawBone.vue +94 -0
  68. package/src/forms/bones/widgets/recordBone.vue +64 -0
  69. package/src/forms/bones/widgets/relationalBone.vue +164 -0
  70. package/src/forms/bones/widgets/relationalBoneSelect.vue +120 -0
  71. package/src/forms/bones/widgets/selectBone.vue +122 -0
  72. package/src/forms/bones/widgets/selectBoneChoose.vue +104 -0
  73. package/src/forms/bones/widgets/spatialBone.vue +111 -0
  74. package/src/forms/bones/widgets/stringBone.vue +139 -0
  75. package/src/forms/bones/widgets/textBone.vue +641 -0
  76. package/src/forms/install.d.ts +4 -0
  77. package/src/forms/install.js +43 -0
  78. package/src/forms/layouts/LayloutList2Columns.vue +57 -0
  79. package/src/forms/layouts/LayoutCategory.vue +38 -0
  80. package/src/forms/layouts/LayoutList.vue +31 -0
  81. package/src/forms/utils.js +601 -0
  82. package/src/public/assets/icons/fontawesome-manifest.json +8 -0
  83. package/src/public/assets/icons/regular/file.svg +1 -0
  84. package/src/public/assets/icons/solid/arrows-left-right.svg +1 -0
  85. package/src/public/assets/icons/solid/bug.svg +1 -0
  86. package/src/public/assets/icons/solid/check.svg +1 -0
  87. package/src/public/assets/icons/solid/circle-info.svg +1 -0
  88. package/src/public/assets/icons/solid/compress.svg +1 -0
  89. package/src/public/assets/icons/solid/download.svg +1 -0
  90. package/src/public/assets/icons/solid/expand.svg +1 -0
  91. package/src/public/assets/icons/solid/grip-vertical.svg +1 -0
  92. package/src/public/assets/icons/solid/plus.svg +1 -0
  93. package/src/public/assets/icons/solid/question.svg +1 -0
  94. package/src/public/assets/icons/solid/triangle-exclamation.svg +1 -0
  95. package/src/public/assets/icons/solid/upload.svg +1 -0
  96. package/src/public/assets/icons/solid/xmark.svg +1 -0
  97. package/src/request/cache.js +183 -0
  98. package/src/request/configStore.js +516 -0
  99. package/src/request/envelope.js +211 -0
  100. package/src/request/index.js +35 -0
  101. package/src/request/messageStore.js +201 -0
  102. package/src/request/request.js +752 -0
  103. package/src/request/skel.js +90 -0
  104. package/src/theme/colorStore.js +168 -0
  105. package/src/translations/de.js +178 -0
  106. package/src/translations/en.js +177 -0
  107. package/src/user/LoginScreen.d.vue.ts +54 -0
  108. package/src/user/LoginScreen.vue +627 -0
  109. package/src/user/google.js +75 -0
  110. package/src/user/index.js +3 -0
  111. package/src/user/store.d.ts +59 -0
  112. package/src/user/store.js +183 -0
  113. package/src/webawesomeCompat.css +72 -0
  114. 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>