@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mythpe/quasar-ui-qui",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MyTh Quasar UI Kit App Extension",
5
5
  "author": {
6
6
  "name": "MyTh Ahmed Faiz",
@@ -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; background-color: ${value};`"
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 v-model="value" />
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: 'delete-media', media: MUploaderMediaItem, result: boolean): void;
73
+ (e: 'deleteMedia', media: MUploaderMediaItem, result: boolean): void;
74
74
 
75
- (e: 'remove-file', File: File): void;
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('delete-media', media, r)
132
+ emit('deleteMedia', media, r)
131
133
  })
132
134
  }
133
135
  })
134
136
  }
135
137
  const deleteUploaderFile = (file: File) => {
136
- nextTick(() => emit('remove-file', file))
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
- <q-btn
283
- v-if="mediaProp.id && mediaProp.url"
284
- :href="mediaProp.url"
285
- :icon="downloadFileIcon"
286
- dense
287
- flat
288
- target="_blank"
289
- v-bind="pluginOptions.uploaderOptions?.downloadBtnProps"
290
- >
291
- <q-tooltip class="m-dt-btn-tooltip">
292
- {{ __('myth.titles.download') }}
293
- </q-tooltip>
294
- </q-btn>
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"
@@ -0,0 +1,6 @@
1
+ export const useColor = () => {
2
+ const toColors = (color: any) => {
3
+
4
+ }
5
+ return { toColors }
6
+ }
@@ -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
@@ -13,6 +13,7 @@
13
13
  @import './style/m-input'
14
14
  @import './style/m-picker'
15
15
  @import './style/m-select'
16
+ @import './style/m-toggle'
16
17
  @import './style/print'
17
18
  @import './style/transition'
18
19
  @import './style/typography'
@@ -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,3 @@
1
+
2
+ .m-toggle__col + .m-toggle__col
3
+ padding-left: 10px
@@ -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
@@ -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 { MDialogProps } from './api/MDialog'
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'