bfg-common 1.2.68 → 1.2.70

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