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,127 @@
1
+ <template>
2
+ <template
3
+ v-for="(action, i) in state.actions"
4
+ :key="action.arg + '_' + name + '_' + lang + '_' + i"
5
+ >
6
+ <!-- title darf ein i18n-Key sein (z.B. "bone.del") — Klartext bleibt
7
+ Klartext ($te-Prüfung, keine missing-Key-Warnungen). -->
8
+ <wa-button
9
+ :disabled="isActionDisabled(action, ctx)"
10
+ size="medium"
11
+ appearance="outlined"
12
+ :variant="action.variant ?? 'neutral'"
13
+ :title="action.title && $te(action.title) ? $t(action.title) : action.title"
14
+ @click="onClick(action)"
15
+ >
16
+ <wa-icon :name="action.icon"></wa-icon>
17
+ </wa-button>
18
+
19
+ <action-dialog
20
+ v-if="action.type === 'assistant'"
21
+ :open="action._opened"
22
+ :name="bone?.descr"
23
+ :module="bone?.module"
24
+ :action="action"
25
+ :params="{ name, value, index, lang, bone, boneState }"
26
+ @close="onClose"
27
+ @next="onNext"
28
+ ></action-dialog>
29
+ </template>
30
+ </template>
31
+
32
+ <script setup>
33
+ import { reactive, computed, onMounted, inject } from "vue"
34
+ import { resolveActions, isActionDisabled, getActionHandler, formConfiguredActions } from "./actions"
35
+ import ActionDialog from "./actions/ActionDialog.vue"
36
+
37
+ const emit = defineEmits(["change"])
38
+
39
+ const props = defineProps({
40
+ name: String,
41
+ value: [Object, String, Number, Boolean, Array],
42
+ index: Number,
43
+ lang: String,
44
+ bone: Object,
45
+ /**
46
+ * Welche Actions diese Leiste zeigt:
47
+ * - "entry": alle bei `boneactions`, sonst nur die Pflicht-Actions (Standard)
48
+ * - "value": nur die Pflicht-Actions — am Wert EINER Sprache eines
49
+ * mehrsprachigen Bones (Löschen/Download betreffen nur diese Sprache)
50
+ * - "bone": nur die übrigen — die Leiste neben den Sprach-Tabs (Übersetzen …)
51
+ */
52
+ scope: {
53
+ type: String,
54
+ default: "entry",
55
+ validator: (value) => ["entry", "value", "bone"].includes(value),
56
+ },
57
+ })
58
+
59
+ const boneState = inject("boneState")
60
+ // Vom HOST bereitgestellte Auswahl für relationale Bones (z.B. ein Admin, der
61
+ // einen Listen-Dialog öffnet): `(request) => Promise<Row[]|null>`. Ohne
62
+ // Provider existiert die zugehörige Action schlicht nicht (`when`-Prädikat).
63
+ const relationSelector = inject("viurRelationSelector", null)
64
+ // Für Mehrfach-Übernahme in multiple-Bones (hängt weitere Einträge an) und
65
+ // die Pflicht-Action "Zeile entfernen".
66
+ const addMultipleEntry = inject("addMultipleEntry", null)
67
+ const removeMultipleEntry = inject("removeMultipleEntry", null)
68
+ // Formular-weite Action-Konfiguration (ViForm `actions`-Prop).
69
+ const boneActionsConfig = inject("boneActionsConfig", null)
70
+
71
+ const state = reactive({
72
+ actions: [],
73
+ })
74
+
75
+ /** Steckt diese Action-Leiste in einer ZEILE eines multiple-Bones? */
76
+ const multipleRow = props.index !== null && props.index !== undefined
77
+
78
+ const ctx = computed(() => ({
79
+ name: props.name,
80
+ value: props.value,
81
+ index: props.index,
82
+ lang: props.lang,
83
+ bone: props.bone,
84
+ boneState,
85
+ relationSelector,
86
+ multipleRow,
87
+ update: (val) => emit("change", props.name, val, props.lang, props.index),
88
+ /** Weiteren Eintrag ans Multiple-Array hängen (Mehrfach-Auswahl). */
89
+ appendEntry: (val) => addMultipleEntry?.(props.lang ?? null, val),
90
+ /** DIESE Zeile aus dem Multiple-Array entfernen (Pflicht-Action). */
91
+ removeEntry: () => removeMultipleEntry?.(props.index, props.lang ?? null),
92
+ }))
93
+
94
+ onMounted(() => {
95
+ // Formular-Konfiguration VOR den Bone-eigenen params.actions (Bone-Lokales
96
+ // ist spezifischer); Minus-Einträge gewinnen quellenunabhängig immer.
97
+ const configured = [...formConfiguredActions(boneActionsConfig, props.name), ...(props.bone?.params?.actions ?? [])]
98
+ const all = resolveActions(props.bone, configured, boneState, { relationSelector, multipleRow })
99
+ // Ohne `boneactions` bleiben nur die PFLICHT-Actions (Gerüst, z.B. das
100
+ // Zeilen-X) — die Leiste als Ganzes ist Opt-in, das Gerüst nicht.
101
+ if (props.scope === "bone") {
102
+ state.actions = all.filter((action) => !action.required)
103
+ } else if (props.scope === "value" || !boneState?.boneactions) {
104
+ state.actions = all.filter((action) => action.required)
105
+ } else {
106
+ state.actions = all
107
+ }
108
+ })
109
+
110
+ function onClick(action) {
111
+ if (action.type === "assistant") {
112
+ action._opened = true
113
+ } else if (action.type === "callback") {
114
+ const handler = getActionHandler(action.arg)
115
+ handler?.(ctx.value)
116
+ }
117
+ }
118
+
119
+ function onClose(val, lang, action) {
120
+ action._opened = false
121
+ if (val) emit("change", props.name, val, lang, props.index)
122
+ }
123
+
124
+ function onNext(val, lang, action) {
125
+ if (val) emit("change", props.name, val, lang, props.index)
126
+ }
127
+ </script>
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <label class="bone-label">
3
+ <slot v-if="!debug"></slot>
4
+ <wa-tooltip
5
+ v-else
6
+ :for="tooltipTargetId"
7
+ :content="name"
8
+ ></wa-tooltip>
9
+ <span
10
+ v-if="debug"
11
+ :id="tooltipTargetId"
12
+ >
13
+ <slot></slot>
14
+ </span>
15
+ <template v-if="debug">
16
+ <div
17
+ class="debug"
18
+ @click="state.debug = !state.debug"
19
+ >
20
+ <wa-icon name="bug"></wa-icon>
21
+ </div>
22
+ </template>
23
+ </label>
24
+
25
+ <wa-dialog
26
+ v-if="state.debug"
27
+ open
28
+ :label="name"
29
+ @wa-after-hide="state.debug = false"
30
+ >
31
+ <pre>
32
+ {{ boneState }}
33
+ </pre
34
+ >
35
+ </wa-dialog>
36
+ </template>
37
+
38
+ <script setup>
39
+ import { reactive, inject, computed, unref } from "vue"
40
+ import { buildBoneDomId } from "./utils"
41
+ const props = defineProps({
42
+ name: String,
43
+ value: Object,
44
+ index: Number,
45
+ lang: String,
46
+ readonly: Boolean,
47
+ params: Object,
48
+ debug: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
52
+ })
53
+
54
+ const emit = defineEmits(["change", "handleClick"])
55
+
56
+ const boneState = inject("boneState")
57
+ const bonePath = inject("bonePath", [])
58
+ const tooltipTargetId = computed(() => buildBoneDomId(unref(bonePath) ?? [], "debug-label"))
59
+ const state = reactive({
60
+ debug: false,
61
+ })
62
+ </script>
63
+ <style scoped>
64
+ @layer foundation.form {
65
+ .bone-label {
66
+ display: flex;
67
+ width: 100%;
68
+ box-sizing: border-box;
69
+ padding: var(--wa-space-xs) var(--wa-space-xs);
70
+ background-color: var(--wa-color-fill-quiet);
71
+ color: var(--wa-color-text-normal);
72
+ border-radius: var(--wa-border-radius-m);
73
+ word-break: break-word;
74
+ align-self: flex-start;
75
+ min-height: var(--wa-form-control-height);
76
+ align-items: center;
77
+ }
78
+ }
79
+ </style>
80
+
81
+ <!--
82
+ <style scoped>
83
+ @layer foundation.form {
84
+ .bone-name {
85
+ display: flex;
86
+ flex-direction: row;
87
+ align-items: center;
88
+ align-self: flex-start;
89
+ font-size: var(--wa-input-font-size-medium);
90
+ min-height: var(--wa-input-height-medium);
91
+ padding: 0.4em 0.7em;
92
+ color: var(--wa-color-neutral-10);
93
+ background-color: var(--wa-color-neutral-90);
94
+ border: none;
95
+ border-top-left-radius: var(--wa-input-border-radius-medium);
96
+ border-bottom-left-radius: var(--wa-input-border-radius-medium);
97
+ word-break: break-word;
98
+
99
+ @media (max-width: 900px) {
100
+ border-top-right-radius: var(--wa-input-border-radius-medium);
101
+ border-bottom-left-radius: 0;
102
+ }
103
+ }
104
+
105
+ .debug {
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ margin-left: auto;
110
+ padding-left: 0.4em;
111
+
112
+ & wa-icon {
113
+ background-color: var(--wa-color-danger-50);
114
+ color: #fff;
115
+ padding: 0.4em;
116
+ border-radius: 50%;
117
+ font-size: 0.55em;
118
+ }
119
+ }
120
+ }
121
+ </style>
122
+ -->
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <template v-if="entries?.length">
3
+ <vue-draggable
4
+ v-model="localEntries"
5
+ :animation="150"
6
+ handle=".bone-drag-button"
7
+ class="wa-stack wa-gap-xs bone-multiple-list"
8
+ >
9
+ <div
10
+ v-for="(val, index) in entries"
11
+ :key="val?._key ? val._key : index + '_' + entries.length"
12
+ class="bone-multiple-wrapper"
13
+ >
14
+ <bone-multiple-wrapper
15
+ :readonly="!readonly"
16
+ :index="index"
17
+ :lang="lang"
18
+ :name="name"
19
+ >
20
+ <component
21
+ :is="is"
22
+ :value="resolveValue(val, index)"
23
+ :index="index"
24
+ :lang="lang"
25
+ :name="name"
26
+ :bone="bonestructure"
27
+ @change="updateValue"
28
+ />
29
+ </bone-multiple-wrapper>
30
+ </div>
31
+ </vue-draggable>
32
+ </template>
33
+ <div
34
+ v-else
35
+ class="bone-multiple-placeholder"
36
+ :class="{ 'is-readonly': readonly }"
37
+ >
38
+ <wa-input
39
+ readonly
40
+ size="medium"
41
+ :placeholder="$t('bone.no_entries')"
42
+ />
43
+ </div>
44
+ <component
45
+ :is="actionbar"
46
+ v-if="!readonly && actionbar"
47
+ :value="entries"
48
+ :name="name"
49
+ :lang="lang"
50
+ @change="updateValue"
51
+ />
52
+ </template>
53
+
54
+ <script setup>
55
+ import { computed, inject } from "vue"
56
+ import { VueDraggable } from "vue-draggable-plus"
57
+ import BoneMultipleWrapper from "./BoneMultipleWrapper.vue"
58
+
59
+ const props = defineProps({
60
+ is: { type: Object, required: true },
61
+ name: { type: String, required: true },
62
+ lang: { type: String, default: null },
63
+ entries: { type: Array, default: null },
64
+ bonestructure: { type: Object, required: true },
65
+ readonly: { type: Boolean, default: false },
66
+ actionbar: { type: Object, default: null },
67
+ })
68
+
69
+ const updateValue = inject("updateValue")
70
+
71
+ const localEntries = computed({
72
+ get: () => props.entries ?? [],
73
+ set: (newVal) => updateValue(props.name, newVal, props.lang),
74
+ })
75
+
76
+ function resolveValue(val, index) {
77
+ const defaultvalue = props.bonestructure?.defaultvalue
78
+ if (!val && defaultvalue) {
79
+ return props.lang ? defaultvalue[props.lang]?.[index] : defaultvalue?.[index]
80
+ }
81
+ return val
82
+ }
83
+ </script>
84
+ <style scoped>
85
+ .bone-multiple-list {
86
+ background-color: var(--wa-color-surface-lowered);
87
+ padding: var(--wa-space-xs);
88
+ border-radius: var(--wa-border-radius-m);
89
+ border: 1px solid var(--wa-color-surface-border);
90
+ }
91
+ </style>
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <div class="wa-flank wa-align-items-start wa-gap-xs bone-multiple-entry">
3
+ <wa-button
4
+ appearance="filled"
5
+ :disabled="boneState.readonly"
6
+ class="bone-drag-button"
7
+ >
8
+ <wa-icon name="grip-vertical"></wa-icon>
9
+ </wa-button>
10
+ <div class="wa-flank:end wa-gap-xs wa-align-items-start">
11
+ <div class="value">
12
+ <slot></slot>
13
+ </div>
14
+ <div class="wa-cluster wa-gap-xs">
15
+ <!-- IMMER gerendert: Das Zeilen-X ist die Pflicht-Action "removeEntry"
16
+ (actions/index.js) — BoneActions filtert ohne `boneactions` selbst
17
+ auf die Pflicht-Actions herunter. -->
18
+ <bone-actions
19
+ :value="state.value"
20
+ :name="name"
21
+ :lang="lang"
22
+ :index="index"
23
+ :bone="boneState.bonestructure"
24
+ @change="updateValue"
25
+ ></bone-actions>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { reactive, inject, computed } from "vue"
33
+ import BoneActions from "./BoneActions.vue"
34
+
35
+ const props = defineProps({
36
+ index: Number,
37
+ name: String,
38
+ lang: {
39
+ default: null,
40
+ },
41
+ })
42
+
43
+ const state = reactive({
44
+ value: computed(() => {
45
+ if (props.lang) {
46
+ return boneState.bonevalue[props.lang][props.index]
47
+ }
48
+ return boneState.bonevalue[props.index]
49
+ }),
50
+ })
51
+
52
+
53
+ const boneState = inject("boneState")
54
+ const updateValue = inject("updateValue")
55
+ </script>
56
+ <!--
57
+ <style scoped>
58
+ .value-line {
59
+ display: flex;
60
+ gap: var(--wa-space-xs);
61
+ align-items: flex-start;
62
+ }
63
+
64
+ .value {
65
+ flex: 1 1 100%;
66
+ width: 1px;
67
+
68
+ & :deep(wa-input) {
69
+ &::part(base) {
70
+ border-bottom-left-radius: var(--wa-border-radius-m);
71
+ border-top-left-radius: var(--wa-border-radius-m);
72
+ }
73
+ }
74
+
75
+ & :deep(.bone-wrapper) {
76
+ & wa-input::part(base) {
77
+ border-bottom-left-radius: 0;
78
+ border-top-left-radius: 0;
79
+ }
80
+ }
81
+
82
+ &:deep(wa-alert) {
83
+ &::part(base) {
84
+ margin-top: 0;
85
+ }
86
+ }
87
+ }
88
+
89
+ .delete-btn {
90
+ transition: all ease 0.3s;
91
+ }
92
+
93
+ .drag-button {
94
+ &::part(base) {
95
+ aspect-ratio: 1;
96
+ }
97
+ }
98
+ .row-wrapper {
99
+ width: 100%;
100
+ display: flex;
101
+ flex-direction: row;
102
+ gap: var(--wa-space-xs);
103
+ }
104
+ </style>
105
+ -->
@@ -0,0 +1,155 @@
1
+ <template>
2
+ <wa-alert
3
+ v-if="!state.globalRegistration"
4
+ open
5
+ variant="danger"
6
+ >
7
+ In Order to use this Bone register the bone component globally in your main file
8
+ </wa-alert>
9
+ <div
10
+ v-else
11
+ class="form"
12
+ >
13
+ <wa-spinner
14
+ v-if="state.updating"
15
+ style="position: absolute; top: 20px; left: -10px"
16
+ ></wa-spinner>
17
+
18
+ <vi-form
19
+ ref="skelRef"
20
+ :internal="mainformState"
21
+ :skel="value"
22
+ :structure="bone['using']"
23
+ :renderer="state.renderer"
24
+ :use-categories="bone?.params['categoryDefaultname'] !== null"
25
+ :category-defaultname="nestedCategory()"
26
+ :collapsed-categories="
27
+ state.isLastEntry ? [] : bone?.params?.['collapsedCategories'] ? bone?.params?.['collapsedCategories'] : []
28
+ "
29
+ :readonly="mainformState.readonly"
30
+ :boneactions="mainformState.boneactions"
31
+ :actions="parentActionsConfig"
32
+ @change="changeEvent"
33
+ ></vi-form>
34
+ </div>
35
+ </template>
36
+
37
+ <script setup>
38
+ import {
39
+ reactive,
40
+ defineComponent,
41
+ onMounted,
42
+ inject,
43
+ computed,
44
+ getCurrentInstance,
45
+ watch,
46
+ ref,
47
+ provide,
48
+ unref,
49
+ } from "vue"
50
+ import { getBoneWidget } from "./index"
51
+ import ViForm from "../ViForm.vue"
52
+ import { useTimeoutFn } from "@vueuse/core"
53
+
54
+ const emit = defineEmits(["change"])
55
+ const props = defineProps({
56
+ name: String,
57
+ value: null,
58
+ index: Number,
59
+ lang: String,
60
+ readonly: Boolean,
61
+ params: Object,
62
+ bone: Object,
63
+ })
64
+ const skelRef = ref(null)
65
+ const boneState = inject("boneState")
66
+ const mainformState = inject("mainformState")
67
+ // Action-Konfiguration des umgebenden Formulars (ViForm `actions`) — das
68
+ // Unterformular stellt sonst seine eigene (leere) bereit.
69
+ const parentActionsConfig = inject("boneActionsConfig", null)
70
+ const parentBonePath = inject("bonePath", [])
71
+ const bonePath = computed(() => {
72
+ const path = [...(unref(parentBonePath) ?? []), props.name]
73
+
74
+ if (props.lang) {
75
+ path.push(`lang-${props.lang}`)
76
+ }
77
+
78
+ if (props.index !== null && props.index !== undefined) {
79
+ path.push(`idx-${props.index}`)
80
+ }
81
+
82
+ return path
83
+ })
84
+ provide("bonePath", bonePath)
85
+ const state = reactive({
86
+ renderer: import.meta.env.VITE_DEFAULT_RENDERER || "json",
87
+ globalRegistration: false,
88
+ isLastEntry: computed(() => {
89
+ if (!props.bone["multiple"]) return false
90
+
91
+ let val = mainformState.skel[props.name]
92
+ if (props.lang) {
93
+ val = mainformState.skel[props.name][props.lang]
94
+ }
95
+ if (!val) {
96
+ return false
97
+ }
98
+ return val.length - 1 === props.index
99
+ }),
100
+ updating: false,
101
+ })
102
+
103
+ function changeEvent(val) {
104
+ state.updating = true
105
+ val["value"] = skelRef.value.state.skel // send whole form
106
+ val["name"] = props.name
107
+ val["index"] = props.index
108
+ val["lang"] = props.lang
109
+ emit("change", val)
110
+ useTimeoutFn(() => {
111
+ state.updating = false
112
+ }, 1000)
113
+ }
114
+
115
+ watch(
116
+ () => props.value,
117
+ (newVal, oldVal) => {
118
+ state.updating = false
119
+ }
120
+ )
121
+
122
+ onMounted(() => {
123
+ let app = getCurrentInstance().appContext
124
+ if (app.components.Bone) {
125
+ state.globalRegistration = true
126
+ } else {
127
+ state.globalRegistration = false
128
+ }
129
+
130
+ let val = {}
131
+ if (props.value) val = props.value
132
+
133
+ for (const [bonename, boneStructure] of Object.entries(props.bone["using"])) {
134
+ if (!Object.keys(val).includes(bonename) && boneStructure["defaultvalue"]) {
135
+ val[bonename] = boneStructure["defaultvalue"]
136
+ }
137
+ }
138
+
139
+ emit("change", props.name, val, props.lang, props.index) //init
140
+ })
141
+ function nestedCategory() {
142
+ if (props.bone?.params?.["categoryDefaultname"]) {
143
+ return props.bone.params["categoryDefaultname"]
144
+ } else if (props.bone?.format) {
145
+ return props.bone.format
146
+ }
147
+ return undefined
148
+ }
149
+ </script>
150
+ <style scoped>
151
+ .form {
152
+ width: 100%;
153
+ position: relative;
154
+ }
155
+ </style>
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <div class="wa-flank:end bone-row wa-gap-xs wa-align-items-start">
3
+ <div class="bone-block">
4
+ <component
5
+ :is="is"
6
+ :value="bonevalue"
7
+ :name="name"
8
+ :index="null"
9
+ :lang="lang"
10
+ :bone="bonestructure"
11
+ :autofocus="autofocus"
12
+ @change="updateValue"
13
+ @keypress.enter="updateValue"
14
+ ></component>
15
+ </div>
16
+ <!-- Pflicht-Actions (Löschen, Download …) auch ohne `boneactions`; am
17
+ Wert DIESES Eintrags, bei Mehrsprachigkeit also seiner Sprache -->
18
+ <div
19
+ v-if="(boneactions && boneState?.hasActions) || boneState?.hasRequiredActions"
20
+ class="wa-cluster wa-gap-xs"
21
+ >
22
+ <bone-actions
23
+ :value="bonevalue"
24
+ :name="name"
25
+ :index="null"
26
+ :lang="lang"
27
+ :bone="bonestructure"
28
+ :scope="boneactions ? 'entry' : 'value'"
29
+ @change="updateValue"
30
+ ></bone-actions>
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script setup>
36
+ import { inject } from "vue"
37
+ import BoneActions from "./BoneActions.vue"
38
+
39
+ const props = defineProps({
40
+ is: { type: Object, required: true },
41
+ name: { type: String, required: true },
42
+ lang: { type: String, default: null },
43
+ bonevalue: { default: null },
44
+ bonestructure: { type: Object, required: true },
45
+ boneactions: { type: Boolean, default: true },
46
+ autofocus: { type: Boolean, default: false },
47
+ })
48
+
49
+ const updateValue = inject("updateValue")
50
+ const boneState = inject("boneState")
51
+ </script>