@mythpe/quasar-ui-qui 0.0.26 → 0.0.27-dev

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 (92) hide show
  1. package/index.d.ts +13 -0
  2. package/package.json +15 -8
  3. package/src/boot/register.ts +14 -0
  4. package/src/components/datatable/MDatatable.vue +2305 -0
  5. package/src/components/datatable/MDtAvatar.vue +49 -0
  6. package/src/components/datatable/MDtBtn.vue +153 -0
  7. package/src/components/datatable/MDtContextmenuItems.vue +54 -0
  8. package/src/components/datatable/index.ts +6 -0
  9. package/src/components/form/MAvatarViewer.vue +327 -0
  10. package/src/components/form/MAxios.vue +144 -0
  11. package/src/components/form/MBtn.vue +271 -93
  12. package/src/components/form/MCheckbox.vue +150 -0
  13. package/src/components/form/MColor.vue +122 -0
  14. package/src/components/form/MDate.vue +50 -0
  15. package/src/components/form/MEditor.vue +285 -0
  16. package/src/components/form/MEmail.vue +43 -0
  17. package/src/components/form/MField.vue +148 -0
  18. package/src/components/form/MFile.vue +215 -0
  19. package/src/components/form/MForm.vue +89 -0
  20. package/src/components/form/MHidden.vue +86 -0
  21. package/src/components/form/MHiddenInput.vue +58 -0
  22. package/src/components/form/MInput.vue +178 -0
  23. package/src/components/form/MInputFieldControl.vue +27 -0
  24. package/src/components/form/MInputLabel.vue +38 -0
  25. package/src/components/form/MMobile.vue +43 -0
  26. package/src/components/form/MOptions.vue +255 -0
  27. package/src/components/form/MOtp.vue +292 -0
  28. package/src/components/form/MPassword.vue +73 -0
  29. package/src/components/form/MPicker.vue +313 -0
  30. package/src/components/form/MRadio.vue +181 -0
  31. package/src/components/form/MSelect.vue +352 -0
  32. package/src/components/form/MTime.vue +48 -0
  33. package/src/components/form/MToggle.vue +211 -0
  34. package/src/components/form/MUploader.vue +511 -0
  35. package/src/components/form/index.ts +63 -0
  36. package/src/components/grid/MBlock.vue +39 -18
  37. package/src/components/grid/MCol.vue +11 -15
  38. package/src/components/grid/MColumn.vue +12 -1
  39. package/src/components/grid/MContainer.vue +22 -13
  40. package/src/components/grid/MHelpRow.vue +13 -12
  41. package/src/components/grid/MRow.vue +31 -10
  42. package/src/components/grid/index.ts +16 -0
  43. package/src/components/index.ts +15 -0
  44. package/src/components/modal/MDialog.vue +58 -0
  45. package/src/components/modal/MModalMenu.vue +62 -0
  46. package/src/components/modal/MTooltip.vue +39 -0
  47. package/src/components/modal/index.ts +5 -0
  48. package/src/components/parials/UploaderItem.vue +298 -0
  49. package/src/components/parials/index.ts +3 -0
  50. package/src/components/transition/MFadeTransition.vue +27 -0
  51. package/src/components/transition/MFadeXTransition.vue +26 -0
  52. package/src/components/transition/MTransition.vue +44 -0
  53. package/src/components/transition/index.ts +13 -0
  54. package/src/components/typography/MTypingString.vue +8 -0
  55. package/src/components/typography/index.ts +11 -0
  56. package/src/composable/index.ts +12 -0
  57. package/src/composable/useBindInput.ts +209 -0
  58. package/src/composable/useError.ts +11 -0
  59. package/src/composable/useMyth.ts +311 -0
  60. package/src/composable/useValue.ts +12 -0
  61. package/src/index.common.js +19 -1
  62. package/src/index.esm.js +18 -3
  63. package/src/index.js +19 -0
  64. package/src/index.sass +9 -26
  65. package/src/index.ts +18 -4
  66. package/src/index.umd.js +17 -2
  67. package/src/style/m-container.sass +13 -0
  68. package/src/style/main.sass +146 -0
  69. package/src/style/print.sass +14 -0
  70. package/src/style/transition.sass +40 -0
  71. package/src/types/api-helpers.d.ts +62 -0
  72. package/src/types/components.d.ts +1075 -27
  73. package/src/types/index.d.ts +21 -1
  74. package/src/types/install-options.d.ts +19 -0
  75. package/src/types/lodash.d.ts +26 -0
  76. package/src/types/m-datatable.d.ts +316 -0
  77. package/src/types/m-geolocation.d.ts +16 -0
  78. package/src/types/m-helpers.d.ts +97 -0
  79. package/src/types/plugin-props-option.d.ts +301 -0
  80. package/src/types/quasar-helpers.d.ts +7 -0
  81. package/src/types/theme.d.ts +12 -0
  82. package/src/utils/Helpers.ts +293 -0
  83. package/src/utils/Str.ts +211 -0
  84. package/src/utils/index.ts +13 -0
  85. package/src/utils/myth.ts +109 -0
  86. package/src/utils/vee-rules.ts +32 -0
  87. package/src/utils/vue-plugin.ts +161 -0
  88. package/tsconfig.json +9 -13
  89. package/src/myth.ts +0 -30
  90. package/src/types/myth.ts +0 -42
  91. package/src/vue-plugin.ts +0 -41
  92. package/types.d.ts +0 -1
@@ -0,0 +1,2305 @@
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
+ import type {
14
+ ApiServiceParams,
15
+ FetchRowsArgs,
16
+ MDatatableDialogsOptions,
17
+ MDatatableFilterForm,
18
+ MDatatableMetaServer,
19
+ MDatatableOptions,
20
+ MDatatablePagination,
21
+ MDatatableProps,
22
+ MDatatableScope,
23
+ MDtExportOptions,
24
+ MDtItem,
25
+ MDtItemIndex,
26
+ MDtMythApiServicesSchema
27
+ } from '../../types'
28
+ import type { AxiosRequestConfig } from 'axios'
29
+ import type { InvalidSubmissionHandler, SubmissionHandler } from 'vee-validate'
30
+ import { useForm } from 'vee-validate'
31
+ import { computed, defineEmits, nextTick, onMounted, reactive, ref, toRef, toValue, useSlots, watch } from 'vue'
32
+ import { is as quasarHelpers, QCardSection, QTable, useQuasar } from 'quasar'
33
+ import lodash from 'lodash'
34
+ import { useRoute, useRouter } from 'vue-router'
35
+ import { useMyth } from '../../composable'
36
+
37
+ const initPaginationOptions: MDatatablePagination = {
38
+ sortBy: undefined,
39
+ descending: undefined,
40
+ page: 1,
41
+ rowsPerPage: 50,
42
+ rowsNumber: 0
43
+ }
44
+ const initMetaServer: MDatatableMetaServer = {
45
+ current_page: null,
46
+ last_page: null,
47
+ total: null
48
+ }
49
+
50
+ interface Props {
51
+ controlKey?: MDatatableProps['controlKey'];
52
+ defaultItem?: MDatatableProps['defaultItem'];
53
+ contextItems?: MDatatableProps['contextItems'];
54
+ hideAutoMessage?: MDatatableProps['hideAutoMessage'];
55
+ headers: MDatatableProps['headers'];
56
+ serviceName?: MDatatableProps['serviceName'];
57
+ requestParams?: MDatatableProps['requestParams'];
58
+ pdf?: MDatatableProps['pdf'];
59
+ excel?: MDatatableProps['excel'];
60
+ exportUrl?: MDatatableProps['exportUrl'];
61
+ hideSearch?: MDatatableProps['hideSearch'];
62
+ searchDebounce?: MDatatableProps['searchDebounce'];
63
+ withIndex?: MDatatableProps['withIndex'];
64
+ withStore?: MDatatableProps['withStore'];
65
+ withShow?: MDatatableProps['withShow'];
66
+ withUpdate?: MDatatableProps['withUpdate'];
67
+ hideAddBtn?: MDatatableProps['hideAddBtn'];
68
+ hideUpdateBtn?: MDatatableProps['hideUpdateBtn'];
69
+ hideShowBtn?: MDatatableProps['hideShowBtn'];
70
+ hideDestroyBtn?: MDatatableProps['hideDestroyBtn'];
71
+ storeRoute?: MDatatableProps['storeRoute'];
72
+ storeQueryParams?: MDatatableProps['storeQueryParams'];
73
+ updateRoute?: MDatatableProps['updateRoute'];
74
+ updateQueryParams?: MDatatableProps['updateQueryParams'];
75
+ showRoute?: MDatatableProps['showRoute'];
76
+ showQueryParams?: MDatatableProps['showQueryParams'];
77
+ mouse?: MDatatableProps['mouse'];
78
+ noRefreshBtn?: MDatatableProps['noRefreshBtn'];
79
+ endReach?: MDatatableProps['endReach'];
80
+ showSelection?: MDatatableProps['showSelection'];
81
+ hideSelection?: MDatatableProps['hideSelection'];
82
+ multiSelection?: MDatatableProps['multiSelection'];
83
+ multiDestroy?: MDatatableProps['multiDestroy'];
84
+ rowsPerPageOptions?: MDatatableProps['rowsPerPageOptions'];
85
+ ignoreKeys?: MDatatableProps['ignoreKeys'];
86
+ grid?: MDatatableProps['grid'];
87
+ title?: MDatatableProps['title'];
88
+ manageColumns?: MDatatableProps['manageColumns'];
89
+ visibleColumns?: MDatatableProps['visibleColumns'];
90
+ searchColumns?: MDatatableProps['searchColumns'];
91
+ addTopBtn?: MDatatableProps['addTopBtn'];
92
+ addListBtn?: MDatatableProps['addListBtn'];
93
+ addFabBtn?: MDatatableProps['addFabBtn'];
94
+ fullscreenBtn?: MDatatableProps['fullscreenBtn'];
95
+ noBodyControl?: MDatatableProps['noBodyControl'];
96
+ showCardControlHeader?: MDatatableProps['showCardControlHeader'];
97
+ dense?: MDatatableProps['dense'];
98
+ bordered?: MDatatableProps['bordered'];
99
+ flat?: MDatatableProps['flat'];
100
+ rows?: MDatatableProps['rows'];
101
+ fixed?: MDatatableProps['fixed'];
102
+ imageColumns?: MDatatableProps['imageColumns'];
103
+ imageMode?: MDatatableProps['imageMode'];
104
+ imageSize?: MDatatableProps['imageSize'];
105
+ }
106
+
107
+ const props = withDefaults(defineProps<Props>(), {
108
+ rows: undefined,
109
+ controlKey: () => 'control',
110
+ defaultItem: undefined,
111
+ contextItems: undefined,
112
+ hideAutoMessage: undefined,
113
+ headers: () => ([]),
114
+ serviceName: () => '',
115
+ requestParams: undefined,
116
+ pdf: undefined,
117
+ excel: undefined,
118
+ exportUrl: undefined,
119
+ hideSearch: undefined,
120
+ searchDebounce: () => 600,
121
+ withIndex: undefined,
122
+ withStore: undefined,
123
+ withShow: undefined,
124
+ withUpdate: undefined,
125
+ hideAddBtn: undefined,
126
+ hideUpdateBtn: undefined,
127
+ hideShowBtn: undefined,
128
+ hideDestroyBtn: undefined,
129
+ storeRoute: undefined,
130
+ storeQueryParams: undefined,
131
+ updateRoute: undefined,
132
+ updateQueryParams: undefined,
133
+ showRoute: undefined,
134
+ showQueryParams: undefined,
135
+ mouse: undefined,
136
+ noRefreshBtn: undefined,
137
+ endReach: undefined,
138
+ showSelection: undefined,
139
+ hideSelection: undefined,
140
+ multiSelection: undefined,
141
+ multiDestroy: undefined,
142
+ rowsPerPageOptions: () => [50, 250, 500, 0],
143
+ ignoreKeys: undefined,
144
+ grid: undefined,
145
+ title: undefined,
146
+ manageColumns: undefined,
147
+ visibleColumns: undefined,
148
+ searchColumns: undefined,
149
+ addTopBtn: undefined,
150
+ addListBtn: undefined,
151
+ addFabBtn: undefined,
152
+ fullscreenBtn: undefined,
153
+ noBodyControl: undefined,
154
+ showCardControlHeader: undefined,
155
+ dense: undefined,
156
+ bordered: undefined,
157
+ flat: undefined,
158
+ fixed: undefined,
159
+ imageColumns: () => ([]),
160
+ imageMode: () => 'image',
161
+ imageSize: () => '35px'
162
+ })
163
+
164
+ interface Emits {
165
+ (e: 'update:rows', value: any): void
166
+ }
167
+
168
+ const emit = defineEmits<Emits>()
169
+
170
+ const $myth = useMyth()
171
+ const {
172
+ __,
173
+ parseHeaders,
174
+ pascalCase,
175
+ pluralize,
176
+ alertError,
177
+ confirmMessage,
178
+ alertSuccess,
179
+ scrollToElementFromErrors,
180
+ downloadFromResponse
181
+ } = $myth
182
+ const { mOptions, api, themeInput, isSmall } = reactive($myth)
183
+ const slots = useSlots()
184
+ const router = useRouter()
185
+ const route = useRoute()
186
+ const $q = useQuasar()
187
+ const defaultItem = computed(() => props.defaultItem)
188
+ const formRef = useForm<any, any>()
189
+ const formScope = reactive(formRef)
190
+ const { resetForm: resetDialogForm, setValues, handleSubmit } = formRef
191
+ watch(() => props.defaultItem, (v) => {
192
+ resetDialogForm({ values: { ...v || {} } }, { force: !0 })
193
+ }, { immediate: !0, deep: !0 })
194
+ const resetVeeForm = async (attrs?: Record<string, any>) => {
195
+ const init: any = {}
196
+ for (const k in props.defaultItem) {
197
+ init[k] = attrs && k in attrs ? attrs[k] : props.defaultItem[k]
198
+ }
199
+ resetDialogForm({ values: init || {} }, { force: !0 })
200
+ attrs && setValues(attrs, !1)
201
+ }
202
+ const serviceName = computed(() => props.serviceName)
203
+ const exportToBlob = computed(() => {
204
+ if (props.exportUrl === undefined && mOptions.datatable?.exportUrl === undefined) {
205
+ return !0
206
+ }
207
+ const t = props.exportUrl === undefined ? mOptions.datatable?.exportUrl : props.exportUrl
208
+ if (t !== undefined) {
209
+ if (t.toString() === 'true' || t.toString() === '') {
210
+ return !1
211
+ }
212
+ if (t.toString() === 'pdf') {
213
+ return 'excel'
214
+ }
215
+ if (t.toString() === 'excel') {
216
+ return 'pdf'
217
+ }
218
+ }
219
+ return !0
220
+ })
221
+ // Prevent user from back
222
+ /* router.beforeResolve(() => {
223
+ if (dialogs.filter) {
224
+ closeFilterDialog()
225
+ return !1
226
+ } else if (dialogs.show) {
227
+ closeShowDialog()
228
+ return !1
229
+ } else if (dialogs.form) {
230
+ closeFormDialog()
231
+ return !1
232
+ }
233
+ return !hasAction.value
234
+ }) */
235
+ const getRows = ref<MDtItem[]>([])
236
+ watch(() => getRows.value, (v) => emit('update:rows', v), { deep: !0 })
237
+ const filterDialogModel = ref(!1)
238
+ const showDialogModel = ref(!1)
239
+ const formDialogModel = ref(!1)
240
+ const isUpdateDialog = ref(!1)
241
+ const dialogItem = ref<MDtItem | undefined>(undefined)
242
+ const dialogItemIndex = ref<MDtItemIndex | undefined>()
243
+ const dialogErrors = ref<any>({})
244
+ const dialogs = reactive<MDatatableDialogsOptions>({
245
+ filter: filterDialogModel,
246
+ show: showDialogModel,
247
+ form: formDialogModel,
248
+ isUpdate: isUpdateDialog,
249
+ item: dialogItem,
250
+ index: dialogItemIndex,
251
+ errors: dialogErrors
252
+ })
253
+ const resetDialogs = () => {
254
+ dialogs.filter = !1
255
+ dialogs.show = !1
256
+ dialogs.form = !1
257
+ dialogs.isUpdate = !1
258
+ dialogs.item = undefined
259
+ dialogs.index = undefined
260
+ dialogs.errors = {}
261
+ }
262
+
263
+ /** Table */
264
+
265
+ /** --- */
266
+ const headersProp = computed(() => props.headers)
267
+ const getHeaders = computed<any[]>(() => parseHeaders(headersProp.value, reactive({ noSort: props.imageColumns })) || [])
268
+ const visibleColumnsProp = computed(() => props.visibleColumns)
269
+ const visibleHeaders = ref(parseHeaders(visibleColumnsProp.value || headersProp.value).map(e => e.name))
270
+ /** --- */
271
+
272
+ const selected = ref<MDtItem[]>([])
273
+ const onUpdateSelectedItems = () => {
274
+ if (contextmenu.value) {
275
+ contextmenu.value = !1
276
+ }
277
+ }
278
+
279
+ const meta = ref<MDatatableMetaServer>({ ...initMetaServer })
280
+ const pagination = ref<MDatatablePagination>({ ...initPaginationOptions })
281
+ const search = ref<string | null>(null)
282
+ const searchColumnsProp = computed(() => props.searchColumns)
283
+ const searchColumnsRef = ref<string[]>(parseHeaders(searchColumnsProp.value || headersProp.value)
284
+ .filter(e => e?.field !== props.controlKey)
285
+ .map(
286
+ e => e.name))
287
+ const searchPlaceholder = computed<string>(() => {
288
+ if (searchColumnsRef.value.length > 0) {
289
+ return __('myth.datatable.searchInputPlaceholder',
290
+ {
291
+ v: getHeaders.value.filter(e => e?.field !== props.controlKey && searchColumnsRef.value.indexOf(e.name) !== -1)
292
+ .map(e => e.label)
293
+ .join(', ')
294
+ })
295
+ }
296
+ return 'myth.datatable.searchInput'
297
+ })
298
+ const loading = ref<boolean>(!1)
299
+ const filterForm = ref<MDatatableFilterForm>({})
300
+ const tempFilterForm = ref<MDatatableFilterForm>({})
301
+ const hasAction = ref<boolean>(!1)
302
+ const fullscreen = ref(!1)
303
+ const tableOptions = reactive<MDatatableOptions>({
304
+ search,
305
+ loading,
306
+ pagination,
307
+ meta,
308
+ filter: filterForm,
309
+ tempFilter: tempFilterForm,
310
+ selected,
311
+ hasAction,
312
+ fullscreen,
313
+ getHeaders,
314
+ visibleHeaders
315
+ })
316
+ /** Table */
317
+
318
+ /** --- */
319
+ const addListBtnComputed = computed(() => {
320
+ if (props.addListBtn !== undefined) {
321
+ return props.addListBtn
322
+ }
323
+ if (mOptions.datatable?.addListBtn !== undefined) {
324
+ return mOptions.datatable?.addListBtn
325
+ }
326
+ return !1
327
+ })
328
+ const hasAddBtn = computed<boolean>(() => {
329
+ if (props.hideAddBtn) {
330
+ return !1
331
+ }
332
+ return !!slots.form || !!props.storeRoute
333
+ })
334
+ const hasUpdateBtn = computed<boolean>(() => {
335
+ if (props.hideUpdateBtn) {
336
+ return !1
337
+ }
338
+ return Boolean(slots.form) || Boolean(props.updateRoute)
339
+ })
340
+ const hasShowBtn = computed<boolean>(() => {
341
+ if (props.hideShowBtn) {
342
+ return !1
343
+ }
344
+ return Boolean(slots.show) || Boolean(props.showRoute)
345
+ })
346
+ const hasDestroyBtn = computed<boolean>(() => !props.hideDestroyBtn)
347
+ const hasFilterDialog = computed<boolean>(() => !!slots.filter)
348
+ const hasMenu = computed<boolean>(() => {
349
+ if (!!props.pdf || !!props.excel) {
350
+ return !0
351
+ }
352
+ return hasAddBtn.value && !!addListBtnComputed.value
353
+ })
354
+
355
+ const isUpdateMode = ref<boolean>(!1)
356
+ const formMode = computed<'update' | 'store'>(() => isUpdateMode.value ? 'update' : 'store')
357
+ const isSingleSelectedItem = computed<boolean>(() => tableOptions.selected.length === 1)
358
+ const firstSelectedItem = computed(() => tableOptions.selected[0] as MDtItem)
359
+ const hasSelectedItem = computed<boolean>(() => tableOptions.selected.length > 0)
360
+
361
+ /* Titles */
362
+ const getShowTitle = computed(() => {
363
+ if (serviceName.value && typeof serviceName.value !== 'function') {
364
+ const c = pascalCase(pluralize(serviceName.value.split('/').pop()))
365
+ return __('replace.show_details', { name: __(`choice.${c}`, 1) })
366
+ }
367
+ return __('show_details')
368
+ })
369
+ const getFormTitle = computed(() => {
370
+ const name = serviceName.value && typeof serviceName.value !== 'function' ? __(`choice.${pascalCase(pluralize(serviceName.value.split(
371
+ '/').pop()))}`, 1) : ''
372
+ return __(`replace.${formMode.value}`, { name })
373
+ })
374
+ const isGrid = computed(() => {
375
+ if (props.grid !== undefined) {
376
+ return props.grid
377
+ }
378
+ return $q.screen.lt.md
379
+ })
380
+ /* Titles */
381
+
382
+ /** Methods */
383
+ const getMythApiServicesSchema = (): MDtMythApiServicesSchema => {
384
+ if (typeof serviceName.value === 'function') {
385
+ return serviceName.value() as MDtMythApiServicesSchema
386
+ }
387
+ const c = api[serviceName.value]
388
+ if (!c) {
389
+ throw Error(`No Service: ${serviceName.value}`)
390
+ }
391
+ return c
392
+ }
393
+ const updateSelectedItems = (selected: MDtItem[]) => {
394
+ tableOptions.selected = selected
395
+ }
396
+ const onScroll = ({ index, to }: any) => {
397
+ if (index && to && index === to) {
398
+ loadMore()
399
+ }
400
+ }
401
+ const loadMore = () => {
402
+ nextTick(() => {
403
+ fetchDatatableItems({
404
+ pagination: {
405
+ ...tableOptions.pagination,
406
+ page: (tableOptions.pagination.page ?? 0) + 1
407
+ },
408
+ filter: tableOptions.search
409
+ })
410
+ })
411
+ }
412
+ const refreshNoUpdate = (done?: () => void) => {
413
+ if (contextmenu.value) {
414
+ contextmenu.value = !1
415
+ }
416
+ meta.value = { ...initMetaServer }
417
+ pagination.value.page = 1
418
+ pagination.value.rowsNumber = 0
419
+ getRows.value = []
420
+ nextTick(() => {
421
+ fetchDatatableItems({
422
+ pagination: pagination.value,
423
+ filter: tableOptions.search
424
+ })
425
+ if (done) {
426
+ done()
427
+ }
428
+ })
429
+ }
430
+ const refresh = (done?: () => void) => refreshNoUpdate(done)
431
+ const getRequestWith = (type: 'withIndex' | 'withShow' | 'withUpdate' | 'withStore'): string | null => {
432
+ let v: any = []
433
+ const params: { [k: string]: string } & string | (() => string | object) = props[type] as any
434
+ if (params) {
435
+ if (typeof params === 'string') {
436
+ v = params.split(',')
437
+ }
438
+
439
+ if (lodash.isArray(params)) {
440
+ v = [...params]
441
+ } else if (lodash.isObject(params) && typeof params !== 'function') {
442
+ let e
443
+ for (const k in params) {
444
+ e = params[k]
445
+ v.push(`${k}=${e}`)
446
+ }
447
+ } else if (lodash.isFunction(params)) {
448
+ const f = params()
449
+ v = typeof f === 'string' ? f.split(',') : f
450
+ }
451
+ }
452
+ return v.join(',') ?? null
453
+ }
454
+ const getDatatableParams = ({ pagination, filter }: FetchRowsArgs = {}, merge: Partial<ApiServiceParams> = {}): ApiServiceParams => {
455
+ let params: ApiServiceParams = {
456
+ // filter: tableOptions.filter,
457
+ // search: filter || undefined,
458
+ headerItems: getHeaders.value.map((e: any) => e.name).join(','),
459
+ // headers: ['name'],
460
+ // ids: tableOptions.selected.map((e: any) => e.id),
461
+ indexType: 'index',
462
+ fdt: 'i',
463
+ itemsPerPage: pagination?.rowsPerPage === 0 ? -1 : (pagination?.rowsPerPage !== undefined ? pagination?.rowsPerPage : 0),
464
+ page: pagination?.page !== undefined ? pagination.page : 0,
465
+ sortBy: pagination?.sortBy !== undefined ? pagination.sortBy : undefined,
466
+ sortDesc: !pagination?.sortBy ? undefined : (pagination?.descending === !0 ? 1 : (pagination?.descending === !1 ? 0 : undefined))
467
+ }
468
+ if (filter) {
469
+ params.search = filter
470
+ }
471
+ if (Object.keys(tableOptions.filter).length > 0) {
472
+ const TempFilter = { ...tableOptions.filter } as any
473
+ for (const fKey in TempFilter) {
474
+ if (lodash.isArray(TempFilter[fKey])) {
475
+ TempFilter[fKey] = TempFilter[fKey].map((elm: any) => {
476
+ if (elm.id) {
477
+ return elm.id
478
+ } else if (elm.value) {
479
+ return elm.value
480
+ }
481
+ return elm
482
+ })
483
+ } else if (lodash.isPlainObject(TempFilter[fKey])) {
484
+ if (TempFilter[fKey].id) {
485
+ TempFilter[fKey] = TempFilter[fKey].id
486
+ } else if (TempFilter[fKey].value) {
487
+ TempFilter[fKey] = TempFilter[fKey].value
488
+ }
489
+ }
490
+ }
491
+ // console.log(JSON.stringify(tableOptions.filter))
492
+ // params.filter = tableOptions.filter
493
+ // console.log(TempFilter)
494
+ params.filter = TempFilter
495
+ }
496
+ if (searchColumnsRef.value.length > 0) {
497
+ params.searchColumns = searchColumnsRef.value.join(',')
498
+ }
499
+ if (props.requestParams) {
500
+ if (typeof props.requestParams === 'function') {
501
+ params = props.requestParams(params) as ApiServiceParams
502
+ } else {
503
+ params = {
504
+ ...params,
505
+ ...props.requestParams
506
+ }
507
+ }
508
+ }
509
+ return { ...params, ...merge }
510
+ }
511
+ const fetchDatatableItems = (opts: FetchRowsArgs = {}) => {
512
+ if (props.endReach && tableOptions.meta.last_page && tableOptions.pagination.page >= tableOptions.meta.last_page) {
513
+ return
514
+ }
515
+ if (tableOptions.loading || !serviceName.value) return
516
+ tableOptions.loading = !0
517
+ tableOptions.selected = []
518
+ nextTick(() => {
519
+ const params = getDatatableParams(opts)
520
+ const requestWith = getRequestWith('withIndex')
521
+ if (requestWith) {
522
+ params.requestWith = requestWith
523
+ }
524
+ // console.log({ params })
525
+ getMythApiServicesSchema().index({ params })
526
+ .then((result: any) => {
527
+ const { _data, _meta } = result
528
+ pagination.value = {
529
+ page: parseInt((_meta?.current_page || 1).toString()) || 1,
530
+ rowsPerPage: parseInt(_meta?.per_page) || 0,
531
+ rowsNumber: parseInt((_meta?.total || 0).toString()) || 0,
532
+ sortBy: opts?.pagination?.sortBy || undefined,
533
+ descending: opts?.pagination?.descending || undefined
534
+ }
535
+ _meta && (meta.value = _meta)
536
+ if (props.endReach) {
537
+ getRows.value = [...getRows.value, ...(_data || [])]
538
+ } else {
539
+ getRows.value = _data || []
540
+ }
541
+ })
542
+ .catch((e) => {
543
+ // console.log(e)
544
+ if (e?.response?.status === 401) {
545
+ logoutDatatable()
546
+ return e
547
+ }
548
+ if (e?._message) {
549
+ alertError(e._message)
550
+ } else if (e?.message) {
551
+ alertError(e.message)
552
+ }
553
+ })
554
+ .finally(() => {
555
+ tableOptions.loading = !1
556
+ })
557
+ })
558
+ }
559
+ const exportData = (type: MDtExportOptions) => {
560
+ if (loading.value) {
561
+ return
562
+ }
563
+ const ex = async () => {
564
+ loading.value = !0
565
+ const toBLob = exportToBlob.value === !0 || exportToBlob.value === type
566
+ const data = getDatatableParams({
567
+ pagination: tableOptions.pagination,
568
+ filter: tableOptions.search
569
+ }, {
570
+ indexType: type,
571
+ fdt: 'e',
572
+ toUrl: toBLob === !0 ? !1 : exportToBlob.value !== type,
573
+ headerItems: getHeaders.value.filter(e => e?.field !== props.controlKey && visibleHeaders.value.indexOf(e.name) !== -1)
574
+ })
575
+ if (tableOptions.selected.length > 0) {
576
+ data.ids = tableOptions.selected.map((e: any) => e.id)
577
+ }
578
+ // console.log(3)
579
+ const config: AxiosRequestConfig = {}
580
+ if (toBLob) {
581
+ config.responseType = 'blob'
582
+ }
583
+ getMythApiServicesSchema().export(data, config)
584
+ .then(async (response) => {
585
+ const { _message } = response || {}
586
+ _message && (alertSuccess(_message))
587
+ try {
588
+ await downloadFromResponse(response)
589
+ } catch (e: any) {
590
+ if (response.status === 200 && response.headers['content-type'] === 'application/json') {
591
+ return response
592
+ }
593
+ if (e?.code) {
594
+ alertError(__(`messages.${e.code}`))
595
+ } else if (e?.message) {
596
+ alertError(e.message)
597
+ }
598
+ console.log(e)
599
+ }
600
+ return response
601
+ })
602
+ .catch((e) => {
603
+ alertError(e?._message || e?.message || 'Error')
604
+ })
605
+ .finally(() => {
606
+ loading.value = !1
607
+ })
608
+ }
609
+ if (!tableOptions.selected.length) {
610
+ confirmMessage(__('messages.export_all')).onOk(() => ex())
611
+ } else {
612
+ ex()
613
+ }
614
+ }
615
+ /** Methods */
616
+
617
+ const openDialogTimeout = 100
618
+ /**
619
+ * Filter Dialog
620
+ */
621
+ const openFilterDialog = () => {
622
+ dialogs.filter = !0
623
+ tempFilterForm.value = { ...tableOptions.filter }
624
+ }
625
+ const saveFilterDialog = () => {
626
+ filterForm.value = { ...tableOptions.tempFilter }
627
+ nextTick(() => {
628
+ dialogs.filter = !1
629
+ })
630
+ }
631
+ const closeFilterDialog = () => {
632
+ dialogs.filter = !1
633
+ tempFilterForm.value = { ...tableOptions.filter }
634
+ }
635
+ const onRemoveFilter = (key: string | number) => {
636
+ const filter = filterForm.value
637
+ delete filter[key]
638
+ filterForm.value = { ...filter }
639
+ if (route.query[key]) {
640
+ const query = { ...route.query }
641
+ delete query[key]
642
+ router.push({ query })
643
+ }
644
+ }
645
+ const updateFilterOptions = (data: Record<string, any>) => {
646
+ filterForm.value = {
647
+ ...tableOptions.filter,
648
+ ...data
649
+ }
650
+ }
651
+ /**
652
+ * Filter Dialog
653
+ */
654
+
655
+ /**
656
+ * Show Dialog
657
+ */
658
+ const openShowDialogNoIndex = async (i: MDtItem) => {
659
+ const item = toRef(i)
660
+ const index = getRows.value.findIndex(e => e.id === item.value.id)
661
+ return openShowDialog(item.value, index)
662
+ }
663
+ const openShowDialog = async (i: MDtItem, index: MDtItemIndex) => {
664
+ const fdt = 's'
665
+ const item = toRef(i)
666
+ if (props.showQueryParams) {
667
+ router.push({ query: { ...route.query, id: item.value.id, fdt } })
668
+ return
669
+ }
670
+ if (props.showRoute) {
671
+ if (typeof props.showRoute === 'string') {
672
+ router.push({ name: props.showRoute, params: { id: item.value.id }, query: route.query })
673
+ } else {
674
+ router.push(props.showRoute)
675
+ }
676
+ return
677
+ }
678
+ if (loading.value) {
679
+ return
680
+ }
681
+ loading.value = !0
682
+ const params: any = { fdt }
683
+ if (getRequestWith('withShow')) {
684
+ params.requestWith = getRequestWith('withShow')
685
+ }
686
+
687
+ getMythApiServicesSchema().show(item.value.id, { params })
688
+ .then(({ _data }) => {
689
+ dialogs.item = _data
690
+ dialogs.index = index
691
+ getRows.value[index as any] = _data as any
692
+ nextTick(() => setTimeout(() => (selected.value = [getRows.value[index] as any]), openDialogTimeout))
693
+ setTimeout(() => (dialogs.show = !0), openDialogTimeout)
694
+ })
695
+ .catch((e: any) => {
696
+ const message = e?._message || e?.message
697
+ message && alertError(message)
698
+ })
699
+ .finally(() => (loading.value = !1))
700
+ }
701
+ const closeShowDialog = () => {
702
+ dialogs.show = !1
703
+ dialogs.item = undefined
704
+ dialogs.index = undefined
705
+ }
706
+ /**
707
+ * Show Dialog
708
+ */
709
+
710
+ /**
711
+ * Form Dialog
712
+ */
713
+ const updateRouteProp = computed(() => props.updateRoute)
714
+ const openUpdateDialogNoIndex = (i: MDtItem) => {
715
+ const item = toRef(i)
716
+ const index = getRows.value.findIndex(e => e.id === item.value.id)
717
+ return openUpdateDialog(item.value, index)
718
+ }
719
+ const openUpdateDialog = (i: MDtItem, index: MDtItemIndex) => {
720
+ const fdt = 'u'
721
+ const item = { ...toValue(i) }
722
+ if (props.updateQueryParams) {
723
+ router.push({ query: { ...route.query, id: item.id, fdt } })
724
+ return
725
+ }
726
+ if (updateRouteProp.value) {
727
+ if (typeof updateRouteProp.value === 'string') {
728
+ router.push({ name: updateRouteProp.value, params: { id: item.id }, query: route.query })
729
+ } else {
730
+ router.push(updateRouteProp.value)
731
+ }
732
+ return
733
+ }
734
+ if (loading.value) {
735
+ return
736
+ }
737
+ loading.value = !0
738
+ isUpdateMode.value = !0
739
+ const params: any = { fdt }
740
+ if (getRequestWith('withUpdate')) {
741
+ params.requestWith = getRequestWith('withUpdate')
742
+ }
743
+ getMythApiServicesSchema().show(item.id, { params })
744
+ .then(({ _data }) => {
745
+ dialogs.item = _data
746
+ dialogs.index = index
747
+ if (_data) {
748
+ getRows.value[index] = { ..._data } as any
749
+ nextTick(() => setTimeout(() => (selected.value = [getRows.value[index] as any]), openDialogTimeout))
750
+ }
751
+ setTimeout(async () => {
752
+ resetVeeForm(_data)
753
+ nextTick()
754
+ dialogs.form = !0
755
+ }, openDialogTimeout)
756
+ })
757
+ .catch((e) => {
758
+ const message = e?._message || e?.message
759
+ message && alertError(message)
760
+ })
761
+ .finally(() => (loading.value = !1))
762
+ }
763
+ const openCreateDialog = (dtItem?: MDtItem) => {
764
+ const fdt = 'c'
765
+ if (props.storeQueryParams) {
766
+ router.push({ query: { ...route.query, id: undefined, fdt } })
767
+ return
768
+ }
769
+ if (props.storeRoute) {
770
+ if (typeof props.storeRoute === 'string') {
771
+ router.push({ name: props.storeRoute, query: route.query })
772
+ } else {
773
+ router.push(props.storeRoute)
774
+ }
775
+ return
776
+ }
777
+ isUpdateMode.value = !1
778
+ dialogs.item = { ...defaultItem.value, ...dtItem } as MDtItem
779
+ dialogs.index = undefined
780
+ setTimeout(() => {
781
+ resetVeeForm(dtItem)
782
+ dialogs.form = !0
783
+ }, openDialogTimeout)
784
+ }
785
+ const closeFormDialog = () => {
786
+ dialogs.form = !1
787
+ isUpdateMode.value = !1
788
+ dialogs.item = undefined
789
+ dialogs.index = undefined
790
+ setTimeout(() => resetVeeForm(), openDialogTimeout)
791
+ }
792
+ /**
793
+ * Form Dialog
794
+ */
795
+
796
+ const updateDatatableItem = (i: MDtItem, index?: MDtItemIndex) => {
797
+ const item = { ...toValue(i) }
798
+ if (item) {
799
+ if (index !== undefined) {
800
+ getRows.value[index] = item
801
+ } else {
802
+ const findIndex = getRows.value.findIndex(e => parseInt(e.id?.toString?.() ?? '') === parseInt(item?.id?.toString?.() ?? ''))
803
+ if (findIndex >= 0) {
804
+ getRows.value[findIndex] = item
805
+ }
806
+ }
807
+ }
808
+ }
809
+ const removeDtItem = (i: MDtItem | number) => {
810
+ const item = toRef<MDtItem | number>(i)
811
+ const id: string | number = typeof item.value !== 'object' ? item.value : item.value.id as string
812
+ if (typeof item.value !== 'object') {
813
+ getRows.value = getRows.value.filter((e, i) => i !== id)
814
+ } else {
815
+ getRows.value = getRows.value.filter((e) => parseInt(e.id?.toString() ?? '') !== parseInt(id.toString()))
816
+ }
817
+ }
818
+ const ignoreKeysProps = computed(() => props.ignoreKeys)
819
+
820
+ const formDialogCartSection = ref<InstanceType<typeof QCardSection> | null>(null)
821
+ const onSuccess: SubmissionHandler = async (form) => {
822
+ if (loading.value) {
823
+ return
824
+ }
825
+ loading.value = !0
826
+ const api = getMythApiServicesSchema()
827
+ const fdt = isUpdateMode.value ? 'u' : 'c'
828
+ if (ignoreKeysProps.value) {
829
+ if (typeof ignoreKeysProps.value === 'function') {
830
+ form = ignoreKeysProps.value(form) as any
831
+ } else {
832
+ for (const k in ignoreKeysProps.value) {
833
+ delete form[ignoreKeysProps.value[k] as any]
834
+ }
835
+ }
836
+ }
837
+
838
+ const ignoreKeys = [
839
+ '_to_string',
840
+ '_to_number_format',
841
+ '_to_date_format',
842
+ 'toString',
843
+ 'toNumberFormat',
844
+ 'toDateFormat'
845
+ ]
846
+
847
+ for (const i in ignoreKeys) {
848
+ for (const k in form) {
849
+ if (ignoreKeys[i] && k.slice(-ignoreKeys[i]?.length) === ignoreKeys[i]) {
850
+ delete form[k]
851
+ }
852
+ }
853
+ }
854
+ const _conf: any = { params: { fdt, requestWith: getRequestWith(isUpdateMode.value ? 'withUpdate' : 'withStore') || undefined } }
855
+ const method = async () => isUpdateMode.value ? await api.update(dialogs.item?.id || '', form, _conf) : await api.store(form, _conf)
856
+ try {
857
+ const { _data, _message, _success }: any = await method()
858
+ _message && alertSuccess(_message)
859
+ if (_success) {
860
+ if (isUpdateMode.value) {
861
+ _data && updateDatatableItem(_data, dialogs.index)
862
+ } else {
863
+ setTimeout(() => refresh(), openDialogTimeout)
864
+ }
865
+ // nextTick()
866
+ closeFormDialog()
867
+ }
868
+ } catch (e: any) {
869
+ const { _message, _errors } = e || {}
870
+ dialogs.errors = _errors || {}
871
+ scrollToElementFromErrors(_errors, undefined, '.m--datatable__dialog-form-container')
872
+ _message && alertError(_message)
873
+ if (_errors) {
874
+ formRef.setErrors(_errors)
875
+ }
876
+ } finally {
877
+ loading.value = !1
878
+ }
879
+ }
880
+ const onInvalidSubmit: InvalidSubmissionHandler = ({ errors }) => {
881
+ const keys: (keyof typeof errors)[] = Object.keys(errors)
882
+ if (keys[0]) {
883
+ const message = errors[keys[0]] as string || $myth.__('messages.the_given_data_was_invalid')
884
+ scrollToElementFromErrors({ [keys[0]]: [message] }, undefined, '.m--datatable__dialog-form-container')
885
+ }
886
+ }
887
+ const defaultSubmitItem = handleSubmit.withControlled(onSuccess, onInvalidSubmit)
888
+
889
+ const hideAutoMessage = computed(() => props.hideAutoMessage)
890
+ const onDeleteItem = (i: MDtItem, index: number) => {
891
+ const item = toRef(i)
892
+ if (loading.value || !item.value?.id) {
893
+ return
894
+ }
895
+ tableOptions.hasAction = !0
896
+ confirmMessage(__('messages.confirm_delete')).onOk(async () => {
897
+ loading.value = !0
898
+ try {
899
+ const { _message, _success } = await getMythApiServicesSchema().destroy(item.value.id)
900
+ if (!hideAutoMessage.value && _success && _message) {
901
+ _message && alertSuccess(_message)
902
+ }
903
+ if (_success) {
904
+ if (tableOptions.pagination.rowsNumber !== undefined) {
905
+ --tableOptions.pagination.rowsNumber
906
+ selected.value = []
907
+ }
908
+ removeDtItem(index)
909
+ }
910
+ } catch (e: any) {
911
+ e?._message && alertError(e._message)
912
+ } finally {
913
+ loading.value = !1
914
+ }
915
+ }).onDismiss(() => {
916
+ tableOptions.hasAction = !1
917
+ })
918
+ }
919
+ const deleteSelectionItem = () => {
920
+ if (!tableOptions.selected.length) return
921
+ if (loading.value || !tableOptions.selected.length) {
922
+ return
923
+ }
924
+ if (tableOptions.selected.length === 1) {
925
+ const dtItem = tableOptions.selected[0] as MDtItem
926
+ const index = getRows.value.findIndex((e: any) => parseInt(e.id) === parseInt(dtItem?.id?.toString() || ''))
927
+ return onDeleteItem(dtItem, index)
928
+ }
929
+ if (!props.multiDestroy) {
930
+ return
931
+ }
932
+ tableOptions.hasAction = !0
933
+ confirmMessage(__('messages.confirm_delete')).onOk(async () => {
934
+ loading.value = !0
935
+ try {
936
+ const { _message, _success } = await getMythApiServicesSchema().destroyAll(tableOptions.selected.map((e: MDtItem) => e.id))
937
+ if (!hideAutoMessage.value && _success && _message) {
938
+ _message && alertSuccess(_message)
939
+ }
940
+ if (_success) {
941
+ refresh()
942
+ }
943
+ } catch (e: any) {
944
+ e?._message && alertError(e._message)
945
+ } finally {
946
+ loading.value = !1
947
+ nextTick()
948
+ selected.value = []
949
+ }
950
+ }).onDismiss(() => {
951
+ tableOptions.hasAction = !1
952
+ })
953
+ }
954
+ const logoutDatatable = () => {
955
+ // const { logout, removeStorage } = $myth.store.state.
956
+ // removeStorage()
957
+ // logout(window.push_token)
958
+ // const name = this.$routes.auth.login
959
+ // if (this.$route.name !== name) {
960
+ // this.$router.replace({ name })
961
+ // }
962
+ }
963
+ /**
964
+ * ---
965
+ */
966
+
967
+ /**
968
+ * Dom
969
+ */
970
+ const contextmenu = ref(!1)
971
+ const onRowContextmenu = (e: MouseEvent | Event, row: MDtItem, index: number | undefined) => {
972
+ e.preventDefault?.()
973
+ nextTick(() => {
974
+ if (index === dialogs.index) {
975
+ selected.value = []
976
+ dialogs.item = undefined
977
+ dialogs.index = undefined
978
+ if (contextmenu.value) {
979
+ contextmenu.value = !1
980
+ }
981
+ return
982
+ }
983
+ selected.value = [row]
984
+ dialogs.item = row
985
+ dialogs.index = index
986
+ if (isGrid.value) {
987
+ contextmenu.value = !0
988
+ }
989
+ })
990
+ }
991
+ const contextmenuItemsProp = computed(() => props.contextItems)
992
+ const contextmenuItems = computed<any>(() => ([
993
+ ...(contextmenuItemsProp.value || []).sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),
994
+ {
995
+ name: 'show',
996
+ label: mOptions?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.show' : undefined,
997
+ click: (item: MDtItem, index: MDtItemIndex) => {
998
+ openShowDialog(item, index)
999
+ },
1000
+ showIf: hasShowBtn.value
1001
+ },
1002
+ {
1003
+ name: 'update',
1004
+ label: mOptions?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.update' : undefined,
1005
+ click: (item: MDtItem, index: MDtItemIndex) => {
1006
+ openUpdateDialog(item, index)
1007
+ },
1008
+ showIf: hasUpdateBtn.value
1009
+ },
1010
+ {
1011
+ name: 'destroy',
1012
+ label: mOptions?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.destroy' : undefined,
1013
+ click: (item: MDtItem, index: MDtItemIndex) => {
1014
+ selected.value = [item]
1015
+ onDeleteItem(item, index)
1016
+ },
1017
+ showIf: hasDestroyBtn.value,
1018
+ attr: {
1019
+ color: 'negative'
1020
+ }
1021
+ }
1022
+ ]))
1023
+ const endReach = computed<boolean>(() => Boolean(props.endReach))
1024
+ const rowsPerPageOptions = computed(() => props.rowsPerPageOptions)
1025
+ const getRowsPerPageOptions = computed<any[]>(() => endReach.value ? [0] : (rowsPerPageOptions.value || [0]))
1026
+
1027
+ /**
1028
+ * Image Dialog Start.
1029
+ */
1030
+ const imageDialog = reactive<MDatatableScope['imageDialog']>({
1031
+ value: !1,
1032
+ src: undefined,
1033
+ asAttachment: undefined
1034
+ })
1035
+ const openImageDialog = (src: string, opts?: { asAttachment?: boolean }) => {
1036
+ imageDialog.src = src
1037
+ imageDialog.asAttachment = opts?.asAttachment
1038
+ nextTick(() => {
1039
+ imageDialog.value = !0
1040
+ })
1041
+ }
1042
+ const closeImageDialog = () => {
1043
+ imageDialog.value = !1
1044
+ nextTick(() => {
1045
+ imageDialog.src = undefined
1046
+ imageDialog.asAttachment = undefined
1047
+ })
1048
+ }
1049
+ /**
1050
+ * Image Dialog End.
1051
+ */
1052
+
1053
+ onMounted(() => refresh())
1054
+
1055
+ watch(loading, v => {
1056
+ if (mOptions?.dt?.useQuasarLoading) {
1057
+ if (v) {
1058
+ $q.loading.show()
1059
+ } else {
1060
+ $q.loading.hide()
1061
+ }
1062
+ }
1063
+ tableOptions.hasAction = !!v
1064
+ })
1065
+ watch([filterForm, () => $q.lang.nativeName], () => refreshNoUpdate(), { deep: !0 })
1066
+ watch(formDialogModel, (v) => {
1067
+ if (!v) {
1068
+ dialogs.errors = {}
1069
+ }
1070
+ })
1071
+
1072
+ const datatableItemsScope = reactive({
1073
+ openShowDialog,
1074
+ openShowDialogNoIndex,
1075
+ closeShowDialog,
1076
+ openUpdateDialog,
1077
+ openUpdateDialogNoIndex,
1078
+ openCreateDialog,
1079
+ closeFormDialog,
1080
+ onDeleteItem,
1081
+ refresh,
1082
+ refreshNoUpdate,
1083
+ tableOptions,
1084
+ isSingleSelectedItem,
1085
+ firstSelectedItem,
1086
+ updateDatatableItem,
1087
+ updateSelectedItems,
1088
+ imageDialog,
1089
+ openImageDialog,
1090
+ closeImageDialog
1091
+ })
1092
+
1093
+ const getShowSelection = computed<boolean | undefined>(() => {
1094
+ if (props.hideSelection) {
1095
+ return !1
1096
+ }
1097
+ return props.showSelection
1098
+ })
1099
+ const defaultTopBtnProps: any = {
1100
+ dense: !0,
1101
+ flat: !0,
1102
+ fabMini: !0
1103
+ }
1104
+ const table = ref<InstanceType<typeof QTable>>()
1105
+ defineExpose({
1106
+ resetDialogs,
1107
+ tableOptions,
1108
+ getMythApiServicesSchema,
1109
+ updateSelectedItems,
1110
+ onScroll,
1111
+ loadMore,
1112
+ refreshNoUpdate,
1113
+ refresh,
1114
+ getRequestWith,
1115
+ getDatatableParams,
1116
+ fetchDatatableItems,
1117
+ exportData,
1118
+ openFilterDialog,
1119
+ saveFilterDialog,
1120
+ closeFilterDialog,
1121
+ onRemoveFilter,
1122
+ updateFilterOptions,
1123
+ openShowDialogNoIndex,
1124
+ openShowDialog,
1125
+ closeShowDialog,
1126
+ openUpdateDialogNoIndex,
1127
+ openUpdateDialog,
1128
+ openCreateDialog,
1129
+ closeFormDialog,
1130
+ updateDatatableItem,
1131
+ removeDtItem,
1132
+ onDeleteItem,
1133
+ deleteSelectionItem,
1134
+ logoutDatatable,
1135
+ openImageDialog,
1136
+ closeImageDialog
1137
+ })
1138
+
1139
+ const getProp = computed(() => (k: keyof Props) => {
1140
+ if (props[k] !== undefined) {
1141
+ return props[k]
1142
+ }
1143
+ if (mOptions.datatable?.[k] !== undefined) {
1144
+ return mOptions.datatable?.[k]
1145
+ }
1146
+ return props[k]
1147
+ })
1148
+ const componentSlots = useSlots()
1149
+ const skipSlots = ['default', 'top', 'title']
1150
+ const getSlots = computed(() => {
1151
+ const keys = Object.keys(componentSlots || {})
1152
+ return keys.filter(e => !skipSlots.includes(e))
1153
+ })
1154
+ defineOptions({
1155
+ name: 'MDatatable',
1156
+ inheritAttrs: !1
1157
+ })
1158
+ </script>
1159
+
1160
+ <template>
1161
+ <div
1162
+ :class="{
1163
+ 'm--datatable-component': !0,
1164
+ 'm--datatable-component__fixed': fixed === undefined ? ( mOptions.datatable?.fixed === undefined ? undefined : mOptions.datatable?.fixed) : fixed,
1165
+ 'm--datatable-component__too_small': $q.screen.height < 900,
1166
+ 'm--datatable-component__fab': hasAddBtn && (addFabBtn === undefined ? !!mOptions.datatable?.addFabBtn : addFabBtn)
1167
+ }"
1168
+ >
1169
+ <!-- Context Menu -->
1170
+ <MModalMenu
1171
+ v-model="contextmenu"
1172
+ class="shadow-6 relative-position"
1173
+ context-menu
1174
+ position="standard"
1175
+ touch-position
1176
+ v-bind="mOptions.dt?.contextmenu?.menu"
1177
+ @hide="resetDialogs()"
1178
+ >
1179
+ <q-list
1180
+ v-if="dialogs.item"
1181
+ :separator="!isSmall"
1182
+ style="min-width: 280px;"
1183
+ v-bind="mOptions.dt?.contextmenu?.list"
1184
+ >
1185
+ <template
1186
+ v-for="(contextmenuItem,i) in contextmenuItems"
1187
+ :key="i"
1188
+ >
1189
+ <MDtBtn
1190
+ v-if="typeof contextmenuItem.showIf === 'function' ? contextmenuItem.showIf(dialogs.item,dialogs.index) : contextmenuItem.showIf"
1191
+ :[contextmenuItem.name]="!0"
1192
+ :dense="dense === undefined ? (themeInput.dense !== undefined ? themeInput.dense : mOptions.datatable?.dense) : dense"
1193
+ :label="contextmenuItem.contextLabel !== undefined ? (contextmenuItem.contextLabel === null ? undefined : __(contextmenuItem.contextLabel)) : __(contextmenuItem.label || contextmenuItem.name) "
1194
+ list-item
1195
+ v-bind="contextmenuItem.attr"
1196
+ @click="contextmenuItem.click ? contextmenuItem.click(dialogs.item,dialogs.index) : undefined"
1197
+ />
1198
+ </template>
1199
+ </q-list>
1200
+ </MModalMenu>
1201
+
1202
+ <q-pull-to-refresh
1203
+ :no-mouse="mouse"
1204
+ color="primary"
1205
+ @refresh="refresh"
1206
+ >
1207
+ <q-table
1208
+ ref="table"
1209
+ v-model:fullscreen="tableOptions.fullscreen"
1210
+ v-model:pagination="pagination"
1211
+ v-model:selected="selected"
1212
+ :class="`m--datatable ` + ($q.screen.lt.md ? 'm--datatable-grid' : '')"
1213
+ :columns="getHeaders"
1214
+ :filter="tableOptions.search"
1215
+ :grid="isGrid"
1216
+ :hide-pagination="endReach"
1217
+ :loading="tableOptions.loading"
1218
+ :rows="getRows"
1219
+ :rows-per-page-options="getRowsPerPageOptions"
1220
+ :selection="getShowSelection ? (multiSelection ? 'multiple' : 'single') : 'none'"
1221
+ :title="title"
1222
+ :visible-columns="visibleHeaders"
1223
+ card-container-class="m--datatable-container"
1224
+ table-class="m--datatable-container"
1225
+ v-bind="{
1226
+ virtualScroll: !0,
1227
+ wrapCells:!0,
1228
+ ...mOptions.datatable,
1229
+ ...$attrs,
1230
+ bordered: bordered === undefined ? mOptions.datatable?.bordered : bordered,
1231
+ dense: dense === undefined ? ( themeInput.dense !== undefined ? themeInput.dense : mOptions.datatable?.dense) : dense,
1232
+ flat: flat === undefined ? mOptions.datatable?.flat : flat,
1233
+ }"
1234
+ @request="fetchDatatableItems"
1235
+ @update:selected="onUpdateSelectedItems"
1236
+ @virtual-scroll="endReach ? onScroll : undefined"
1237
+ @row-contextmenu="onRowContextmenu"
1238
+ >
1239
+ <template
1240
+ v-for="slotName in getSlots"
1241
+ :key="slotName"
1242
+ #[slotName]="inputSlot"
1243
+ >
1244
+ <slot
1245
+ :dt="datatableItemsScope"
1246
+ :form="formScope"
1247
+ :index="dialogItemIndex"
1248
+ :item="dialogItem"
1249
+ :name="slotName"
1250
+ v-bind="inputSlot || {}"
1251
+ />
1252
+ </template>
1253
+
1254
+ <template #item="iTempProps">
1255
+ <slot
1256
+ :dt="datatableItemsScope"
1257
+ :form="formScope"
1258
+ name="item"
1259
+ v-bind="iTempProps"
1260
+ >
1261
+ <div
1262
+ :style="iTempProps.selected ? 'transform: scale(0.95);' : ''"
1263
+ class="q-pa-xs col-xs-12 col-sm-6 col-md-4 col-lg-3 grid-style-transition"
1264
+ >
1265
+ <q-card
1266
+ :class="iTempProps.selected ? ($q.dark.isActive ? 'bg-grey-9' : 'bg-grey-2') : ''"
1267
+ >
1268
+ <template v-if="getShowSelection || iTempProps.colsMap[controlKey ?? 'control'] !== undefined">
1269
+ <div class="m--datatable-card-control-header">
1270
+ <MRow class="items-center justify-between">
1271
+ <div class="q-px-md">
1272
+ <q-checkbox
1273
+ v-if="getShowSelection"
1274
+ v-model="iTempProps.selected"
1275
+ dense
1276
+ />
1277
+ </div>
1278
+ <div>
1279
+ <MDtBtn
1280
+ flat
1281
+ icon="ion-ios-more"
1282
+ @click="onRowContextmenu($event,iTempProps.row,iTempProps.rowIndex)"
1283
+ />
1284
+ </div>
1285
+ </MRow>
1286
+ </div>
1287
+ <q-separator />
1288
+ </template>
1289
+ <MContainer>
1290
+ <template
1291
+ v-for="col in iTempProps.cols"
1292
+ :key="col.name"
1293
+ >
1294
+ <MRow
1295
+ v-if="col.name !== controlKey || (col.name === controlKey && ( showCardControlHeader === undefined ? mOptions.datatable?.showCardControlHeader : showCardControlHeader ))"
1296
+ class="justify-between"
1297
+ >
1298
+ <MCol
1299
+ v-if="col.name !== controlKey"
1300
+ auto
1301
+ >
1302
+ {{ col.label }}
1303
+ </MCol>
1304
+ <MCol
1305
+ :auto="col.name !== controlKey"
1306
+ :class="`overflow-hidden ${col.name === controlKey ? 'text-right col-12 q-pb-xs' : ''}`"
1307
+ >
1308
+ <template v-if="imageColumns?.indexOf(col.name) !== -1">
1309
+ <template v-if="getProp('imageMode') === 'icon'">
1310
+ <q-btn
1311
+ v-if="!!col.value"
1312
+ dense
1313
+ fab-mini
1314
+ flat
1315
+ icon="ion-ios-eye"
1316
+ @click="openImageDialog(col.value)"
1317
+ >
1318
+ <q-tooltip class="m--dt-btn-tooltip">
1319
+ {{ __('myth.titles.show') }}
1320
+ </q-tooltip>
1321
+ </q-btn>
1322
+ </template>
1323
+ <template v-else-if="getProp('imageMode') === 'image'">
1324
+ <q-img
1325
+ v-if="col.value"
1326
+ :src="col.value"
1327
+ :style="`width: ${getProp('imageSize')}; height: ${getProp('imageSize')}`"
1328
+ class="cursor-pointer"
1329
+ fit="contain"
1330
+ no-spinner
1331
+ @click="openImageDialog(col.value)"
1332
+ >
1333
+ <q-tooltip class="m--dt-btn-tooltip">
1334
+ {{ __('myth.titles.show') }}
1335
+ </q-tooltip>
1336
+ </q-img>
1337
+ </template>
1338
+ </template>
1339
+ <template v-else-if="col.name === controlKey">
1340
+ <MRow
1341
+ class="m--dt-context_menu_items"
1342
+ gutter
1343
+ space="xs"
1344
+ >
1345
+ <!--Grid-->
1346
+ <MDtContextmenuItems
1347
+ :index="iTempProps.rowIndex"
1348
+ :item="iTempProps.row"
1349
+ :items="contextmenuItems"
1350
+ />
1351
+ </MRow>
1352
+ </template>
1353
+ <template v-else>
1354
+ <div class="m--datatable-card-value">
1355
+ <slot
1356
+ :name="`card-cell-${col.name}`"
1357
+ v-bind="iTempProps"
1358
+ >
1359
+ {{ col.value }}
1360
+ </slot>
1361
+ </div>
1362
+ </template>
1363
+ </MCol>
1364
+ </MRow>
1365
+ </template>
1366
+ </MContainer>
1367
+ </q-card>
1368
+ </div>
1369
+ </slot>
1370
+ </template>
1371
+
1372
+ <template #top="topSlotProps">
1373
+ <MCol col="12">
1374
+ <MContainer class="no-padding">
1375
+ <slot
1376
+ :dt="datatableItemsScope"
1377
+ :form="formScope"
1378
+ :index="dialogItemIndex"
1379
+ :item="dialogItem"
1380
+ name="top"
1381
+ v-bind="topSlotProps"
1382
+ />
1383
+ <MRow col>
1384
+ <slot
1385
+ :dt="datatableItemsScope"
1386
+ :form="formScope"
1387
+ :index="dialogItemIndex"
1388
+ :item="dialogItem"
1389
+ name="title"
1390
+ >
1391
+ <MCol
1392
+ v-if="!!title"
1393
+ col="12"
1394
+ >
1395
+ <div
1396
+ class="text-h5 bordered-bottom"
1397
+ v-text="title"
1398
+ />
1399
+ </MCol>
1400
+ </slot>
1401
+ <slot
1402
+ :dt="datatableItemsScope"
1403
+ :form="formScope"
1404
+ :index="dialogItemIndex"
1405
+ :item="dialogItem"
1406
+ name="top-search"
1407
+ />
1408
+ <MInput
1409
+ v-if="!hideSearch && !dialogs.form"
1410
+ v-model="tableOptions.search"
1411
+ :debounce="searchDebounce"
1412
+ :dense="dense === undefined ? (mOptions.datatable?.dense !== undefined ? mOptions.datatable?.dense : !0) : dense"
1413
+ :placeholder="searchPlaceholder"
1414
+ autocomplete="none"
1415
+ col="12"
1416
+ name="search"
1417
+ outlined
1418
+ v-bind="mOptions.dt?.searchInput?.props"
1419
+ >
1420
+ <template #prepend>
1421
+ <q-icon
1422
+ v-if="!tableOptions.search"
1423
+ name="ion-ios-search"
1424
+ >
1425
+ <q-tooltip class="m--dt-btn-tooltip">
1426
+ {{ __('myth.datatable.searchInput') }}
1427
+ </q-tooltip>
1428
+ </q-icon>
1429
+ <q-icon
1430
+ v-else
1431
+ class="cursor-pointer"
1432
+ name="ion-ios-close"
1433
+ @click="tableOptions.search = ''"
1434
+ >
1435
+ <q-tooltip class="m--dt-btn-tooltip">
1436
+ {{ __('myth.datatable.searchInputClear') }}
1437
+ </q-tooltip>
1438
+ </q-icon>
1439
+ </template>
1440
+ <template #after>
1441
+ <q-btn
1442
+ :aria-label="__('menu')"
1443
+ :icon="mOptions.dt?.searchInput?.optionsIcon || 'ion-ios-options'"
1444
+ dense
1445
+ flat
1446
+ round
1447
+ v-bind="mOptions.dt?.searchInput?.menuBtn"
1448
+ >
1449
+ <MModalMenu
1450
+ :offset="[10,10]"
1451
+ no-close-btn
1452
+ v-bind="mOptions.dt?.searchInput?.menuProps as any"
1453
+ >
1454
+ <q-toolbar>
1455
+ <q-toolbar-title>
1456
+ {{ __('myth.datatable.searchColumns') }}
1457
+ </q-toolbar-title>
1458
+ </q-toolbar>
1459
+ <q-separator />
1460
+ <MContainer style="min-width: 250px; max-width: 450px">
1461
+ <MRow class="items-center">
1462
+ <MCol col="12">
1463
+ <template
1464
+ v-for="h in getHeaders.filter( e => e.field !== controlKey)"
1465
+ :key="h.name"
1466
+ >
1467
+ <q-checkbox
1468
+ v-model="searchColumnsRef"
1469
+ :disable="searchColumnsRef.length < 2 && searchColumnsRef.indexOf(h.name) !== -1"
1470
+ :label="h.label"
1471
+ :val="h.name"
1472
+ />
1473
+ </template>
1474
+ </MCol>
1475
+ </MRow>
1476
+ </MContainer>
1477
+ <MRow>
1478
+ <q-btn
1479
+ v-close-popup
1480
+ :class="{'full-width': $q.screen.lt.md}"
1481
+ :label="__('myth.titles.done')"
1482
+ :size="$q.screen.lt.md ? 'lg' : 'md'"
1483
+ flat
1484
+ no-caps
1485
+ style="min-width: 68px"
1486
+ unelevated
1487
+ @click="tableOptions.search ? refresh() : undefined"
1488
+ />
1489
+ </MRow>
1490
+ </MModalMenu>
1491
+ <MTooltip>
1492
+ {{ __('myth.datatable.searchColumns') }}
1493
+ </MTooltip>
1494
+ </q-btn>
1495
+ </template>
1496
+ </MInput>
1497
+ <slot
1498
+ :dt="datatableItemsScope"
1499
+ :form="formScope"
1500
+ :index="dialogItemIndex"
1501
+ :item="dialogItem"
1502
+ name="bottom-search"
1503
+ />
1504
+ </MRow>
1505
+ <!--Buttons-->
1506
+ <MRow class="row q-gutter-x-sm q-gutter-xs-y-sm items-center justify-between">
1507
+ <!--More Menu-->
1508
+ <MDtBtn
1509
+ v-if="hasMenu"
1510
+ key="more-selection-btn"
1511
+ :disable="tableOptions.loading"
1512
+ icon="ion-ios-options"
1513
+ tooltip="myth.datatable.hints.more"
1514
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.buttons?.more}"
1515
+ >
1516
+ <MModalMenu
1517
+ :offset="[10,10]"
1518
+ position="top"
1519
+ v-bind="mOptions.dt?.buttons?.moreMenu as any"
1520
+ >
1521
+ <q-list
1522
+ style="min-width: 250px"
1523
+ v-bind="mOptions.dt?.buttons?.moreList"
1524
+ >
1525
+ <!-- Add Btn -->
1526
+ <q-item
1527
+ v-if="hasAddBtn && !!addListBtnComputed"
1528
+ v-close-popup
1529
+ clickable
1530
+ v-bind="mOptions.dt?.buttons?.moreItem"
1531
+ @click="openCreateDialog()"
1532
+ >
1533
+ <q-item-section thumbnail>
1534
+ <q-icon
1535
+ color="primary"
1536
+ name="add"
1537
+ right
1538
+ size="xs"
1539
+ />
1540
+ </q-item-section>
1541
+ <q-item-section>
1542
+ <span> {{ getFormTitle }}</span>
1543
+ </q-item-section>
1544
+ </q-item>
1545
+ <q-item
1546
+ v-if="pdf"
1547
+ v-close-popup
1548
+ clickable
1549
+ v-bind="mOptions.dt?.buttons?.moreItem"
1550
+ @click="exportData('pdf')"
1551
+ >
1552
+ <q-item-section thumbnail>
1553
+ <q-icon
1554
+ color="red"
1555
+ name="fa-solid fa-file-pdf"
1556
+ right
1557
+ size="xs"
1558
+ />
1559
+ </q-item-section>
1560
+ <q-item-section>
1561
+ <span>
1562
+ {{ __('myth.titles.exportPdf') }}
1563
+ <q-badge
1564
+ v-if="tableOptions.selected.length > 1"
1565
+ :label="tableOptions.selected.length"
1566
+ align="top"
1567
+ rounded
1568
+ />
1569
+ </span>
1570
+ </q-item-section>
1571
+ </q-item>
1572
+ <q-item
1573
+ v-if="excel"
1574
+ v-close-popup
1575
+ clickable
1576
+ v-bind="mOptions.dt?.buttons?.moreItem"
1577
+ @click="exportData('excel')"
1578
+ >
1579
+ <q-item-section thumbnail>
1580
+ <q-icon
1581
+ color="green"
1582
+ name="fa-solid fa-file-excel"
1583
+ right
1584
+ size="xs"
1585
+ />
1586
+ </q-item-section>
1587
+ <q-item-section>
1588
+ <span>
1589
+ {{ __('myth.titles.exportExcel') }}
1590
+ <q-badge
1591
+ v-if="tableOptions.selected.length>1"
1592
+ :label="tableOptions.selected.length"
1593
+ align="top"
1594
+ rounded
1595
+ />
1596
+ </span>
1597
+ </q-item-section>
1598
+ </q-item>
1599
+ <q-item
1600
+ v-if="fullscreenBtn === undefined ? ( !!mOptions.datatable?.fullscreenBtn) : fullscreenBtn"
1601
+ v-close-popup
1602
+ clickable
1603
+ v-bind="mOptions.dt?.buttons?.moreItem"
1604
+ @click="tableOptions.fullscreen = !tableOptions.fullscreen"
1605
+ >
1606
+ <q-item-section thumbnail>
1607
+ <q-icon
1608
+ :name="tableOptions.fullscreen ? 'ion-ios-contract' : 'ion-ios-desktop'"
1609
+ right
1610
+ />
1611
+ </q-item-section>
1612
+ <q-item-section>
1613
+ <q-item-label>{{ __('myth.datatable.' + (tableOptions.fullscreen ? 'exitFullscreen' : 'fullscreen')) }}</q-item-label>
1614
+ </q-item-section>
1615
+ </q-item>
1616
+ </q-list>
1617
+ </MModalMenu>
1618
+ </MDtBtn>
1619
+ <!-- Filter dialog -->
1620
+ <MDtBtn
1621
+ v-if="hasFilterDialog"
1622
+ key="filter-selection-btn"
1623
+ icon="o_filter_alt"
1624
+ tooltip="myth.datatable.hints.filter"
1625
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.buttons?.filter}"
1626
+ @click="openFilterDialog()"
1627
+ >
1628
+ <MModalMenu
1629
+ no-close-btn
1630
+ persistent
1631
+ v-bind="mOptions.dt?.filterDialogProps"
1632
+ >
1633
+ <q-card
1634
+ :style="$q.screen.gt.sm?`width: ${Math.ceil($q.screen.width/2)}px` : undefined"
1635
+ flat
1636
+ square
1637
+ >
1638
+ <MContainer class="q-pa-md">
1639
+ <q-toolbar :class="{'q-pa-none': $q.screen.lt.md}">
1640
+ <q-toolbar-title>
1641
+ {{ __('myth.datatable.filter.title') }}
1642
+ </q-toolbar-title>
1643
+ </q-toolbar>
1644
+ <q-separator />
1645
+ <MRow class="items-center">
1646
+ <MCol col="12">
1647
+ <MContainer class="q-pa-md">
1648
+ <slot
1649
+ :dt="datatableItemsScope"
1650
+ :filter="tableOptions.tempFilter"
1651
+ :form="formScope"
1652
+ :index="dialogItemIndex"
1653
+ :item="dialogItem"
1654
+ name="filter"
1655
+ />
1656
+ </MContainer>
1657
+ </MCol>
1658
+ <MCol
1659
+ class="q-pt-lg"
1660
+ col="12"
1661
+ >
1662
+ <MRow class="justify-between">
1663
+ <MBtn
1664
+ v-close-popup
1665
+ :label="__('myth.datatable.filter.cancel')"
1666
+ color="negative"
1667
+ flat
1668
+ v-bind="mOptions.dt?.dialogButtonsProps"
1669
+ @click="closeFilterDialog"
1670
+ />
1671
+ <MBtn
1672
+ v-close-popup
1673
+ :label="__('myth.datatable.filter.save')"
1674
+ color="positive"
1675
+ flat
1676
+ v-bind="mOptions.dt?.dialogButtonsProps"
1677
+ @click="saveFilterDialog"
1678
+ />
1679
+ </MRow>
1680
+ </MCol>
1681
+ </MRow>
1682
+ </MContainer>
1683
+ </q-card>
1684
+ </MModalMenu>
1685
+ </MDtBtn>
1686
+ <!--Refresh-->
1687
+ <MDtBtn
1688
+ v-if="!noRefreshBtn"
1689
+ key="refresh-selection-btn"
1690
+ :disable="tableOptions.loading"
1691
+ icon="ion-ios-refresh"
1692
+ tooltip="myth.datatable.hints.refresh"
1693
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.buttons?.refresh}"
1694
+ @click="refreshNoUpdate()"
1695
+ />
1696
+ <!--Fullscreen-->
1697
+ <MDtBtn
1698
+ v-if="fullscreenBtn === undefined ? ( !!mOptions.datatable?.fullscreenBtn) : fullscreenBtn"
1699
+ key="fullscreen-selection-btn"
1700
+ :disable="tableOptions.loading"
1701
+ :icon="tableOptions.fullscreen ? 'ion-ios-contract' : 'ion-ios-desktop'"
1702
+ :tooltip="`$myth.datatable.${tableOptions.fullscreen ? 'exitFullscreen' : 'fullscreen'}`"
1703
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.buttons?.fullscreen}"
1704
+ @click="tableOptions.fullscreen = !tableOptions.fullscreen"
1705
+ />
1706
+
1707
+ <template v-if="hasSelectedItem">
1708
+ <MDtBtn
1709
+ v-if="hasUpdateBtn && isSingleSelectedItem"
1710
+ key="update-dt-selection-btn"
1711
+ :disable="tableOptions.loading"
1712
+ icon="ion-ios-create"
1713
+ update
1714
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.topSelection?.btn}"
1715
+ @click="openUpdateDialogNoIndex(tableOptions.selected[0] as any)"
1716
+ />
1717
+ <MDtBtn
1718
+ v-if="hasShowBtn && isSingleSelectedItem"
1719
+ key="show-dt-selection-btn"
1720
+ :disable="tableOptions.loading"
1721
+ icon="ion-ios-eye"
1722
+ show
1723
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.topSelection?.btn}"
1724
+ @click="openShowDialogNoIndex(tableOptions.selected[0] as any)"
1725
+ />
1726
+ <MDtBtn
1727
+ v-if="tableOptions.selected.length > 1 ? (hasDestroyBtn && multiDestroy) : hasDestroyBtn"
1728
+ key="destroy-dt-selection-btn"
1729
+ :disable="!hasSelectedItem || tableOptions.loading"
1730
+ color="negative"
1731
+ destroy
1732
+ icon="ion-ios-trash"
1733
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.topSelection?.btn}"
1734
+ @click="deleteSelectionItem()"
1735
+ />
1736
+ <template
1737
+ v-for="(contextBtn,i) in contextItems"
1738
+ :key="`top-s-${i}`"
1739
+ >
1740
+ <MDtBtn
1741
+ v-if="(typeof contextBtn.showIf === 'function' ? contextBtn.showIf(tableOptions.selected[0],0) : contextBtn.showIf) && ( (contextBtn.click && isSingleSelectedItem) || (contextBtn.multiClick && !isSingleSelectedItem) )"
1742
+ :tooltip="__(contextBtn.tooltip || contextBtn.name)"
1743
+ v-bind="{...defaultTopBtnProps,...mOptions.dt?.topSelection?.btn,...contextBtn,...contextBtn.attr}"
1744
+ @click="contextBtn.click ? contextBtn.click(tableOptions.selected[0],0) : (contextBtn.multiClick ? contextBtn.multiClick(tableOptions.selected) : undefined)"
1745
+ />
1746
+ </template>
1747
+ </template>
1748
+
1749
+ <q-space />
1750
+ <!-- Add Btn -->
1751
+ <template
1752
+ v-if="hasAddBtn && (addTopBtn===undefined?(mOptions.datatable?.addTopBtn===undefined?!0:mOptions.datatable?.addTopBtn):addTopBtn)"
1753
+ >
1754
+ <MBtn
1755
+ :label="getFormTitle"
1756
+ icon="ion-ios-add"
1757
+ @click="openCreateDialog()"
1758
+ />
1759
+ </template>
1760
+ </MRow>
1761
+
1762
+ <!-- Manage Columns -->
1763
+ <MRow
1764
+ v-if="manageColumns"
1765
+ class="items-center"
1766
+ >
1767
+ <q-list
1768
+ bordered
1769
+ class="rounded-borders col-12"
1770
+ >
1771
+ <q-expansion-item
1772
+ :caption="__('myth.datatable.columnsToShowCaption')"
1773
+ :label="__('myth.datatable.columnsToShow')"
1774
+ expand-separator
1775
+ icon="ion-ios-list"
1776
+ >
1777
+ <q-card>
1778
+ <q-card-section>
1779
+ <template
1780
+ v-for="h in getHeaders"
1781
+ :key="h.name"
1782
+ >
1783
+ <q-checkbox
1784
+ v-model="visibleHeaders"
1785
+ :disable="visibleHeaders.length < 2 && visibleHeaders.indexOf(h.name) !== -1"
1786
+ :label="h.label"
1787
+ :val="h.name"
1788
+ />
1789
+ </template>
1790
+ </q-card-section>
1791
+ </q-card>
1792
+ </q-expansion-item>
1793
+ </q-list>
1794
+ </MRow>
1795
+
1796
+ <!-- Filter Row -->
1797
+ <MRow
1798
+ v-if="Object.values(tableOptions.filter).filter(e => e !== undefined && e !== null).length > 0"
1799
+ class="items-center"
1800
+ >
1801
+ <MCol col="auto">
1802
+ <span class="text-subtitle1 q-mr-sm">{{ __('myth.datatable.filteredBy') }}</span>
1803
+ </MCol>
1804
+ <template
1805
+ v-for="(filterValue,filterKey) in tableOptions.filter"
1806
+ :key="`filter-${filterKey}`"
1807
+ >
1808
+ <MCol
1809
+ v-if="filterValue !== null && filterValue !== undefined"
1810
+ col="auto"
1811
+ >
1812
+ <q-chip
1813
+ class="q-pr-md"
1814
+ clickable
1815
+ color="primary"
1816
+ icon-remove="clear"
1817
+ outline
1818
+ removable
1819
+ @click="openFilterDialog()"
1820
+ @remove="onRemoveFilter(filterKey)"
1821
+ >
1822
+ <span>{{ getHeaders.find(e => e.name === filterKey)?.label || __(`attributes.${filterKey}`) }}</span>
1823
+ <span v-if="typeof filterValue === 'boolean'">: {{ __(filterValue ? 'yes' : 'no') }}</span>
1824
+ <span v-else-if="typeof filterValue === 'string'">: {{ filterValue }}</span>
1825
+ <span v-else-if="lodash.isArray(filterValue) && !quasarHelpers.object(filterValue[0])">: {{ filterValue.join(', ') }}</span>
1826
+ <span v-else-if="lodash.isArray(filterValue) && quasarHelpers.object(filterValue[0])">: {{
1827
+ filterValue.map(e => e.label).join(', ')
1828
+ }}</span>
1829
+ <span v-else-if="quasarHelpers.object(filterValue) && filterValue.label">: {{ filterValue.label }}</span>
1830
+ </q-chip>
1831
+ </MCol>
1832
+ </template>
1833
+ </MRow>
1834
+
1835
+ <!-- Selection Row -->
1836
+ <MRow
1837
+ v-if="!!$slots.selection"
1838
+ class="items-center q-gutter-xs"
1839
+ style="min-height: 38px"
1840
+ >
1841
+ <slot
1842
+ :dt="datatableItemsScope"
1843
+ :form="formScope"
1844
+ :index="dialogItemIndex"
1845
+ :item="dialogItem"
1846
+ name="selection"
1847
+ />
1848
+ </MRow>
1849
+ </MContainer>
1850
+ </MCol>
1851
+ </template>
1852
+
1853
+ <template
1854
+ v-if="endReach"
1855
+ #bottom
1856
+ >
1857
+ <q-space />
1858
+ <div v-text="__('replace.from_to', { from: pagination.rowsNumber, to: getRows.length })" />
1859
+ </template>
1860
+
1861
+ <template
1862
+ v-if="!noBodyControl"
1863
+ #[`body-cell-${controlKey}`]="noBodyProps"
1864
+ >
1865
+ <slot
1866
+ :dt="datatableItemsScope"
1867
+ :form="formScope"
1868
+ :index="dialogItemIndex"
1869
+ :item="dialogItem"
1870
+ :name="`body-cell-${controlKey}`"
1871
+ v-bind="noBodyProps"
1872
+ >
1873
+ <q-td :props="noBodyProps">
1874
+ <!--Control-->
1875
+ <q-btn-dropdown
1876
+ v-if="contextmenuItems.length>3"
1877
+ v-close-popup
1878
+ :menu-offset="[0,10]"
1879
+ color="primary"
1880
+ dense
1881
+ outline
1882
+ v-bind="mOptions.dt?.controlDropdown"
1883
+ >
1884
+ <q-list>
1885
+ <MDtContextmenuItems
1886
+ :index="noBodyProps.rowIndex"
1887
+ :item="noBodyProps.row"
1888
+ :items="contextmenuItems"
1889
+ display-mode="item"
1890
+ />
1891
+ <!--<q-item-->
1892
+ <!-- v-for="contextmenuItem in contextmenuItems"-->
1893
+ <!-- :key="`dt-r${contextmenuItem.name}`"-->
1894
+ <!-- v-close-popup-->
1895
+ <!-- clickable-->
1896
+ <!--&gt;-->
1897
+ <!-- <pre>{{ contextmenuItem }}</pre>-->
1898
+ <!-- <q-item-section>-->
1899
+ <!-- <q-item-label>{{ contextmenuItem.label }}</q-item-label>-->
1900
+ <!-- </q-item-section>-->
1901
+ <!--</q-item>-->
1902
+ </q-list>
1903
+ </q-btn-dropdown>
1904
+ <MRow
1905
+ v-else
1906
+ class="m--dt-context_menu_items"
1907
+ gutter
1908
+ space="xs"
1909
+ >
1910
+ <!--Control-->
1911
+ <MDtContextmenuItems
1912
+ :index="noBodyProps.rowIndex"
1913
+ :item="noBodyProps.row"
1914
+ :items="contextmenuItems"
1915
+ />
1916
+ </MRow>
1917
+ </q-td>
1918
+ </slot>
1919
+ </template>
1920
+ <template
1921
+ v-for="c in imageColumns"
1922
+ :key="`a-c-${c}`"
1923
+ #[`body-cell-${c}`]="colProps"
1924
+ >
1925
+ <q-td :props="colProps">
1926
+ <template v-if="getProp('imageMode') === 'icon'">
1927
+ <q-btn
1928
+ v-if="colProps.row[c]"
1929
+ :src="colProps.row[c]"
1930
+ dense
1931
+ fab-mini
1932
+ flat
1933
+ icon="ion-ios-eye"
1934
+ @click="openImageDialog(colProps.row[c])"
1935
+ >
1936
+ <q-tooltip class="m--dt-btn-tooltip">
1937
+ {{ __('myth.titles.show') }}
1938
+ </q-tooltip>
1939
+ </q-btn>
1940
+ </template>
1941
+ <template v-else-if="getProp('imageMode') === 'image'">
1942
+ <q-img
1943
+ v-if="colProps.row[c]"
1944
+ :src="colProps.row[c]"
1945
+ :style="`width: ${getProp('imageSize')}; height: ${getProp('imageSize')}`"
1946
+ class="cursor-pointer"
1947
+ fit="contain"
1948
+ no-spinner
1949
+ @click="openImageDialog(colProps.row[c])"
1950
+ >
1951
+ <q-tooltip class="m--dt-btn-tooltip">
1952
+ {{ __('myth.titles.show') }}
1953
+ </q-tooltip>
1954
+ </q-img>
1955
+ </template>
1956
+ </q-td>
1957
+ </template>
1958
+ </q-table>
1959
+ <slot
1960
+ :dt="datatableItemsScope"
1961
+ :form="formScope"
1962
+ :index="dialogItemIndex"
1963
+ :item="dialogItem"
1964
+ name="default"
1965
+ />
1966
+ </q-pull-to-refresh>
1967
+
1968
+ <!-- Show Dialog -->
1969
+ <MDialog
1970
+ v-model="dialogs.show"
1971
+ v-bind="mOptions.dt?.showDialogProps"
1972
+ >
1973
+ <q-card class="m--dialog-card">
1974
+ <q-card-section ref="showTitleRef">
1975
+ <q-toolbar>
1976
+ <q-toolbar-title>
1977
+ <q-btn
1978
+ :icon="`ion-ios-arrow-${$q.lang.rtl ? 'forward' : 'back'}`"
1979
+ fab-mini
1980
+ flat
1981
+ @click="closeShowDialog()"
1982
+ >
1983
+ <q-tooltip class="m--dt-btn-tooltip">
1984
+ {{ __('myth.titles.back') }}
1985
+ </q-tooltip>
1986
+ </q-btn>
1987
+ {{ getShowTitle }}
1988
+ </q-toolbar-title>
1989
+ </q-toolbar>
1990
+ </q-card-section>
1991
+ <q-separator />
1992
+ <q-card-section
1993
+ :style="`height: ${($q.screen.height || 100) - 3 - (($refs.showActionsRef as any)?.$el?.offsetHeight || 60) - (($refs.showTitleRef as any)?.$el?.offsetHeight || 80)}px`"
1994
+ class="scroll"
1995
+ >
1996
+ <slot
1997
+ :dt="datatableItemsScope"
1998
+ :form="formScope"
1999
+ :index="dialogItemIndex"
2000
+ :item="dialogItem"
2001
+ name="show"
2002
+ />
2003
+ </q-card-section>
2004
+ <q-separator />
2005
+ <q-card-actions
2006
+ ref="showActionsRef"
2007
+ align="left"
2008
+ class="print-hide"
2009
+ >
2010
+ <MBtn
2011
+ :label="__('myth.titles.close')"
2012
+ color="negative"
2013
+ v-bind="mOptions.dt?.dialogButtonsProps"
2014
+ @click="closeShowDialog"
2015
+ />
2016
+ </q-card-actions>
2017
+ </q-card>
2018
+ </MDialog>
2019
+
2020
+ <!-- Form Dialog -->
2021
+ <MDialog
2022
+ v-model="dialogs.form"
2023
+ v-bind="mOptions.dt?.formDialogProps"
2024
+ >
2025
+ <div
2026
+ class="m--form__container full-height no-wrap"
2027
+ >
2028
+ <form
2029
+ class="m--form column full-height justify-between no-wrap"
2030
+ @submit="defaultSubmitItem"
2031
+ >
2032
+ <!--<MForm-->
2033
+ <!-- v-slot="form"-->
2034
+ <!-- :errors="dialogs.errors"-->
2035
+ <!-- :form="dialogs.item"-->
2036
+ <!-- :form-props="{class: 'column full-height justify-between no-wrap'}"-->
2037
+ <!-- class="full-height no-wrap"-->
2038
+ <!-- @submit="defaultSubmitItem"-->
2039
+ <!--&gt;-->
2040
+ <q-card class="m--dialog-card">
2041
+ <q-card-section ref="formTitle">
2042
+ <q-toolbar :class="{'q-pa-none': $q.screen.lt.md}">
2043
+ <slot
2044
+ :dt="datatableItemsScope"
2045
+ :form="formScope"
2046
+ :index="dialogItemIndex"
2047
+ :item="dialogItem"
2048
+ name="form-title-left"
2049
+ />
2050
+ <slot
2051
+ :dt="datatableItemsScope"
2052
+ :form="formScope"
2053
+ :index="dialogItemIndex"
2054
+ :item="dialogItem"
2055
+ name="form-title"
2056
+ >
2057
+ <q-toolbar-title>
2058
+ <template v-if="tableOptions.loading && !dialogs.item">
2059
+ <q-skeleton width="200px" />
2060
+ </template>
2061
+ <template v-else>
2062
+ <q-btn
2063
+ :icon="`ion-ios-arrow-${$q.lang.rtl ? 'forward' : 'back'}`"
2064
+ fab-mini
2065
+ flat
2066
+ @click="closeFormDialog"
2067
+ >
2068
+ <q-tooltip class="m--dt-btn-tooltip">
2069
+ {{ __('myth.titles.back') }}
2070
+ </q-tooltip>
2071
+ </q-btn>
2072
+ {{ getFormTitle }}
2073
+ </template>
2074
+ </q-toolbar-title>
2075
+ </slot>
2076
+ <slot
2077
+ :dt="datatableItemsScope"
2078
+ :form="formScope"
2079
+ :index="dialogItemIndex"
2080
+ :item="dialogItem"
2081
+ name="form-title-right"
2082
+ />
2083
+ </q-toolbar>
2084
+ </q-card-section>
2085
+ <q-separator />
2086
+ <q-card-section
2087
+ ref="formDialogCartSection"
2088
+ :style="`height: ${($q.screen.height || 100) - 3 - (($refs.formActions as any)?.$el?.offsetHeight || 60) - (($refs.formTitle as any)?.$el?.offsetHeight || 80)}px`"
2089
+ class="scroll m--datatable__dialog-form-container"
2090
+ >
2091
+ <MContainer v-if="tableOptions.loading && !dialogs.item">
2092
+ <MRow
2093
+ v-if="tableOptions.loading"
2094
+ col
2095
+ >
2096
+ <template
2097
+ v-for="ai in 15"
2098
+ :key="`form-skeleton-${ai}`"
2099
+ >
2100
+ <MCol
2101
+ col="12"
2102
+ md="6"
2103
+ >
2104
+ <q-skeleton type="QInput" />
2105
+ </MCol>
2106
+ </template>
2107
+ </MRow>
2108
+ </MContainer>
2109
+ <slot
2110
+ v-else
2111
+ :dt="datatableItemsScope"
2112
+ :form="formScope"
2113
+ :index="dialogItemIndex"
2114
+ :item="dialogItem"
2115
+ name="form"
2116
+ />
2117
+ </q-card-section>
2118
+ <q-separator />
2119
+ <q-card-actions
2120
+ ref="formActions"
2121
+ class="m--datatable-form-actions print-hide"
2122
+ >
2123
+ <slot
2124
+ :dt="datatableItemsScope"
2125
+ :form="formScope"
2126
+ :index="dialogItemIndex"
2127
+ :item="dialogItem"
2128
+ name="form-actions"
2129
+ >
2130
+ <MBtn
2131
+ :class="{'full-width': $q.screen.lt.sm}"
2132
+ :label="__('myth.titles.' + (isUpdateMode ? 'save' : 'store'))"
2133
+ :loading="tableOptions.loading"
2134
+ color="positive"
2135
+ no-caps
2136
+ type="submit"
2137
+ v-bind="mOptions.dt?.dialogButtonsProps"
2138
+ />
2139
+ </slot>
2140
+ <MBtn
2141
+ v-if="$q.screen.gt.sm"
2142
+ :disable="tableOptions.loading"
2143
+ :label="__('myth.titles.close')"
2144
+ color="negative"
2145
+ no-caps
2146
+ v-bind="mOptions.dt?.dialogButtonsProps"
2147
+ @click="closeFormDialog"
2148
+ />
2149
+ </q-card-actions>
2150
+ </q-card>
2151
+ <!--</MForm>-->
2152
+ </form>
2153
+ </div>
2154
+ </MDialog>
2155
+
2156
+ <!-- Image Dialog -->
2157
+ <MDialog v-model="imageDialog.value">
2158
+ <q-card>
2159
+ <div class="row full-height">
2160
+ <MCol col="12">
2161
+ <MRow class="q-pa-sm justify-between items-center">
2162
+ <q-btn
2163
+ :href="imageDialog.src"
2164
+ :label="__('myth.titles.download')"
2165
+ flat
2166
+ icon="ion-ios-cloud-download"
2167
+ target="_blank"
2168
+ />
2169
+ <q-btn
2170
+ fab-mini
2171
+ flat
2172
+ icon="ion-ios-close"
2173
+ round
2174
+ @click="closeImageDialog()"
2175
+ />
2176
+ </MRow>
2177
+ <q-separator />
2178
+ </MCol>
2179
+ <MCol
2180
+ class="text-center"
2181
+ col="12"
2182
+ >
2183
+ <q-img
2184
+ v-if="imageDialog.src && !imageDialog.asAttachment"
2185
+ :height="`${$q.screen.height - 70}px`"
2186
+ :src="imageDialog.src"
2187
+ class="self-center"
2188
+ fit="contain"
2189
+ />
2190
+ <iframe
2191
+ v-if="imageDialog.src && imageDialog.asAttachment"
2192
+ :height="`${$q.screen.height - 70}px`"
2193
+ :src="imageDialog.src"
2194
+ allowfullscreen
2195
+ class="full-width"
2196
+ frameborder="0"
2197
+ scrolling="no"
2198
+ />
2199
+ </MCol>
2200
+ </div>
2201
+ </q-card>
2202
+ </MDialog>
2203
+
2204
+ <!-- Add Btn -->
2205
+ <q-page-sticky
2206
+ v-if="hasAddBtn && (addFabBtn === undefined ? !!mOptions.datatable?.addFabBtn : addFabBtn)"
2207
+ :offset="mOptions.dt?.fabBtn?.offset|| [25,25]"
2208
+ :position="mOptions.dt?.fabBtn?.position || 'bottom-right'"
2209
+ v-bind="mOptions.dt?.fabBtn?.pageStickyProps"
2210
+ >
2211
+ <q-btn
2212
+ color="primary"
2213
+ fab
2214
+ icon="ion-ios-add"
2215
+ v-bind="mOptions.dt?.fabBtn?.buttonProps"
2216
+ @click="openCreateDialog()"
2217
+ >
2218
+ <MTooltip
2219
+ anchor="top middle"
2220
+ class="m--datatable-fab-tooltip"
2221
+ self="bottom start"
2222
+ >
2223
+ <span class="text-caption">{{ getFormTitle }}</span>
2224
+ </MTooltip>
2225
+ </q-btn>
2226
+ </q-page-sticky>
2227
+ </div>
2228
+ </template>
2229
+
2230
+ <style lang="sass">
2231
+ .touch
2232
+ .m--datatable-fab-tooltip
2233
+ display: none
2234
+
2235
+ .body--light
2236
+ .m--datatable
2237
+ .q-table__top,
2238
+ .q-table__bottom,
2239
+ thead tr:first-child th
2240
+ background: #fff
2241
+
2242
+ .body--dark
2243
+ .m--datatable
2244
+ .q-table__top,
2245
+ .q-table__bottom,
2246
+ thead tr:first-child th
2247
+ background: var(--q-dark-page)
2248
+
2249
+ .m--datatable-component
2250
+ .m--datatable.m--datatable-grid
2251
+ .q-table__top
2252
+ padding-left: 0
2253
+ padding-right: 0
2254
+
2255
+ &__fab
2256
+ margin-bottom: 12rem
2257
+
2258
+ &__fixed
2259
+ thead tr th
2260
+ position: sticky
2261
+ z-index: 1
2262
+
2263
+ thead tr:first-child th
2264
+ top: 0
2265
+
2266
+ .m--datatable:not(.m--datatable-grid)
2267
+ max-height: 80vh
2268
+
2269
+ .m--datatable:not(.m--datatable-grid).q-table--dense
2270
+ &.q-table--loading thead tr:last-child th
2271
+ top: 26px
2272
+
2273
+ .m--datatable:not(.m--datatable-grid):not(.q-table--dense)
2274
+ &.q-table--loading thead tr:last-child th
2275
+ top: 55px
2276
+
2277
+ .q-table__bottom
2278
+ justify-content: start !important
2279
+
2280
+ .q-table__separator.col
2281
+ display: none !important
2282
+
2283
+ .q-table__top
2284
+ align-items: center
2285
+
2286
+ .q-table__separator.col
2287
+ display: none !important
2288
+
2289
+ .q-table__control:last-child
2290
+ padding-left: 8px
2291
+ display: block
2292
+ flex: 10000 1 0
2293
+ width: auto
2294
+ min-width: 0
2295
+ max-width: 100%
2296
+
2297
+ .m--dialog-card
2298
+ .q-card__actions
2299
+ .q-btn
2300
+ padding: 4px 16px !important
2301
+
2302
+ .grid-style-transition
2303
+ transition: transform .28s, background-color .28s
2304
+
2305
+ </style>