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,202 @@
1
+ import Utils from "../utils"
2
+ import { computed, reactive } from "vue"
3
+
4
+ export const languageMap = [
5
+ { lang: "en", country: "US", label: "English", flag: "\uD83C\uDDEC\uD83C\uDDE7" },
6
+ { lang: "de", country: "DE", label: "Deutsch", flag: "\ud83c\udde9\ud83c\uddea" },
7
+ { lang: "ch", country: "CH", label: "Schweiz", flag: "\ud83c\udde8\ud83c\udded" },
8
+ { lang: "fr", country: "FR", label: "Français", flag: "\ud83c\uddeb\ud83c\uddf7" },
9
+ { lang: "es", country: "ES", label: "Español", flag: "\ud83c\uddea\ud83c\uddf8" },
10
+ { lang: "pt", country: "BR", label: "Português", flag: "\ud83c\udde7\ud83c\uddf7" },
11
+ { lang: "it", country: "IT", label: "Italiano", flag: "\ud83c\uddee\ud83c\uddf9" },
12
+ { lang: "nl", country: "NL", label: "Nederlands", flag: "\ud83c\uddf3\ud83c\uddf1" },
13
+ { lang: "pl", country: "PL", label: "Polski", flag: "\ud83c\uddf5\ud83c\uddf1" },
14
+ { lang: "ru", country: "RU", label: "Русский", flag: "\ud83c\uddf7\ud83c\uddfa" },
15
+ { lang: "tr", country: "TR", label: "Türkçe", flag: "\ud83c\uddf9\ud83c\uddf7" },
16
+ { lang: "zh", country: "CN", label: "中文", flag: "\ud83c\udde8\ud83c\uddf3" },
17
+ { lang: "ja", country: "JP", label: "日本語", flag: "\ud83c\uddef\ud83c\uddf5" },
18
+ { lang: "ko", country: "KR", label: "한국어", flag: "\ud83c\uddf0\ud83c\uddf7" },
19
+ { lang: "ar", country: "SA", label: "العربية", flag: "\ud83c\uddf8\ud83c\udde6" },
20
+ { lang: "hi", country: "IN", label: "हिन्दी", flag: "\ud83c\uddee\ud83c\uddf3" },
21
+ { lang: "sv", country: "SE", label: "Svenska", flag: "\ud83c\uddf8\ud83c\uddea" },
22
+ { lang: "no", country: "NO", label: "Norsk", flag: "\ud83c\uddf3\ud83c\uddf4" },
23
+ { lang: "da", country: "DK", label: "Dansk", flag: "\ud83c\udde9\ud83c\uddf0" },
24
+ { lang: "fi", country: "FI", label: "Suomi", flag: "\ud83c\uddeb\ud83c\uddee" },
25
+ { lang: "cs", country: "CZ", label: "Čeština", flag: "\ud83c\udde8\ud83c\uddff" },
26
+ ]
27
+
28
+ const boneLogic = (skel, structure, showflags = false, selectedLang = null, t = null) => {
29
+ let bones_state = reactive({
30
+ skel: computed(() => {
31
+ if (skel && skel.constructor.name === "RefImpl") {
32
+ return skel.value
33
+ }
34
+ return skel
35
+ }),
36
+ structure: computed(() => {
37
+ if (structure.constructor.name === "RefImpl") {
38
+ return structure.value
39
+ }
40
+ return structure
41
+ }),
42
+ })
43
+
44
+ function getBoneValue(boneName, options = null, skel = null) {
45
+ if (skel === null) {
46
+ skel = bones_state.skel
47
+ }
48
+ if (skel === null) {
49
+ return "-"
50
+ }
51
+ if (Utils.objectEmpty(skel) || Utils.objectEmpty(bones_state.structure)) {
52
+ return "-"
53
+ }
54
+ let languages = hasLanguages(boneName)
55
+ if (languages) {
56
+ let values = []
57
+
58
+ for (let lang of languages) {
59
+ if (isMultiple(boneName)) {
60
+ if (selectedLang) {
61
+ if (selectedLang === lang) {
62
+ values.push(langPrefix(lang, renderMultipleValue(boneName, options, skel, lang)))
63
+ }
64
+ } else {
65
+ values.push(langPrefix(lang, renderMultipleValue(boneName, options, skel, lang)))
66
+ }
67
+ } else {
68
+ if (selectedLang) {
69
+ if (selectedLang === lang) {
70
+ values.push(langPrefix(lang, renderSingleValue(boneName, options, null, skel, lang)))
71
+ }
72
+ } else {
73
+ values.push(langPrefix(lang, renderSingleValue(boneName, options, null, skel, lang)))
74
+ }
75
+ }
76
+ }
77
+ return values.join(", ")
78
+ } else {
79
+ if (isMultiple(boneName)) {
80
+ return renderMultipleValue(boneName, options, skel, null)
81
+ } else {
82
+ return renderSingleValue(boneName, options, null, skel, null)
83
+ }
84
+ }
85
+ }
86
+
87
+ function hasLanguages(boneName) {
88
+ return bones_state.structure[boneName] && bones_state.structure[boneName]["languages"]
89
+ ? bones_state.structure[boneName]["languages"]
90
+ : false
91
+ }
92
+
93
+ function isMultiple(boneName) {
94
+ return bones_state.structure[boneName] && bones_state.structure[boneName]["multiple"]
95
+ ? !!bones_state.structure[boneName]["multiple"]
96
+ : false
97
+ }
98
+
99
+ function langPrefix(lang, value) {
100
+ if (showflags) {
101
+ const entry = languageMap.find(({ lang: l }) => l === lang)
102
+ if (entry) {
103
+ value = `${entry.flag} ${value}`
104
+ }
105
+ }
106
+ return value
107
+ }
108
+
109
+ function renderSingleValue(boneName, options, value = null, skel = null, lang = null) {
110
+ let boneStructure = bones_state.structure[boneName]
111
+ if (boneStructure == null) {
112
+ return "-"
113
+ }
114
+ if (!boneStructure["multiple"]) {
115
+ if (skel === null) {
116
+ skel = bones_state.skel
117
+ }
118
+ value = skel[boneName]
119
+ if (lang) {
120
+ value = skel[boneName][lang]
121
+ }
122
+ }
123
+
124
+ if (value === "" || value === null || value === undefined || !boneStructure) {
125
+ return "-"
126
+ } else if (boneStructure["type"] === "select" || boneStructure["type"].startsWith("select.")) {
127
+ // extract description from tuple
128
+ if (boneStructure["values"].length > 0) {
129
+ let returnValue = boneStructure["values"]?.filter((x) => x[0].toString() === value.toString())?.[0]?.[1]
130
+ return returnValue ? returnValue : "-"
131
+ }
132
+ if (Object.keys(boneStructure["values"]).length > 0) {
133
+ let returnValue = Object.entries(boneStructure["values"])?.filter(
134
+ (x) => x[0].toString() === value.toString()
135
+ )?.[0]?.[1]
136
+ return returnValue ? returnValue : "-"
137
+ }
138
+
139
+ return "-"
140
+ } else if (boneStructure["type"] === "date" || boneStructure["type"].startsWith("date.")) {
141
+ if (options && options === "time") {
142
+ return new Date(value).toLocaleTimeString()
143
+ } else if (options && options === "date") {
144
+ return new Date(value).toLocaleDateString()
145
+ }
146
+ return new Date(value).toLocaleString()
147
+ } else if (boneStructure["type"] === "relational" || boneStructure["type"].startsWith("relational.")) {
148
+ return Utils.formatString(boneStructure["format"], { ...value, lang: lang })
149
+ } else if (boneStructure["type"] === "hierarchy" || boneStructure["type"].startsWith("hierarchy.")) {
150
+ return Utils.formatString(boneStructure["format"], { ...value, lang: lang })
151
+ } else if (boneStructure["type"] === "record" || boneStructure["type"].startsWith("record.")) {
152
+ if (boneStructure["format"]?.includes("$(dest.")) {
153
+ return Utils.formatString(boneStructure["format"], { dest: value, lang: lang })
154
+ }
155
+ return Utils.formatString(boneStructure["format"], { ...value, lang: lang })
156
+ } else if (boneStructure["type"] === "bool") {
157
+ if (typeof t === "function") {
158
+ return value ? t("bones.bool.true") : t("bones.bool.false")
159
+ }
160
+ return value ? "Ja" : "Nein"
161
+ } else if (boneStructure["type"] === "raw.json") {
162
+ return JSON.stringify(value)
163
+ } else {
164
+ value = Utils.unescape(value)
165
+ return value.toString()
166
+ }
167
+ }
168
+
169
+ function renderMultipleValue(boneName, options, skel = null, lang = null) {
170
+ if (skel === null) {
171
+ skel = bones_state.skel
172
+ }
173
+
174
+ let value = skel[boneName]
175
+
176
+ if (lang) {
177
+ value = skel[boneName][lang]
178
+ }
179
+
180
+ if (value && value.length > 0) {
181
+ return value
182
+ .map((x) => {
183
+ if (selectedLang) {
184
+ if (selectedLang === lang) {
185
+ return renderSingleValue(boneName, options, x, skel, lang)
186
+ }
187
+ } else {
188
+ return renderSingleValue(boneName, options, x, skel, lang)
189
+ }
190
+ })
191
+ .join(", ")
192
+ }
193
+ return "-"
194
+ }
195
+
196
+ return {
197
+ getBoneValue,
198
+ bones_state,
199
+ }
200
+ }
201
+
202
+ export default boneLogic
@@ -0,0 +1,5 @@
1
+ import base_item from "./base_item.vue"
2
+
3
+ export default {
4
+ base_item,
5
+ }
@@ -0,0 +1,83 @@
1
+ <template>
2
+ <wa-switch
3
+ class="bone-widget bone-widget-boolean bone-widget-boolean-default"
4
+ :class="[`bone-widget-boolean-${name}`]"
5
+ :disabled="boneState.readonly"
6
+ :checked="state.value"
7
+ :data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
8
+ @change="changeEvent"
9
+ ></wa-switch>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { reactive, onMounted, inject, computed } from "vue"
14
+ /**
15
+ * Schalter für boolesche Werte (true/false).
16
+ *
17
+ * Verfügbar in drei Varianten: `bool` (Switch), `bool.choose` (Ja/Nein-Buttons),
18
+ * `bool.select` (Dropdown). Der Wert `null`, `undefined`, `false` und `""` wird
19
+ * einheitlich als `false` behandelt.
20
+ *
21
+ * @displayName BooleanBone
22
+ *
23
+ * @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
24
+ * @cssClass bone-widget-boolean - Kennzeichnet den Bone-Typ
25
+ * @cssClass bone-widget-boolean-default - Standard-Variante
26
+ * @cssClass bone-widget-boolean-{name} - Dynamisch gesetzt auf den Bone-Namen
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 Bone-Wert. `null`, `undefined`, `false` und `""` gelten als `false`. */
35
+ value: [Object, String, Number, Boolean, Array],
36
+ /** Index im Multiple-Array; `undefined` bei Einzelwert. */
37
+ index: Number,
38
+ /** Sprach-Key bei mehrsprachigen Bones, z.B. `"de"`. */
39
+ lang: String,
40
+ /** Bone-Struktur (aktuell nicht aktiv genutzt). */
41
+ bone: Object,
42
+ /** Autofocus (aktuell nicht aktiv genutzt). */
43
+ autofocus: Boolean,
44
+ })
45
+
46
+ const emit = defineEmits([
47
+ /**
48
+ * Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
49
+ * @arg {string} name - Bone-Name
50
+ * @arg {boolean} value - Aktueller Wert
51
+ * @arg {string|undefined} lang - Sprach-Key
52
+ * @arg {number|undefined} index - Index im Multiple-Array
53
+ */
54
+ "change",
55
+ ])
56
+ const boneState = inject("boneState")
57
+ const state = reactive({
58
+ value: computed(() => {
59
+ return ![false, null, undefined, ""].includes(props.value)
60
+ }),
61
+ })
62
+
63
+ function changeEvent(event) {
64
+ emit("change", props.name, event.target.checked, props.lang, props.index)
65
+ }
66
+
67
+ onMounted(() => {
68
+ let val = props.value
69
+ if (!val) {
70
+ val = false
71
+ }
72
+ emit("change", props.name, val, props.lang, props.index) //init
73
+ })
74
+ </script>
75
+
76
+ <style scoped>
77
+ wa-switch {
78
+ padding: calc(var(--wa-form-control-height) / 2 - var(--wa-form-control-toggle-size) / 2 - 1px);
79
+ border: 1px solid var(--wa-color-border-quiet);
80
+ border-radius: var(--wa-border-radius-m);
81
+ background-color: var(--wa-color-neutral-on-loud);
82
+ }
83
+ </style>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <!-- readonly an der Gruppe: wa-radio-group markiert nur Radios, die selbst
3
+ nicht disabled sind — sonst wäre bei readonly keine Auswahl sichtbar -->
4
+ <wa-radio-group
5
+ :value="state.value"
6
+ :disabled="boneState.readonly"
7
+ class="bone-widget bone-widget-boolean bone-widget-boolean-choose"
8
+ :class="[`bone-widget-boolean-${name}`]"
9
+ :required="
10
+ boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
11
+ "
12
+ :data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
13
+ @change="changeEvent"
14
+ >
15
+ <wa-radio :value="'true'">
16
+ {{ $t("bones.bool.true") }}
17
+ </wa-radio>
18
+ <wa-radio :value="'false'">
19
+ {{ $t("bones.bool.false") }}
20
+ </wa-radio>
21
+ </wa-radio-group>
22
+ </template>
23
+
24
+ <script setup>
25
+ import { reactive, onMounted, inject, computed } from "vue"
26
+ defineOptions({
27
+ inheritAttrs: false,
28
+ })
29
+ const props = defineProps({
30
+ name: String,
31
+ value: [Object, String, Number, Boolean, Array],
32
+ index: Number,
33
+ lang: String,
34
+ bone: Object,
35
+ autofocus: Boolean,
36
+ })
37
+
38
+ const emit = defineEmits(["change"])
39
+ const boneState = inject("boneState")
40
+ const state = reactive({
41
+ value: computed(() => {
42
+ return "" + ![false, null, undefined, "", "false"].includes(props.value)
43
+ }),
44
+ })
45
+
46
+ function changeEvent(event) {
47
+ emit("change", props.name, event.target.value, props.lang, props.index)
48
+ }
49
+
50
+ onMounted(() => {
51
+ let val = props.value
52
+ if (!val) {
53
+ val = false
54
+ }
55
+ emit("change", props.name, val, props.lang, props.index) //init
56
+ })
57
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <wa-select
3
+ :disabled="boneState.readonly"
4
+ :value="state.value"
5
+ max-options-visible="0"
6
+ class="bone-widget bone-widget-boolean bone-widget-boolean-select"
7
+ :class="[`bone-widget-boolean-${name}`]"
8
+ :data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
9
+ @change="changeEvent"
10
+ >
11
+ <!-- String-Werte: wa-select gleicht seinen Wert beim Setzen mit den
12
+ Options-Werten ab; boolesche werden erst später zu Strings, der
13
+ gespeicherte Wert erschiene dann nicht -->
14
+ <wa-option
15
+ v-for="val in ['true', 'false']"
16
+ :key="val"
17
+ :value="val"
18
+ >
19
+ {{ $t(`bones.bool.${val}`) }}
20
+ </wa-option>
21
+ </wa-select>
22
+ </template>
23
+
24
+ <script setup>
25
+ import { reactive, onMounted, inject, computed } from "vue"
26
+ defineOptions({
27
+ inheritAttrs: false,
28
+ })
29
+ const props = defineProps({
30
+ name: String,
31
+ value: [Object, String, Number, Boolean, Array],
32
+ index: Number,
33
+ lang: String,
34
+ bone: Object,
35
+ autofocus: Boolean,
36
+ })
37
+
38
+ const emit = defineEmits(["change"])
39
+
40
+ const boneState = inject("boneState")
41
+ const state = reactive({
42
+ value: computed(() => {
43
+ return "" + ![false, null, undefined, "", "false"].includes(props.value)
44
+ }),
45
+ })
46
+
47
+ function changeEvent(event) {
48
+ emit("change", props.name, event.target.value, props.lang, props.index)
49
+ }
50
+
51
+ onMounted(() => {
52
+ let val = props.value
53
+ if (!val) {
54
+ val = false
55
+ }
56
+ emit("change", props.name, val, props.lang, props.index) //init
57
+ })
58
+ </script>
@@ -0,0 +1,214 @@
1
+ <template>
2
+ <div
3
+ class="bone-widget-wrapper bone-widget bone-widget-raw bone-widget-raw-default"
4
+ :class="{ 'is-max-size': state.max }"
5
+ >
6
+ <textarea
7
+ ref="element"
8
+ :disabled="boneState?.readonly"
9
+ :placeholder="state.placeholder"
10
+ :data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
11
+ ></textarea>
12
+ <wa-button
13
+ appearance="plain"
14
+ class="bone-expand-button"
15
+ @click="state.max = !state.max"
16
+ >
17
+ <wa-icon :name="state.max ? 'compress' : 'expand'"></wa-icon>
18
+ </wa-button>
19
+ </div>
20
+
21
+ <input
22
+ type="text"
23
+ class="bone-widget-raw-hidden"
24
+ :value="state.value"
25
+ :required="
26
+ boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
27
+ "
28
+ />
29
+ </template>
30
+
31
+ <script setup>
32
+ import { reactive, onMounted, inject, computed, ref } from "vue"
33
+ import { basicSetup } from "codemirror"
34
+ import { EditorView, keymap, drawSelection } from "@codemirror/view"
35
+ import { syntaxHighlighting, defaultHighlightStyle, LanguageDescription } from "@codemirror/language"
36
+ import { indentLess, indentMore, indentWithTab, indentSelection, insertTab } from "@codemirror/commands"
37
+ import { indentString, indentUnit } from "@codemirror/language"
38
+ import { EditorState } from "@codemirror/state"
39
+ import { languages } from "@codemirror/language-data"
40
+
41
+ /**
42
+ * Code-Editor auf Basis von CodeMirror für syntaxhervorgehobene Quelltext-Eingabe.
43
+ *
44
+ * Erkennt die Programmiersprache anhand von `boneState.bonestructure.type`
45
+ * (z.B. `raw.code.python`, `raw.code.html`, `raw.code.javascript`) und lädt
46
+ * das passende CodeMirror-Language-Plugin dynamisch nach. Unterstützt
47
+ * Mehrfachwerte (`multiple`). Ein Expand-Button ermöglicht das Aufklappen
48
+ * auf volle Höhe.
49
+ *
50
+ * @displayName CodeBone
51
+ *
52
+ * @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
53
+ * @cssClass bone-widget-raw - Kennzeichnet den übergeordneten Bone-Typ
54
+ * @cssClass bone-widget-raw-default - Standard-Variante
55
+ * @cssClass bone-widget-wrapper - Umschließt Editor und Expand-Button
56
+ * @cssClass is-max-size - Wird per Expand-Button umgeschaltet; hebt die maximale Höhe auf
57
+ */
58
+ defineOptions({
59
+ inheritAttrs: false,
60
+ })
61
+
62
+ const element = ref(null)
63
+ const view = ref(null)
64
+
65
+ const props = defineProps({
66
+ /** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
67
+ name: String,
68
+ /** Aktueller Bone-Wert (Quelltext als String). */
69
+ value: [Object, String, Number, Boolean, Array],
70
+ /** Index im Multiple-Array; `undefined` bei Einzelwert. */
71
+ index: Number,
72
+ /** Sprach-Key bei mehrsprachigen Bones (CodeBone unterstützt kein multilang). */
73
+ lang: String,
74
+ /** Bone-Struktur (aktuell nicht aktiv genutzt). */
75
+ bone: Object,
76
+ /** Setzt den Fokus nach dem Mount auf den CodeMirror-Editor. */
77
+ autofocus: Boolean,
78
+ })
79
+
80
+ const emit = defineEmits([
81
+ /**
82
+ * Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
83
+ * @arg {string} name - Bone-Name
84
+ * @arg {string} value - Aktueller Quelltext
85
+ * @arg {string|undefined} lang - Sprach-Key
86
+ * @arg {number|undefined} index - Index im Multiple-Array
87
+ */
88
+ "change",
89
+ ])
90
+
91
+ const boneState = inject("boneState")
92
+ const state = reactive({
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
+ value: "",
102
+ max: false,
103
+ })
104
+
105
+ function editorFromTextArea(textarea, extensions) {
106
+ let _view = new EditorView({ doc: textarea.value, extensions })
107
+
108
+ textarea.parentNode.insertBefore(_view.dom, textarea)
109
+ textarea.style.display = "none"
110
+
111
+ if (textarea.form) {
112
+ textarea.form.addEventListener("submit", () => {
113
+ textarea.value = _view.state.doc.toString()
114
+ })
115
+ }
116
+
117
+ indentString(_view.state, 4)
118
+
119
+ let listener = () => {
120
+ let lines = ""
121
+ for (let i = 0; i < _view.state.doc.lines; ++i) {
122
+ let line = _view.state.doc.line(i + 1).text
123
+ lines += line + "\n"
124
+ }
125
+ state.value = lines
126
+ emit("change", props.name, state.value, props.lang, props.index)
127
+ }
128
+
129
+ _view.dom.addEventListener("input", listener)
130
+ _view.dom.addEventListener("keyup", listener)
131
+ _view.dom.addEventListener("paste", listener)
132
+ _view.dom.addEventListener("cut", listener)
133
+ return _view
134
+ }
135
+
136
+ async function build() {
137
+ const exts = [
138
+ basicSetup,
139
+ syntaxHighlighting(defaultHighlightStyle),
140
+ keymap.of([indentWithTab]),
141
+ indentUnit.of(" "),
142
+ EditorState.tabSize.of(4),
143
+ EditorView.lineWrapping,
144
+ drawSelection(),
145
+ ]
146
+
147
+ let langDesc = LanguageDescription.matchLanguageName(
148
+ languages,
149
+ boneState?.bonestructure?.type.replace("raw.code.", "")
150
+ )
151
+
152
+ if (langDesc) {
153
+ const support = await langDesc.load()
154
+ exts.push(support)
155
+ }
156
+
157
+ if (boneState?.readonly) {
158
+ exts.push(EditorView.editable.of(false))
159
+ }
160
+
161
+ return exts
162
+ }
163
+
164
+ onMounted(async () => {
165
+ if (props.value !== null) {
166
+ state.value = props.value
167
+ }
168
+ const exts = await build()
169
+ view.value = editorFromTextArea(element.value, exts)
170
+
171
+ //init editor
172
+ view.value.dispatch({
173
+ changes: { from: 0, to: view.value.state.doc.length, insert: state.value },
174
+ })
175
+
176
+ emit("change", props.name, props.value, props.lang, props.index) //init
177
+ })
178
+ </script>
179
+ <style scoped>
180
+ .bone-widget-raw-hidden {
181
+ max-height: 0;
182
+ display: block;
183
+ visibility: hidden;
184
+ width: 100%;
185
+ pointer-events: none;
186
+ }
187
+
188
+ .bone-expand-button {
189
+ position: absolute;
190
+ bottom: 0px;
191
+ right: 10px;
192
+ z-index: 1;
193
+ }
194
+
195
+ .bone-widget-wrapper {
196
+ position: relative;
197
+ display: flex;
198
+ height: 100%;
199
+ width: 100%;
200
+ }
201
+ :deep(.cm-editor) {
202
+ width: 100%;
203
+
204
+ & .cm-scroller {
205
+ min-height: 3em;
206
+ max-height: 28em;
207
+ overflow-y: auto;
208
+ }
209
+ }
210
+
211
+ .bone-widget-wrapper.is-max-size :deep(.cm-editor) .cm-scroller {
212
+ max-height: none;
213
+ }
214
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div class="wa-flank wa-align-item-center">
3
+ <wa-color-picker
4
+ class="bone-widget bone-widget-color bone-widget-color-default"
5
+ :class="[`bone-widget-color-${name}`]"
6
+ :disabled="boneState.readonly"
7
+ :value="value"
8
+ :required="
9
+ boneState.bonestructure.required && !boneState.bonestructure.multiple && !boneState.bonestructure.languages
10
+ "
11
+ :data-user-invalid="boneState.errorMessages.length === 0 ? undefined : true"
12
+ @change="changeEvent"
13
+ ></wa-color-picker>
14
+ <span>{{ value || "-" }}</span>
15
+ <wa-copy-button
16
+ :disabled="!value"
17
+ :value="value"
18
+ ></wa-copy-button>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { reactive, onMounted, inject } from "vue"
24
+ /**
25
+ * Farbauswahl-Feld über ein natives `<wa-color-picker>`-Element.
26
+ *
27
+ * Speichert den gewählten Farbwert als Hex-String, z.B. `"#e74c3c"`.
28
+ * Unterstützt mehrfache Werte (`multiple`), aber keine Mehrsprachigkeit.
29
+ *
30
+ * @displayName ColorBone
31
+ *
32
+ * @cssClass bone-widget - Basis-Klasse aller Bone-Widgets
33
+ * @cssClass bone-widget-color - Kennzeichnet den Bone-Typ
34
+ * @cssClass bone-widget-color-default - Standard-Variante
35
+ * @cssClass bone-widget-color-{name} - Dynamisch gesetzt auf den Bone-Namen, z.B. `bone-widget-color-color`
36
+ */
37
+ defineOptions({
38
+ inheritAttrs: false,
39
+ })
40
+ const props = defineProps({
41
+ /** Bone-Name – wird in jedem `change`-Event als erstes Argument übergeben. */
42
+ name: String,
43
+ /** Aktueller Bone-Wert als Hex-String, z.B. `"#e74c3c"`, oder `null`. */
44
+ value: [Object, String, Number, Boolean, Array],
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 (aktuell nicht aktiv genutzt). */
50
+ bone: Object,
51
+ /** Setzt den Fokus nach 600 ms auf das Input-Element. */
52
+ autofocus: Boolean,
53
+ })
54
+
55
+ const emit = defineEmits([
56
+ /**
57
+ * Wird bei jeder Wertänderung sowie beim Mount (Initialisierung) gefeuert.
58
+ * @arg {string} name - Bone-Name
59
+ * @arg {string|null} value - Gewählter Farbwert als Hex-String oder `null`
60
+ * @arg {string|undefined} lang - Sprach-Key
61
+ * @arg {number|undefined} index - Index im Multiple-Array
62
+ */
63
+ "change",
64
+ ])
65
+ const boneState = inject("boneState")
66
+ const state = reactive({})
67
+
68
+ function changeEvent(event) {
69
+ emit("change", props.name, event.target.value, props.lang, props.index)
70
+ }
71
+
72
+ onMounted(() => {
73
+ emit("change", props.name, props.value, props.lang, props.index) //init
74
+ })
75
+ </script>