@raclettejs/workbench 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 (89) hide show
  1. package/README.md +1 -0
  2. package/config/compositions.js +282 -0
  3. package/config/interactionLinks.js +164 -0
  4. package/i18n/de-DE.json +245 -0
  5. package/i18n/en-EU.json +245 -0
  6. package/i18n/sk-SK.json +245 -0
  7. package/package.json +63 -0
  8. package/packages.json +33 -0
  9. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionConfiguration.vue +140 -0
  10. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionIntegration.vue +3 -0
  11. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +129 -0
  12. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +53 -0
  13. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +99 -0
  14. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +122 -0
  15. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetRenderer.vue +65 -0
  16. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetSettings.vue +96 -0
  17. package/plugins/pacifico__compositions/frontend/widgets/CompositionCreateWidget.vue +51 -0
  18. package/plugins/pacifico__compositions/frontend/widgets/CompositionEditWidget.vue +66 -0
  19. package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +132 -0
  20. package/plugins/pacifico__compositions/raclette.plugin.ts +8 -0
  21. package/plugins/pacifico__core/backend/index.ts +25 -0
  22. package/plugins/pacifico__core/backend/routes.ts +220 -0
  23. package/plugins/pacifico__core/raclette.plugin.ts +9 -0
  24. package/plugins/pacifico__interactionLinks/frontend/components/InteractionLinkConfiguration.vue +145 -0
  25. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkCreateWidget.vue +48 -0
  26. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkEditWidget.vue +66 -0
  27. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +126 -0
  28. package/plugins/pacifico__interactionLinks/raclette.plugin.ts +8 -0
  29. package/plugins/pacifico__plugins/frontend/index.ts +42 -0
  30. package/plugins/pacifico__plugins/frontend/widgets/PluginDetailWidget.vue +26 -0
  31. package/plugins/pacifico__plugins/frontend/widgets/PluginListWidget.vue +59 -0
  32. package/plugins/pacifico__plugins/raclette.plugin.ts +8 -0
  33. package/plugins/pacifico__tags/frontend/components/TagConfiguration.vue +99 -0
  34. package/plugins/pacifico__tags/frontend/widgets/TagCreateWidget.vue +43 -0
  35. package/plugins/pacifico__tags/frontend/widgets/TagEditWidget.vue +64 -0
  36. package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +71 -0
  37. package/plugins/pacifico__tags/raclette.plugin.ts +8 -0
  38. package/plugins/pacifico__users/frontend/components/UserConfiguration.vue +110 -0
  39. package/plugins/pacifico__users/frontend/widgets/UserCreateWidget.vue +48 -0
  40. package/plugins/pacifico__users/frontend/widgets/UserEditWidget.vue +64 -0
  41. package/plugins/pacifico__users/frontend/widgets/UserListWidget.vue +86 -0
  42. package/plugins/pacifico__users/raclette.plugin.ts +8 -0
  43. package/raclette.config.js +150 -0
  44. package/services/frontend/.DS_Store +0 -0
  45. package/services/frontend/public/favicon.png +0 -0
  46. package/services/frontend/public/home-icon.png +0 -0
  47. package/services/frontend/public/logo.png +0 -0
  48. package/services/frontend/src/app/.DS_Store +0 -0
  49. package/services/frontend/src/app/components/BaseDataTable.vue +284 -0
  50. package/services/frontend/src/app/components/dynamicForm/DynamicForm.vue +207 -0
  51. package/services/frontend/src/app/components/dynamicForm/DynamicFormTypes.ts +63 -0
  52. package/services/frontend/src/app/components/dynamicForm/DynamicInput.vue +344 -0
  53. package/services/frontend/src/app/components/dynamicForm/TextDivider.vue +13 -0
  54. package/services/frontend/src/app/components/dynamicForm/testing/DynamicFormInputTesting.vue +477 -0
  55. package/services/frontend/src/app/components/dynamicForm/typedInputs/CheckboxInput.vue +16 -0
  56. package/services/frontend/src/app/components/dynamicForm/typedInputs/ColorInput.vue +52 -0
  57. package/services/frontend/src/app/components/dynamicForm/typedInputs/CompositionInput.vue +32 -0
  58. package/services/frontend/src/app/components/dynamicForm/typedInputs/IconInput.vue +40 -0
  59. package/services/frontend/src/app/components/dynamicForm/typedInputs/ListInput.vue +51 -0
  60. package/services/frontend/src/app/components/dynamicForm/typedInputs/LocalizedInput.vue +51 -0
  61. package/services/frontend/src/app/components/dynamicForm/typedInputs/PasswordInput.vue +23 -0
  62. package/services/frontend/src/app/components/dynamicForm/typedInputs/RecordInput.vue +91 -0
  63. package/services/frontend/src/app/components/dynamicForm/typedInputs/SwitchInput.vue +17 -0
  64. package/services/frontend/src/app/components/dynamicForm/typedInputs/TagsInput.vue +58 -0
  65. package/services/frontend/src/app/components/dynamicForm/typedInputs/TriggerInput.vue +159 -0
  66. package/services/frontend/src/app/components/dynamicForm/typedInputs/UserInput.vue +63 -0
  67. package/services/frontend/src/app/components/dynamicForm/typedInputs/mdi-icons.ts +7002 -0
  68. package/services/frontend/src/app/components/dynamicForm/validators.ts +19 -0
  69. package/services/frontend/src/app/components/stepNavigator/StepNavigator.vue +331 -0
  70. package/services/frontend/src/app/components/stepNavigator/StepNavigatorTypes.ts +12 -0
  71. package/services/frontend/src/app/composables/useUserHelper.ts +254 -0
  72. package/services/frontend/src/app/composables/useVantaFog.ts +37 -0
  73. package/services/frontend/src/app/composables/useWorkbenchPlugins.ts +44 -0
  74. package/services/frontend/src/orchestrator/.DS_Store +0 -0
  75. package/services/frontend/src/orchestrator/LoginApp.vue +62 -0
  76. package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -0
  77. package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +39 -0
  78. package/services/frontend/src/orchestrator/assets/styles/themes/highContrast.ts +39 -0
  79. package/services/frontend/src/orchestrator/assets/styles/themes/light.ts +39 -0
  80. package/services/frontend/src/orchestrator/components/InitProjectScreen.vue +298 -0
  81. package/services/frontend/src/orchestrator/components/LoginScreen.vue +73 -0
  82. package/services/frontend/src/orchestrator/components/RacletteAdCorner.vue +313 -0
  83. package/services/frontend/src/orchestrator/components/layout/AppButton.vue +109 -0
  84. package/services/frontend/src/orchestrator/components/layout/AppHeadline.vue +46 -0
  85. package/services/frontend/src/orchestrator/components/layout/AppLabelledInput.vue +94 -0
  86. package/services/frontend/src/orchestrator/components/layout/AppModal.vue +28 -0
  87. package/services/frontend/src/orchestrator/components/layout/SvgButtonArrow.vue +14 -0
  88. package/services/frontend/src/orchestrator/composables/useWelcomeScreen.ts +19 -0
  89. package/yarn.lock +3435 -0
@@ -0,0 +1,477 @@
1
+ <template>
2
+ <div class="tw:max-w-4xl tw:mx-auto tw:p-6">
3
+ <div class="tw:mb-6">
4
+ <h1 class="tw:text-2xl tw:font-bold tw:mb-2">
5
+ Dynamic Form Validation Test
6
+ </h1>
7
+ <p class="tw:text-gray-600">
8
+ This form demonstrates all input types with various validation rules.
9
+ </p>
10
+ </div>
11
+
12
+ <!-- Validation Status -->
13
+ <div class="tw:mb-6 tw:p-4 tw:rounded-lg" :class="validationStatusClass">
14
+ <div class="tw:flex tw:items-center tw:gap-2">
15
+ <div
16
+ class="tw:w-3 tw:h-3 tw:rounded-full"
17
+ :class="validationIndicatorClass"
18
+ ></div>
19
+ <span class="tw:font-medium">{{ validationStatusText }}</span>
20
+ </div>
21
+ <div
22
+ v-if="!isValid && Object.keys(errors).length > 0"
23
+ class="tw:mt-2 tw:text-sm"
24
+ >
25
+ <strong>Errors:</strong>
26
+ <ul class="tw:list-disc tw:list-inside tw:mt-1">
27
+ <li v-for="(error, field) in errors" :key="field">
28
+ {{ field }}: {{ error }}
29
+ </li>
30
+ </ul>
31
+ </div>
32
+ </div>
33
+
34
+ <div>
35
+ <v-number-input
36
+ v-model="inputFilter"
37
+ variant="outlined"
38
+ :min="0"
39
+ :max="inputFields.filter((input) => !('divider' in input)).length"
40
+ />
41
+ </div>
42
+
43
+ <!-- The Dynamic Form -->
44
+ <DynamicForm
45
+ ref="dynamicFormRef"
46
+ v-model="testData"
47
+ :data="filteredInputs"
48
+ :disabled="false"
49
+ @validation-change="onValidationChange"
50
+ />
51
+
52
+ <!-- Action Buttons -->
53
+ <div class="tw:mt-8 tw:flex tw:gap-4">
54
+ <v-btn @click="validateForm" color="primary" variant="outlined">
55
+ Validate Form
56
+ </v-btn>
57
+ <v-btn @click="resetForm" color="secondary" variant="outlined">
58
+ Reset Form
59
+ </v-btn>
60
+ <v-btn
61
+ @click="showFormData = !showFormData"
62
+ color="info"
63
+ variant="outlined"
64
+ >
65
+ {{ showFormData ? "Hide" : "Show" }} Form Data
66
+ </v-btn>
67
+ </div>
68
+
69
+ <!-- Form Data Display -->
70
+ <div v-if="showFormData" class="tw:mt-6 tw:p-4 tw:bg-gray-50 tw:rounded-lg">
71
+ <h3 class="tw:text-lg tw:font-semibold tw:mb-2">Current Form Data:</h3>
72
+ <pre class="tw:text-sm tw:overflow-auto">{{
73
+ JSON.stringify(testData, null, 2)
74
+ }}</pre>
75
+ </div>
76
+ </div>
77
+ </template>
78
+
79
+ <script setup lang="ts">
80
+ import { computed, ref, reactive, useTemplateRef } from "vue"
81
+ import DynamicForm from "@app/components/dynamicForm/DynamicForm.vue"
82
+ import type { DynamicFormItem } from "@app/components/dynamicForm/DynamicFormTypes"
83
+ import type { Trigger } from "@raclettejs/core"
84
+ import { emailValidator } from "../validators"
85
+
86
+ // Test data interface
87
+ interface TestFormData {
88
+ // Text inputs
89
+ requiredText: string
90
+ optionalText: string
91
+ textWithLength: string
92
+ textWithPattern: string
93
+
94
+ // Textarea
95
+ description: string
96
+
97
+ // Number
98
+ requiredNumber: number
99
+ numberWithRange: number
100
+
101
+ // Select inputs
102
+ singleSelect: string
103
+ multiSelect: string[]
104
+
105
+ // Boolean inputs
106
+ requiredSwitch: boolean
107
+ optionalCheckbox: boolean
108
+
109
+ // Array inputs
110
+ tags: string[]
111
+ permissions: string[]
112
+ listItems: string[]
113
+
114
+ // Object inputs
115
+ localized: Record<string, string>
116
+ record: Record<string, string>
117
+
118
+ // Special inputs
119
+ password: string
120
+ color: string
121
+ user: string
122
+ composition: string
123
+ triggers: Trigger[]
124
+ }
125
+
126
+ const dynamicFormRef = useTemplateRef("dynamicFormRef")
127
+ const isValid = ref(true)
128
+ const errors = ref<Record<string, string>>({})
129
+ const showFormData = ref(false)
130
+ const inputFilter = ref(0)
131
+
132
+ // Test data with initial values
133
+ const testData = reactive<TestFormData>({
134
+ requiredText: "",
135
+ optionalText: "",
136
+ textWithLength: "",
137
+ textWithPattern: "",
138
+ description: "",
139
+ requiredNumber: 0,
140
+ numberWithRange: 0,
141
+ singleSelect: "",
142
+ multiSelect: [],
143
+ requiredSwitch: false,
144
+ optionalCheckbox: false,
145
+ tags: [],
146
+ permissions: [],
147
+ listItems: [],
148
+ localized: { default: "", de: "", en: "" },
149
+ record: {},
150
+ password: "",
151
+ color: "#000000",
152
+ user: "",
153
+ composition: "",
154
+ triggers: [],
155
+ })
156
+
157
+ // Validation status computed properties
158
+ const validationStatusClass = computed(() => ({
159
+ "tw:bg-green-50 tw:border tw:border-green-200": isValid.value,
160
+ "tw:bg-red-50 tw:border tw:border-red-200": !isValid.value,
161
+ }))
162
+
163
+ const validationIndicatorClass = computed(() => ({
164
+ "tw:bg-green-500": isValid.value,
165
+ "tw:bg-red-500": !isValid.value,
166
+ }))
167
+
168
+ const validationStatusText = computed(() =>
169
+ isValid.value ? "Form is valid ✓" : "Form has validation errors ✗",
170
+ )
171
+
172
+ const filteredInputs = computed(() => {
173
+ if (inputFilter.value === 0) {
174
+ return inputFields.value
175
+ }
176
+
177
+ const result: typeof inputFields.value = []
178
+ let count = 0
179
+
180
+ for (const input of inputFields.value) {
181
+ result.push(input)
182
+
183
+ if (!("divider" in input)) {
184
+ count++
185
+ }
186
+
187
+ if (count >= inputFilter.value) {
188
+ break
189
+ }
190
+ }
191
+
192
+ return result
193
+ })
194
+
195
+ const onValidationChange = (valid: boolean) => {
196
+ isValid.value = valid
197
+ errors.value = dynamicFormRef.value?.errors ?? {}
198
+ }
199
+
200
+ const validateForm = () => {
201
+ dynamicFormRef.value?.validateAllFields()
202
+ }
203
+
204
+ const resetForm = () => {
205
+ Object.assign(testData, {
206
+ requiredText: "",
207
+ optionalText: "",
208
+ textWithLength: "",
209
+ textWithPattern: "",
210
+ description: "",
211
+ requiredNumber: 0,
212
+ numberWithRange: 0,
213
+ singleSelect: "",
214
+ multiSelect: [],
215
+ requiredSwitch: false,
216
+ optionalCheckbox: false,
217
+ tags: [],
218
+ permissions: [],
219
+ listItems: [],
220
+ localized: {},
221
+ record: {},
222
+ password: "",
223
+ color: "#000000",
224
+ user: "",
225
+ composition: "",
226
+ triggers: [],
227
+ })
228
+ }
229
+
230
+ // Form field definitions
231
+ const inputFields = computed<DynamicFormItem<TestFormData>[]>(() => [
232
+ { divider: "Text Inputs" },
233
+ {
234
+ title: "Required Text",
235
+ description: "This field is required and must be at least 3 characters",
236
+ inputType: "text",
237
+ field: "requiredText",
238
+ prependIcon: "text",
239
+ validation: {
240
+ required: true,
241
+ minLength: 3,
242
+ },
243
+ },
244
+ {
245
+ title: "Optional Text",
246
+ description: "This field is optional but has a max length of 50 characters",
247
+ inputType: "text",
248
+ field: "optionalText",
249
+ prependIcon: "text-short",
250
+ validation: {
251
+ maxLength: 50,
252
+ },
253
+ },
254
+ {
255
+ title: "Text with Length Validation",
256
+ description: "Must be between 5 and 20 characters",
257
+ inputType: "text",
258
+ field: "textWithLength",
259
+ prependIcon: "ruler",
260
+ validation: {
261
+ minLength: 5,
262
+ maxLength: 20,
263
+ },
264
+ },
265
+ {
266
+ title: "Email Address",
267
+ description: "Must be a valid email format",
268
+ inputType: "text",
269
+ field: "textWithPattern",
270
+ prependIcon: "email",
271
+ validation: {
272
+ required: true,
273
+ customValidator: emailValidator,
274
+ },
275
+ },
276
+
277
+ { divider: "Textarea" },
278
+ {
279
+ title: "Description",
280
+ description: "Optional description with max 200 characters",
281
+ inputType: "textarea",
282
+ field: "description",
283
+ prependIcon: "text",
284
+ validation: {
285
+ maxLength: 200,
286
+ },
287
+ },
288
+
289
+ { divider: "Number Inputs" },
290
+ {
291
+ title: "Required Number",
292
+ description: "This number field is required",
293
+ inputType: "number",
294
+ field: "requiredNumber",
295
+ prependIcon: "numeric",
296
+ validation: {
297
+ required: true,
298
+ },
299
+ },
300
+ {
301
+ title: "Number with Range",
302
+ description: "Must be between 10 and 100",
303
+ inputType: "number",
304
+ field: "numberWithRange",
305
+ prependIcon: "numeric-1",
306
+ validation: {
307
+ customValidator: (value: number) => {
308
+ if (value < 10 || value > 100) {
309
+ return "Number must be between 10 and 100"
310
+ }
311
+ return null
312
+ },
313
+ },
314
+ },
315
+
316
+ { divider: "Select Inputs" },
317
+ {
318
+ title: "Single Select",
319
+ description: "Choose one option (required)",
320
+ inputType: "select",
321
+ field: "singleSelect",
322
+ selectItems: ["Option 1", "Option 2", "Option 3", "Option 4"],
323
+ prependIcon: "form-select",
324
+ validation: {
325
+ required: true,
326
+ },
327
+ },
328
+ {
329
+ title: "Multi Select",
330
+ description: "Choose multiple options (at least 2 required)",
331
+ inputType: "multiselect",
332
+ field: "multiSelect",
333
+ selectItems: ["Tag A", "Tag B", "Tag C", "Tag D", "Tag E"],
334
+ prependIcon: "form-select",
335
+ validation: {
336
+ required: true,
337
+ customValidator: (value: string[]) => {
338
+ if (value.length < 2) {
339
+ return "Please select at least 2 options"
340
+ }
341
+ return null
342
+ },
343
+ },
344
+ },
345
+
346
+ { divider: "Boolean Inputs" },
347
+ {
348
+ title: "Required Switch",
349
+ description: "This switch must be enabled",
350
+ inputType: "switch",
351
+ field: "requiredSwitch",
352
+ validation: {
353
+ required: true,
354
+ customValidator: (value: boolean) =>
355
+ value ? null : "This switch must be enabled",
356
+ },
357
+ },
358
+ {
359
+ title: "Optional Checkbox",
360
+ description: "This checkbox is optional",
361
+ inputType: "checkbox",
362
+ field: "optionalCheckbox",
363
+ },
364
+
365
+ { divider: "Array Inputs" },
366
+ {
367
+ title: "Tags",
368
+ description: "Add at least 2 tags",
369
+ inputType: "tags",
370
+ field: "tags",
371
+ validation: {
372
+ required: true,
373
+ customValidator: (value: string[]) => {
374
+ if (value.length < 2) {
375
+ return "Please add at least 2 tags"
376
+ }
377
+ return null
378
+ },
379
+ },
380
+ },
381
+ {
382
+ title: "Permissions",
383
+ description: "Optional permissions list",
384
+ inputType: "permissions",
385
+ field: "permissions",
386
+ },
387
+ {
388
+ title: "List Items",
389
+ description: "Optional list of items",
390
+ inputType: "list",
391
+ field: "listItems",
392
+ validation: {
393
+ required: true,
394
+ },
395
+ },
396
+
397
+ { divider: "Object Inputs" },
398
+ {
399
+ title: "Localized Content",
400
+ description: "Add content in at least one language",
401
+ inputType: "localized",
402
+ field: "localized",
403
+ validation: {
404
+ required: true,
405
+ customValidator: (value: Record<string, string>) => {
406
+ if (!value || !value.default?.length) {
407
+ return "A default localization is required"
408
+ }
409
+
410
+ return null
411
+ },
412
+ },
413
+ },
414
+ {
415
+ title: "Record Data",
416
+ description: "Optional key-value pairs",
417
+ inputType: "record",
418
+ field: "record",
419
+ },
420
+
421
+ { divider: "Special Inputs" },
422
+ {
423
+ title: "Password",
424
+ description: "Must be at least 8 characters with special characters",
425
+ inputType: "password",
426
+ field: "password",
427
+ prependIcon: "lock",
428
+ validation: {
429
+ required: true,
430
+ minLength: 8,
431
+ customValidator: (value: string) => {
432
+ if (!/(?=.*[!@#$%^&*])/.test(value)) {
433
+ return "Password must contain at least one special character (!@#$%^&*)"
434
+ }
435
+ return null
436
+ },
437
+ },
438
+ },
439
+ {
440
+ title: "Color",
441
+ description: "Choose a color (required)",
442
+ inputType: "color",
443
+ field: "color",
444
+ prependIcon: "palette",
445
+ validation: {
446
+ required: true,
447
+ },
448
+ },
449
+ {
450
+ title: "User",
451
+ description: "Select a user (required)",
452
+ inputType: "user",
453
+ field: "user",
454
+ prependIcon: "account",
455
+ validation: {
456
+ required: true,
457
+ },
458
+ },
459
+ {
460
+ title: "Composition",
461
+ description: "Optional composition field",
462
+ inputType: "composition",
463
+ field: "composition",
464
+ prependIcon: "file-document",
465
+ validation: {
466
+ required: true,
467
+ },
468
+ },
469
+ {
470
+ title: "Triggers",
471
+ description: "Optional triggers configuration",
472
+ inputType: "triggers",
473
+ field: "triggers",
474
+ prependIcon: "gesture-tap-button",
475
+ },
476
+ ])
477
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <v-checkbox
3
+ v-model="modelValue"
4
+ :id="inputId"
5
+ variant="outlined"
6
+ hide-details
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ defineProps<{
12
+ inputId: string
13
+ }>()
14
+
15
+ const modelValue = defineModel<boolean>({ default: false })
16
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <v-menu v-model="showPicker" :close-on-content-click="false">
3
+ <template #activator="{ props }">
4
+ <v-text-field
5
+ v-model="color"
6
+ v-bind="{ ...props, ...$attrs }"
7
+ :label="$t('color')"
8
+ :color="color"
9
+ variant="outlined"
10
+ hide-details
11
+ :append-inner-icon="
12
+ !showPicker ? 'mdi-chevron-left' : 'mdi-chevron-down'
13
+ "
14
+ readonly
15
+ >
16
+ <template #prepend-inner>
17
+ <v-chip
18
+ :style="{
19
+ 'background-color': color,
20
+ }"
21
+ size="x-small"
22
+ :color="color"
23
+ label
24
+ class="tw:m-2 tw:w-[45px]"
25
+ />
26
+ </template>
27
+ </v-text-field>
28
+ </template>
29
+ <v-color-picker v-model="color" show-swatches />
30
+ <template #header></template>
31
+ </v-menu>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { ref } from "vue"
36
+
37
+ defineOptions({
38
+ inheritAttrs: false,
39
+ })
40
+
41
+ defineProps({
42
+ readOnly: Boolean,
43
+ inputId: {
44
+ type: String,
45
+ default: "",
46
+ },
47
+ })
48
+
49
+ const color = defineModel<string>({ required: true })
50
+
51
+ const showPicker = ref(false)
52
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <v-autocomplete
3
+ v-model="modelValue"
4
+ :id="inputId"
5
+ :items="compositions"
6
+ :loading="query.loading"
7
+ :disabled="query.loading"
8
+ item-value="_id"
9
+ item-title="title"
10
+ variant="outlined"
11
+ hide-details
12
+ />
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
17
+ import { computed } from "vue"
18
+
19
+ defineProps<{
20
+ inputId: string
21
+ }>()
22
+
23
+ const modelValue = defineModel<string | null>({ required: true })
24
+
25
+ const { $data } = usePluginApi("raclette__core")
26
+
27
+ const { data, query } = $data.composition.getAll({
28
+ options: { immediate: true },
29
+ })
30
+
31
+ const compositions = computed(() => Object.values(data.value || {}))
32
+ </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <v-autocomplete
3
+ v-model="modelProxy"
4
+ :items="icons"
5
+ label="Icon"
6
+ item-title="name"
7
+ item-value="name"
8
+ variant="outlined"
9
+ hide-details
10
+ :menu-props="{ maxHeight: 400 }"
11
+ :virtual-scroll="true"
12
+ >
13
+ <template #item="{ props, item }">
14
+ <v-list-item v-bind="props" :prepend-icon="`mdi-${item.raw}`" />
15
+ </template>
16
+
17
+ <template #selection="{ item }">
18
+ <v-icon :icon="`mdi-${item.value}`" class="me-2" />
19
+ {{ item.value }}
20
+ </template>
21
+ </v-autocomplete>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import icons from "./mdi-icons"
26
+ import { computed } from "vue"
27
+
28
+ const modelValue = defineModel<string | null>()
29
+
30
+ const modelProxy = computed({
31
+ get() {
32
+ return modelValue.value?.startsWith("mdi-")
33
+ ? modelValue.value.slice(4)
34
+ : modelValue.value
35
+ },
36
+ set(newValue: string | null) {
37
+ modelValue.value = newValue ? `mdi-${newValue}` : null
38
+ },
39
+ })
40
+ </script>
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <div class="tw:grid tw:gap-2">
3
+ <!-- existing tags -->
4
+ <v-text-field
5
+ v-for="(_, index) in modelValue"
6
+ v-bind="$attrs"
7
+ :key="index"
8
+ :id="index === 0 ? inputId : undefined"
9
+ v-model="modelValue[index]"
10
+ variant="outlined"
11
+ append-icon="mdi-minus"
12
+ @click:append="() => modelValue.length > 1 && modelValue.splice(index, 1)"
13
+ />
14
+
15
+ <!-- add new entry -->
16
+ <v-text-field
17
+ :id="modelValue?.length === 0 ? inputId : undefined"
18
+ v-bind="$attrs"
19
+ v-model="virtualInput"
20
+ variant="outlined"
21
+ hide-details
22
+ append-icon="mdi-plus"
23
+ @click:append="addVirtualInput"
24
+ @blur="addVirtualInput"
25
+ />
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { ref } from "vue"
31
+
32
+ defineOptions({
33
+ inheritAttrs: false,
34
+ })
35
+
36
+ defineProps<{
37
+ inputId?: string
38
+ }>()
39
+
40
+ const modelValue = defineModel<string[]>({ required: true })
41
+
42
+ const virtualInput = ref("")
43
+
44
+ const addVirtualInput = () => {
45
+ const val = virtualInput.value.trim()
46
+ if (val !== "") {
47
+ modelValue.value.push(val)
48
+ virtualInput.value = ""
49
+ }
50
+ }
51
+ </script>