@mythpe/quasar-ui-qui 0.3.0 → 0.3.2
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/package.json +1 -1
- package/src/components/form/MColor.vue +8 -5
- package/src/components/form/MToggle.vue +1 -1
- package/src/components/form/MUploader.vue +16 -3
- package/src/components/modal/MDialogFile.vue +111 -0
- package/src/components/parials/MUploaderItem.vue +31 -17
- package/src/composable/useColor.ts +6 -0
- package/src/composable/useMyth.ts +16 -1
- package/src/index.sass +1 -0
- package/src/plugin/defineAsyncComponents.ts +1 -0
- package/src/style/m-toggle.sass +3 -0
- package/src/types/api/MDialogFile.d.ts +19 -0
- package/src/types/components.d.ts +2 -0
- package/src/types/index.d.ts +2 -2
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import type { MInputProps as Props, MInputSlots } from '../../types'
|
|
14
14
|
import { reactive, toValue, useTemplateRef } from 'vue'
|
|
15
15
|
import { useField } from 'vee-validate'
|
|
16
|
-
import { useBindInput } from '../../composable'
|
|
16
|
+
import { useBindInput, useMyth } from '../../composable'
|
|
17
17
|
import type { QField, QInput } from 'quasar'
|
|
18
18
|
|
|
19
19
|
type P = {
|
|
@@ -48,7 +48,7 @@ const inputScope = useField<Props['modelValue']>(() => props.name, inputRules, {
|
|
|
48
48
|
...toValue<any>(props.fieldOptions)
|
|
49
49
|
})
|
|
50
50
|
const { value, errorMessage, handleChange, handleBlur } = inputScope
|
|
51
|
-
|
|
51
|
+
const { colorStyle } = useMyth()
|
|
52
52
|
const listeners = {
|
|
53
53
|
blur: (v: any) => handleBlur(v, !0),
|
|
54
54
|
'update:modelValue': (v: Props['modelValue']) => handleChange(v, !!errorMessage.value)
|
|
@@ -82,13 +82,13 @@ defineOptions({
|
|
|
82
82
|
>
|
|
83
83
|
<template #prepend>
|
|
84
84
|
<div
|
|
85
|
-
:style="`width: 20px; height: 20px;
|
|
85
|
+
:style="`width: 20px; height: 20px; ${colorStyle(value,!0)}`"
|
|
86
86
|
class="m-input__color-preview"
|
|
87
87
|
/>
|
|
88
88
|
</template>
|
|
89
89
|
<template
|
|
90
|
-
#append
|
|
91
90
|
v-if="!viewMode && !readonly && !disable"
|
|
91
|
+
#append
|
|
92
92
|
>
|
|
93
93
|
<q-icon
|
|
94
94
|
class="cursor-pointer"
|
|
@@ -99,7 +99,10 @@ defineOptions({
|
|
|
99
99
|
transition-hide="scale"
|
|
100
100
|
transition-show="scale"
|
|
101
101
|
>
|
|
102
|
-
<q-color
|
|
102
|
+
<q-color
|
|
103
|
+
v-model="value"
|
|
104
|
+
:name="name"
|
|
105
|
+
/>
|
|
103
106
|
</q-popup-proxy>
|
|
104
107
|
</q-icon>
|
|
105
108
|
</template>
|
|
@@ -123,7 +123,7 @@ defineOptions({
|
|
|
123
123
|
<template>
|
|
124
124
|
<MCol
|
|
125
125
|
:auto="auto"
|
|
126
|
-
:class="[$attrs.class,{'m-input__required':inputRules?.required!==undefined,'m-input__error':!!errorMessage,'m-input__view':viewMode}]"
|
|
126
|
+
:class="[$attrs.class,{'m-input__required':inputRules?.required!==undefined,'m-input__error':!!errorMessage,'m-input__view':viewMode},'m-toggle__col']"
|
|
127
127
|
:col="col"
|
|
128
128
|
:lg="lg"
|
|
129
129
|
:md="md"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
setup
|
|
11
11
|
>
|
|
12
12
|
import { QList, type QRejectedEntry, QUploader, type QUploaderFactoryObject } from 'quasar'
|
|
13
|
-
import { computed, nextTick, useTemplateRef, watch } from 'vue'
|
|
13
|
+
import { computed, nextTick, ref, toValue, useTemplateRef, watch } from 'vue'
|
|
14
14
|
import { useBindInput, useError, useMyth, useValue } from '../../composable'
|
|
15
15
|
import type { MUploaderMediaItem, MUploaderProps as Props, MUploaderXhrInfo } from '../../types'
|
|
16
16
|
import { useFieldArray, useFieldValue, useFormValues, useResetForm } from 'vee-validate'
|
|
@@ -138,7 +138,7 @@ type Emits = {
|
|
|
138
138
|
(e: 'remove-file', file: File): void;
|
|
139
139
|
}
|
|
140
140
|
const emit = defineEmits<Emits>()
|
|
141
|
-
|
|
141
|
+
// const $q = useQuasar()
|
|
142
142
|
type Form = Record<string, any>;
|
|
143
143
|
const { alertError, alertSuccess, __, api, props: pluginOptions } = useMyth()
|
|
144
144
|
const fieldId = useFieldValue<string | undefined>('id')
|
|
@@ -301,6 +301,11 @@ const onDeleteUploaderFile = (file: File) => {
|
|
|
301
301
|
emit('remove-file', file)
|
|
302
302
|
}
|
|
303
303
|
const onDeleteMedia = (media: MUploaderMediaItem, result: boolean) => emit('delete-media', media, result)
|
|
304
|
+
const onShowMedia = (media: MUploaderMediaItem) => {
|
|
305
|
+
showMediaItem.value = toValue(media)
|
|
306
|
+
}
|
|
307
|
+
const showMediaItem = ref<MUploaderMediaItem | null>(null)
|
|
308
|
+
|
|
304
309
|
watch(() => uploader.value?.isUploading, (v) => {
|
|
305
310
|
uploading.value = Boolean(v)
|
|
306
311
|
})
|
|
@@ -495,12 +500,13 @@ defineOptions({
|
|
|
495
500
|
@delete-media="onDeleteMedia"
|
|
496
501
|
/>
|
|
497
502
|
</template>
|
|
498
|
-
<template v-if="attachments">
|
|
503
|
+
<template v-if="!!attachments">
|
|
499
504
|
<template
|
|
500
505
|
v-for="f in attachments"
|
|
501
506
|
:key="f.key"
|
|
502
507
|
>
|
|
503
508
|
<MUploaderItem
|
|
509
|
+
:audio-props="audioProps"
|
|
504
510
|
:collection="collection"
|
|
505
511
|
:default-file-icon="defaultFileIcon"
|
|
506
512
|
:delete-media-icon="deleteMediaIcon"
|
|
@@ -516,14 +522,21 @@ defineOptions({
|
|
|
516
522
|
:scope="scope"
|
|
517
523
|
:service="service"
|
|
518
524
|
:update-btn="updateBtn"
|
|
525
|
+
:video-props="videoProps"
|
|
519
526
|
@values="setModelValue"
|
|
520
527
|
@remove-file="onDeleteUploaderFile"
|
|
521
528
|
@delete-media="onDeleteMedia"
|
|
529
|
+
@show-media="onShowMedia"
|
|
522
530
|
/>
|
|
523
531
|
</template>
|
|
524
532
|
</template>
|
|
525
533
|
</q-list>
|
|
526
534
|
</template>
|
|
527
535
|
</q-uploader>
|
|
536
|
+
<MDialogFile
|
|
537
|
+
:type="showMediaItem?.type"
|
|
538
|
+
:url="showMediaItem?.url"
|
|
539
|
+
@hide="showMediaItem = null"
|
|
540
|
+
/>
|
|
528
541
|
</MCol>
|
|
529
542
|
</template>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- MyTh Ahmed Faiz Copyright © 2016-2024 All rights reserved.
|
|
3
|
+
- Email: mythpe@gmail.com
|
|
4
|
+
- Mobile: +966590470092
|
|
5
|
+
- Website: https://www.4myth.com
|
|
6
|
+
- Github: https://github.com/mythpe
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<script
|
|
10
|
+
lang="ts"
|
|
11
|
+
setup
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
import { computed } from 'vue'
|
|
15
|
+
import type { MDialogFileProps as Props } from '../../types'
|
|
16
|
+
import { useQuasar } from 'quasar'
|
|
17
|
+
|
|
18
|
+
interface P {
|
|
19
|
+
url?: Props['url'] | undefined;
|
|
20
|
+
type?: Props['type'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = defineProps<P>()
|
|
24
|
+
|
|
25
|
+
type Emits = {
|
|
26
|
+
(e: 'hide'): void;
|
|
27
|
+
}
|
|
28
|
+
const emit = defineEmits<Emits>()
|
|
29
|
+
|
|
30
|
+
const $q = useQuasar()
|
|
31
|
+
const dialog = computed(() => !!props.url)
|
|
32
|
+
const isPdf = computed(() => {
|
|
33
|
+
if (props.type === 'pdf') return !0
|
|
34
|
+
return props.url?.endsWith('.pdf')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const isImage = computed(() => {
|
|
38
|
+
if (props.type === 'image') return !0
|
|
39
|
+
if (!props.url) return false
|
|
40
|
+
return /\.(png|jpe?g|gif|webp|svg)$/i.test(props.url.split('?')[0] || '')
|
|
41
|
+
})
|
|
42
|
+
const isVideo = computed(() => {
|
|
43
|
+
if (props.type === 'video') return !0
|
|
44
|
+
const url = props.url
|
|
45
|
+
if (!url) return false
|
|
46
|
+
const videoExtensions = [
|
|
47
|
+
'mp4', 'mov', 'avi', 'wmv', 'flv', 'webm', 'mkv',
|
|
48
|
+
'm4v', '3gp', 'ogv', 'mpeg', 'mpg'
|
|
49
|
+
]
|
|
50
|
+
const urlWithoutParams = (url.split('?')[0] || '').split('#')[0] || ''
|
|
51
|
+
const extension = urlWithoutParams.split('.').pop()?.toLowerCase()
|
|
52
|
+
return !!extension && videoExtensions.includes(extension)
|
|
53
|
+
})
|
|
54
|
+
const isAudio = computed(() => {
|
|
55
|
+
if (props.type === 'audio') return true
|
|
56
|
+
const url = props.url
|
|
57
|
+
if (!url?.trim()) return false
|
|
58
|
+
const audioExtensions = new Set([
|
|
59
|
+
'mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a',
|
|
60
|
+
'wma', 'opus', 'amr', 'mid', 'midi', 'weba'
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const parsedUrl = new URL(url)
|
|
65
|
+
const pathname = parsedUrl.pathname
|
|
66
|
+
const extension = pathname.split('.').pop()?.toLowerCase() || ''
|
|
67
|
+
return audioExtensions.has(extension)
|
|
68
|
+
} catch {
|
|
69
|
+
const simplePath = url.split('?')[0]?.split('#')[0] || ''
|
|
70
|
+
const extension = simplePath.split('.').pop()?.toLowerCase() || ''
|
|
71
|
+
return audioExtensions.has(extension)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
defineOptions({
|
|
75
|
+
name: 'MDialogFile',
|
|
76
|
+
inheritAttrs: !1
|
|
77
|
+
})
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<q-dialog
|
|
82
|
+
:model-value="dialog"
|
|
83
|
+
maximized
|
|
84
|
+
persistent
|
|
85
|
+
transition-hide="slide-down"
|
|
86
|
+
transition-show="slide-up"
|
|
87
|
+
@hide="emit('hide')"
|
|
88
|
+
>
|
|
89
|
+
<q-card v-if="!!url">
|
|
90
|
+
<q-card-section>
|
|
91
|
+
<MBtn
|
|
92
|
+
v-close-popup
|
|
93
|
+
label="labels.close"
|
|
94
|
+
/>
|
|
95
|
+
</q-card-section>
|
|
96
|
+
<iframe
|
|
97
|
+
v-if="isPdf || isVideo || isAudio"
|
|
98
|
+
:height="$q.screen.height - 120 + 'px'"
|
|
99
|
+
:src="url"
|
|
100
|
+
frameborder="0"
|
|
101
|
+
width="100%"
|
|
102
|
+
/>
|
|
103
|
+
<q-img
|
|
104
|
+
v-else-if="isImage"
|
|
105
|
+
:height="$q.screen.height - 120 + 'px'"
|
|
106
|
+
:src="url"
|
|
107
|
+
fit="contain"
|
|
108
|
+
/>
|
|
109
|
+
</q-card>
|
|
110
|
+
</q-dialog>
|
|
111
|
+
</template>
|
|
@@ -70,9 +70,11 @@ const modelValue = defineModel<ModelValue>({ required: !0, default: () => ({} as
|
|
|
70
70
|
const mediaProp = computed<MUploaderMediaItem>(() => modelValue.value as MUploaderMediaItem)
|
|
71
71
|
|
|
72
72
|
interface Emits {
|
|
73
|
-
(e: '
|
|
73
|
+
(e: 'deleteMedia', media: MUploaderMediaItem, result: boolean): void;
|
|
74
74
|
|
|
75
|
-
(e: '
|
|
75
|
+
(e: 'showMedia', media: MUploaderMediaItem, event: Event): void;
|
|
76
|
+
|
|
77
|
+
(e: 'removeFile', File: File): void;
|
|
76
78
|
|
|
77
79
|
(e: 'values', value: Props['modelValue']): void;
|
|
78
80
|
}
|
|
@@ -127,13 +129,13 @@ const deleteMedia = (media: MUploaderMediaItem) => {
|
|
|
127
129
|
.finally(() => {
|
|
128
130
|
deleting.value = !1
|
|
129
131
|
q.loading.hide()
|
|
130
|
-
emit('
|
|
132
|
+
emit('deleteMedia', media, r)
|
|
131
133
|
})
|
|
132
134
|
}
|
|
133
135
|
})
|
|
134
136
|
}
|
|
135
137
|
const deleteUploaderFile = (file: File) => {
|
|
136
|
-
nextTick(() => emit('
|
|
138
|
+
nextTick(() => emit('removeFile', file))
|
|
137
139
|
}
|
|
138
140
|
const onUpdateField = (data: any) => {
|
|
139
141
|
const model = modelValue.value as MUploaderMediaItem
|
|
@@ -279,19 +281,31 @@ defineOptions({
|
|
|
279
281
|
v-if="(mediaProp.id && mediaProp.url) || (!hideDeleteMedia || (hideDeleteMedia && !mediaProp.id))"
|
|
280
282
|
class="print-hide justify-between"
|
|
281
283
|
>
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
284
|
+
<template v-if="mediaProp.id && mediaProp.url">
|
|
285
|
+
<q-btn
|
|
286
|
+
:href="mediaProp.url"
|
|
287
|
+
:icon="downloadFileIcon"
|
|
288
|
+
dense
|
|
289
|
+
flat
|
|
290
|
+
target="_blank"
|
|
291
|
+
v-bind="pluginOptions.uploaderOptions?.downloadBtnProps"
|
|
292
|
+
>
|
|
293
|
+
<q-tooltip class="m-dt-btn-tooltip">
|
|
294
|
+
{{ __('myth.titles.download') }}
|
|
295
|
+
</q-tooltip>
|
|
296
|
+
</q-btn>
|
|
297
|
+
<q-btn
|
|
298
|
+
dense
|
|
299
|
+
flat
|
|
300
|
+
icon="ion-ios-eye"
|
|
301
|
+
v-bind="pluginOptions.uploaderOptions?.downloadBtnProps"
|
|
302
|
+
@click="emit('showMedia', mediaProp, $event)"
|
|
303
|
+
>
|
|
304
|
+
<q-tooltip class="m-dt-btn-tooltip">
|
|
305
|
+
{{ __('labels.show') }}
|
|
306
|
+
</q-tooltip>
|
|
307
|
+
</q-btn>
|
|
308
|
+
</template>
|
|
295
309
|
<q-btn
|
|
296
310
|
v-if="(!hideDeleteMedia || (hideDeleteMedia && !mediaProp.id))"
|
|
297
311
|
:disable="deleting || scope.isBusy || scope.isUploading"
|
|
@@ -10,7 +10,7 @@ import { useI18n } from 'vue-i18n'
|
|
|
10
10
|
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
|
11
11
|
import { useRoute } from 'vue-router'
|
|
12
12
|
import type { QDialogOptions, QDialogProps, QNotifyCreateOptions } from 'quasar'
|
|
13
|
-
import { copyToClipboard, extend, useQuasar } from 'quasar'
|
|
13
|
+
import { colors, copyToClipboard, extend, useQuasar } from 'quasar'
|
|
14
14
|
import { Helpers, MythKey, type MythType, Str, veeRules } from '../utils'
|
|
15
15
|
import type { MDtColumn, MDtHeadersParameter, ParseHeaderOptions, Vue3MAlertMessage, Vue3MAlertMessageOptions, Vue3MConfirmMessage } from '../types'
|
|
16
16
|
import { computed, inject } from 'vue'
|
|
@@ -277,6 +277,20 @@ export const useMyth = () => {
|
|
|
277
277
|
})
|
|
278
278
|
}
|
|
279
279
|
const isSmall = computed(() => q.screen.lt.md)
|
|
280
|
+
const colorStyle = (value: any, bg: boolean = false) => {
|
|
281
|
+
if (!value) {
|
|
282
|
+
return ''
|
|
283
|
+
}
|
|
284
|
+
if (!value.startsWith('#')) {
|
|
285
|
+
value = colors.getPaletteColor(value)
|
|
286
|
+
}
|
|
287
|
+
const attr = bg ? 'background-color: ' : 'color: '
|
|
288
|
+
if (value === 'transparent') {
|
|
289
|
+
return `${attr} transparent;`
|
|
290
|
+
}
|
|
291
|
+
return `${attr} ${value};`
|
|
292
|
+
}
|
|
293
|
+
|
|
280
294
|
return {
|
|
281
295
|
__,
|
|
282
296
|
getPageTitle,
|
|
@@ -291,6 +305,7 @@ export const useMyth = () => {
|
|
|
291
305
|
...Str,
|
|
292
306
|
veeRules,
|
|
293
307
|
isSmall,
|
|
308
|
+
colorStyle,
|
|
294
309
|
...plugin
|
|
295
310
|
}
|
|
296
311
|
}
|
package/src/index.sass
CHANGED
|
@@ -49,6 +49,7 @@ export const defineAsyncComponents = function (app: App) {
|
|
|
49
49
|
|
|
50
50
|
// Modals.
|
|
51
51
|
app.component('MDialog', defineAsyncComponent(() => import('../components/modal/MDialog.vue')))
|
|
52
|
+
app.component('MDialogFile', defineAsyncComponent(() => import('../components/modal/MDialogFile.vue')))
|
|
52
53
|
app.component('MModalMenu', defineAsyncComponent(() => import('../components/modal/MModalMenu.vue')))
|
|
53
54
|
app.component('MTooltip', defineAsyncComponent(() => import('../components/modal/MTooltip.vue')))
|
|
54
55
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MyTh Ahmed Faiz Copyright © 2016-2025 All rights reserved.
|
|
3
|
+
* Email: mythpe@gmail.com
|
|
4
|
+
* Mobile: +966590470092
|
|
5
|
+
* Website: https://www.4myth.com
|
|
6
|
+
* Github: https://github.com/mythpe
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { VNode } from 'vue'
|
|
10
|
+
import type { MediaType } from './MUploader'
|
|
11
|
+
|
|
12
|
+
export type MDialogFileProps = {
|
|
13
|
+
url: string | undefined | null;
|
|
14
|
+
type?: MediaType | null | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type MDialogFileSlots = {
|
|
18
|
+
default: () => VNode[];
|
|
19
|
+
}
|
|
@@ -69,6 +69,7 @@ import type { MTooltipProps, MTooltipSlots } from './api/MTooltip'
|
|
|
69
69
|
import type { MSarIconProps, MSarIconSlots, MSarStringProps, MSarStringSlots } from './api/MSar'
|
|
70
70
|
import type { MMapProps, MMapSlots } from './api/MMap'
|
|
71
71
|
import type { MInnerLoadingProps, MInnerLoadingSlots } from './api/MInnerLoading'
|
|
72
|
+
import type { MDialogFileProps, MDialogFileSlots } from './api/MDialogFile'
|
|
72
73
|
|
|
73
74
|
declare module '@vue/runtime-core' {
|
|
74
75
|
interface GlobalComponents {
|
|
@@ -120,6 +121,7 @@ declare module '@vue/runtime-core' {
|
|
|
120
121
|
|
|
121
122
|
// Modals.
|
|
122
123
|
MDialog: GlobalComponentConstructor<MDialogProps, MDialogSlots>;
|
|
124
|
+
MDialogFile: GlobalComponentConstructor<MDialogFileProps, MDialogFileSlots>;
|
|
123
125
|
MModalMenu: GlobalComponentConstructor<MModalMenuProps, MModalMenuSlots>;
|
|
124
126
|
MTooltip: GlobalComponentConstructor<MTooltipProps, MTooltipSlots>;
|
|
125
127
|
// Datatable
|
package/src/types/index.d.ts
CHANGED
|
@@ -28,8 +28,8 @@ export { MSarIconSlots } from './api/MSar'
|
|
|
28
28
|
export { MSarIconProps } from './api/MSar'
|
|
29
29
|
export { MCkeditorSlots } from './api/MCkeditor'
|
|
30
30
|
export { MCkeditorProps } from './api/MCkeditor'
|
|
31
|
-
export { MDialogSlots } from './api/MDialog'
|
|
32
|
-
export {
|
|
31
|
+
export { MDialogSlots, MDialogProps } from './api/MDialog'
|
|
32
|
+
export { MDialogFileSlots, MDialogFileProps } from './api/MDialogFile'
|
|
33
33
|
export { MTooltipSlots } from './api/MTooltip'
|
|
34
34
|
export { MTooltipProps } from './api/MTooltip'
|
|
35
35
|
export { MModalMenuSlots } from './api/MModalMenu'
|