quasar-ui-danx 0.4.2 → 0.4.3
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.
- package/dist/danx.es.js +7127 -6615
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +11 -5
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -1
- package/src/components/ActionTable/ActionTable.vue +28 -41
- package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +6 -6
- package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
- package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
- package/src/components/ActionTable/Filters/index.ts +2 -2
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
- package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
- package/src/components/ActionTable/Form/Fields/SelectField.vue +24 -38
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
- package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
- package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
- package/src/components/ActionTable/Form/Fields/index.ts +2 -0
- package/src/components/ActionTable/Form/RenderedForm.vue +50 -9
- package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
- package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
- package/src/components/ActionTable/Form/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +16 -15
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +6 -6
- package/src/components/ActionTable/listControls.ts +104 -166
- package/src/components/ActionTable/listHelpers.ts +2 -3
- package/src/components/ActionTable/tableColumns.ts +3 -27
- package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
- package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +10 -24
- package/src/components/Utility/Dialogs/DialogLayout.vue +10 -28
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
- package/src/components/Utility/Dialogs/index.ts +1 -0
- package/src/components/Utility/Files/FilePreview.vue +76 -73
- package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
- package/src/components/Utility/Tools/ActionVnode.vue +3 -3
- package/src/components/Utility/Tools/RenderVnode.vue +1 -1
- package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
- package/src/components/Utility/Transitions/index.ts +1 -0
- package/src/config/index.ts +36 -31
- package/src/helpers/FileUpload.ts +295 -297
- package/src/helpers/FlashMessages.ts +80 -71
- package/src/helpers/actions.ts +102 -82
- package/src/helpers/download.ts +189 -189
- package/src/helpers/downloadPdf.ts +55 -52
- package/src/helpers/formats.ts +151 -109
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +72 -58
- package/src/helpers/objectStore.ts +52 -0
- package/src/helpers/request.ts +70 -51
- package/src/helpers/routes.ts +29 -0
- package/src/helpers/storage.ts +7 -3
- package/src/helpers/utils.ts +47 -29
- package/src/styles/quasar-reset.scss +16 -1
- package/src/styles/themes/danx/dialogs.scss +4 -0
- package/src/types/actions.d.ts +43 -0
- package/src/types/config.d.ts +15 -0
- package/src/types/controls.d.ts +99 -0
- package/src/types/dialogs.d.ts +32 -0
- package/src/types/fields.d.ts +20 -0
- package/src/types/files.d.ts +54 -0
- package/src/types/formats.d.ts +4 -0
- package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
- package/src/types/index.d.ts +12 -0
- package/src/types/requests.d.ts +13 -0
- package/src/types/shared.d.ts +15 -0
- package/src/types/tables.d.ts +27 -0
- package/types/index.d.ts +1 -1
- /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
@@ -28,37 +28,37 @@
|
|
28
28
|
</template>
|
29
29
|
</QTabs>
|
30
30
|
</template>
|
31
|
-
<script setup>
|
31
|
+
<script setup lang="ts">
|
32
32
|
import { QTab } from "quasar";
|
33
|
+
import { ActionPanel } from "../../types";
|
33
34
|
import { RenderVnode } from "../Utility";
|
34
35
|
|
35
36
|
defineEmits(["update:model-value"]);
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
37
|
+
|
38
|
+
interface Props {
|
39
|
+
modelValue?: string | number;
|
40
|
+
panels: ActionPanel[];
|
41
|
+
}
|
42
|
+
|
43
|
+
withDefaults(defineProps<Props>(), {
|
44
|
+
modelValue: "general"
|
45
45
|
});
|
46
46
|
</script>
|
47
47
|
|
48
48
|
<style lang="scss" module="cls">
|
49
49
|
.panel-tabs {
|
50
|
-
|
50
|
+
@apply p-4 h-auto;
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
:global(.q-tab) {
|
53
|
+
justify-content: start !important;
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
:global(.q-focus-helper), :global(.q-tab__indicator) {
|
56
|
+
display: none;
|
57
|
+
}
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
:global(.q-tab__content) {
|
60
|
+
@apply p-0;
|
61
|
+
}
|
62
|
+
}
|
63
63
|
}
|
64
64
|
</style>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<template>
|
2
|
+
<ConfirmDialog
|
3
|
+
class="dx-confirm-action-dialog"
|
4
|
+
v-bind="props"
|
5
|
+
:confirm-text="confirmText || computedConfirmText"
|
6
|
+
:title="title || computedTitle"
|
7
|
+
:content="content || computedContentText"
|
8
|
+
@confirm="$emit('confirm')"
|
9
|
+
@close="$emit('close')"
|
10
|
+
>
|
11
|
+
<template
|
12
|
+
v-for="slotName in childSlots"
|
13
|
+
#[slotName]
|
14
|
+
>
|
15
|
+
<slot :name="slotName" />
|
16
|
+
</template>
|
17
|
+
<slot />
|
18
|
+
</ConfirmDialog>
|
19
|
+
</template>
|
20
|
+
|
21
|
+
<script setup lang="ts">
|
22
|
+
import { computed } from "vue";
|
23
|
+
import { fNameOrCount } from "../../../helpers";
|
24
|
+
import { ConfirmActionDialogProps } from "../../../types";
|
25
|
+
import { default as ConfirmDialog } from "./ConfirmDialog";
|
26
|
+
|
27
|
+
defineEmits(["confirm", "close"]);
|
28
|
+
|
29
|
+
const props = withDefaults(defineProps<ConfirmActionDialogProps>(), {
|
30
|
+
message: "Are you sure you want to"
|
31
|
+
});
|
32
|
+
|
33
|
+
const nameLabel = computed(() => fNameOrCount(props.target, props.label || props.action));
|
34
|
+
const computedTitle = computed(() => `Confirm ${props.action}`);
|
35
|
+
const computedConfirmText = computed(() => `${props.action}`);
|
36
|
+
const computedContentText = computed(() => `${props.message} ${props.action.toLowerCase()}${nameLabel.value ? " " + nameLabel.value : ""}?`);
|
37
|
+
|
38
|
+
const childSlots = computed(() => ["title", "subtitle", "default", "toolbar", "actions"]);
|
39
|
+
</script>
|
@@ -10,6 +10,7 @@
|
|
10
10
|
>
|
11
11
|
<slot :name="slotName" />
|
12
12
|
</template>
|
13
|
+
<slot />
|
13
14
|
|
14
15
|
<template #actions>
|
15
16
|
<div class="dx-dialog-button-cancel">
|
@@ -42,37 +43,22 @@
|
|
42
43
|
</DialogLayout>
|
43
44
|
</template>
|
44
45
|
|
45
|
-
<script setup>
|
46
|
+
<script setup lang="ts">
|
46
47
|
import { computed } from "vue";
|
48
|
+
import { ConfirmDialogProps } from "../../../types";
|
47
49
|
import DialogLayout from "./DialogLayout";
|
48
50
|
|
49
51
|
const emit = defineEmits(["update:model-value", "confirm", "close"]);
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
confirmText: {
|
57
|
-
type: String,
|
58
|
-
default: "Confirm"
|
59
|
-
},
|
60
|
-
cancelText: {
|
61
|
-
type: String,
|
62
|
-
default: "Cancel"
|
63
|
-
},
|
64
|
-
confirmClass: {
|
65
|
-
type: String,
|
66
|
-
default: ""
|
67
|
-
},
|
68
|
-
contentClass: {
|
69
|
-
type: String,
|
70
|
-
default: ""
|
71
|
-
}
|
52
|
+
|
53
|
+
const props = withDefaults(defineProps<ConfirmDialogProps>(), {
|
54
|
+
confirmText: "Confirm",
|
55
|
+
cancelText: "Cancel",
|
56
|
+
confirmClass: "",
|
57
|
+
contentClass: ""
|
72
58
|
});
|
73
59
|
|
74
60
|
const layoutProps = computed(() => ({ ...props, disabled: undefined }));
|
75
|
-
const childSlots = computed(() => ["title", "subtitle", "
|
61
|
+
const childSlots = computed(() => ["title", "subtitle", "toolbar"]);
|
76
62
|
|
77
63
|
function onConfirm() {
|
78
64
|
emit("confirm");
|
@@ -3,7 +3,7 @@
|
|
3
3
|
class="dx-dialog"
|
4
4
|
:full-height="fullHeight"
|
5
5
|
:full-width="fullWidth"
|
6
|
-
:model-value="
|
6
|
+
:model-value="true"
|
7
7
|
:no-backdrop-dismiss="!backdropDismiss"
|
8
8
|
:maximized="maximized"
|
9
9
|
@update:model-value="onClose"
|
@@ -57,36 +57,18 @@
|
|
57
57
|
</QDialog>
|
58
58
|
</template>
|
59
59
|
|
60
|
-
<script setup>
|
60
|
+
<script setup lang="ts">
|
61
61
|
import { XIcon as CloseIcon } from "@heroicons/vue/outline";
|
62
|
+
import { DialogLayoutProps } from "../../../types";
|
62
63
|
|
63
64
|
const emit = defineEmits(["close"]);
|
64
|
-
|
65
|
-
|
66
|
-
title:
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
type: String,
|
72
|
-
default: ""
|
73
|
-
},
|
74
|
-
subtitle: {
|
75
|
-
type: String,
|
76
|
-
default: ""
|
77
|
-
},
|
78
|
-
content: {
|
79
|
-
type: String,
|
80
|
-
default: ""
|
81
|
-
},
|
82
|
-
backdropDismiss: Boolean,
|
83
|
-
maximized: Boolean,
|
84
|
-
fullWidth: Boolean,
|
85
|
-
fullHeight: Boolean,
|
86
|
-
contentClass: {
|
87
|
-
type: String,
|
88
|
-
default: ""
|
89
|
-
}
|
65
|
+
|
66
|
+
withDefaults(defineProps<DialogLayoutProps>(), {
|
67
|
+
title: "",
|
68
|
+
titleClass: "",
|
69
|
+
subtitle: "",
|
70
|
+
content: "",
|
71
|
+
contentClass: ""
|
90
72
|
});
|
91
73
|
|
92
74
|
function onClose() {
|
@@ -22,6 +22,7 @@
|
|
22
22
|
:key="'file-' + file.id"
|
23
23
|
:name="file.id"
|
24
24
|
:img-src="getThumbUrl(file)"
|
25
|
+
class="bg-black"
|
25
26
|
>
|
26
27
|
<div :class="cls['slide-image']">
|
27
28
|
<template v-if="isVideo(file)">
|
@@ -30,7 +31,7 @@
|
|
30
31
|
controls
|
31
32
|
>
|
32
33
|
<source
|
33
|
-
:src="file
|
34
|
+
:src="getPreviewUrl(file) + '#t=0.1'"
|
34
35
|
:type="file.mime"
|
35
36
|
>
|
36
37
|
</video>
|
@@ -38,7 +39,7 @@
|
|
38
39
|
<img
|
39
40
|
v-else
|
40
41
|
:alt="file.filename"
|
41
|
-
:src="file
|
42
|
+
:src="getPreviewUrl(file)"
|
42
43
|
>
|
43
44
|
</div>
|
44
45
|
</QCarouselSlide>
|
@@ -56,54 +57,59 @@ import { XIcon as CloseIcon } from "../../../svg";
|
|
56
57
|
|
57
58
|
defineEmits(["close"]);
|
58
59
|
const props = defineProps({
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
files: {
|
61
|
+
type: Array,
|
62
|
+
default: () => []
|
63
|
+
},
|
64
|
+
defaultSlide: {
|
65
|
+
type: String,
|
66
|
+
default: ""
|
67
|
+
}
|
67
68
|
});
|
68
69
|
|
69
70
|
const carousel = ref(null);
|
70
71
|
const currentSlide = ref(props.defaultSlide);
|
71
72
|
function isVideo(file) {
|
72
|
-
|
73
|
+
return file.mime?.startsWith("video");
|
73
74
|
}
|
75
|
+
|
76
|
+
function getPreviewUrl(file) {
|
77
|
+
return file.transcodes?.compress?.url || file.blobUrl || file.url;
|
78
|
+
}
|
79
|
+
|
74
80
|
function getThumbUrl(file) {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
if (file.thumb) {
|
82
|
+
return file.thumb.url;
|
83
|
+
} else if (isVideo(file)) {
|
84
|
+
// Base64 encode a PlayIcon for the placeholder image
|
85
|
+
return `data:image/svg+xml;base64,${btoa(
|
86
|
+
`<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>`
|
87
|
+
)}`;
|
88
|
+
} else {
|
89
|
+
return getPreviewUrl(file);
|
90
|
+
}
|
85
91
|
}
|
86
92
|
</script>
|
87
93
|
<style module="cls" lang="scss">
|
88
94
|
.slide-image {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
width: 100%;
|
96
|
+
height: 100%;
|
97
|
+
background: black;
|
98
|
+
display: flex;
|
99
|
+
justify-content: center;
|
100
|
+
align-items: center;
|
95
101
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
102
|
+
img {
|
103
|
+
max-height: 100%;
|
104
|
+
max-width: 100%;
|
105
|
+
object-fit: contain;
|
106
|
+
}
|
101
107
|
}
|
102
108
|
|
103
109
|
.carousel {
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
110
|
+
:deep(.q-carousel__navigation--bottom) {
|
111
|
+
position: relative;
|
112
|
+
bottom: 8em;
|
113
|
+
}
|
108
114
|
}
|
109
115
|
</style>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
export { default as ConfirmActionDialog } from "./ConfirmActionDialog.vue";
|
1
2
|
export { default as ConfirmDialog } from "./ConfirmDialog.vue";
|
2
3
|
export { default as FullScreenCarouselDialog } from "./FullscreenCarouselDialog.vue";
|
3
4
|
export { default as FullScreenDialog } from "./FullScreenDialog.vue";
|
@@ -27,7 +27,7 @@
|
|
27
27
|
</div>
|
28
28
|
<QImg
|
29
29
|
v-if="thumbUrl || isPreviewable"
|
30
|
-
fit="
|
30
|
+
:fit="imageFit"
|
31
31
|
class="non-selectable max-h-full max-w-full h-full"
|
32
32
|
:src="(thumbUrl || previewUrl) + '#t=0.1'"
|
33
33
|
preload="auto"
|
@@ -54,11 +54,11 @@
|
|
54
54
|
<slot name="action-button" />
|
55
55
|
</div>
|
56
56
|
<div
|
57
|
-
v-if="
|
57
|
+
v-if="file && file.progress !== undefined"
|
58
58
|
class="absolute-bottom w-full"
|
59
59
|
>
|
60
60
|
<QLinearProgress
|
61
|
-
:value="
|
61
|
+
:value="file.progress"
|
62
62
|
size="15px"
|
63
63
|
color="green-600"
|
64
64
|
stripe
|
@@ -78,7 +78,7 @@
|
|
78
78
|
<QBtn
|
79
79
|
v-if="downloadable && computedImage?.url"
|
80
80
|
size="sm"
|
81
|
-
class="
|
81
|
+
class="dx-file-preview-download py-1 px-2 opacity-70 hover:opacity-100"
|
82
82
|
:class="downloadButtonClass"
|
83
83
|
@click.stop="download(computedImage.url)"
|
84
84
|
>
|
@@ -88,7 +88,7 @@
|
|
88
88
|
<QBtn
|
89
89
|
v-if="removable"
|
90
90
|
size="sm"
|
91
|
-
class="bg-red-900 text-white
|
91
|
+
class="dx-file-preview-remove bg-red-900 text-white opacity-50 hover:opacity-100 py-1 px-2"
|
92
92
|
@click.stop="onRemove"
|
93
93
|
>
|
94
94
|
<div
|
@@ -107,106 +107,109 @@
|
|
107
107
|
<FullScreenCarouselDialog
|
108
108
|
v-if="showPreview && !disabled"
|
109
109
|
:files="relatedFiles || [computedImage]"
|
110
|
-
:default-slide="computedImage
|
110
|
+
:default-slide="computedImage?.id || ''"
|
111
111
|
@close="showPreview = false"
|
112
112
|
/>
|
113
113
|
</div>
|
114
114
|
</template>
|
115
115
|
|
116
|
-
<script setup>
|
116
|
+
<script setup lang="ts">
|
117
117
|
import { DocumentTextIcon as TextFileIcon, DownloadIcon, PlayIcon } from "@heroicons/vue/outline";
|
118
|
-
import {
|
118
|
+
import { UploadedFile } from "src/types";
|
119
|
+
import { computed, ComputedRef, ref } from "vue";
|
119
120
|
import { download } from "../../../helpers";
|
120
121
|
import { ImageIcon, PdfIcon, TrashIcon as RemoveIcon } from "../../../svg";
|
121
122
|
import { FullScreenCarouselDialog } from "../Dialogs";
|
122
123
|
|
124
|
+
export interface FilePreviewProps {
|
125
|
+
src?: string;
|
126
|
+
file?: UploadedFile;
|
127
|
+
relatedFiles?: UploadedFile[];
|
128
|
+
missingIcon?: any;
|
129
|
+
downloadButtonClass?: string;
|
130
|
+
imageFit?: "cover" | "contain" | "fill" | "none" | "scale-down";
|
131
|
+
downloadable?: boolean;
|
132
|
+
removable?: boolean;
|
133
|
+
disabled?: boolean;
|
134
|
+
square?: boolean;
|
135
|
+
}
|
136
|
+
|
123
137
|
const emit = defineEmits(["remove"]);
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
},
|
137
|
-
missingIcon: {
|
138
|
-
type: [Function, Object],
|
139
|
-
default: ImageIcon
|
140
|
-
},
|
141
|
-
downloadButtonClass: {
|
142
|
-
type: String,
|
143
|
-
default: "bg-blue-600 text-white"
|
144
|
-
},
|
145
|
-
downloadable: Boolean,
|
146
|
-
removable: Boolean,
|
147
|
-
disabled: Boolean,
|
148
|
-
square: Boolean
|
138
|
+
|
139
|
+
const props = withDefaults(defineProps<FilePreviewProps>(), {
|
140
|
+
src: "",
|
141
|
+
file: null,
|
142
|
+
relatedFiles: null,
|
143
|
+
missingIcon: ImageIcon,
|
144
|
+
downloadButtonClass: "bg-blue-600 text-white",
|
145
|
+
imageFit: "cover",
|
146
|
+
downloadable: false,
|
147
|
+
removable: false,
|
148
|
+
disabled: false,
|
149
|
+
square: false
|
149
150
|
});
|
150
151
|
|
151
152
|
const showPreview = ref(false);
|
152
|
-
const computedImage = computed(() => {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
153
|
+
const computedImage: ComputedRef<UploadedFile | null> = computed(() => {
|
154
|
+
if (props.file) {
|
155
|
+
return props.file;
|
156
|
+
} else if (props.src) {
|
157
|
+
return {
|
158
|
+
id: props.src,
|
159
|
+
url: props.src,
|
160
|
+
type: "image/" + props.src.split(".").pop()?.toLowerCase(),
|
161
|
+
name: "",
|
162
|
+
size: 0
|
163
|
+
};
|
164
|
+
}
|
165
|
+
return null;
|
163
166
|
});
|
164
167
|
const mimeType = computed(
|
165
|
-
|
168
|
+
() => computedImage.value?.type || computedImage.value?.mime || ""
|
166
169
|
);
|
167
|
-
const isImage = computed(() => mimeType.value.match(/^image\//));
|
168
|
-
const isVideo = computed(() => mimeType.value.match(/^video\//));
|
169
|
-
const isPdf = computed(() => mimeType.value.match(/^application\/pdf/));
|
170
|
+
const isImage = computed(() => !!mimeType.value.match(/^image\//));
|
171
|
+
const isVideo = computed(() => !!mimeType.value.match(/^video\//));
|
172
|
+
const isPdf = computed(() => !!mimeType.value.match(/^application\/pdf/));
|
170
173
|
const previewUrl = computed(
|
171
|
-
|
174
|
+
() => computedImage.value?.transcodes?.compress?.url || computedImage.value?.blobUrl || computedImage.value?.url
|
172
175
|
);
|
173
176
|
const thumbUrl = computed(() => {
|
174
|
-
|
177
|
+
return computedImage.value?.transcodes?.thumb?.url;
|
175
178
|
});
|
176
179
|
const isPreviewable = computed(() => {
|
177
|
-
|
180
|
+
return !!thumbUrl.value || isVideo.value || isImage.value;
|
178
181
|
});
|
179
182
|
const isConfirmingRemove = ref(false);
|
180
183
|
function onRemove() {
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
184
|
+
if (!isConfirmingRemove.value) {
|
185
|
+
isConfirmingRemove.value = true;
|
186
|
+
setTimeout(() => {
|
187
|
+
isConfirmingRemove.value = false;
|
188
|
+
}, 2000);
|
189
|
+
} else {
|
190
|
+
emit("remove");
|
191
|
+
}
|
189
192
|
}
|
190
193
|
</script>
|
191
194
|
|
192
195
|
<style module="cls" lang="scss">
|
193
196
|
.action-button {
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
197
|
+
position: absolute;
|
198
|
+
bottom: 1.5em;
|
199
|
+
right: 1em;
|
200
|
+
z-index: 1;
|
198
201
|
}
|
199
202
|
|
200
203
|
.play-button {
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
204
|
+
position: absolute;
|
205
|
+
top: 0;
|
206
|
+
left: 0;
|
207
|
+
display: flex;
|
208
|
+
justify-content: center;
|
209
|
+
align-items: center;
|
210
|
+
width: 100%;
|
211
|
+
height: 100%;
|
212
|
+
pointer-events: none;
|
213
|
+
@apply text-blue-200;
|
211
214
|
}
|
212
215
|
</style>
|
@@ -1,10 +1,11 @@
|
|
1
1
|
<template>
|
2
2
|
<QDialog
|
3
|
-
|
3
|
+
:model-value="show"
|
4
4
|
maximized
|
5
5
|
:position="position"
|
6
6
|
:seamless="seamless"
|
7
7
|
:class="{'hide-backdrop': !overlay}"
|
8
|
+
@hide="$emit('update:show', false)"
|
8
9
|
>
|
9
10
|
<div>
|
10
11
|
<div
|
@@ -21,45 +22,37 @@
|
|
21
22
|
</QDialog>
|
22
23
|
</template>
|
23
24
|
|
24
|
-
<script setup>
|
25
|
-
import {
|
25
|
+
<script setup lang="ts">
|
26
|
+
import { QDialogProps } from "quasar";
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
default: "bottom"
|
36
|
-
},
|
37
|
-
contentClass: {
|
38
|
-
type: String,
|
39
|
-
default: "py-8 px-12"
|
40
|
-
},
|
41
|
-
title: {
|
42
|
-
type: String,
|
43
|
-
default: "Edit"
|
44
|
-
}
|
45
|
-
});
|
28
|
+
export interface ContentDrawerProps {
|
29
|
+
show?: boolean,
|
30
|
+
overlay?: boolean,
|
31
|
+
position?: QDialogProps["position"],
|
32
|
+
seamless?: boolean,
|
33
|
+
contentClass?: string,
|
34
|
+
title?: string
|
35
|
+
}
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
defineEmits(["update:show"]);
|
38
|
+
withDefaults(defineProps<ContentDrawerProps>(), {
|
39
|
+
show: false,
|
40
|
+
position: "bottom",
|
41
|
+
contentClass: "py-8 px-12",
|
42
|
+
title: "Edit"
|
50
43
|
});
|
51
44
|
</script>
|
52
45
|
|
53
46
|
<style lang="scss" module="cls">
|
54
47
|
.dialog-title {
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
48
|
+
@apply font-medium uppercase text-xs px-6 py-3 border-b rounded-t-md bg-slate-100 text-gray-500 border-gray-200;
|
49
|
+
font-family: "Roboto", sans-serif;
|
50
|
+
letter-spacing: 0.05em;
|
51
|
+
box-shadow: 0px -4px 12px rgba(0, 0, 0, 0.25);
|
59
52
|
}
|
60
53
|
|
61
54
|
.dialog-content {
|
62
|
-
|
63
|
-
|
55
|
+
@apply bg-white;
|
56
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
64
57
|
}
|
65
58
|
</style>
|
@@ -16,8 +16,8 @@ import RenderVnode from "./RenderVnode";
|
|
16
16
|
|
17
17
|
const isSaving = ref(false);
|
18
18
|
async function onConfirm(input) {
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
isSaving.value = true;
|
20
|
+
await activeActionVnode.value.confirm(input);
|
21
|
+
isSaving.value = false;
|
22
22
|
}
|
23
23
|
</script>
|