pgo-ui 1.0.74 → 1.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Radio-BMsJLVIa.js +4 -0
- package/dist/{index-C17iCr8Z.js → index-BgeiUi3s.js} +4122 -4122
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +23 -23
- package/package.json +2 -1
- package/src/components/pgo/DataTable copy.vue +258 -150
- package/src/components/pgo/forms/DynamicForm.vue +4 -2
- package/dist/Radio-D8uUtNff.js +0 -4
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
inputBorder
|
|
21
21
|
]"
|
|
22
22
|
@input="debounceSearch"
|
|
23
|
-
|
|
23
|
+
>
|
|
24
24
|
<div class="absolute left-3 top-1/2 transform -translate-y-1/2">
|
|
25
25
|
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
26
26
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
<!-- Loading Bar -->
|
|
40
40
|
<div class="absolute top-0 left-0 right-0 z-10 h-1 overflow-hidden">
|
|
41
41
|
<div v-if="loading" class="bg-gray-200 w-full">
|
|
42
|
-
<div class="h-full bg-primary animate-loading-bar"
|
|
42
|
+
<div class="h-full bg-primary animate-loading-bar" />
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
|
-
<table class="w-full caption-bottom text-sm">
|
|
46
|
+
<table :dir="dir" class="w-full caption-bottom text-sm">
|
|
47
47
|
<!-- Table Header -->
|
|
48
48
|
<thead :class="[headerBg]">
|
|
49
49
|
<tr :class="[headerBorder, 'transition-colors']">
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
:indeterminate.prop="someSelected"
|
|
57
57
|
class="h-4 w-4 rounded border border-gray-200 text-primary focus:ring-2 focus:ring-primary-500 focus:ring-offset-0"
|
|
58
58
|
@change="toggleSelectAll"
|
|
59
|
-
|
|
59
|
+
>
|
|
60
60
|
</div>
|
|
61
61
|
</th>
|
|
62
62
|
<!-- Column Headers -->
|
|
@@ -72,15 +72,16 @@
|
|
|
72
72
|
'hover:bg-input-hover-border',
|
|
73
73
|
{ 'bg-surface': isSorted(header.value) }
|
|
74
74
|
]"
|
|
75
|
-
@click="header.sortable
|
|
75
|
+
@click="header.sortable == true ? toggleSort(header.value) : null"
|
|
76
76
|
>
|
|
77
|
-
<div :class="['flex items-center space-x-2', textAlign[header.headerAlign
|
|
78
|
-
<span
|
|
79
|
-
:
|
|
77
|
+
<div :class="['flex items-center space-x-2', textAlign[header.headerAlign ?? 'defaults']]">
|
|
78
|
+
<span
|
|
79
|
+
:class="['w-full', header.width ? 'truncate' : 'whitespace-nowrap',]"
|
|
80
|
+
:style="{ maxWidth: header.width || 'auto', }"
|
|
80
81
|
>
|
|
81
82
|
{{ header.title }}
|
|
82
83
|
</span>
|
|
83
|
-
<div v-if="header.sortable
|
|
84
|
+
<div v-if="header.sortable == true" class="flex h-4 w-4 items-center justify-center">
|
|
84
85
|
<!-- Unsorted state -->
|
|
85
86
|
<svg
|
|
86
87
|
v-if="!isSorted(header.value)"
|
|
@@ -115,10 +116,11 @@
|
|
|
115
116
|
</div>
|
|
116
117
|
</th>
|
|
117
118
|
<!-- Actions column -->
|
|
118
|
-
<th
|
|
119
|
-
|
|
119
|
+
<th
|
|
120
|
+
v-if="$slots['item-actions'] || showActions" :class="['h-10 px-3 text-end align-middle font-medium', headerText]"
|
|
121
|
+
:style="{ width: '25px' || 'auto' }"
|
|
120
122
|
>
|
|
121
|
-
<Button
|
|
123
|
+
<Button
|
|
122
124
|
v-if="inlineEdit"
|
|
123
125
|
:label="editMode ? 'Cancel Edit' : 'Inline Edit'"
|
|
124
126
|
:color="editMode ? 'danger' : 'primary'"
|
|
@@ -156,7 +158,7 @@
|
|
|
156
158
|
@click="handleRowClick(item, index)"
|
|
157
159
|
>
|
|
158
160
|
<!-- Selection checkbox -->
|
|
159
|
-
<td v-if="selectable" class="px-
|
|
161
|
+
<td v-if="selectable" class="px-2 py-2">
|
|
160
162
|
<div class="flex items-center justify-center">
|
|
161
163
|
<input
|
|
162
164
|
type="checkbox"
|
|
@@ -164,21 +166,21 @@
|
|
|
164
166
|
class="h-4 w-4 rounded border-2 border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500 focus:ring-offset-0"
|
|
165
167
|
@click.stop
|
|
166
168
|
@change="toggleItemSelection(item, index)"
|
|
167
|
-
|
|
169
|
+
>
|
|
168
170
|
</div>
|
|
169
171
|
</td>
|
|
170
172
|
|
|
171
173
|
<!-- Data columns -->
|
|
172
174
|
<template v-for="header in headers" :key="header.value">
|
|
173
175
|
<td
|
|
174
|
-
:style="{ width: header.width || 'flex', maxWidth: header.width || '
|
|
176
|
+
:style="{ width: header.width || 'flex', maxWidth: header.width || 'auto' }"
|
|
175
177
|
:class="[
|
|
176
|
-
'px-
|
|
178
|
+
'px-2 py-2 ',
|
|
177
179
|
]"
|
|
178
180
|
@click.stop
|
|
179
181
|
>
|
|
180
182
|
<div
|
|
181
|
-
class="
|
|
183
|
+
class=" "
|
|
182
184
|
>
|
|
183
185
|
<slot
|
|
184
186
|
:name="`item.${header.value}`"
|
|
@@ -189,47 +191,74 @@
|
|
|
189
191
|
>
|
|
190
192
|
|
|
191
193
|
<!-- html -->
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
<div
|
|
195
|
+
v-if="header.displayType == 'html'" :class="[
|
|
196
|
+
'text-sm truncate shrink',
|
|
197
|
+
cellText,
|
|
198
|
+
textAlign[header?.align ?? 'defaults'],
|
|
199
|
+
]" v-html="getCustomColumnData(header, item)"
|
|
200
|
+
/>
|
|
201
|
+
|
|
202
|
+
<!-- Custom column -->
|
|
203
|
+
<div
|
|
204
|
+
v-else-if="header.displayType == 'custom'" :class="[
|
|
205
|
+
'text-sm truncate shrink',
|
|
206
|
+
cellText,
|
|
207
|
+
textAlign[header?.align ?? 'defaults'],
|
|
208
|
+
]"
|
|
209
|
+
>
|
|
210
|
+
<template v-if="getCustomColumnData(header, item)?.type === 'CopyTextBox'">
|
|
211
|
+
<div class="flex flex-wrap items-center gap-1">
|
|
212
|
+
<template
|
|
213
|
+
v-for="(chip, idx) in getCustomColumnData(header, item).CopyTextBox"
|
|
214
|
+
:key="idx"
|
|
215
|
+
>
|
|
216
|
+
<CopyTextBox
|
|
217
|
+
v-bind="chip"
|
|
218
|
+
class="mr-1 "
|
|
219
|
+
/>
|
|
220
|
+
|
|
221
|
+
</template>
|
|
222
|
+
</div>
|
|
223
|
+
</template>
|
|
224
|
+
<div v-else v-html="getCustomColumnData(header, item)" />
|
|
199
225
|
</div>
|
|
200
226
|
<!-- Chip Display -->
|
|
201
|
-
|
|
202
|
-
<div
|
|
227
|
+
|
|
228
|
+
<div
|
|
229
|
+
v-else-if="header.displayType == 'chip' "
|
|
203
230
|
:class="['align-middle leading-0', getColumnFont(lang)]"
|
|
204
231
|
>
|
|
205
|
-
|
|
206
|
-
<Chip
|
|
207
|
-
size="small"
|
|
232
|
+
<!-- <pre style="font-size:10px">{{ JSON.stringify(getNestedValue(item, header.value)) }}</pre> -->
|
|
233
|
+
<Chip
|
|
234
|
+
size="small"
|
|
208
235
|
v-if="header?.chip && header?.chip[getNestedValue(item, header.value)]"
|
|
209
236
|
v-bind="header.chip[getNestedValue(item, header.value)]"
|
|
210
237
|
/>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
238
|
+
<span v-else class="text-xs text-gray-400">
|
|
239
|
+
{{ getNestedValue(item, header.value) || '-' }}
|
|
240
|
+
</span>
|
|
214
241
|
</div>
|
|
215
|
-
|
|
242
|
+
|
|
216
243
|
<!-- Checkbox (Inline Editable) -->
|
|
217
|
-
<div
|
|
244
|
+
<div
|
|
245
|
+
v-else-if="header.displayType == 'checkbox'"
|
|
218
246
|
:class="['align-middle leading-none', getColumnFont(header.lang)]"
|
|
219
247
|
>
|
|
220
|
-
<Checkbox
|
|
248
|
+
<Checkbox
|
|
221
249
|
:model-value="!!getNestedValue(item, header.value)"
|
|
222
250
|
v-bind="header.displayProps"
|
|
223
251
|
:disabled="!editMode || !header.inlineEditable"
|
|
224
252
|
@update:model-value="(value) => handleCellUpdate(item, header.value, value, index)"
|
|
225
253
|
/>
|
|
226
254
|
</div>
|
|
227
|
-
|
|
255
|
+
|
|
228
256
|
<!-- Select (Inline Editable) -->
|
|
229
|
-
<div
|
|
257
|
+
<div
|
|
258
|
+
v-else-if="header.inputType == 'select' && header.inlineEditable"
|
|
230
259
|
:class="['align-middle leading-none', getColumnFont(header.lang)]"
|
|
231
260
|
>
|
|
232
|
-
<Select
|
|
261
|
+
<Select
|
|
233
262
|
v-if="editMode && header.inlineEditable"
|
|
234
263
|
:model-value="getNestedValue(item, header.value)"
|
|
235
264
|
v-bind="header.select"
|
|
@@ -239,9 +268,10 @@
|
|
|
239
268
|
{{ formatCellValue(getNestedValue(item, header.value), header) }}
|
|
240
269
|
</div>
|
|
241
270
|
</div>
|
|
242
|
-
|
|
271
|
+
|
|
243
272
|
<!-- Text Input (Inline Editable) -->
|
|
244
|
-
<div
|
|
273
|
+
<div
|
|
274
|
+
v-else-if="header.displayType == 'input' && header.inlineEditable"
|
|
245
275
|
:class="['align-middle leading-none', getColumnFont(header.lang)]"
|
|
246
276
|
>
|
|
247
277
|
<input
|
|
@@ -250,33 +280,60 @@
|
|
|
250
280
|
type="text"
|
|
251
281
|
class="w-full px-2 py-1 text-sm border rounded focus:outline-none focus:ring-2 focus:ring-primary"
|
|
252
282
|
@input="(e) => handleCellUpdate(item, header.value, e.target.value, index)"
|
|
253
|
-
|
|
254
|
-
|
|
283
|
+
>
|
|
284
|
+
<div v-else :class="['text-sm', cellText]">
|
|
255
285
|
{{ formatCellValue(getNestedValue(item, header.value), header) }}
|
|
256
286
|
</div>
|
|
257
287
|
</div>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
'
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
288
|
+
<!-- Document Viewer -->
|
|
289
|
+
<div
|
|
290
|
+
v-else-if="header.displayType == 'docButton'"
|
|
291
|
+
:class="['align-middle leading-none', getColumnFont(header.lang)]"
|
|
292
|
+
>
|
|
293
|
+
<FileDisplay
|
|
294
|
+
:item="getNestedValue(item, header.value)"
|
|
295
|
+
v-bind="header.docButton"
|
|
296
|
+
@view-file="emit('viewpdf', $event)"
|
|
297
|
+
/>
|
|
298
|
+
<!-- <Button
|
|
299
|
+
icon="document-text"
|
|
300
|
+
icon-type="solid"
|
|
301
|
+
variant="text"
|
|
302
|
+
size="xs"
|
|
303
|
+
@click="emit('viewpdf', formatCellValue(getNestedValue(item, header.value), header))"
|
|
304
|
+
/> -->
|
|
305
|
+
<!-- @click="handlePdfView(formatCellValue(getNestedValue(item, header.value), header))" -->
|
|
267
306
|
</div>
|
|
307
|
+
|
|
308
|
+
<!-- Default Text Display -->
|
|
309
|
+
<!-- <bdi class="flex items-center"></bdi> -->
|
|
310
|
+
<bdi
|
|
311
|
+
v-else :class="[
|
|
312
|
+
'text-sm',
|
|
313
|
+
cellText,
|
|
314
|
+
textAlign[header.align ?? 'defaults'],
|
|
315
|
+
header.displayType == 'englishText' || header.displayType == 'date' ? 'eng-font' : getColumnFont(lang),
|
|
316
|
+
]"
|
|
317
|
+
>
|
|
318
|
+
<span
|
|
319
|
+
:class="['w-full', header.width ? 'truncate' : 'truncate whitespace-nowrap',]"
|
|
320
|
+
:style="{ maxWidth: header.width || 'auto', }"
|
|
321
|
+
>
|
|
322
|
+
{{ formatCellValue(getNestedValue(item, header.value), header) }}
|
|
323
|
+
</span>
|
|
324
|
+
</bdi>
|
|
268
325
|
</slot>
|
|
269
|
-
</div>
|
|
326
|
+
</div>
|
|
270
327
|
</td>
|
|
271
328
|
</template>
|
|
272
329
|
|
|
273
330
|
<!-- Actions -->
|
|
274
|
-
<td v-if="$slots['item-actions'] || showActions" class="px-
|
|
331
|
+
<td v-if="$slots['item-actions'] || showActions" class="px-2 py-2">
|
|
275
332
|
<slot name="item-actions" :item="item" :index="index">
|
|
276
333
|
<div class="flex items-center justify-end space-x-2">
|
|
277
|
-
<Button v-if="showView" @click.stop="emit('view', item)" size="md" color="info" variant="text" icon="eye" icon-type="outline"
|
|
278
|
-
<Button v-if="showEdit" @click.stop="emit('edit', item)" size="md" icon="pencil-square" color="warning" variant="text" label="" />
|
|
279
|
-
<Button v-if="showDelete" @click.stop="emit('delete', item)" size="md" color="danger" variant="text" icon="trash" label="" />
|
|
334
|
+
<Button v-if="showView && evaluateCondition(settings?.actions?.viewCondition, item)" v-tooltip="{en: 'View', dv: 'ވިއު'}" @click.stop="emit('view', item)" size="md" color="info" variant="text" icon="eye" icon-type="outline" label="" />
|
|
335
|
+
<Button v-if="showEdit && evaluateCondition(settings?.actions?.editCondition, item)" v-tooltip="{en: 'Edit', dv: 'އެޑިޓް'}" @click.stop="emit('edit', item)" size="md" icon="pencil-square" color="warning" variant="text" label="" />
|
|
336
|
+
<Button v-if="showDelete && evaluateCondition(settings?.actions?.deleteCondition, item)" v-tooltip="{en: 'Delete', dv: 'ޑިލީޓް'}" @click.stop="emit('delete', item)" size="md" color="danger" variant="text" icon="trash" label="" />
|
|
280
337
|
</div>
|
|
281
338
|
</slot>
|
|
282
339
|
</td>
|
|
@@ -307,21 +364,34 @@
|
|
|
307
364
|
<slot name="footer" />
|
|
308
365
|
</div>
|
|
309
366
|
|
|
367
|
+
<!-- Delete Confirmation Modal -->
|
|
368
|
+
<template>
|
|
369
|
+
<!-- <ConfirmationModal
|
|
370
|
+
v-model="showConfirmation2"
|
|
371
|
+
:item="DeleteItem"
|
|
372
|
+
title="Delete Confirmation"
|
|
373
|
+
subtitle="Are you sure you want to delete this item?"
|
|
374
|
+
message="Are you sure you want to delete this item?"
|
|
375
|
+
type="warning"
|
|
376
|
+
@confirm="handleDeleteConfirm"
|
|
377
|
+
/> -->
|
|
378
|
+
</template>
|
|
379
|
+
|
|
310
380
|
<!-- Confirmation Modal -->
|
|
311
|
-
|
|
312
|
-
v-model="showConfirmation"
|
|
313
|
-
persistent
|
|
381
|
+
<Model
|
|
382
|
+
v-model="showConfirmation"
|
|
383
|
+
persistent
|
|
314
384
|
lang="en"
|
|
315
385
|
|
|
316
386
|
title="Confirm Update"
|
|
317
387
|
>
|
|
318
388
|
<p class="text-sm text-gray-600 mb-6">
|
|
319
|
-
|
|
320
|
-
|
|
389
|
+
Are you sure you want to update <strong>{{ pendingUpdate.header }}</strong>
|
|
390
|
+
from <strong>{{ pendingUpdate.oldValue }}</strong> to <strong>{{ pendingUpdate.newValue }}</strong>?
|
|
321
391
|
</p>
|
|
322
392
|
<template #footer>
|
|
323
393
|
<div class="flex justify-end gap-2">
|
|
324
|
-
|
|
394
|
+
<Button
|
|
325
395
|
label="Cancel"
|
|
326
396
|
color="secondary"
|
|
327
397
|
variant="outlined"
|
|
@@ -335,7 +405,7 @@
|
|
|
335
405
|
/>
|
|
336
406
|
</div>
|
|
337
407
|
</template>
|
|
338
|
-
|
|
408
|
+
</Model>
|
|
339
409
|
</div>
|
|
340
410
|
</template>
|
|
341
411
|
|
|
@@ -344,10 +414,17 @@ import { ref, computed, watch, reactive, onMounted, inject } from 'vue'
|
|
|
344
414
|
import Button from '../pgo/Button.vue'
|
|
345
415
|
import Pagination from '../pgo/Pagination.vue'
|
|
346
416
|
import Chip from '../pgo/buttons/Chip.vue'
|
|
417
|
+
import CopyTextBox from './CopyTextBox.vue'
|
|
347
418
|
import Checkbox from '../pgo/inputs/Checkbox.vue'
|
|
348
419
|
import Select from '../pgo/inputs/Select.vue'
|
|
349
420
|
import Model from '../pgo/Modal.vue'
|
|
421
|
+
import ConfirmationModal from './ConfirmationModal.vue'
|
|
422
|
+
|
|
350
423
|
import { textAlign, initializeFunctions } from '../../pgo-components/lib/componentConfig.js'
|
|
424
|
+
import FileDisplay from './FileDisplay.vue'
|
|
425
|
+
|
|
426
|
+
const api = inject('api')
|
|
427
|
+
const snackbar = inject('snackbar')
|
|
351
428
|
|
|
352
429
|
const props = defineProps({
|
|
353
430
|
// Data
|
|
@@ -359,7 +436,7 @@ const props = defineProps({
|
|
|
359
436
|
type: [Array, Object],
|
|
360
437
|
required: true
|
|
361
438
|
},
|
|
362
|
-
|
|
439
|
+
|
|
363
440
|
// Server-side options
|
|
364
441
|
serverSideOptions: {
|
|
365
442
|
type: Object,
|
|
@@ -389,7 +466,7 @@ const props = defineProps({
|
|
|
389
466
|
type: String,
|
|
390
467
|
default: 'No data available'
|
|
391
468
|
},
|
|
392
|
-
|
|
469
|
+
|
|
393
470
|
// Features
|
|
394
471
|
showActions: {
|
|
395
472
|
type: Boolean,
|
|
@@ -451,7 +528,7 @@ const props = defineProps({
|
|
|
451
528
|
type: String,
|
|
452
529
|
default: 'shadow-none'
|
|
453
530
|
},
|
|
454
|
-
|
|
531
|
+
|
|
455
532
|
// Header styling
|
|
456
533
|
headerBg: {
|
|
457
534
|
type: String,
|
|
@@ -465,7 +542,7 @@ const props = defineProps({
|
|
|
465
542
|
type: String,
|
|
466
543
|
default: 'text-textcolor'
|
|
467
544
|
},
|
|
468
|
-
|
|
545
|
+
|
|
469
546
|
// Row styling
|
|
470
547
|
rowBorder: {
|
|
471
548
|
type: String,
|
|
@@ -483,13 +560,13 @@ const props = defineProps({
|
|
|
483
560
|
type: String,
|
|
484
561
|
default: 'text-textcolor'
|
|
485
562
|
},
|
|
486
|
-
|
|
563
|
+
|
|
487
564
|
// Input styling
|
|
488
565
|
inputBorder: {
|
|
489
566
|
type: String,
|
|
490
567
|
default: 'border-input-border focus:border-input-focus-border focus:ring-input-focus-ring'
|
|
491
568
|
},
|
|
492
|
-
|
|
569
|
+
|
|
493
570
|
// Button styling
|
|
494
571
|
buttonPrimary: {
|
|
495
572
|
type: String,
|
|
@@ -499,7 +576,7 @@ const props = defineProps({
|
|
|
499
576
|
type: String,
|
|
500
577
|
default: 'bg-surface hover:bg-input-hover-border text-textcolor border border-input-border'
|
|
501
578
|
},
|
|
502
|
-
|
|
579
|
+
|
|
503
580
|
// Pagination styling
|
|
504
581
|
paginationBg: {
|
|
505
582
|
type: String,
|
|
@@ -532,6 +609,9 @@ const props = defineProps({
|
|
|
532
609
|
lang: {
|
|
533
610
|
type: String,
|
|
534
611
|
default: 'dv'
|
|
612
|
+
},
|
|
613
|
+
dir: {
|
|
614
|
+
type: String
|
|
535
615
|
}
|
|
536
616
|
|
|
537
617
|
})
|
|
@@ -545,9 +625,11 @@ const emit = defineEmits([
|
|
|
545
625
|
'search',
|
|
546
626
|
'view',
|
|
547
627
|
'edit',
|
|
548
|
-
'delete'
|
|
628
|
+
'delete',
|
|
629
|
+
'viewpdf'
|
|
549
630
|
])
|
|
550
631
|
|
|
632
|
+
|
|
551
633
|
// Internal state
|
|
552
634
|
const searchQuery = ref('')
|
|
553
635
|
const selectedItems = ref([])
|
|
@@ -555,9 +637,9 @@ const searchTimeout = ref(null)
|
|
|
555
637
|
const editMode = ref(false)
|
|
556
638
|
const showConfirmation = ref(false)
|
|
557
639
|
const updateLoading = ref(false)
|
|
558
|
-
const headers =
|
|
559
|
-
|
|
560
|
-
|
|
640
|
+
const headers = computed(() => props.settings.headers || [])
|
|
641
|
+
const compiledFunctions = ref({})
|
|
642
|
+
const tableVariables = reactive({ ...props.settings?.variables })
|
|
561
643
|
|
|
562
644
|
const pendingUpdate = ref({
|
|
563
645
|
item: null,
|
|
@@ -603,43 +685,65 @@ const someSelected = computed(() => {
|
|
|
603
685
|
return selectedItems.value.length > 0 && !allSelected.value
|
|
604
686
|
})
|
|
605
687
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
688
|
+
const initializeAllFunctions = () => {
|
|
689
|
+
|
|
690
|
+
if (props.settings?.functions) {
|
|
691
|
+
|
|
692
|
+
const functionContext = {
|
|
693
|
+
items: props.items,
|
|
694
|
+
headers: headers.value,
|
|
695
|
+
selectedItems,
|
|
696
|
+
searchQuery,
|
|
697
|
+
internalOptions,
|
|
698
|
+
tableVariables,
|
|
699
|
+
variables: tableVariables,
|
|
700
|
+
emit,
|
|
701
|
+
props,
|
|
702
|
+
console,
|
|
703
|
+
snackbar: snackbar,
|
|
704
|
+
api: api,
|
|
705
|
+
this: {
|
|
706
|
+
variables: tableVariables
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
compiledFunctions.value = initializeFunctions(props.settings.functions, functionContext)
|
|
711
|
+
} else {
|
|
712
|
+
console.log('No functions found in settings')
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Execute function helper
|
|
717
|
+
const executeFunction = (functionName, ...args) => {
|
|
718
|
+
|
|
719
|
+
if (compiledFunctions.value[functionName]) {
|
|
720
|
+
try {
|
|
721
|
+
const result = compiledFunctions.value[functionName](...args)
|
|
722
|
+
return result
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error(`Error executing function ${functionName}:`, error)
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
console.warn(`Function ${functionName} not found`)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Add this method in your script section
|
|
732
|
+
const getCustomColumnData = (header, item) => {
|
|
733
|
+
|
|
734
|
+
if (header.columnData && (header.displayType === 'html' || header.displayType === 'custom')) {
|
|
735
|
+
// Extract function name from columnData (e.g., "customeColumnData(item)")
|
|
736
|
+
const functionMatch = header.columnData.match(/(\w+)\(/)
|
|
737
|
+
if (functionMatch) {
|
|
738
|
+
const functionName = functionMatch[1]
|
|
739
|
+
// Execute the compiled function with the item data as the first argument
|
|
740
|
+
const result = executeFunction(functionName, item)
|
|
741
|
+
return result || ''
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return ''
|
|
745
|
+
}
|
|
630
746
|
|
|
631
|
-
// // Execute function helper
|
|
632
|
-
// const executeFunction = (functionName, ...args) => {
|
|
633
|
-
// if (compiledFunctions.value[functionName]) {
|
|
634
|
-
// try {
|
|
635
|
-
// return compiledFunctions.value[functionName](...args)
|
|
636
|
-
// } catch (error) {
|
|
637
|
-
// console.error(`Error executing function ${functionName}:`, error)
|
|
638
|
-
// }
|
|
639
|
-
// } else {
|
|
640
|
-
// console.warn(`Function ${functionName} not found`)
|
|
641
|
-
// }
|
|
642
|
-
// }
|
|
643
747
|
// Methods
|
|
644
748
|
const getItemKey = (item, index) => {
|
|
645
749
|
return item[props.itemKey] ?? index
|
|
@@ -653,6 +757,15 @@ const getNestedValue = (obj, path) => {
|
|
|
653
757
|
return path.split('.').reduce((current, key) => current?.[key], obj)
|
|
654
758
|
}
|
|
655
759
|
|
|
760
|
+
const evaluateCondition = (condition, item) => {
|
|
761
|
+
if (!condition) return true
|
|
762
|
+
try {
|
|
763
|
+
return new Function('item', `return ${condition}`)(item)
|
|
764
|
+
} catch {
|
|
765
|
+
return true
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
656
769
|
const formatCellValue = (value, header) => {
|
|
657
770
|
if (header.format && typeof header.format === 'function') {
|
|
658
771
|
return header.format(value)
|
|
@@ -663,41 +776,41 @@ const formatCellValue = (value, header) => {
|
|
|
663
776
|
const toggleSort = (key) => {
|
|
664
777
|
// Find the header to check if it's sortable
|
|
665
778
|
const header = props.settings.headers.find(h => h.key === key)
|
|
666
|
-
|
|
779
|
+
|
|
667
780
|
// Don't sort if explicitly marked as not sortable
|
|
668
781
|
if (header && header.sortable === false) {
|
|
669
|
-
console.log('Column is not sortable:', key)
|
|
782
|
+
// console.log('Column is not sortable:', key)
|
|
670
783
|
return
|
|
671
784
|
}
|
|
672
|
-
|
|
785
|
+
|
|
673
786
|
const sortBy = [...internalOptions.value.sortBy]
|
|
674
787
|
const sortDesc = [...internalOptions.value.sortDesc]
|
|
675
|
-
|
|
788
|
+
|
|
676
789
|
const index = sortBy.findIndex(item => item === key)
|
|
677
|
-
|
|
790
|
+
|
|
678
791
|
if (index === -1) {
|
|
679
792
|
// Add new sort
|
|
680
793
|
sortBy.push(key)
|
|
681
794
|
sortDesc.push(false)
|
|
682
|
-
console.log('Adding new sort - ASC')
|
|
795
|
+
// console.log('Adding new sort - ASC')
|
|
683
796
|
} else {
|
|
684
797
|
// Toggle existing sort
|
|
685
798
|
if (!sortDesc[index]) {
|
|
686
799
|
sortDesc[index] = true
|
|
687
|
-
console.log('Changing to DESC')
|
|
800
|
+
// console.log('Changing to DESC')
|
|
688
801
|
} else {
|
|
689
802
|
// Remove sort
|
|
690
803
|
sortBy.splice(index, 1)
|
|
691
804
|
sortDesc.splice(index, 1)
|
|
692
|
-
console.log('Removing sort')
|
|
805
|
+
// console.log('Removing sort')
|
|
693
806
|
}
|
|
694
807
|
}
|
|
695
|
-
|
|
808
|
+
|
|
696
809
|
internalOptions.value.sortBy = sortBy
|
|
697
810
|
internalOptions.value.sortDesc = sortDesc
|
|
698
811
|
internalOptions.value.page = 1 // Reset to first page
|
|
699
|
-
|
|
700
|
-
|
|
812
|
+
|
|
813
|
+
|
|
701
814
|
emitOptionsUpdate()
|
|
702
815
|
}
|
|
703
816
|
|
|
@@ -713,13 +826,13 @@ const getSortDirection = (key) => {
|
|
|
713
826
|
|
|
714
827
|
const getColumnFont = (lang) => {
|
|
715
828
|
if (!lang) return ''
|
|
716
|
-
|
|
829
|
+
|
|
717
830
|
const fontMap = {
|
|
718
831
|
'en': 'eng-font', // or whatever your English font is
|
|
719
832
|
'dv': 'faruma', // Dhivehi font
|
|
720
833
|
// Add more languages as needed
|
|
721
834
|
}
|
|
722
|
-
|
|
835
|
+
|
|
723
836
|
return fontMap[lang] || ''
|
|
724
837
|
}
|
|
725
838
|
|
|
@@ -727,7 +840,7 @@ const debounceSearch = () => {
|
|
|
727
840
|
if (searchTimeout.value) {
|
|
728
841
|
clearTimeout(searchTimeout.value)
|
|
729
842
|
}
|
|
730
|
-
|
|
843
|
+
|
|
731
844
|
searchTimeout.value = setTimeout(() => {
|
|
732
845
|
internalOptions.value.search = searchQuery.value
|
|
733
846
|
internalOptions.value.page = 1
|
|
@@ -743,13 +856,13 @@ const handleRowClick = (item, index) => {
|
|
|
743
856
|
const toggleItemSelection = (item, index) => {
|
|
744
857
|
const key = getItemKey(item, index)
|
|
745
858
|
const currentIndex = selectedItems.value.indexOf(key)
|
|
746
|
-
|
|
859
|
+
|
|
747
860
|
if (currentIndex === -1) {
|
|
748
861
|
selectedItems.value.push(key)
|
|
749
862
|
} else {
|
|
750
863
|
selectedItems.value.splice(currentIndex, 1)
|
|
751
864
|
}
|
|
752
|
-
|
|
865
|
+
|
|
753
866
|
emit('selection-change', selectedItems.value, getSelectedItems())
|
|
754
867
|
}
|
|
755
868
|
|
|
@@ -759,12 +872,12 @@ const toggleSelectAll = () => {
|
|
|
759
872
|
} else {
|
|
760
873
|
selectedItems.value = props.items.map((item, index) => getItemKey(item, index))
|
|
761
874
|
}
|
|
762
|
-
|
|
875
|
+
|
|
763
876
|
emit('selection-change', selectedItems.value, getSelectedItems())
|
|
764
877
|
}
|
|
765
878
|
|
|
766
879
|
const getSelectedItems = () => {
|
|
767
|
-
return props.items.filter((item, index) =>
|
|
880
|
+
return props.items.filter((item, index) =>
|
|
768
881
|
selectedItems.value.includes(getItemKey(item, index))
|
|
769
882
|
)
|
|
770
883
|
}
|
|
@@ -786,10 +899,10 @@ const toggleEditMode = () => {
|
|
|
786
899
|
const handleCellUpdate = (item, key, newValue, index) => {
|
|
787
900
|
const header = props.settings.headers.find(h => h.key === key)
|
|
788
901
|
const oldValue = getNestedValue(item, key)
|
|
789
|
-
|
|
902
|
+
|
|
790
903
|
// Only show confirmation if value actually changed
|
|
791
904
|
if (oldValue === newValue) return
|
|
792
|
-
|
|
905
|
+
|
|
793
906
|
pendingUpdate.value = {
|
|
794
907
|
item,
|
|
795
908
|
key,
|
|
@@ -798,23 +911,23 @@ const handleCellUpdate = (item, key, newValue, index) => {
|
|
|
798
911
|
header: header?.title || key,
|
|
799
912
|
index
|
|
800
913
|
}
|
|
801
|
-
|
|
914
|
+
|
|
802
915
|
showConfirmation.value = true
|
|
803
916
|
}
|
|
804
917
|
|
|
805
918
|
// Confirm and send update
|
|
806
919
|
const confirmUpdate = async () => {
|
|
807
920
|
updateLoading.value = true
|
|
808
|
-
|
|
921
|
+
|
|
809
922
|
try {
|
|
810
923
|
const { item, key, newValue, index } = pendingUpdate.value
|
|
811
|
-
|
|
924
|
+
|
|
812
925
|
// Prepare update data
|
|
813
926
|
const updateData = {
|
|
814
927
|
[props.itemKey]: item[props.itemKey],
|
|
815
928
|
[key]: newValue
|
|
816
929
|
}
|
|
817
|
-
|
|
930
|
+
|
|
818
931
|
// Emit event to parent
|
|
819
932
|
emit('inline-update', {
|
|
820
933
|
item,
|
|
@@ -823,19 +936,16 @@ const confirmUpdate = async () => {
|
|
|
823
936
|
index,
|
|
824
937
|
updateData
|
|
825
938
|
})
|
|
826
|
-
|
|
939
|
+
|
|
827
940
|
// If updateUrl is provided, send API request
|
|
828
941
|
if (props.updateUrl) {
|
|
829
|
-
const api = inject('api', null)
|
|
830
|
-
if (api) {
|
|
831
942
|
const url = `${props.updateUrl}/${item[props.itemKey]}`
|
|
832
943
|
await api.patch(url, updateData)
|
|
833
|
-
}
|
|
834
944
|
}
|
|
835
|
-
|
|
945
|
+
|
|
836
946
|
// Update local data
|
|
837
947
|
item[key] = newValue
|
|
838
|
-
|
|
948
|
+
|
|
839
949
|
// Reset confirmation
|
|
840
950
|
showConfirmation.value = false
|
|
841
951
|
updateLoading.value = false
|
|
@@ -877,12 +987,11 @@ watch(() => props.items, () => {
|
|
|
877
987
|
selectedItems.value = []
|
|
878
988
|
}, { deep: true })
|
|
879
989
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
console.log('Headers updated:', headers.value) // Debug log to check headers
|
|
990
|
+
// Watch for function changes
|
|
991
|
+
watch(() => props.settings, (newSettings) => {
|
|
992
|
+
if (newSettings?.functions) {
|
|
993
|
+
initializeAllFunctions()
|
|
994
|
+
}
|
|
886
995
|
}, { deep: true })
|
|
887
996
|
|
|
888
997
|
// Initialize
|
|
@@ -890,9 +999,8 @@ onMounted(() => {
|
|
|
890
999
|
if (props.serverSideOptions) {
|
|
891
1000
|
internalOptions.value = { ...internalOptions.value, ...props.serverSideOptions }
|
|
892
1001
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
// }
|
|
1002
|
+
|
|
1003
|
+
initializeAllFunctions()
|
|
896
1004
|
|
|
897
1005
|
})
|
|
898
1006
|
</script>
|
|
@@ -916,7 +1024,7 @@ onMounted(() => {
|
|
|
916
1024
|
|
|
917
1025
|
/* Table collapse */
|
|
918
1026
|
tables {
|
|
919
|
-
border-collapse: separate;
|
|
1027
|
+
border-collapse: separate;
|
|
920
1028
|
border-spacing: 0;
|
|
921
1029
|
}
|
|
922
1030
|
|