bfg-common 1.2.67 → 1.2.69

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 (81) hide show
  1. package/components/atoms/alert/Notification.vue +167 -167
  2. package/components/atoms/autocomplete/Autocomplete.vue +236 -236
  3. package/components/atoms/datepicker/Datepicker.vue +612 -612
  4. package/components/atoms/list/SelectList.vue +62 -62
  5. package/components/atoms/nav/NavBar.vue +116 -116
  6. package/components/atoms/nav/VerticalNavBar.vue +89 -89
  7. package/components/atoms/notificationBar/NotificationBar.vue +175 -175
  8. package/components/atoms/perPage/PerPage.vue +52 -52
  9. package/components/atoms/popup/SimplePopup.vue +90 -90
  10. package/components/atoms/step/VerticalStep.vue +104 -104
  11. package/components/atoms/switch/Switch.vue +107 -107
  12. package/components/atoms/table/dataGrid/DataGridColumnSwitch.vue +206 -206
  13. package/components/atoms/table/dataGrid/DataGridPage.vue +189 -189
  14. package/components/atoms/tabs/Tabs.vue +189 -189
  15. package/components/atoms/tooltip/Signpost.vue +224 -224
  16. package/components/common/BadBrowser.vue +114 -114
  17. package/components/common/accordion/Accordion.vue +7 -14
  18. package/components/common/accordion/utils/accordion.ts +1 -1
  19. package/components/common/adapterManager/AddAdapterWarningModal.vue +82 -82
  20. package/components/common/adapterManager/NoActiveAdaptersModal.vue +62 -62
  21. package/components/common/adapterManager/NoConnectedActiveAdaptersModal.vue +62 -62
  22. package/components/common/browse/Browse.vue +182 -182
  23. package/components/common/browse/blocks/Title.vue +66 -66
  24. package/components/common/diagramMain/Diagram.vue +186 -186
  25. package/components/common/diagramMain/DiagramMain.vue +986 -986
  26. package/components/common/diagramMain/adapter/AdapterItem.vue +227 -227
  27. package/components/common/diagramMain/adapter/AdapterItems.vue +57 -57
  28. package/components/common/diagramMain/modals/editSettings/tabs/port/IpvFourSettings.vue +339 -339
  29. package/components/common/diagramMain/modals/editSettings/tabs/port/PortProperties.vue +202 -202
  30. package/components/common/diagramMain/modals/remove/RemoveModal.vue +226 -226
  31. package/components/common/diagramMain/network/Contents.vue +238 -238
  32. package/components/common/feedback/Buttons.vue +227 -227
  33. package/components/common/feedback/Feedback.vue +261 -261
  34. package/components/common/feedback/Message.vue +506 -506
  35. package/components/common/feedback/VisitPortal.vue +53 -53
  36. package/components/common/help/navbar/left/Left.vue +164 -159
  37. package/components/common/help/navbar/left/utils/constructAccordion.ts +27 -27
  38. package/components/common/modals/Rename.vue +165 -165
  39. package/components/common/monitor/advanced/tools/chartOptionsModal/ChartOptionsModal.vue +2 -1
  40. package/components/common/monitor/advanced/tools/chartOptionsModal/counters/table/Table.vue +9 -3
  41. package/components/common/perPage/PerPage.vue +51 -51
  42. package/components/common/recursionTree/RecursionTree.vue +201 -201
  43. package/components/common/vm/actions/clone/Clone.vue +545 -545
  44. package/components/common/vm/actions/clone/select/options/Options.vue +65 -65
  45. package/components/common/vm/actions/clone/utils.ts +43 -43
  46. package/components/common/vm/actions/common/config/capabilities.ts +338 -338
  47. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +678 -678
  48. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/CoresPerSocket.vue +109 -109
  49. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/Cpu.vue +383 -383
  50. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/Limit.vue +234 -234
  51. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/config/options.ts +28 -28
  52. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/Memory.vue +316 -316
  53. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/config/memoryOptions.ts +35 -35
  54. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +445 -445
  55. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +288 -288
  56. package/components/common/vm/actions/common/customizeHardware/virtualHardware/reservation/Reservation.vue +235 -235
  57. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +177 -177
  58. package/components/common/vm/actions/common/customizeHardware/vmoptions/bootOptions/order/Order.vue +197 -197
  59. package/components/common/vm/actions/common/customizeHardware/vmoptions/generalOptions/GeneralOptions.vue +181 -181
  60. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/CopyPaste.vue +51 -51
  61. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/RemoteConsoleOptions.vue +253 -253
  62. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/keymap/Keymap.vue +55 -55
  63. package/components/common/vm/actions/common/select/storage/Storage.vue +194 -194
  64. package/components/common/vm/actions/common/select/storage/config/config.ts +154 -154
  65. package/components/common/wizards/datastore/add/nfs/Nfs.vue +94 -94
  66. package/components/common/wizards/datastore/add/nfs/configuration/Configuration.vue +377 -377
  67. package/components/common/wizards/datastore/add/nfs/configuration/serversList/ServersList.vue +123 -123
  68. package/components/common/wizards/datastore/add/nfs/kerberosAuthentication/KerberosAuthentication.vue +54 -54
  69. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/hostTable/HostTable.vue +131 -131
  70. package/components/common/wizards/datastore/add/sharedStorm/partitionConfiguration/PartitionConfiguration.vue +230 -230
  71. package/composables/useLocal.ts +38 -38
  72. package/minify.js +146 -146
  73. package/models/store/interfaces.ts +37 -37
  74. package/models/store/storage/interfaces.ts +32 -32
  75. package/models/store/types.ts +32 -32
  76. package/modules/fixContentBuild/utils/methods.ts +114 -114
  77. package/package.json +1 -1
  78. package/public/spice-console/application/codec.js +257 -257
  79. package/public/spice-console/lib/rasterEngine.js +907 -907
  80. package/public/spice-console/network/spicechannel.js +369 -369
  81. package/utils/contentBuild.ts +34 -34
@@ -1,506 +1,506 @@
1
- <template>
2
- <div class="message">
3
- <div class="main-block">
4
- <span class="text">{{ localization.description }}</span>
5
- <textarea
6
- id="feedback-message-textarea"
7
- v-model="descriptionLocal"
8
- :disabled="props.type === undefined"
9
- class="clr-textarea ng-pristine ng-invalid ng-touched textarea"
10
- :placeholder="descriptionTextPlaceholder"
11
- ></textarea>
12
- </div>
13
- <div class="main-block">
14
- <span class="text">{{ localization.emailAddress }} </span>
15
- <atoms-tooltip-error
16
- class="tooltip-error"
17
- :has-error="isInvalidEmail"
18
- selector="#vm-wizard-vcpus-field input"
19
- >
20
- <template #elem>
21
- <input
22
- id="feedback-email-input"
23
- v-model="emailLocal"
24
- type="email"
25
- :class="['email', { 'has-error': isInvalidEmail }]"
26
- :placeholder="localization.optionalInCaseWeNeed"
27
- @blur="isStartEmailValidation = true"
28
- />
29
- </template>
30
- <template #content>{{ localization.emailErrorText }}</template>
31
- </atoms-tooltip-error>
32
- </div>
33
- <div class="main-block">
34
- <span class="text">{{ localization.additionalDetails }} </span>
35
- <button
36
- v-if="!hasTakeScreenshot"
37
- id="feedback-take-screenshot-button"
38
- class="btn btn-sm ng-star-inserted"
39
- @click="takeScreenshot"
40
- >
41
- {{ localization.takeScreenshot }}
42
- </button>
43
-
44
- <button
45
- v-else
46
- id="feedback-remove-button"
47
- class="btn btn-sm btn-warning-outline ng-star-inserted"
48
- @click="removeScreenshot"
49
- >
50
- {{ localization.remove }}
51
- </button>
52
- </div>
53
- <template v-if="hasTakeScreenshot">
54
- <div class="main-block main-block__last">
55
- <span class="text"></span>
56
- {{ localization.clickAndDragTheMouseToAnnotate }}
57
- <!-- <a
58
- v-for="tab in drawingPanel"
59
- :key="tab.function"
60
- href="javascript:void(0)"
61
- role="button"
62
- class="content-padding"
63
- @click="tab.function"
64
- >
65
- {{ tab.text }}</a
66
- > -->
67
- <a
68
- id="feedback-use-red-marker-button"
69
- href="javascript:void(0)"
70
- role="button"
71
- class="content-padding"
72
- @click="useRedMarker"
73
- >
74
- {{ localization.useRedMarker }}</a
75
- >
76
- <a
77
- id="feedback-use-grey-marker-button"
78
- href="javascript:void(0)"
79
- role="button"
80
- class="content-padding"
81
- @click="useGrayEraser"
82
- >
83
- {{ localization.useGrayEraser }}</a
84
- >
85
- <a
86
- id="feedback-undo-button"
87
- href="javascript:void(0)"
88
- role="button"
89
- class="content-padding"
90
- @click="undoLast"
91
- >
92
- {{ localization.undo }}</a
93
- >
94
- <a
95
- id="feedback-clear-all-button"
96
- href="javascript:void(0)"
97
- role="button"
98
- class="content-padding"
99
- @click="clearAll"
100
- >
101
- {{ localization.clearAll }}</a
102
- >
103
- </div>
104
- <div class="main-block main-block__last">
105
- <span class="text"></span>
106
- <atoms-alert
107
- v-show="true"
108
- test-id="feedback-message"
109
- status="alert-info"
110
- :items="[localization.screenshotQualityMayVary]"
111
- />
112
- </div>
113
- </template>
114
- <div v-else class="main-block main-block__last">
115
- <span class="text"></span>
116
- <div>
117
- <div class="btn btn-sm loadFile">
118
- <label id="labelFileLoad" for="fileLoad">{{
119
- localization.uploadImage
120
- }}</label>
121
- <input
122
- id="fileLoad"
123
- ref="isFileLoad"
124
- type="file"
125
- accept=".png, .jpeg"
126
- @change="onChangeImgLoad"
127
- />
128
- </div>
129
- <span>{{ localization.pngJpegFilesSupported }}</span>
130
- </div>
131
- </div>
132
-
133
- <div id="feedbackImgWrap" ref="feedbackImgWrap">
134
- <canvas
135
- id="feedbackImg"
136
- ref="feedbackImg"
137
- :class="{ feedbackImg: !hasTakeScreenshot }"
138
- ></canvas>
139
- <canvas id="feedbackImgBlank" style="display: none"></canvas>
140
- </div>
141
- </div>
142
- </template>
143
-
144
- <script setup lang="ts">
145
- import html2canvas from 'html2canvas'
146
- import { UI_I_Localization } from '~/models/interfaces'
147
- import { UI_I_MessageImg } from '~/components/common/feedback/models/interfaces'
148
- import { UI_T_FeedbackType } from '~/components/common/feedback/models/types'
149
-
150
- const props = defineProps<{
151
- type: UI_T_FeedbackType
152
- description: string
153
- email: string
154
- }>()
155
- const emits = defineEmits<{
156
- (event: 'screen-shot', value: UI_I_MessageImg): void
157
- (event: 'update:description', value: string): void
158
- (event: 'update:email', value: string): void
159
- }>()
160
-
161
- const descriptionLocal = computed<string>({
162
- get() {
163
- return props.description
164
- },
165
- set(value) {
166
- emits('update:description', value)
167
- },
168
- })
169
- const emailLocal = computed<string>({
170
- get() {
171
- return props.email
172
- },
173
- set(value) {
174
- emits('update:email', value)
175
- },
176
- })
177
- const isInvalidEmail = computed<boolean>(
178
- () => isStartEmailValidation.value && !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailLocal.value)
179
- )
180
- const isStartEmailValidation = ref<boolean>(false)
181
- const localization = computed<UI_I_Localization>(() => useLocal())
182
- // const drawingPanel = computed(() => drawingPanelFunc(localization.value))
183
-
184
- const descriptionTextPlaceholder = computed<string>(() => {
185
- if (props.type === 0) return localization.value.whatIsTheProblemHowCan
186
- if (props.type === 1) return localization.value.tellUsWhatYouEnjoyedSo
187
- return localization.value.selectFeedbackType
188
- })
189
- const hasTakeScreenshot = ref<boolean>(false)
190
- const feedbackImgWrap = ref()
191
- const feedbackImg = ref()
192
- const isFileLoad = ref(null)
193
- const memoryParentMain = ref<string>('')
194
- const memoryParentModalDialog = ref<string>('')
195
-
196
- const takeScreenshot = () => {
197
- hasTakeScreenshot.value = true
198
- const value = {
199
- hasTakeScreenshot: true,
200
- width: window.outerWidth + 'px',
201
- }
202
-
203
- emits('screen-shot', value)
204
- const parentMain = document.getElementById('feedbackId')
205
- const parentModalDialog = document.getElementById('feedback-modal-dialog-Id')
206
- if (!parentMain || !parentModalDialog) return
207
-
208
- const display = parentMain.style.display
209
- memoryParentMain.value = parentMain.style.display
210
- parentMain.style.display = 'none'
211
- html2canvas(document.body).then(function (thisCanvas) {
212
- parentMain.style.display = display
213
- memoryParentMain.value = parentMain.style.width
214
- memoryParentModalDialog.value = parentModalDialog.style.width
215
- parentMain.style.width = window.outerWidth + 'px'
216
- parentModalDialog.style.width = window.outerWidth + 'px'
217
- parentModalDialog.style.margin = '20px'
218
- const context = feedbackImg.value.getContext('2d')
219
- feedbackImg.value.width = thisCanvas.width
220
- feedbackImg.value.height = thisCanvas.height
221
- context.drawImage(
222
- thisCanvas,
223
- 0,
224
- 0,
225
- feedbackImg.value.width,
226
- feedbackImg.value.height
227
- )
228
- drawingArray.length = 0
229
- indexArray = 0
230
- drawingArray.push(
231
- context.getImageData(
232
- 0,
233
- 0,
234
- feedbackImg.value.width,
235
- feedbackImg.value.height
236
- )
237
- )
238
- })
239
- }
240
- const removeScreenshot = () => {
241
- hasTakeScreenshot.value = false
242
- const parentMain = document.getElementById('feedbackId')
243
- const parentModalDialog = document.getElementById('feedback-modal-dialog-Id')
244
- if (!parentMain || !parentModalDialog) return
245
-
246
- parentMain.style.width = memoryParentMain.value
247
- parentModalDialog.style.width = memoryParentModalDialog.value
248
- const context = feedbackImg.value.getContext('2d')
249
- context.fillStyle = 'green'
250
- context.fillRect(0, 0, feedbackImg.value.width, feedbackImg.value.height)
251
- feedbackImg.value.width = 0
252
- feedbackImg.value.height = 0
253
- feedbackImgWrap.value.style.width = 0 + 'px'
254
- feedbackImgWrap.value.style.height = 0 + 'px'
255
- const value = {
256
- hasTakeScreenshot: false,
257
- width: memoryParentModalDialog.value,
258
- }
259
- emits('screen-shot', value)
260
- clearAll()
261
- }
262
-
263
- const onChangeImgLoad = () => {
264
- hasTakeScreenshot.value = true
265
- if (!isFileLoad.value) return
266
-
267
- const isFileRead = new FileReader()
268
- if (!isFileRead) return
269
-
270
- isFileRead.readAsDataURL(isFileLoad.value.files[0])
271
- isFileRead.onload = () => {
272
- const img = new Image()
273
- if (!feedbackImg.value) return
274
-
275
- const context = feedbackImg.value.getContext('2d')
276
- img.onload = () => {
277
- feedbackImg.value.width = img.width
278
- feedbackImg.value.height = img.height
279
- context.drawImage(img, 0, 0, img.width, img.height)
280
- drawingArray.length = 0
281
- indexArray = 0
282
- drawingArray.push(
283
- context.getImageData(
284
- 0,
285
- 0,
286
- feedbackImg.value.width,
287
- feedbackImg.value.height
288
- )
289
- )
290
- const value = {
291
- hasTakeScreenshot: true,
292
- width: window.outerWidth + 'px',
293
- }
294
- emits('screen-shot', value)
295
- const parentMain = document.getElementById('feedbackId')
296
- const parentModalDialog = document.getElementById(
297
- 'feedback-modal-dialog-Id'
298
- )
299
- if (!parentMain || !parentModalDialog) return
300
-
301
- parentMain.style.width = window.outerWidth + 'px'
302
- parentModalDialog.style.width = window.outerWidth + 'px'
303
- parentModalDialog.style.margin = '20px'
304
- }
305
- img.src = isFileRead.result
306
- }
307
- }
308
- const drawingArray: any = []
309
- let indexArray = 0 // index of feedbackImg itself
310
- let isDrawing = false
311
- let startDrawingX: number | null = null
312
- let startDrawingY: number | null = null
313
- let drawColor = ''
314
- let drawWidth = ''
315
-
316
- const useRedMarker = () => {
317
- drawColor = '#f76945'
318
- drawWidth = '5'
319
- }
320
- const useGrayEraser = () => {
321
- drawColor = 'lightgray'
322
- drawWidth = '14'
323
- }
324
- const undoLast = () => {
325
- drawColor = ''
326
- drawWidth = ''
327
- const context = feedbackImg.value.getContext('2d')
328
- if (indexArray > 0) {
329
- indexArray -= 1
330
- drawingArray.pop()
331
- context.putImageData(drawingArray[indexArray], 0, 0)
332
- }
333
- }
334
- const clearAll = () => {
335
- const context = feedbackImg.value.getContext('2d')
336
- drawColor = ''
337
- drawWidth = ''
338
- if (indexArray <= 0) return
339
- while (indexArray > 0) {
340
- drawingArray.pop()
341
- indexArray -= 1
342
- context.putImageData(drawingArray[indexArray], 0, 0)
343
- }
344
- }
345
- const start = (event: MouseEvent) => {
346
- if (!drawColor) return
347
- const context = feedbackImg.value.getContext('2d')
348
- const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
349
- startDrawingX = event.pageX - canvasCoordinates.left
350
- startDrawingY = event.pageY - canvasCoordinates.top
351
- isDrawing = true
352
- context.beginPath()
353
- context.moveTo(
354
- event.pageX - canvasCoordinates.left,
355
- event.pageY - canvasCoordinates.top
356
- )
357
- event.preventDefault()
358
- }
359
- const draw = (event: MouseEvent) => {
360
- if (!drawColor) return
361
- const context = feedbackImg.value.getContext('2d')
362
- const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
363
- if (!isDrawing) return
364
- context.lineTo(
365
- event.pageX - canvasCoordinates.left,
366
- event.pageY - canvasCoordinates.top
367
- )
368
- context.strokeStyle = drawColor
369
- context.lineWidth = drawWidth
370
- context.lineCap = 'round'
371
- context.lineJoin = 'round'
372
- context.stroke()
373
- event.preventDefault()
374
- }
375
-
376
- const stop = (event: MouseEvent) => {
377
- if (!drawColor) return
378
-
379
- const context = feedbackImg.value.getContext('2d')
380
- const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
381
- if (!isDrawing) return
382
-
383
- context.stroke()
384
- context.closePath()
385
- isDrawing = false
386
- event.preventDefault()
387
- if (
388
- startDrawingX !== event.pageX - canvasCoordinates.left &&
389
- startDrawingY !== event.pageY - canvasCoordinates.top
390
- ) {
391
- drawingArray.push(
392
- context.getImageData(
393
- 0,
394
- 0,
395
- feedbackImg.value.width,
396
- feedbackImg.value.height
397
- )
398
- )
399
- indexArray += 1
400
- }
401
- }
402
- onMounted(() => {
403
- feedbackImg.value.addEventListener('touchstart', start, false)
404
- feedbackImg.value.addEventListener('touchmove', draw, false)
405
- feedbackImg.value.addEventListener('mousedown', start, false)
406
- feedbackImg.value.addEventListener('mousemove', draw, false)
407
- feedbackImg.value.addEventListener('touchend', stop, false)
408
- feedbackImg.value.addEventListener('mouseup', stop, false)
409
- feedbackImg.value.addEventListener('mouseout', stop, false)
410
- })
411
-
412
- onBeforeUnmount(() => {
413
- removeScreenshot()
414
- feedbackImg.value.removeEventListener('touchstart', start, false)
415
- feedbackImg.value.removeEventListener('touchmove', draw, false)
416
- feedbackImg.value.removeEventListener('mousedown', start, false)
417
- feedbackImg.value.removeEventListener('mousemove', draw, false)
418
- feedbackImg.value.removeEventListener('touchend', stop, false)
419
- feedbackImg.value.removeEventListener('mouseup', stop, false)
420
- feedbackImg.value.removeEventListener('mouseout', stop, false)
421
- })
422
- </script>
423
-
424
- <style scoped lang="scss">
425
- .message {
426
- overflow-x: auto;
427
- .main-block {
428
- display: flex;
429
- flex-direction: row;
430
- align-items: flex-start;
431
- margin-bottom: 20px;
432
- .text {
433
- padding-top: 0;
434
- min-width: 140px;
435
- margin-right: 10px;
436
- }
437
- .textarea {
438
- height: 150px;
439
- width: 100%;
440
- vertical-align: start;
441
- background-color: var(--modal-bg-color);
442
- }
443
- .email {
444
- width: 100%;
445
- }
446
-
447
- &__last {
448
- margin-top: -20px;
449
- width: 100%;
450
- .content-padding {
451
- padding-left: 4px;
452
- color: var(--a-link-color);
453
- }
454
- }
455
- :deep(.alert.alert-info) {
456
- width: 100%;
457
- }
458
-
459
- .tooltip-error {
460
- flex: 1;
461
-
462
- :deep(.element-content) {
463
- width: 100%;
464
- }
465
- }
466
- .has-error {
467
- border: 1px solid #db2100;
468
- }
469
- }
470
- }
471
- .loadFile {
472
- width: fit-content;
473
- margin-right: 10px;
474
- padding: 0;
475
- z-index: 11;
476
- label {
477
- color: inherit;
478
- cursor: pointer;
479
- padding: 0 10px;
480
- line-height: calc(1rem - 1px);
481
- letter-spacing: 0.073em;
482
- font-size: 0.458333rem;
483
- font-weight: 500;
484
- height: 1rem;
485
- padding: 0 0.5rem;
486
- }
487
- input {
488
- width: 0.01px;
489
- height: 0.01px;
490
- opacity: 0;
491
- overflow: hidden;
492
- z-index: -1;
493
- }
494
- }
495
- .feedbackImg {
496
- display: none;
497
- }
498
- </style>
499
- <style>
500
- :root {
501
- --a-link-color: #0072a3;
502
- }
503
- :root.dark-theme {
504
- --a-link-color: #4aaed9;
505
- }
506
- </style>
1
+ <template>
2
+ <div class="message">
3
+ <div class="main-block">
4
+ <span class="text">{{ localization.description }}</span>
5
+ <textarea
6
+ id="feedback-message-textarea"
7
+ v-model="descriptionLocal"
8
+ :disabled="props.type === undefined"
9
+ class="clr-textarea ng-pristine ng-invalid ng-touched textarea"
10
+ :placeholder="descriptionTextPlaceholder"
11
+ ></textarea>
12
+ </div>
13
+ <div class="main-block">
14
+ <span class="text">{{ localization.emailAddress }} </span>
15
+ <atoms-tooltip-error
16
+ class="tooltip-error"
17
+ :has-error="isInvalidEmail"
18
+ selector="#vm-wizard-vcpus-field input"
19
+ >
20
+ <template #elem>
21
+ <input
22
+ id="feedback-email-input"
23
+ v-model="emailLocal"
24
+ type="email"
25
+ :class="['email', { 'has-error': isInvalidEmail }]"
26
+ :placeholder="localization.optionalInCaseWeNeed"
27
+ @blur="isStartEmailValidation = true"
28
+ />
29
+ </template>
30
+ <template #content>{{ localization.emailErrorText }}</template>
31
+ </atoms-tooltip-error>
32
+ </div>
33
+ <div class="main-block">
34
+ <span class="text">{{ localization.additionalDetails }} </span>
35
+ <button
36
+ v-if="!hasTakeScreenshot"
37
+ id="feedback-take-screenshot-button"
38
+ class="btn btn-sm ng-star-inserted"
39
+ @click="takeScreenshot"
40
+ >
41
+ {{ localization.takeScreenshot }}
42
+ </button>
43
+
44
+ <button
45
+ v-else
46
+ id="feedback-remove-button"
47
+ class="btn btn-sm btn-warning-outline ng-star-inserted"
48
+ @click="removeScreenshot"
49
+ >
50
+ {{ localization.remove }}
51
+ </button>
52
+ </div>
53
+ <template v-if="hasTakeScreenshot">
54
+ <div class="main-block main-block__last">
55
+ <span class="text"></span>
56
+ {{ localization.clickAndDragTheMouseToAnnotate }}
57
+ <!-- <a
58
+ v-for="tab in drawingPanel"
59
+ :key="tab.function"
60
+ href="javascript:void(0)"
61
+ role="button"
62
+ class="content-padding"
63
+ @click="tab.function"
64
+ >
65
+ {{ tab.text }}</a
66
+ > -->
67
+ <a
68
+ id="feedback-use-red-marker-button"
69
+ href="javascript:void(0)"
70
+ role="button"
71
+ class="content-padding"
72
+ @click="useRedMarker"
73
+ >
74
+ {{ localization.useRedMarker }}</a
75
+ >
76
+ <a
77
+ id="feedback-use-grey-marker-button"
78
+ href="javascript:void(0)"
79
+ role="button"
80
+ class="content-padding"
81
+ @click="useGrayEraser"
82
+ >
83
+ {{ localization.useGrayEraser }}</a
84
+ >
85
+ <a
86
+ id="feedback-undo-button"
87
+ href="javascript:void(0)"
88
+ role="button"
89
+ class="content-padding"
90
+ @click="undoLast"
91
+ >
92
+ {{ localization.undo }}</a
93
+ >
94
+ <a
95
+ id="feedback-clear-all-button"
96
+ href="javascript:void(0)"
97
+ role="button"
98
+ class="content-padding"
99
+ @click="clearAll"
100
+ >
101
+ {{ localization.clearAll }}</a
102
+ >
103
+ </div>
104
+ <div class="main-block main-block__last">
105
+ <span class="text"></span>
106
+ <atoms-alert
107
+ v-show="true"
108
+ test-id="feedback-message"
109
+ status="alert-info"
110
+ :items="[localization.screenshotQualityMayVary]"
111
+ />
112
+ </div>
113
+ </template>
114
+ <div v-else class="main-block main-block__last">
115
+ <span class="text"></span>
116
+ <div>
117
+ <div class="btn btn-sm loadFile">
118
+ <label id="labelFileLoad" for="fileLoad">{{
119
+ localization.uploadImage
120
+ }}</label>
121
+ <input
122
+ id="fileLoad"
123
+ ref="isFileLoad"
124
+ type="file"
125
+ accept=".png, .jpeg"
126
+ @change="onChangeImgLoad"
127
+ />
128
+ </div>
129
+ <span>{{ localization.pngJpegFilesSupported }}</span>
130
+ </div>
131
+ </div>
132
+
133
+ <div id="feedbackImgWrap" ref="feedbackImgWrap">
134
+ <canvas
135
+ id="feedbackImg"
136
+ ref="feedbackImg"
137
+ :class="{ feedbackImg: !hasTakeScreenshot }"
138
+ ></canvas>
139
+ <canvas id="feedbackImgBlank" style="display: none"></canvas>
140
+ </div>
141
+ </div>
142
+ </template>
143
+
144
+ <script setup lang="ts">
145
+ import html2canvas from 'html2canvas'
146
+ import { UI_I_Localization } from '~/models/interfaces'
147
+ import { UI_I_MessageImg } from '~/components/common/feedback/models/interfaces'
148
+ import { UI_T_FeedbackType } from '~/components/common/feedback/models/types'
149
+
150
+ const props = defineProps<{
151
+ type: UI_T_FeedbackType
152
+ description: string
153
+ email: string
154
+ }>()
155
+ const emits = defineEmits<{
156
+ (event: 'screen-shot', value: UI_I_MessageImg): void
157
+ (event: 'update:description', value: string): void
158
+ (event: 'update:email', value: string): void
159
+ }>()
160
+
161
+ const descriptionLocal = computed<string>({
162
+ get() {
163
+ return props.description
164
+ },
165
+ set(value) {
166
+ emits('update:description', value)
167
+ },
168
+ })
169
+ const emailLocal = computed<string>({
170
+ get() {
171
+ return props.email
172
+ },
173
+ set(value) {
174
+ emits('update:email', value)
175
+ },
176
+ })
177
+ const isInvalidEmail = computed<boolean>(
178
+ () => isStartEmailValidation.value && !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailLocal.value)
179
+ )
180
+ const isStartEmailValidation = ref<boolean>(false)
181
+ const localization = computed<UI_I_Localization>(() => useLocal())
182
+ // const drawingPanel = computed(() => drawingPanelFunc(localization.value))
183
+
184
+ const descriptionTextPlaceholder = computed<string>(() => {
185
+ if (props.type === 0) return localization.value.whatIsTheProblemHowCan
186
+ if (props.type === 1) return localization.value.tellUsWhatYouEnjoyedSo
187
+ return localization.value.selectFeedbackType
188
+ })
189
+ const hasTakeScreenshot = ref<boolean>(false)
190
+ const feedbackImgWrap = ref()
191
+ const feedbackImg = ref()
192
+ const isFileLoad = ref(null)
193
+ const memoryParentMain = ref<string>('')
194
+ const memoryParentModalDialog = ref<string>('')
195
+
196
+ const takeScreenshot = () => {
197
+ hasTakeScreenshot.value = true
198
+ const value = {
199
+ hasTakeScreenshot: true,
200
+ width: window.outerWidth + 'px',
201
+ }
202
+
203
+ emits('screen-shot', value)
204
+ const parentMain = document.getElementById('feedbackId')
205
+ const parentModalDialog = document.getElementById('feedback-modal-dialog-Id')
206
+ if (!parentMain || !parentModalDialog) return
207
+
208
+ const display = parentMain.style.display
209
+ memoryParentMain.value = parentMain.style.display
210
+ parentMain.style.display = 'none'
211
+ html2canvas(document.body).then(function (thisCanvas) {
212
+ parentMain.style.display = display
213
+ memoryParentMain.value = parentMain.style.width
214
+ memoryParentModalDialog.value = parentModalDialog.style.width
215
+ parentMain.style.width = window.outerWidth + 'px'
216
+ parentModalDialog.style.width = window.outerWidth + 'px'
217
+ parentModalDialog.style.margin = '20px'
218
+ const context = feedbackImg.value.getContext('2d')
219
+ feedbackImg.value.width = thisCanvas.width
220
+ feedbackImg.value.height = thisCanvas.height
221
+ context.drawImage(
222
+ thisCanvas,
223
+ 0,
224
+ 0,
225
+ feedbackImg.value.width,
226
+ feedbackImg.value.height
227
+ )
228
+ drawingArray.length = 0
229
+ indexArray = 0
230
+ drawingArray.push(
231
+ context.getImageData(
232
+ 0,
233
+ 0,
234
+ feedbackImg.value.width,
235
+ feedbackImg.value.height
236
+ )
237
+ )
238
+ })
239
+ }
240
+ const removeScreenshot = () => {
241
+ hasTakeScreenshot.value = false
242
+ const parentMain = document.getElementById('feedbackId')
243
+ const parentModalDialog = document.getElementById('feedback-modal-dialog-Id')
244
+ if (!parentMain || !parentModalDialog) return
245
+
246
+ parentMain.style.width = memoryParentMain.value
247
+ parentModalDialog.style.width = memoryParentModalDialog.value
248
+ const context = feedbackImg.value.getContext('2d')
249
+ context.fillStyle = 'green'
250
+ context.fillRect(0, 0, feedbackImg.value.width, feedbackImg.value.height)
251
+ feedbackImg.value.width = 0
252
+ feedbackImg.value.height = 0
253
+ feedbackImgWrap.value.style.width = 0 + 'px'
254
+ feedbackImgWrap.value.style.height = 0 + 'px'
255
+ const value = {
256
+ hasTakeScreenshot: false,
257
+ width: memoryParentModalDialog.value,
258
+ }
259
+ emits('screen-shot', value)
260
+ clearAll()
261
+ }
262
+
263
+ const onChangeImgLoad = () => {
264
+ hasTakeScreenshot.value = true
265
+ if (!isFileLoad.value) return
266
+
267
+ const isFileRead = new FileReader()
268
+ if (!isFileRead) return
269
+
270
+ isFileRead.readAsDataURL(isFileLoad.value.files[0])
271
+ isFileRead.onload = () => {
272
+ const img = new Image()
273
+ if (!feedbackImg.value) return
274
+
275
+ const context = feedbackImg.value.getContext('2d')
276
+ img.onload = () => {
277
+ feedbackImg.value.width = img.width
278
+ feedbackImg.value.height = img.height
279
+ context.drawImage(img, 0, 0, img.width, img.height)
280
+ drawingArray.length = 0
281
+ indexArray = 0
282
+ drawingArray.push(
283
+ context.getImageData(
284
+ 0,
285
+ 0,
286
+ feedbackImg.value.width,
287
+ feedbackImg.value.height
288
+ )
289
+ )
290
+ const value = {
291
+ hasTakeScreenshot: true,
292
+ width: window.outerWidth + 'px',
293
+ }
294
+ emits('screen-shot', value)
295
+ const parentMain = document.getElementById('feedbackId')
296
+ const parentModalDialog = document.getElementById(
297
+ 'feedback-modal-dialog-Id'
298
+ )
299
+ if (!parentMain || !parentModalDialog) return
300
+
301
+ parentMain.style.width = window.outerWidth + 'px'
302
+ parentModalDialog.style.width = window.outerWidth + 'px'
303
+ parentModalDialog.style.margin = '20px'
304
+ }
305
+ img.src = isFileRead.result
306
+ }
307
+ }
308
+ const drawingArray: any = []
309
+ let indexArray = 0 // index of feedbackImg itself
310
+ let isDrawing = false
311
+ let startDrawingX: number | null = null
312
+ let startDrawingY: number | null = null
313
+ let drawColor = ''
314
+ let drawWidth = ''
315
+
316
+ const useRedMarker = () => {
317
+ drawColor = '#f76945'
318
+ drawWidth = '5'
319
+ }
320
+ const useGrayEraser = () => {
321
+ drawColor = 'lightgray'
322
+ drawWidth = '14'
323
+ }
324
+ const undoLast = () => {
325
+ drawColor = ''
326
+ drawWidth = ''
327
+ const context = feedbackImg.value.getContext('2d')
328
+ if (indexArray > 0) {
329
+ indexArray -= 1
330
+ drawingArray.pop()
331
+ context.putImageData(drawingArray[indexArray], 0, 0)
332
+ }
333
+ }
334
+ const clearAll = () => {
335
+ const context = feedbackImg.value.getContext('2d')
336
+ drawColor = ''
337
+ drawWidth = ''
338
+ if (indexArray <= 0) return
339
+ while (indexArray > 0) {
340
+ drawingArray.pop()
341
+ indexArray -= 1
342
+ context.putImageData(drawingArray[indexArray], 0, 0)
343
+ }
344
+ }
345
+ const start = (event: MouseEvent) => {
346
+ if (!drawColor) return
347
+ const context = feedbackImg.value.getContext('2d')
348
+ const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
349
+ startDrawingX = event.pageX - canvasCoordinates.left
350
+ startDrawingY = event.pageY - canvasCoordinates.top
351
+ isDrawing = true
352
+ context.beginPath()
353
+ context.moveTo(
354
+ event.pageX - canvasCoordinates.left,
355
+ event.pageY - canvasCoordinates.top
356
+ )
357
+ event.preventDefault()
358
+ }
359
+ const draw = (event: MouseEvent) => {
360
+ if (!drawColor) return
361
+ const context = feedbackImg.value.getContext('2d')
362
+ const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
363
+ if (!isDrawing) return
364
+ context.lineTo(
365
+ event.pageX - canvasCoordinates.left,
366
+ event.pageY - canvasCoordinates.top
367
+ )
368
+ context.strokeStyle = drawColor
369
+ context.lineWidth = drawWidth
370
+ context.lineCap = 'round'
371
+ context.lineJoin = 'round'
372
+ context.stroke()
373
+ event.preventDefault()
374
+ }
375
+
376
+ const stop = (event: MouseEvent) => {
377
+ if (!drawColor) return
378
+
379
+ const context = feedbackImg.value.getContext('2d')
380
+ const canvasCoordinates = feedbackImg.value.getBoundingClientRect()
381
+ if (!isDrawing) return
382
+
383
+ context.stroke()
384
+ context.closePath()
385
+ isDrawing = false
386
+ event.preventDefault()
387
+ if (
388
+ startDrawingX !== event.pageX - canvasCoordinates.left &&
389
+ startDrawingY !== event.pageY - canvasCoordinates.top
390
+ ) {
391
+ drawingArray.push(
392
+ context.getImageData(
393
+ 0,
394
+ 0,
395
+ feedbackImg.value.width,
396
+ feedbackImg.value.height
397
+ )
398
+ )
399
+ indexArray += 1
400
+ }
401
+ }
402
+ onMounted(() => {
403
+ feedbackImg.value.addEventListener('touchstart', start, false)
404
+ feedbackImg.value.addEventListener('touchmove', draw, false)
405
+ feedbackImg.value.addEventListener('mousedown', start, false)
406
+ feedbackImg.value.addEventListener('mousemove', draw, false)
407
+ feedbackImg.value.addEventListener('touchend', stop, false)
408
+ feedbackImg.value.addEventListener('mouseup', stop, false)
409
+ feedbackImg.value.addEventListener('mouseout', stop, false)
410
+ })
411
+
412
+ onBeforeUnmount(() => {
413
+ removeScreenshot()
414
+ feedbackImg.value.removeEventListener('touchstart', start, false)
415
+ feedbackImg.value.removeEventListener('touchmove', draw, false)
416
+ feedbackImg.value.removeEventListener('mousedown', start, false)
417
+ feedbackImg.value.removeEventListener('mousemove', draw, false)
418
+ feedbackImg.value.removeEventListener('touchend', stop, false)
419
+ feedbackImg.value.removeEventListener('mouseup', stop, false)
420
+ feedbackImg.value.removeEventListener('mouseout', stop, false)
421
+ })
422
+ </script>
423
+
424
+ <style scoped lang="scss">
425
+ .message {
426
+ overflow-x: auto;
427
+ .main-block {
428
+ display: flex;
429
+ flex-direction: row;
430
+ align-items: flex-start;
431
+ margin-bottom: 20px;
432
+ .text {
433
+ padding-top: 0;
434
+ min-width: 140px;
435
+ margin-right: 10px;
436
+ }
437
+ .textarea {
438
+ height: 150px;
439
+ width: 100%;
440
+ vertical-align: start;
441
+ background-color: var(--modal-bg-color);
442
+ }
443
+ .email {
444
+ width: 100%;
445
+ }
446
+
447
+ &__last {
448
+ margin-top: -20px;
449
+ width: 100%;
450
+ .content-padding {
451
+ padding-left: 4px;
452
+ color: var(--a-link-color);
453
+ }
454
+ }
455
+ :deep(.alert.alert-info) {
456
+ width: 100%;
457
+ }
458
+
459
+ .tooltip-error {
460
+ flex: 1;
461
+
462
+ :deep(.element-content) {
463
+ width: 100%;
464
+ }
465
+ }
466
+ .has-error {
467
+ border: 1px solid #db2100;
468
+ }
469
+ }
470
+ }
471
+ .loadFile {
472
+ width: fit-content;
473
+ margin-right: 10px;
474
+ padding: 0;
475
+ z-index: 11;
476
+ label {
477
+ color: inherit;
478
+ cursor: pointer;
479
+ padding: 0 10px;
480
+ line-height: calc(1rem - 1px);
481
+ letter-spacing: 0.073em;
482
+ font-size: 0.458333rem;
483
+ font-weight: 500;
484
+ height: 1rem;
485
+ padding: 0 0.5rem;
486
+ }
487
+ input {
488
+ width: 0.01px;
489
+ height: 0.01px;
490
+ opacity: 0;
491
+ overflow: hidden;
492
+ z-index: -1;
493
+ }
494
+ }
495
+ .feedbackImg {
496
+ display: none;
497
+ }
498
+ </style>
499
+ <style>
500
+ :root {
501
+ --a-link-color: #0072a3;
502
+ }
503
+ :root.dark-theme {
504
+ --a-link-color: #4aaed9;
505
+ }
506
+ </style>