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