pgo-ui 1.0.76 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgo-ui",
3
- "version": "1.0.76",
3
+ "version": "1.0.78",
4
4
  "description": "A Vue 3 component library with PGO design system",
5
5
  "private": false,
6
6
  "type": "module",
package/src/App.vue CHANGED
@@ -18,7 +18,6 @@
18
18
  fixed
19
19
  :dense="isDense"
20
20
  elevation="4"
21
- II
22
21
  border="border-b"
23
22
  p="px-2"
24
23
  :rtl="rtl || isRtl"
@@ -59,7 +58,7 @@
59
58
  <!-- ACTIONS -->
60
59
  <template #actions>
61
60
  <button class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-slate-700">
62
- <HeroIcon name="bell" size="22" />
61
+ <HeroIcon name="bell" size="22" class="text-black" />
63
62
  </button>
64
63
 
65
64
  <button class="hidden md:block p-2 rounded-full hover:bg-gray-100 dark:hover:bg-slate-700">
Binary file
@@ -9,6 +9,7 @@
9
9
  <div>Form Valid: {{ isValid }}</div>
10
10
  <div>Form Data: {{ data }}</div>
11
11
  </div>
12
+
12
13
 
13
14
  <!-- New Vuetify-style Form -->
14
15
  <Form
@@ -17,6 +18,18 @@
17
18
  validate-on="blur"
18
19
  padding="p-4 space-y-4"
19
20
  >
21
+ <div class="flex gap-1 items-center mb-4">
22
+ <Select
23
+ v-model="data.country"
24
+ label="Country"
25
+ :items="countries"
26
+ item-title="text"
27
+ item-value="value"
28
+ :rules="[rules.required]"
29
+ :searchable="true"
30
+ :multiple="false"
31
+ />
32
+ </div>
20
33
  <div class="space-y-4">
21
34
  <InputSearch
22
35
  v-model="data.name"
@@ -37,7 +37,7 @@
37
37
  class="md:hidden inline-flex items-center justify-center .vts-pa-2 rounded-md"
38
38
  >
39
39
  <slot name="drawer-icon">
40
- <HeroIcon name="bars-3" size="24" color="text-gray-600" />
40
+ <HeroIcon name="bars-3" size="24" color="text-current" />
41
41
  </slot>
42
42
  </button>
43
43
 
@@ -65,10 +65,10 @@
65
65
  <!-- Overflow actions -->
66
66
  <div v-if="overflowActions.length" class="relative">
67
67
  <button @click="toggleOverflow" class="p-2 rounded-md hover:bg-black/5 dark:hover:bg-white/10">
68
- <svg class="h-5 w-5" fill="none" stroke="currentColor">
68
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
69
+ <circle cx="5" cy="12" r="2" />
69
70
  <circle cx="12" cy="12" r="2" />
70
71
  <circle cx="19" cy="12" r="2" />
71
- <circle cx="5" cy="12" r="2" />
72
72
  </svg>
73
73
  </button>
74
74
 
@@ -86,10 +86,37 @@
86
86
  </div>
87
87
  </div>
88
88
 
89
+ <!-- Font Size Accessibility -->
90
+ <div class="flex items-center">
91
+ <button
92
+ @click="onDecreaseFontScale"
93
+ :disabled="fontScaleAtMin"
94
+ class="px-1.5 py-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"
95
+ title="Decrease font size"
96
+ >
97
+ <span class="font-bold" style="font-size: 0.7rem;">A-</span>
98
+ </button>
99
+ <button
100
+ @click="onResetFontScale"
101
+ class="px-1 py-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer"
102
+ title="Reset font size"
103
+ >
104
+ <span style="font-size: 0.65rem;">{{ currentFontScale }}%</span>
105
+ </button>
106
+ <button
107
+ @click="onIncreaseFontScale"
108
+ :disabled="fontScaleAtMax"
109
+ class="px-1.5 py-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"
110
+ title="Increase font size"
111
+ >
112
+ <span class="font-bold" style="font-size: 0.85rem;">A+</span>
113
+ </button>
114
+ </div>
115
+
89
116
  <!-- Language Selector -->
90
117
  <div class="relative" ref="langMenuRef">
91
118
  <button @click="toggleLangMenu" class="px-2 py-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer">
92
- <HeroIcon name="language" size="22" />
119
+ <HeroIcon name="language" size="22" color="text-current" />
93
120
  </button>
94
121
  <div
95
122
  v-if="isLangOpen"
@@ -185,6 +212,19 @@
185
212
 
186
213
  const globalRtl = inject('globalRtl', ref(false))
187
214
  const selectedRtl = computed(() => props.rtl !== undefined ? props.rtl : globalRtl.value)
215
+
216
+ /* ---------------------------------------------
217
+ * font size accessibility
218
+ ----------------------------------------------*/
219
+ const globalFontScale = inject('globalFontScale', ref(100))
220
+ const onIncreaseFontScale = inject('increaseFontScale', () => {}) as () => void
221
+ const onDecreaseFontScale = inject('decreaseFontScale', () => {}) as () => void
222
+ const onResetFontScale = inject('resetFontScale', () => {}) as () => void
223
+
224
+ const FONT_SCALE_STEPS = [85, 100, 115, 130, 145]
225
+ const currentFontScale = computed(() => globalFontScale.value)
226
+ const fontScaleAtMin = computed(() => globalFontScale.value <= FONT_SCALE_STEPS[0])
227
+ const fontScaleAtMax = computed(() => globalFontScale.value >= FONT_SCALE_STEPS[FONT_SCALE_STEPS.length - 1])
188
228
  /* ---------------------------------------------
189
229
  * layout integration
190
230
  ----------------------------------------------*/
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <Chip
3
+ v-tooltip="{en: 'Copy', dv: 'ކޮޕީ'}"
4
+ v-bind="chipProps"
5
+ clickable
6
+ :class="[
7
+ 'transition-all duration-200 cursor-pointer select-none',
8
+ 'hover:opacity-80 hover:scale-101',
9
+ copied ? 'animate-pulse' : '',
10
+ $attrs.class
11
+ ]"
12
+ @click="handleChipClick"
13
+ >
14
+ <template #append v-if="showIcon && appendIcon === undefined">
15
+ <HeroIcon v-if="copied" :name="copied ? 'check' : 'clipboard'" type="outline" :size="iconSizes['sm']" color="text-current" />
16
+ <HeroIcon v-else name="clipboard" type="outline" :size="iconSizes['sm']" color="text-current" />
17
+ </template>
18
+ </Chip>
19
+ </template>
20
+
21
+ <script setup>
22
+ import { ref, computed, inject } from 'vue'
23
+ import Chip from './buttons/Chip.vue'
24
+ import { iconSizes} from '../../pgo-components/lib/componentConfig'
25
+
26
+ const props = defineProps({
27
+ // Text to copy (optional - will use label if not provided)
28
+ text: {
29
+ type: String,
30
+ default: ''
31
+ },
32
+
33
+ // All Chip props
34
+ label: String,
35
+ size: {
36
+ type: String,
37
+ default: 'sm'
38
+ },
39
+ color: {
40
+ type: String,
41
+ default: 'primary'
42
+ },
43
+
44
+ // CopyChip specific props
45
+ showIcon: {
46
+ type: Boolean,
47
+ default: false
48
+ },
49
+ showSuccessMessage: {
50
+ type: Boolean,
51
+ default: true
52
+ },
53
+ successMessage: {
54
+ type: String,
55
+ default: ''
56
+ },
57
+ successDuration: {
58
+ type: Number,
59
+ default: 1500
60
+ },
61
+
62
+ // Pass through all other Chip props
63
+ variant: String,
64
+ prependIcon: String,
65
+ appendIcon: String,
66
+ disabled: Boolean,
67
+ rounded: { type: String, default: 'sm' },
68
+ })
69
+
70
+ const emit = defineEmits(['copy', 'copy-success', 'copy-error'])
71
+
72
+ const copied = ref(false)
73
+ const snackbar = inject('snackbar', null)
74
+
75
+ // Computed for append icon
76
+ // const getAppendIcon = computed(() => {
77
+ // if (props.showIcon) {
78
+ // return copied.value ? 'check' : 'clipboard'
79
+ // }
80
+ // return props.appendIcon || undefined
81
+ // })
82
+
83
+ // Computed props to pass to Chip (excluding CopyChip specific props AND appendIcon)
84
+ const chipProps = computed(() => {
85
+ const {
86
+ text,
87
+ showIcon,
88
+ showSuccessMessage,
89
+ successMessage,
90
+ successDuration,
91
+ ...rest
92
+ } = props
93
+ return rest
94
+ })
95
+
96
+ // Use text prop if provided, otherwise use label
97
+ const textToCopy = computed(() => {
98
+ return props.text || props.label || ''
99
+ })
100
+
101
+ const handleChipClick = (event) => {
102
+ // console.log('Chip clicked!')
103
+ if (event && event.stopPropagation) {
104
+ event.stopPropagation()
105
+ }
106
+ handleCopy()
107
+ }
108
+
109
+ const handleCopy = async () => {
110
+ // console.log('handleCopy called')
111
+ // console.log('textToCopy:', textToCopy.value)
112
+ // console.log('disabled:', props.disabled)
113
+
114
+ if (props.disabled || !textToCopy.value) {
115
+ // console.log('Copy cancelled - disabled or no text')
116
+ return
117
+ }
118
+
119
+ // Check if clipboard API is available
120
+ // console.log('navigator.clipboard available:', !!navigator.clipboard)
121
+
122
+ try {
123
+ await navigator.clipboard.writeText(textToCopy.value)
124
+ // console.log('Copy successful via clipboard API')
125
+
126
+ // Show copied state
127
+ copied.value = true
128
+ setTimeout(() => {
129
+ copied.value = false
130
+ }, props.successDuration)
131
+
132
+ // Show success message
133
+ if (props.showSuccessMessage && snackbar) {
134
+ const message = props.successMessage || `Copied: ${textToCopy.value}`
135
+ snackbar.show({
136
+ message,
137
+ variant: 'success',
138
+ duration: props.successDuration
139
+ })
140
+ } else {
141
+ console.log('No snackbar available or showSuccessMessage is false')
142
+ }
143
+
144
+ emit('copy', textToCopy.value)
145
+ emit('copy-success', textToCopy.value)
146
+
147
+ } catch (err) {
148
+ console.error('Clipboard API failed:', err)
149
+
150
+ // Fallback for older browsers or HTTPS issues
151
+ try {
152
+ const textArea = document.createElement('textarea')
153
+ textArea.value = textToCopy.value
154
+ textArea.style.position = 'fixed'
155
+ textArea.style.left = '-999999px'
156
+ textArea.style.top = '-999999px'
157
+ document.body.appendChild(textArea)
158
+ textArea.focus()
159
+ textArea.select()
160
+ const successful = document.execCommand('copy')
161
+ document.body.removeChild(textArea)
162
+
163
+ if (successful) {
164
+ // console.log('Copy successful via fallback method')
165
+ copied.value = true
166
+ setTimeout(() => {
167
+ copied.value = false
168
+ }, props.successDuration)
169
+ emit('copy-success', textToCopy.value)
170
+ } else {
171
+ throw new Error('execCommand failed')
172
+ }
173
+
174
+ } catch (fallbackErr) {
175
+ console.error('Fallback copy also failed:', fallbackErr)
176
+ emit('copy-error', fallbackErr)
177
+
178
+ if (snackbar) {
179
+ snackbar.show({
180
+ message: 'Failed to copy text',
181
+ variant: 'error'
182
+ })
183
+ }
184
+ }
185
+ }
186
+ }
187
+ </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <Chip
2
+ <!-- <Chip
3
3
  v-tooltip="{en: 'Copy', dv: 'ކޮޕީ'}"
4
4
  v-bind="chipProps"
5
5
  clickable
@@ -15,13 +15,16 @@
15
15
  <HeroIcon v-if="copied" :name="copied ? 'check' : 'clipboard'" type="outline" :size="iconSizes['sm']" color="text-current" />
16
16
  <HeroIcon v-else name="clipboard" type="outline" :size="iconSizes['sm']" color="text-current" />
17
17
  </template>
18
- </Chip>
18
+ </Chip> -->
19
+ <div v-tooltip="{en: 'Copy', dv: 'ކޮޕީ'}" class="cursor-pointer hover:text-gray-800" @click="handleChipClick">
20
+ {{ text }}
21
+ </div>
19
22
  </template>
20
23
 
21
24
  <script setup>
22
25
  import { ref, computed, inject } from 'vue'
23
- import Chip from './buttons/Chip.vue'
24
- import { iconSizes} from '../../pgo-components/lib/componentConfig'
26
+ // import Chip from './buttons/Chip.vue'
27
+ // import { iconSizes} from '../../pgo-components/lib/componentConfig'
25
28
 
26
29
  const props = defineProps({
27
30
  // Text to copy (optional - will use label if not provided)
@@ -29,23 +32,7 @@ const props = defineProps({
29
32
  type: String,
30
33
  default: ''
31
34
  },
32
-
33
- // All Chip props
34
- label: String,
35
- size: {
36
- type: String,
37
- default: 'sm'
38
- },
39
- color: {
40
- type: String,
41
- default: 'primary'
42
- },
43
-
44
- // CopyChip specific props
45
- showIcon: {
46
- type: Boolean,
47
- default: false
48
- },
35
+
49
36
  showSuccessMessage: {
50
37
  type: Boolean,
51
38
  default: true
@@ -57,14 +44,7 @@ const props = defineProps({
57
44
  successDuration: {
58
45
  type: Number,
59
46
  default: 1500
60
- },
61
-
62
- // Pass through all other Chip props
63
- variant: String,
64
- prependIcon: String,
65
- appendIcon: String,
66
- disabled: Boolean,
67
- rounded: { type: String, default: 'sm' },
47
+ }
68
48
  })
69
49
 
70
50
  const emit = defineEmits(['copy', 'copy-success', 'copy-error'])
@@ -84,7 +64,7 @@ const snackbar = inject('snackbar', null)
84
64
  const chipProps = computed(() => {
85
65
  const {
86
66
  text,
87
- showIcon,
67
+ // showIcon,
88
68
  showSuccessMessage,
89
69
  successMessage,
90
70
  successDuration,
@@ -225,6 +225,19 @@
225
225
  </div>
226
226
  <!-- Chip Display -->
227
227
 
228
+ <div
229
+ v-else-if="header.displayType == 'copyButton' "
230
+ :class="['align-middle leading-0', getColumnFont(lang)]"
231
+ >
232
+ <!-- <pre style="font-size:10px">{{ JSON.stringify(getNestedValue(item, header.value)) }}</pre> -->
233
+ <CopyTextBox
234
+ :text="getNestedValue(item, header.value)"
235
+ class="mr-1 "
236
+ />
237
+ <!-- <span v-else class="text-xs text-gray-400">
238
+ {{ getNestedValue(item, header.value) || '-' }}
239
+ </span> -->
240
+ </div>
228
241
  <div
229
242
  v-else-if="header.displayType == 'chip' "
230
243
  :class="['align-middle leading-0', getColumnFont(lang)]"
@@ -43,7 +43,18 @@
43
43
  </div>
44
44
  </div>
45
45
 
46
- <table :dir="dir" class="w-full caption-bottom text-sm">
46
+ <!-- Desktop Table View -->
47
+ <table :dir="dir" class="w-full caption-bottom text-sm hidden md:table" style="table-layout: fixed;">
48
+ <!-- Colgroup for consistent column widths -->
49
+ <colgroup>
50
+ <col v-if="selectable" style="width: 48px;" />
51
+ <col
52
+ v-for="header in headers"
53
+ :key="'col-' + header.value"
54
+ :style="{ width: header.width || 'auto' }"
55
+ />
56
+ <col v-if="$slots['item-actions'] || showActions" style="width: 120px;" />
57
+ </colgroup>
47
58
  <!-- Table Header -->
48
59
  <thead :class="[headerBg]">
49
60
  <tr :class="[headerBorder, 'transition-colors']">
@@ -63,7 +74,6 @@
63
74
  <th
64
75
  v-for="header in headers"
65
76
  :key="header.value"
66
- :style="{ width: header.width || '' }"
67
77
  :class="[
68
78
  'min-h-8 px-3 align-middle font-medium cursor-pointer select-none transition-colors',
69
79
  headerText,
@@ -75,13 +85,10 @@
75
85
  @click="header.sortable == true ? toggleSort(header.value) : null"
76
86
  >
77
87
  <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', }"
81
- >
88
+ <span class="w-full truncate whitespace-nowrap">
82
89
  {{ header.title }}
83
90
  </span>
84
- <div v-if="header.sortable == true" class="flex h-4 w-4 items-center justify-center">
91
+ <div v-if="header.sortable == true" class="shrink-0 flex h-4 w-4 items-center justify-center">
85
92
  <!-- Unsorted state -->
86
93
  <svg
87
94
  v-if="!isSorted(header.value)"
@@ -118,7 +125,6 @@
118
125
  <!-- Actions column -->
119
126
  <th
120
127
  v-if="$slots['item-actions'] || showActions" :class="['h-10 px-3 text-end align-middle font-medium', headerText]"
121
- :style="{ width: '25px' || 'auto' }"
122
128
  >
123
129
  <Button
124
130
  v-if="inlineEdit"
@@ -173,15 +179,12 @@
173
179
  <!-- Data columns -->
174
180
  <template v-for="header in headers" :key="header.value">
175
181
  <td
176
- :style="{ width: header.width || 'flex', maxWidth: header.width || 'auto' }"
177
182
  :class="[
178
- 'px-2 py-2 ',
183
+ 'px-2 py-2 overflow-hidden',
179
184
  ]"
180
185
  @click.stop
181
186
  >
182
- <div
183
- class=" "
184
- >
187
+ <div class="min-w-0">
185
188
  <slot
186
189
  :name="`item.${header.value}`"
187
190
  :item="item"
@@ -193,7 +196,7 @@
193
196
  <!-- html -->
194
197
  <div
195
198
  v-if="header.displayType == 'html'" :class="[
196
- 'text-sm truncate shrink',
199
+ 'text-sm truncate',
197
200
  cellText,
198
201
  textAlign[header?.align ?? 'defaults'],
199
202
  ]" v-html="getCustomColumnData(header, item)"
@@ -202,22 +205,22 @@
202
205
  <!-- Custom column -->
203
206
  <div
204
207
  v-else-if="header.displayType == 'custom'" :class="[
205
- 'text-sm truncate shrink',
208
+ 'text-sm',
206
209
  cellText,
207
210
  textAlign[header?.align ?? 'defaults'],
208
211
  ]"
209
212
  >
210
213
  <template v-if="getCustomColumnData(header, item)?.type === 'CopyTextBox'">
211
- <div class="flex flex-wrap items-center gap-1">
214
+ <div class="flex flex-wrap items-center gap-1">fff
212
215
  <template
213
216
  v-for="(chip, idx) in getCustomColumnData(header, item).CopyTextBox"
214
217
  :key="idx"
215
- >
218
+ >dda
216
219
  <CopyTextBox
217
- v-bind="chip"
220
+ :text="chip.text"
218
221
  class="mr-1 "
219
222
  />
220
-
223
+ b
221
224
  </template>
222
225
  </div>
223
226
  </template>
@@ -225,11 +228,20 @@
225
228
  </div>
226
229
  <!-- Chip Display -->
227
230
 
231
+ <div
232
+ v-else-if="header.displayType == 'copyButton'"
233
+ :class="['align-middle leading-0', getColumnFont(lang)]"
234
+ >
235
+
236
+ <CopyTextBox
237
+ :text="getNestedValue(item, header.value)"
238
+ class="mr-1 "
239
+ />
240
+ </div>
228
241
  <div
229
242
  v-else-if="header.displayType == 'chip' "
230
243
  :class="['align-middle leading-0', getColumnFont(lang)]"
231
244
  >
232
- <!-- <pre style="font-size:10px">{{ JSON.stringify(getNestedValue(item, header.value)) }}</pre> -->
233
245
  <Chip
234
246
  size="small"
235
247
  v-if="header?.chip && header?.chip[getNestedValue(item, header.value)]"
@@ -295,18 +307,9 @@
295
307
  v-bind="header.docButton"
296
308
  @view-file="emit('viewpdf', $event)"
297
309
  />
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))" -->
306
310
  </div>
307
311
 
308
312
  <!-- Default Text Display -->
309
- <!-- <bdi class="flex items-center"></bdi> -->
310
313
  <bdi
311
314
  v-else :class="[
312
315
  'text-sm',
@@ -316,8 +319,7 @@
316
319
  ]"
317
320
  >
318
321
  <span
319
- :class="['w-full', header.width ? 'truncate' : 'truncate whitespace-nowrap',]"
320
- :style="{ maxWidth: header.width || 'auto', }"
322
+ class="block truncate"
321
323
  >
322
324
  {{ formatCellValue(getNestedValue(item, header.value), header) }}
323
325
  </span>
@@ -340,6 +342,101 @@
340
342
  </tr>
341
343
  </tbody>
342
344
  </table>
345
+
346
+ <!-- Mobile Card View -->
347
+ <div class="md:hidden" :dir="dir">
348
+ <!-- No Data State -->
349
+ <div v-if="!items || items.length === 0" class="px-6 py-12 text-center">
350
+ <div class="flex flex-col items-center">
351
+ <svg class="w-12 h-12 text-gray-300 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
352
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
353
+ </svg>
354
+ <span class="text-sm text-gray-500">{{ noDataText }}</span>
355
+ </div>
356
+ </div>
357
+
358
+ <!-- Card Items -->
359
+ <div
360
+ v-else
361
+ v-for="(item, index) in items"
362
+ :key="'card-' + getItemKey(item, index)"
363
+ :class="[
364
+ 'border-b border-input-border p-4 transition-colors cursor-pointer',
365
+ rowHover,
366
+ selectedItems.includes(getItemKey(item, index)) ? rowSelected : ''
367
+ ]"
368
+ @click="handleRowClick(item, index)"
369
+ >
370
+ <!-- Selection checkbox for mobile -->
371
+ <div v-if="selectable" class="flex items-center mb-3">
372
+ <input
373
+ type="checkbox"
374
+ :checked="selectedItems.includes(getItemKey(item, index))"
375
+ 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"
376
+ @click.stop
377
+ @change="toggleItemSelection(item, index)"
378
+ >
379
+ </div>
380
+
381
+ <!-- Each header-value pair as a row -->
382
+ <div
383
+ v-for="header in headers"
384
+ :key="'card-field-' + header.value"
385
+ class="flex justify-between items-start py-1.5 gap-4"
386
+ @click.stop
387
+ >
388
+ <!-- Label -->
389
+ <span :class="['text-xs font-medium shrink-0 w-2/5', headerText, getColumnFont(lang)]">
390
+ {{ header.title }}
391
+ </span>
392
+ <!-- Value -->
393
+ <div class="flex-1 min-w-0 text-end">
394
+ <slot
395
+ :name="`item.${header.value}`"
396
+ :item="item"
397
+ :value="getNestedValue(item, header.value)"
398
+ :header="header"
399
+ :index="index"
400
+ >
401
+ <div v-if="header.displayType == 'html'" :class="['text-sm', cellText]" v-html="getCustomColumnData(header, item)" />
402
+ <div v-else-if="header.displayType == 'custom'" :class="['text-sm', cellText]">
403
+ <template v-if="getCustomColumnData(header, item)?.type === 'CopyTextBox'">
404
+ <div class="flex flex-wrap items-center gap-1 justify-end">
405
+ <CopyTextBox v-for="(chip, idx) in getCustomColumnData(header, item).CopyTextBox" :key="idx" v-bind="chip" />
406
+ </div>
407
+ </template>
408
+ <div v-else v-html="getCustomColumnData(header, item)" />
409
+ </div>
410
+ <div v-else-if="header.displayType == 'copyButton'" :class="['align-middle', getColumnFont(lang)]">
411
+ <CopyTextBox :text="getNestedValue(item, header.value)" />
412
+ </div>
413
+ <div v-else-if="header.displayType == 'chip'" :class="['align-middle', getColumnFont(lang)]">
414
+ <Chip size="small" v-if="header?.chip && header?.chip[getNestedValue(item, header.value)]" v-bind="header.chip[getNestedValue(item, header.value)]" />
415
+ <span v-else class="text-xs text-gray-400">{{ getNestedValue(item, header.value) || '-' }}</span>
416
+ </div>
417
+ <div v-else-if="header.displayType == 'checkbox'" :class="['align-middle', getColumnFont(header.lang)]">
418
+ <Checkbox :model-value="!!getNestedValue(item, header.value)" v-bind="header.displayProps" :disabled="!editMode || !header.inlineEditable" @update:model-value="(value) => handleCellUpdate(item, header.value, value, index)" />
419
+ </div>
420
+ <div v-else-if="header.displayType == 'docButton'" :class="['align-middle', getColumnFont(header.lang)]">
421
+ <FileDisplay :item="getNestedValue(item, header.value)" v-bind="header.docButton" @view-file="emit('viewpdf', $event)" />
422
+ </div>
423
+ <bdi v-else :class="['text-sm', cellText, header.displayType == 'englishText' || header.displayType == 'date' ? 'eng-font' : getColumnFont(lang)]">
424
+ {{ formatCellValue(getNestedValue(item, header.value), header) }}
425
+ </bdi>
426
+ </slot>
427
+ </div>
428
+ </div>
429
+
430
+ <!-- Actions for mobile -->
431
+ <div v-if="$slots['item-actions'] || showActions" class="flex items-center justify-end space-x-2 mt-3 pt-2 border-t border-input-border">
432
+ <slot name="item-actions" :item="item" :index="index">
433
+ <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="" />
434
+ <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="" />
435
+ <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="" />
436
+ </slot>
437
+ </div>
438
+ </div>
439
+ </div>
343
440
  </div>
344
441
 
345
442
  <!-- Pagination Component -->