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.
@@ -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"></div>
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 !== false ? toggleSort(header.value) : null"
75
+ @click="header.sortable == true ? toggleSort(header.value) : null"
76
76
  >
77
- <div :class="['flex items-center space-x-2', textAlign[header.headerAlign] || 'defaults',]">
78
- <span :class="['w-full', header.width ? 'truncate' : 'whitespace-nowrap',]"
79
- :style="{ maxWidth: header.width || '120px', }"
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 !== false" class="flex h-4 w-4 items-center justify-center">
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 v-if="$slots['item-actions'] || showActions" :class="['h-10 px-3 text-end align-middle font-medium', headerText]"
119
- :style="{ width: '25px' || 'auto' }"
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-6 py-4">
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 || '120px' }"
176
+ :style="{ width: header.width || 'flex', maxWidth: header.width || 'auto' }"
175
177
  :class="[
176
- 'px-6 py-4',
178
+ 'px-2 py-2 ',
177
179
  ]"
178
180
  @click.stop
179
181
  >
180
182
  <div
181
- class="truncate "
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
- <div v-if="header.displayType == 'html'" :class="[
193
- 'text-sm truncate shrink ',
194
- cellText,
195
- textAlign[header?.align] || 'defaults',
196
-
197
- ]">
198
- {{ header.columntData }}
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 v-else-if="header.displayType == 'chip' && (header.inlineEditable && !editMode)"
227
+
228
+ <div
229
+ v-else-if="header.displayType == 'chip' "
203
230
  :class="['align-middle leading-0', getColumnFont(lang)]"
204
231
  >
205
- <!-- <pre style="font-size:10px">{{ JSON.stringify(getNestedValue(item, header.value)) }}</pre> -->
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
- <span v-else class="text-xs text-gray-400">
212
- {{ getNestedValue(item, header.value) || '-' }}
213
- </span>
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 v-else-if="header.displayType == 'checkbox'"
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 v-else-if="header.inputType == 'select' && header.inlineEditable"
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 v-else-if="header.displayType == 'input' && header.inlineEditable"
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
- <div v-else :class="['text-sm', cellText]">
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
- <!-- Default Text Display -->
260
- <div v-else :class="[
261
- 'text-sm truncate shrink ',
262
- cellText,
263
- textAlign[header.align] || 'defaults',
264
- header.displayType == 'englishText' || header.displayType == 'date' ? 'eng-font' : getColumnFont(lang),
265
- ]">
266
- {{ formatCellValue(getNestedValue(item, header.value), header) }}
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-6 py-4">
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" label="" />
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
- <Model
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
- Are you sure you want to update <strong>{{ pendingUpdate.header }}</strong>
320
- from <strong>{{ pendingUpdate.oldValue }}</strong> to <strong>{{ pendingUpdate.newValue }}</strong>?
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
- <Button
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
- </Model>
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 = ref({})
559
- // const compiledFunctions = ref({})
560
- // const tableVariables = reactive({ ...props.settings?.variables })
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
- // Add this function
607
- // const initializeAllFunctions = () => {
608
- // if (props.settings.functions) {
609
- // const functionContext = {
610
- // items: props.items,
611
- // headers: props.settings?.headers,
612
- // selectedItems,
613
- // searchQuery,
614
- // internalOptions,
615
- // tableVariables,
616
- // variables: tableVariables,
617
- // emit,
618
- // props,
619
- // console,
620
- // snackbar: inject('snackbar', null),
621
- // api: inject('api', null),
622
- // this: {
623
- // variables: tableVariables
624
- // }
625
- // }
626
-
627
- // compiledFunctions.value = initializeFunctions(props.functions, functionContext)
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
- watch(() => props.settings, () => {
881
- headers.value = props.settings?.headers || []
882
- // if (props.settings?.functions) {
883
- // initializeAllFunctions()
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
- // if (props.settings?.functions) {
894
- // initializeAllFunctions()
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