bfg-common 1.2.70 → 1.2.72

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 (79) 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 +140 -116
  6. package/components/atoms/nav/VerticalNavBar.vue +89 -89
  7. package/components/atoms/notificationBar/NotificationBar.vue +175 -175
  8. package/components/atoms/perPage/PerPage.vue +52 -52
  9. package/components/atoms/popup/SimplePopup.vue +90 -90
  10. package/components/atoms/step/VerticalStep.vue +104 -104
  11. package/components/atoms/switch/Switch.vue +107 -107
  12. package/components/atoms/table/dataGrid/DataGridColumnSwitch.vue +206 -206
  13. package/components/atoms/table/dataGrid/DataGridPage.vue +189 -189
  14. package/components/atoms/tabs/Tabs.vue +217 -189
  15. package/components/atoms/tooltip/Signpost.vue +224 -224
  16. package/components/common/BadBrowser.vue +114 -114
  17. package/components/common/accordion/Accordion.vue +7 -14
  18. package/components/common/accordion/utils/accordion.ts +1 -1
  19. package/components/common/adapterManager/AddAdapterWarningModal.vue +82 -82
  20. package/components/common/adapterManager/NoActiveAdaptersModal.vue +62 -62
  21. package/components/common/adapterManager/NoConnectedActiveAdaptersModal.vue +62 -62
  22. package/components/common/browse/Browse.vue +182 -182
  23. package/components/common/browse/blocks/Title.vue +66 -66
  24. package/components/common/diagramMain/Diagram.vue +186 -186
  25. package/components/common/diagramMain/DiagramMain.vue +986 -986
  26. package/components/common/diagramMain/adapter/AdapterItem.vue +227 -227
  27. package/components/common/diagramMain/adapter/AdapterItems.vue +57 -57
  28. package/components/common/diagramMain/modals/editSettings/tabs/port/IpvFourSettings.vue +339 -339
  29. package/components/common/diagramMain/modals/editSettings/tabs/port/PortProperties.vue +202 -202
  30. package/components/common/diagramMain/modals/remove/RemoveModal.vue +226 -226
  31. package/components/common/diagramMain/network/Contents.vue +238 -238
  32. package/components/common/feedback/Buttons.vue +227 -227
  33. package/components/common/feedback/Feedback.vue +261 -261
  34. package/components/common/feedback/Message.vue +506 -509
  35. package/components/common/feedback/VisitPortal.vue +53 -53
  36. package/components/common/help/navbar/left/Left.vue +164 -159
  37. package/components/common/help/navbar/left/utils/constructAccordion.ts +27 -27
  38. package/components/common/modals/Rename.vue +165 -165
  39. package/components/common/perPage/PerPage.vue +51 -51
  40. package/components/common/recursionTree/RecursionTree.vue +201 -201
  41. package/components/common/vm/actions/clone/Clone.vue +545 -545
  42. package/components/common/vm/actions/clone/select/options/Options.vue +65 -65
  43. package/components/common/vm/actions/clone/utils.ts +43 -43
  44. package/components/common/vm/actions/common/config/capabilities.ts +338 -338
  45. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +678 -678
  46. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/CoresPerSocket.vue +109 -109
  47. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/Cpu.vue +383 -383
  48. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/Limit.vue +234 -234
  49. package/components/common/vm/actions/common/customizeHardware/virtualHardware/limit/config/options.ts +28 -28
  50. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/Memory.vue +316 -316
  51. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/config/memoryOptions.ts +35 -35
  52. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +445 -445
  53. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +288 -288
  54. package/components/common/vm/actions/common/customizeHardware/virtualHardware/reservation/Reservation.vue +235 -235
  55. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +177 -177
  56. package/components/common/vm/actions/common/customizeHardware/vmoptions/bootOptions/order/Order.vue +197 -197
  57. package/components/common/vm/actions/common/customizeHardware/vmoptions/generalOptions/GeneralOptions.vue +181 -181
  58. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/CopyPaste.vue +51 -51
  59. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/RemoteConsoleOptions.vue +253 -253
  60. package/components/common/vm/actions/common/customizeHardware/vmoptions/remoteConsoleOptions/keymap/Keymap.vue +55 -55
  61. package/components/common/vm/actions/common/select/storage/Storage.vue +194 -194
  62. package/components/common/vm/actions/common/select/storage/config/config.ts +154 -154
  63. package/components/common/wizards/datastore/add/nfs/Nfs.vue +94 -94
  64. package/components/common/wizards/datastore/add/nfs/configuration/Configuration.vue +377 -377
  65. package/components/common/wizards/datastore/add/nfs/configuration/serversList/ServersList.vue +123 -123
  66. package/components/common/wizards/datastore/add/nfs/kerberosAuthentication/KerberosAuthentication.vue +54 -54
  67. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/hostTable/HostTable.vue +131 -131
  68. package/components/common/wizards/datastore/add/sharedStorm/partitionConfiguration/PartitionConfiguration.vue +230 -230
  69. package/composables/useLocal.ts +38 -38
  70. package/minify.js +146 -146
  71. package/models/store/interfaces.ts +37 -37
  72. package/models/store/storage/interfaces.ts +32 -32
  73. package/models/store/types.ts +32 -32
  74. package/modules/fixContentBuild/utils/methods.ts +114 -114
  75. package/package.json +1 -1
  76. package/public/spice-console/application/codec.js +257 -257
  77. package/public/spice-console/lib/rasterEngine.js +907 -907
  78. package/public/spice-console/network/spicechannel.js +369 -369
  79. package/utils/contentBuild.ts +34 -34
@@ -1,509 +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
- () =>
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>
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>