quasar-ui-danx 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. package/package.json +6 -1
  2. package/src/components/ActionTable/ActionTable.vue +49 -41
  3. package/src/components/ActionTable/BatchActionMenu.vue +20 -20
  4. package/src/components/ActionTable/EmptyTableState.vue +5 -5
  5. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +11 -11
  6. package/src/components/ActionTable/Filters/FilterGroupItem.vue +7 -7
  7. package/src/components/ActionTable/Filters/FilterGroupList.vue +29 -29
  8. package/src/components/ActionTable/Filters/FilterListToggle.vue +15 -15
  9. package/src/components/ActionTable/Filters/FilterableField.vue +82 -80
  10. package/src/components/ActionTable/Filters/index.ts +5 -0
  11. package/src/components/ActionTable/Form/Fields/BooleanField.vue +13 -13
  12. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +11 -11
  13. package/src/components/ActionTable/Form/Fields/DateField.vue +13 -13
  14. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +25 -25
  15. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +21 -21
  16. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +23 -23
  17. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +31 -31
  18. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +19 -19
  19. package/src/components/ActionTable/Form/Fields/IntegerField.vue +7 -7
  20. package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
  21. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +19 -19
  22. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +40 -40
  23. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +23 -23
  24. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +10 -10
  25. package/src/components/ActionTable/Form/Fields/NumberField.vue +29 -29
  26. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +33 -33
  27. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +36 -36
  28. package/src/components/ActionTable/Form/Fields/SelectField.vue +66 -66
  29. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +23 -23
  30. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +32 -32
  31. package/src/components/ActionTable/Form/Fields/TextField.vue +36 -36
  32. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +16 -16
  33. package/src/components/ActionTable/Form/Fields/index.ts +23 -23
  34. package/src/components/ActionTable/Form/RenderedForm.vue +27 -25
  35. package/src/components/ActionTable/Form/index.ts +2 -0
  36. package/src/components/ActionTable/TableSummaryRow.vue +33 -33
  37. package/src/components/ActionTable/index.ts +8 -13
  38. package/src/components/ActionTable/listActions.ts +340 -339
  39. package/src/components/ActionTable/listHelpers.ts +74 -0
  40. package/src/components/ActionTable/tableColumns.ts +56 -56
  41. package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
  42. package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
  43. package/src/components/DragAndDrop/index.ts +0 -1
  44. package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
  45. package/src/components/Utility/CollapsableSidebar.vue +35 -35
  46. package/src/components/Utility/ContentDrawer.vue +20 -20
  47. package/src/components/Utility/Dialogs/ConfirmDialog.vue +55 -55
  48. package/src/components/Utility/Dialogs/FullScreenDialog.vue +18 -18
  49. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
  50. package/src/components/Utility/Dialogs/InfoDialog.vue +10 -10
  51. package/src/components/Utility/Dialogs/InputDialog.vue +13 -13
  52. package/src/components/Utility/ImagePreview.vue +192 -0
  53. package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
  54. package/src/components/Utility/Transitions/StaggeredListTransition.vue +15 -15
  55. package/src/components/Utility/index.ts +11 -9
  56. package/src/components/index.ts +1 -1
  57. package/src/helpers/FileUpload.ts +274 -273
  58. package/src/helpers/compatibility.ts +45 -45
  59. package/src/helpers/date.ts +2 -2
  60. package/src/helpers/download.ts +166 -158
  61. package/src/helpers/downloadPdf.ts +48 -48
  62. package/src/helpers/files.ts +42 -42
  63. package/src/helpers/index.ts +2 -0
  64. package/src/helpers/multiFileUpload.ts +56 -56
  65. package/src/helpers/singleFileUpload.ts +49 -49
  66. package/src/index.esm.js +3 -4
  67. package/src/svg/FilterIcon.svg +7 -0
  68. package/src/svg/ImageIcon.svg +30 -0
  69. package/src/svg/PdfIcon.svg +21 -0
  70. package/src/svg/PercentIcon.svg +13 -0
  71. package/src/svg/TrashIcon.svg +15 -0
  72. package/src/svg/XIcon.svg +18 -0
  73. package/src/svg/index.ts +8 -0
  74. package/src/vendor/tinymce-config.ts +1 -0
  75. package/src/vue-plugin.js +7 -4
  76. package/tsconfig.json +14 -13
  77. package/src/components/ActionTable/tableHelpers.ts +0 -83
  78. package/src/components/DragAndDrop/Icons/index.ts +0 -2
  79. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
  80. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -1,95 +1,95 @@
1
1
  <template>
2
- <QDialog
3
- :full-height="fullHeight"
4
- :full-width="fullWidth"
5
- :model-value="!!modelValue"
6
- :no-backdrop-dismiss="!backdropDismiss"
7
- :maximized="maximized"
8
- @update:model-value="onClose"
2
+ <q-dialog
3
+ :full-height="fullHeight"
4
+ :full-width="fullWidth"
5
+ :model-value="!!modelValue"
6
+ :no-backdrop-dismiss="!backdropDismiss"
7
+ :maximized="maximized"
8
+ @update:model-value="onClose"
9
9
  >
10
- <QCard class="flex flex-col flex-nowrap">
11
- <QCardSection
12
- v-if="title || $slots.title"
13
- class="pl-6 pr-10 border-b border-gray-medium"
10
+ <q-card class="flex flex-col flex-nowrap">
11
+ <q-card-section
12
+ v-if="title || $slots.title"
13
+ class="pl-6 pr-10 border-b border-gray-medium"
14
14
  >
15
15
  <h3
16
- class="font-normal flex items-center"
17
- :class="titleClass"
16
+ class="font-normal flex items-center"
17
+ :class="titleClass"
18
18
  >
19
19
  <slot name="title">{{ title }}</slot>
20
20
  </h3>
21
21
  <div
22
- v-if="subtitle"
23
- class="mt-1 text-sm"
22
+ v-if="subtitle"
23
+ class="mt-1 text-sm"
24
24
  >{{ subtitle }}
25
25
  </div>
26
- </QCardSection>
27
- <QCardSection v-if="$slots.toolbar">
26
+ </q-card-section>
27
+ <q-card-section v-if="$slots.toolbar">
28
28
  <slot name="toolbar" />
29
- </QCardSection>
30
- <QCardSection
31
- v-if="content || $slots.default"
32
- class="px-6 bg-neutral-plus-7 flex-grow max-h-full overflow-y-auto"
33
- :class="contentClass"
29
+ </q-card-section>
30
+ <q-card-section
31
+ v-if="content || $slots.default"
32
+ class="px-6 bg-neutral-plus-7 flex-grow max-h-full overflow-y-auto"
33
+ :class="contentClass"
34
34
  >
35
35
  <slot>{{ content }}</slot>
36
- </QCardSection>
36
+ </q-card-section>
37
37
  <div class="flex px-6 py-4 border-t border-gray-medium">
38
38
  <div class="flex-grow">
39
- <QBtn
40
- :label="cancelText"
41
- class="action-btn btn-white-gray"
42
- @click="onClose"
39
+ <q-btn
40
+ :label="cancelText"
41
+ class="action-btn btn-white-gray"
42
+ @click="onClose"
43
43
  >
44
44
  <slot name="cancel-text" />
45
- </QBtn>
45
+ </q-btn>
46
46
  </div>
47
47
  <slot name="actions" />
48
48
  <div v-if="!hideConfirm">
49
- <QBtn
50
- :label="$slots['confirm-text'] ? '' : confirmText"
51
- class="action-btn ml-4"
52
- :class="confirmClass"
53
- :loading="isSaving"
54
- :disable="disabled"
55
- data-testid="confirm-button"
56
- @click="onConfirm"
49
+ <q-btn
50
+ :label="$slots['confirm-text'] ? '' : confirmText"
51
+ class="action-btn ml-4"
52
+ :class="confirmClass"
53
+ :loading="isSaving"
54
+ :disable="disabled"
55
+ data-testid="confirm-button"
56
+ @click="onConfirm"
57
57
  >
58
58
  <slot name="confirm-text" />
59
- </QBtn>
59
+ </q-btn>
60
60
  </div>
61
61
  </div>
62
62
  <a
63
- class="absolute top-0 right-0 p-4 text-black"
64
- @click="onClose"
63
+ class="absolute top-0 right-0 p-4 text-black"
64
+ @click="onClose"
65
65
  >
66
66
  <CloseIcon class="w-5" />
67
67
  </a>
68
- </QCard>
69
- </QDialog>
68
+ </q-card>
69
+ </q-dialog>
70
70
  </template>
71
71
 
72
72
  <script setup>
73
- import { XIcon as CloseIcon } from "@heroicons/vue/outline";
73
+ import { XIcon as CloseIcon } from '@heroicons/vue/outline';
74
74
 
75
- const emit = defineEmits(["update:model-value", "confirm", "close"]);
75
+ const emit = defineEmits(['update:model-value', 'confirm', 'close']);
76
76
  const props = defineProps({
77
77
  modelValue: { type: [String, Boolean, Object], default: true },
78
78
  title: {
79
79
  type: String,
80
- default: ""
80
+ default: ''
81
81
  },
82
82
  titleClass: {
83
83
  type: String,
84
- default: ""
84
+ default: ''
85
85
  },
86
86
  subtitle: {
87
87
  type: String,
88
- default: ""
88
+ default: ''
89
89
  },
90
90
  content: {
91
91
  type: String,
92
- default: ""
92
+ default: ''
93
93
  },
94
94
  backdropDismiss: Boolean,
95
95
  maximized: Boolean,
@@ -101,32 +101,32 @@ const props = defineProps({
101
101
  hideConfirm: Boolean,
102
102
  confirmText: {
103
103
  type: String,
104
- default: "Confirm"
104
+ default: 'Confirm'
105
105
  },
106
106
  cancelText: {
107
107
  type: String,
108
- default: "Cancel"
108
+ default: 'Cancel'
109
109
  },
110
110
  confirmClass: {
111
111
  type: String,
112
- default: "bg-blue-base text-white"
112
+ default: 'bg-blue-base text-white'
113
113
  },
114
114
  contentClass: {
115
115
  type: String,
116
- default: ""
116
+ default: ''
117
117
  }
118
118
  });
119
119
 
120
120
  function onConfirm() {
121
- emit("confirm");
121
+ emit('confirm');
122
122
 
123
123
  if (props.closeOnConfirm) {
124
- emit("close");
124
+ emit('close');
125
125
  }
126
126
  }
127
127
 
128
128
  function onClose() {
129
- emit("update:model-value", false);
130
- emit("close");
129
+ emit('update:model-value', false);
130
+ emit('close');
131
131
  }
132
132
  </script>
@@ -1,29 +1,29 @@
1
1
  <template>
2
- <QDialog
3
- :model-value="modelValue"
4
- maximized
5
- transition-show="slide-up"
6
- transition-hide="slide-down"
7
- @update:model-value="onClose"
2
+ <q-dialog
3
+ :model-value="modelValue"
4
+ maximized
5
+ transition-show="slide-up"
6
+ transition-hide="slide-down"
7
+ @update:model-value="onClose"
8
8
  >
9
9
  <div class="flex justify-center min-w-xs" :class="computedClass">
10
10
  <div
11
- v-if="closeable"
12
- v-close-popup
13
- class="p-4 m-4 absolute-top-right top right cursor-pointer"
11
+ v-if="closeable"
12
+ v-close-popup
13
+ class="p-4 m-4 absolute-top-right top right cursor-pointer"
14
14
  >
15
15
  <XIcon class="w-5 h-5" />
16
16
  </div>
17
17
  <slot />
18
18
  </div>
19
- </QDialog>
19
+ </q-dialog>
20
20
  </template>
21
21
 
22
22
  <script setup>
23
- import { XIcon } from "src/svg";
24
- import { computed } from "vue";
23
+ import { XIcon } from '@ui/svg';
24
+ import { computed } from 'vue';
25
25
 
26
- const emit = defineEmits(["update:model-value", "close"]);
26
+ const emit = defineEmits(['update:model-value', 'close']);
27
27
  const props = defineProps({
28
28
  modelValue: Boolean,
29
29
  center: Boolean,
@@ -33,14 +33,14 @@ const props = defineProps({
33
33
 
34
34
  let computedClass = computed(() => {
35
35
  return {
36
- "bg-blue-base text-white": props.blue,
37
- "bg-white text-gray-base": !props.blue,
38
- "items-center": props.center
36
+ 'bg-blue-base text-white': props.blue,
37
+ 'bg-white text-gray-base': !props.blue,
38
+ 'items-center': props.center
39
39
  };
40
40
  });
41
41
 
42
42
  function onClose() {
43
- emit("update:model-value", false);
44
- emit("close");
43
+ emit('update:model-value', false);
44
+ emit('close');
45
45
  }
46
46
  </script>
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <q-dialog
3
+ :model-value="true"
4
+ maximized
5
+ @update:model-value="$emit('close')"
6
+ @keyup.left="carousel.previous()"
7
+ @keyup.right="carousel.next()"
8
+ >
9
+ <div class="absolute top-0 left-0 w-full h-full">
10
+ <q-carousel
11
+ ref="carousel"
12
+ v-model="currentSlide"
13
+ height="100%"
14
+ swipeable
15
+ animated
16
+ :thumbnails="files.length > 1"
17
+ infinite
18
+ class="carousel"
19
+ >
20
+ <q-carousel-slide
21
+ v-for="file in files"
22
+ :key="'file-' + file.id"
23
+ :name="file.id"
24
+ :img-src="getThumbUrl(file)"
25
+ >
26
+ <div class="slide-image">
27
+ <template v-if="isVideo(file)">
28
+ <video
29
+ class="max-h-full w-full"
30
+ controls
31
+ >
32
+ <source
33
+ :src="file.url + '#t=0.1'"
34
+ :type="file.mime"
35
+ />
36
+ </video>
37
+ </template>
38
+ <img v-else :alt="file.filename" :src="file.url" />
39
+ </div>
40
+ </q-carousel-slide>
41
+ </q-carousel>
42
+ <CloseIcon
43
+ class="absolute top-4 right-4 cursor-pointer text-white w-8 h-8"
44
+ @click="$emit('close')"
45
+ />
46
+ </div>
47
+ </q-dialog>
48
+ </template>
49
+ <script setup>
50
+ import { XIcon as CloseIcon } from '@ui/svg';
51
+ import { ref } from 'vue';
52
+
53
+ defineEmits(['close']);
54
+ const props = defineProps({
55
+ files: {
56
+ type: Array,
57
+ default: () => []
58
+ },
59
+ defaultSlide: {
60
+ type: String,
61
+ default: ''
62
+ }
63
+ });
64
+
65
+ const carousel = ref(null);
66
+ const currentSlide = ref(props.defaultSlide);
67
+ function isVideo(file) {
68
+ return file.mime?.startsWith('video');
69
+ }
70
+ function getThumbUrl(file) {
71
+ if (file.thumb) {
72
+ return file.thumb.url;
73
+ } else if (isVideo(file)) {
74
+ // Base64 encode a PlayIcon for the placeholder image
75
+ return `data:image/svg+xml;base64,${btoa(
76
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M0 0h24v24H0z" fill="none"/><path d="M8 5v14l11-7z"/></svg>`
77
+ )}`;
78
+ } else {
79
+ return file.url;
80
+ }
81
+ }
82
+ </script>
83
+ <style scoped lang="scss">
84
+ .slide-image {
85
+ width: 100%;
86
+ height: 100%;
87
+ background: black;
88
+ display: flex;
89
+ justify-content: center;
90
+ align-items: center;
91
+
92
+ img {
93
+ max-height: 100%;
94
+ max-width: 100%;
95
+ object-fit: contain;
96
+ }
97
+ }
98
+
99
+ .carousel {
100
+ :deep(.q-carousel__navigation--bottom) {
101
+ position: relative;
102
+ bottom: 8em;
103
+ }
104
+ }
105
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <QDialog
2
+ <q-dialog
3
3
  :full-height="fullHeight"
4
4
  :full-width="fullWidth"
5
5
  :model-value="!!modelValue"
@@ -7,8 +7,8 @@
7
7
  :maximized="maximized"
8
8
  @update:model-value="onClose"
9
9
  >
10
- <QCard class="flex flex-col flex-nowrap">
11
- <QCardSection
10
+ <q-card class="flex flex-col flex-nowrap">
11
+ <q-card-section
12
12
  v-if="title || $slots.title"
13
13
  class="pl-6 pr-10 border-b border-gray-medium"
14
14
  >
@@ -23,24 +23,24 @@
23
23
  class="mt-1 text-sm"
24
24
  >{{ subtitle }}
25
25
  </div>
26
- </QCardSection>
27
- <QCardSection
26
+ </q-card-section>
27
+ <q-card-section
28
28
  v-if="content || $slots.default"
29
29
  class="px-6 bg-neutral-plus-7 flex-grow max-h-full overflow-y-auto"
30
30
  >
31
31
  <slot>{{ content }}</slot>
32
- </QCardSection>
32
+ </q-card-section>
33
33
  <div
34
34
  class="flex items-center justify-center px-6 py-4 border-t border-gray-medium"
35
35
  >
36
36
  <div class="flex-grow text-right">
37
- <QBtn
37
+ <q-btn
38
38
  :label="doneText"
39
39
  class="action-btn btn-white-gray"
40
40
  @click="onClose"
41
41
  >
42
42
  <slot name="done-text" />
43
- </QBtn>
43
+ </q-btn>
44
44
  </div>
45
45
  </div>
46
46
  <a
@@ -49,8 +49,8 @@
49
49
  >
50
50
  <CloseIcon class="w-5" />
51
51
  </a>
52
- </QCard>
53
- </QDialog>
52
+ </q-card>
53
+ </q-dialog>
54
54
  </template>
55
55
 
56
56
  <script setup>
@@ -1,33 +1,33 @@
1
1
  <template>
2
2
  <ConfirmDialog
3
- v-bind="$props"
4
- @confirm="$emit('confirm', newInput)"
5
- @close="$emit('close', $event)"
3
+ v-bind="$props"
4
+ @confirm="$emit('confirm', newInput)"
5
+ @close="$emit('close', $event)"
6
6
  >
7
7
  <slot>
8
- <QInput
9
- v-model="newInput"
10
- type="number"
11
- class="w-full"
12
- @input="$emit('update:input', $event)"
8
+ <q-input
9
+ v-model="newInput"
10
+ type="number"
11
+ class="w-full"
12
+ @input="$emit('update:input', $event)"
13
13
  />
14
14
  </slot>
15
15
  </ConfirmDialog>
16
16
  </template>
17
17
  <script setup>
18
- import ConfirmDialog from "danx/src/components/Utility/Dialogs/ConfirmDialog";
19
- import { ref } from "vue";
18
+ import { ref } from 'vue';
19
+ import ConfirmDialog from './ConfirmDialog';
20
20
 
21
- defineEmits(["confirm", "close", "update:input"]);
21
+ defineEmits(['confirm', 'close', 'update:input']);
22
22
  const props = defineProps({
23
23
  ...ConfirmDialog.props,
24
24
  title: {
25
25
  type: String,
26
- default: "Enter Value"
26
+ default: 'Enter Value'
27
27
  },
28
28
  input: {
29
29
  type: [Number, String],
30
- default: ""
30
+ default: ''
31
31
  }
32
32
  });
33
33
 
@@ -0,0 +1,192 @@
1
+ <template>
2
+ <div class="relative flex justify-center bg-neutral-plus-7" :class="{'rounded-2xl': !square}">
3
+ <template v-if="computedImage">
4
+ <div
5
+ class="grow h-full overflow-hidden"
6
+ :class="{'rounded-2xl': !square}"
7
+ @click="showPreview = true"
8
+ >
9
+ <div
10
+ v-if="isVideo"
11
+ class="relative max-h-full max-w-full w-full flex justify-center"
12
+ >
13
+ <video
14
+ class="max-h-full"
15
+ preload="auto"
16
+ >
17
+ <source
18
+ :src="previewUrl + '#t=0.1'"
19
+ :type="mimeType"
20
+ />
21
+ </video>
22
+ <button class="play-button text-blue-lighter">
23
+ <PlayIcon class="w-16" />
24
+ </button>
25
+ </div>
26
+ <div
27
+ v-if="isPdf && !thumbUrl"
28
+ class="flex items-center justify-center h-full"
29
+ >
30
+ <PdfIcon class="w-24" />
31
+ </div>
32
+ <q-img
33
+ v-else
34
+ fit="scale-down"
35
+ class="non-selectable max-h-full max-w-full h-full"
36
+ :src="(thumbUrl || previewUrl) + '#t=0.1'"
37
+ preload="auto"
38
+ data-testid="previewed-image"
39
+ data-dusk="previewed-image"
40
+ />
41
+ </div>
42
+ <div
43
+ v-if="$slots['action-button']"
44
+ class="action-button"
45
+ >
46
+ <slot name="action-button" />
47
+ </div>
48
+ <div
49
+ v-if="image && image.progress !== undefined"
50
+ class="absolute-bottom w-full"
51
+ >
52
+ <q-linear-progress
53
+ :value="image.progress"
54
+ size="15px"
55
+ color="green-base"
56
+ stripe
57
+ />
58
+ </div>
59
+ </template>
60
+ <template v-else>
61
+ <slot name="missing">
62
+ <component
63
+ :is="missingIcon"
64
+ class="w-full h-full p-2 text-gray-medium"
65
+ />
66
+ </slot>
67
+ </template>
68
+
69
+ <div class="absolute top-1 right-1 flex items-center justify-between space-x-1">
70
+ <q-btn
71
+ v-if="downloadable && computedImage?.url"
72
+ size="sm"
73
+ class="!p-1 opacity-70 hover:opacity-100"
74
+ :class="downloadButtonClass"
75
+ @click.stop="download(computedImage.url)"
76
+ >
77
+ <DownloadIcon class="w-4 h-5" />
78
+ </q-btn>
79
+
80
+ <q-btn
81
+ v-if="removable"
82
+ size="sm"
83
+ class="bg-red-dark text-white !p-1 opacity-50 hover:opacity-100"
84
+ @click.stop="onRemove"
85
+ >
86
+ <div v-if="isConfirmingRemove" class="font-bold text-[1rem] leading-[1.2rem]">?</div>
87
+ <RemoveIcon v-else class="w-3" />
88
+ </q-btn>
89
+ </div>
90
+
91
+ <FullScreenCarouselDialog
92
+ v-if="showPreview && !disabled"
93
+ :files="relatedFiles || [computedImage]"
94
+ :default-slide="computedImage.id"
95
+ @close="showPreview = false"
96
+ />
97
+ </div>
98
+ </template>
99
+
100
+ <script setup>
101
+ import { DownloadIcon, PlayIcon } from '@heroicons/vue/outline';
102
+ import { FullScreenCarouselDialog } from '@ui/components';
103
+ import { download } from '@ui/helpers';
104
+ import { ImageIcon, PdfIcon, TrashIcon as RemoveIcon } from '@ui/svg';
105
+ import { computed, ref } from 'vue';
106
+
107
+ const emit = defineEmits(['remove']);
108
+ const props = defineProps({
109
+ src: {
110
+ type: String,
111
+ default: ''
112
+ },
113
+ image: {
114
+ type: Object,
115
+ default: null
116
+ },
117
+ relatedFiles: {
118
+ type: Array,
119
+ default: null
120
+ },
121
+ missingIcon: {
122
+ type: [Function, Object],
123
+ default: ImageIcon
124
+ },
125
+ downloadButtonClass: {
126
+ type: String,
127
+ default: 'bg-blue-base text-white'
128
+ },
129
+ downloadable: Boolean,
130
+ removable: Boolean,
131
+ disabled: Boolean,
132
+ square: Boolean
133
+ });
134
+
135
+ const showPreview = ref(false);
136
+ const computedImage = computed(() => {
137
+ if (props.image) {
138
+ return props.image;
139
+ } else if (props.src) {
140
+ return {
141
+ id: props.src,
142
+ url: props.src,
143
+ type: 'image/' + props.src.split('.').pop().toLowerCase()
144
+ };
145
+ }
146
+ return null;
147
+ });
148
+ const mimeType = computed(
149
+ () => computedImage.value.type || computedImage.value.mime
150
+ );
151
+ const isVideo = computed(() => mimeType.value.match(/^video\//));
152
+ const isPdf = computed(() => mimeType.value.match(/^application\/pdf/));
153
+ const previewUrl = computed(
154
+ () => computedImage.value.transcodes?.compress?.url || computedImage.value.blobUrl || computedImage.value.url
155
+ );
156
+ const thumbUrl = computed(() => {
157
+ return computedImage.value.transcodes?.thumb?.url;
158
+ });
159
+
160
+ const isConfirmingRemove = ref(false);
161
+ function onRemove() {
162
+ if (!isConfirmingRemove.value) {
163
+ isConfirmingRemove.value = true;
164
+ setTimeout(() => {
165
+ isConfirmingRemove.value = false;
166
+ }, 2000);
167
+ } else {
168
+ emit('remove');
169
+ }
170
+ }
171
+ </script>
172
+
173
+ <style scoped lang="scss">
174
+ .action-button {
175
+ position: absolute;
176
+ bottom: 1.5em;
177
+ right: 1em;
178
+ z-index: 1;
179
+ }
180
+
181
+ .play-button {
182
+ position: absolute;
183
+ top: 0;
184
+ left: 0;
185
+ display: flex;
186
+ justify-content: center;
187
+ align-items: center;
188
+ width: 100%;
189
+ height: 100%;
190
+ pointer-events: none;
191
+ }
192
+ </style>