bfg-common 1.2.67 → 1.2.68

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 (85) hide show
  1. package/assets/localization/local_be.json +2 -1
  2. package/assets/localization/local_en.json +2 -1
  3. package/assets/localization/local_hy.json +2 -1
  4. package/assets/localization/local_kk.json +2 -1
  5. package/assets/localization/local_ru.json +2 -1
  6. package/assets/localization/local_zh.json +2 -1
  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 +232 -227
  37. package/components/common/feedback/Feedback.vue +261 -261
  38. package/components/common/feedback/Message.vue +506 -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/ChartOptionsModal.vue +2 -1
  44. package/components/common/monitor/advanced/tools/chartOptionsModal/counters/table/Table.vue +136 -130
  45. package/components/common/perPage/PerPage.vue +51 -51
  46. package/components/common/recursionTree/RecursionTree.vue +201 -201
  47. package/components/common/vm/actions/clone/Clone.vue +545 -545
  48. package/components/common/vm/actions/clone/select/options/Options.vue +65 -65
  49. package/components/common/vm/actions/clone/utils.ts +43 -43
  50. package/components/common/vm/actions/common/config/capabilities.ts +338 -338
  51. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +678 -678
  52. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/CoresPerSocket.vue +109 -109
  53. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/Cpu.vue +383 -383
  54. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/Limit.vue +234 -234
  55. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/config/options.ts +28 -28
  56. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/Memory.vue +316 -316
  57. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/config/memoryOptions.ts +35 -35
  58. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +445 -445
  59. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +288 -288
  60. package/components/common/vm/actions/common/customizeHardware/virtualHardware/reservation/Reservation.vue +235 -235
  61. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +177 -177
  62. package/components/common/vm/actions/common/customizeHardware/vmoptions/bootOptions/order/Order.vue +197 -197
  63. package/components/common/vm/actions/common/customizeHardware/vmoptions/generalOptions/GeneralOptions.vue +181 -181
  64. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/CopyPaste.vue +51 -51
  65. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/RemoteConsoleOptions.vue +253 -253
  66. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/keymap/Keymap.vue +55 -55
  67. package/components/common/vm/actions/common/select/storage/Storage.vue +194 -194
  68. package/components/common/vm/actions/common/select/storage/config/config.ts +154 -154
  69. package/components/common/wizards/datastore/add/nfs/Nfs.vue +94 -94
  70. package/components/common/wizards/datastore/add/nfs/configuration/Configuration.vue +377 -377
  71. package/components/common/wizards/datastore/add/nfs/configuration/serversList/ServersList.vue +123 -123
  72. package/components/common/wizards/datastore/add/nfs/kerberosAuthentication/KerberosAuthentication.vue +54 -54
  73. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/hostTable/HostTable.vue +131 -131
  74. package/components/common/wizards/datastore/add/sharedStorm/partitionConfiguration/PartitionConfiguration.vue +230 -230
  75. package/composables/useLocal.ts +38 -38
  76. package/minify.js +146 -146
  77. package/models/store/interfaces.ts +37 -37
  78. package/models/store/storage/interfaces.ts +32 -32
  79. package/models/store/types.ts +32 -32
  80. package/modules/fixContentBuild/utils/methods.ts +114 -114
  81. package/package.json +1 -1
  82. package/public/spice-console/application/codec.js +257 -257
  83. package/public/spice-console/lib/rasterEngine.js +907 -907
  84. package/public/spice-console/network/spicechannel.js +369 -369
  85. 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>