@usssa/component-library 1.0.0-alpha.13 → 1.0.0-alpha.131

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 (55) hide show
  1. package/README.md +6 -3
  2. package/package.json +23 -5
  3. package/src/assets/files.png +0 -0
  4. package/src/assets/logo.svg +19 -0
  5. package/src/assets/no-result.png +0 -0
  6. package/src/assets/quasar-logo-vertical.svg +15 -0
  7. package/src/components/core/UAvatar.vue +56 -24
  8. package/src/components/core/UAvatarGroup.vue +62 -52
  9. package/src/components/core/UBadgeStd.vue +6 -1
  10. package/src/components/core/UBannerStd.vue +100 -33
  11. package/src/components/core/UBreadCrumbs.vue +93 -0
  12. package/src/components/core/UBtnIcon.vue +60 -48
  13. package/src/components/core/UBtnStd.vue +54 -42
  14. package/src/components/core/UBtnToggle.vue +11 -6
  15. package/src/components/core/UCheckboxStd.vue +26 -20
  16. package/src/components/core/UChip.vue +56 -27
  17. package/src/components/core/UDate.vue +574 -0
  18. package/src/components/core/UDialogStd.vue +87 -40
  19. package/src/components/core/UDrawer.vue +240 -0
  20. package/src/components/core/UInnerLoader.vue +67 -0
  21. package/src/components/core/UInputAddressLookup.vue +471 -0
  22. package/src/components/core/UInputPhoneStd.vue +73 -68
  23. package/src/components/core/UInputTextStd.vue +132 -108
  24. package/src/components/core/UInputTypeaheadAdvanceSearch.vue +64 -0
  25. package/src/components/core/UMenuButtonStd.vue +280 -0
  26. package/src/components/core/UMenuDropdown.vue +80 -0
  27. package/src/components/core/UMenuDropdownAdvancedSearch.vue +288 -0
  28. package/src/components/core/UMenuItem.vue +150 -0
  29. package/src/components/core/UMenuSearch.vue +831 -0
  30. package/src/components/core/UMultiSelectStd.vue +73 -57
  31. package/src/components/core/UPagination.vue +46 -24
  32. package/src/components/core/URadioBtn.vue +32 -22
  33. package/src/components/core/URadioStd.vue +19 -12
  34. package/src/components/core/USelectStd.vue +125 -80
  35. package/src/components/core/USheet.vue +338 -0
  36. package/src/components/core/UTabBtnStd.vue +87 -59
  37. package/src/components/core/UTable/UTable.vue +75 -41
  38. package/src/components/core/UTableStd.vue +701 -281
  39. package/src/components/core/UTabsStd.vue +33 -16
  40. package/src/components/core/UToggleStd.vue +43 -29
  41. package/src/components/core/UToolbar/UCustomMenuIcon.vue +60 -0
  42. package/src/components/core/UToolbar/UToolbar.vue +206 -0
  43. package/src/components/core/UTooltip.vue +31 -10
  44. package/src/components/core/UUploader.vue +508 -0
  45. package/src/components/index.js +59 -21
  46. package/src/composables/useNotify.js +16 -16
  47. package/src/composables/useOverlayLoader.js +23 -0
  48. package/src/composables/useScreenType.js +30 -0
  49. package/src/css/app.sass +168 -0
  50. package/src/css/colors.sass +101 -0
  51. package/src/css/media.sass +1 -0
  52. package/src/css/quasar.variables.sass +121 -0
  53. package/src/css/typography.sass +0 -0
  54. package/src/css/vars/colors.variables.sass +127 -0
  55. package/src/utils/data.ts +146 -0
@@ -1,86 +1,108 @@
1
1
  <script setup>
2
- import UTd from './UTable/UTd.vue'
3
- import UTh from './UTable/UTh.vue'
4
- import UTr from './UTable/UTr.vue'
5
- import UTable from './UTable/UTable.vue'
6
2
  import { computed, ref } from 'vue'
7
- import UCheckboxStd from './UCheckboxStd.vue'
8
- import UChip from './UChip.vue'
9
3
  import UAvatar from './UAvatar.vue'
4
+ import UBtnIcon from './UBtnIcon.vue'
10
5
  import UBtnStd from './UBtnStd.vue'
11
- import UTooltip from './UTooltip.vue'
6
+ import UCheckboxStd from './UCheckboxStd.vue'
7
+ import UChip from './UChip.vue'
8
+ import UMenuDropdown from './UMenuDropdown.vue'
12
9
  import UPagination from './UPagination.vue'
10
+ import UTable from './UTable/UTable.vue'
11
+ import UTd from './UTable/UTd.vue'
12
+ import UTh from './UTable/UTh.vue'
13
+ import UTr from './UTable/UTr.vue'
14
+ import UTooltip from './UTooltip.vue'
15
+
16
+ const emit = defineEmits(['onCustomSort'])
17
+ const columns = defineModel('columns', {
18
+ default: () => [],
19
+ type: Array,
20
+ })
21
+ const filteredRows = defineModel('filteredRows', { default: [], type: Array })
22
+ const loading = defineModel('loading', {
23
+ default: () => {},
24
+ type: Boolean,
25
+ })
26
+ const moreActionDialogData = defineModel('moreActionDialogData', {
27
+ type: Object,
28
+ default: null,
29
+ })
30
+ const pagination = defineModel('pagination', {
31
+ default: { page: 1, rowsPerPage: 5 },
32
+ type: Object,
33
+ })
34
+ const rows = defineModel('rows', {
35
+ default: () => [],
36
+ type: Array,
37
+ })
38
+ const selectedRows = defineModel('selectedRows', {
39
+ default: () => [],
40
+ type: Array,
41
+ })
42
+
13
43
  const props = defineProps({
14
- title: {
44
+ bordered: {
45
+ type: Boolean,
46
+ default: false,
47
+ },
48
+ customClass: {
15
49
  type: String,
16
50
  default: '',
17
51
  },
18
- tableRowHover: {
19
- type: Boolean,
20
- default: false,
52
+ dataTestId: {
53
+ type: String,
54
+ default: 'table-std',
21
55
  },
22
- separator: {
56
+ filter: {
23
57
  type: String,
24
- default: 'horizontal',
58
+ default: '',
25
59
  },
26
- multiSelection: {
60
+ flat: {
27
61
  type: Boolean,
28
62
  default: false,
29
63
  },
30
- flat: {
64
+ grid: {
31
65
  type: Boolean,
32
66
  default: false,
33
67
  },
34
- bordered: {
68
+ isCustomSort: {
35
69
  type: Boolean,
36
70
  default: false,
37
71
  },
38
- grid: {
72
+ multiSelection: {
39
73
  type: Boolean,
40
74
  default: false,
41
75
  },
42
- virtualScroll: {
76
+ separator: {
77
+ type: String,
78
+ default: 'horizontal',
79
+ },
80
+ showPagination: {
43
81
  type: Boolean,
44
- default: false,
82
+ default: true,
45
83
  },
46
84
  stickyHeader: {
47
85
  type: Boolean,
48
86
  default: false,
49
87
  },
50
- isCustomSort: {
88
+ tableRowHover: {
51
89
  type: Boolean,
52
90
  default: false,
53
91
  },
54
- customClass: {
92
+ title: {
55
93
  type: String,
56
94
  default: '',
57
95
  },
58
- maxPageLink: {
59
- type: Number,
60
- default: 3,
96
+ verticalMoreActions: {
97
+ type: Boolean,
98
+ default: false,
99
+ },
100
+ virtualScroll: {
101
+ type: Boolean,
102
+ default: false,
61
103
  },
62
104
  })
63
105
 
64
- const selectedRows = defineModel('selectedRows', {
65
- default: () => [],
66
- type: Array,
67
- })
68
- const rows = defineModel('rows', {
69
- default: () => [],
70
- type: Array,
71
- })
72
- const columns = defineModel('columns', {
73
- default: () => [],
74
- type: Array,
75
- })
76
- const pagination = defineModel('pagination', {
77
- default: () => {},
78
- type: Object,
79
- })
80
- const loading = defineModel('loading', {
81
- default: () => {},
82
- type: Boolean,
83
- })
84
106
  const customLoading = ref(false)
85
107
  const rowsPerPageOptions = ref([
86
108
  { label: '5 / per page', value: 5 },
@@ -89,20 +111,9 @@ const rowsPerPageOptions = ref([
89
111
  { label: '20 / per page', value: 20 },
90
112
  { label: '25 / per page', value: 25 },
91
113
  ])
92
-
93
114
  const tableDataChip = ref(true) // this is required to show chip
115
+ const tailClass = ref(null)
94
116
 
95
- //adding a new row to selectedRows
96
- const handleToSelectRow = (row) => {
97
- if (row) {
98
- const index = selectedRows.value.findIndex((item) => item._id === row._id)
99
- if (index === -1) {
100
- selectedRows.value.push(row)
101
- } else {
102
- selectedRows.value.splice(index, 1)
103
- }
104
- }
105
- }
106
117
  // if virtual scroll is enbaled then adding large rows per page to view in virtual scroll
107
118
  const getRowsPerPageOptions = computed(() => {
108
119
  if (props.virtualScroll) {
@@ -117,6 +128,49 @@ const getRowsPerPageOptions = computed(() => {
117
128
  }
118
129
  })
119
130
 
131
+ // chekcing the menu position after show
132
+ const checkMenuPosition = (id) => {
133
+ const menuElement = document.getElementById(`actionPopupRef-${id}`) // Access the menu by ID
134
+ const buttonElement = document.getElementById(`actionPopupRefBtn-${id}`) // Access the button element
135
+
136
+ if (menuElement && buttonElement) {
137
+ const menuRect = menuElement.getBoundingClientRect() // Menu position
138
+ const buttonRect = buttonElement.getBoundingClientRect() // Button position
139
+
140
+ // Determine if the menu opens above or below
141
+ if (menuRect.top < buttonRect.top) {
142
+ tailClass.value = 'tail-bottom' // Menu opens below, tail at the bottom
143
+ } else {
144
+ tailClass.value = 'tail-top' // Menu opens above, tail at the top
145
+ }
146
+ }
147
+ }
148
+
149
+ // sorting funtion to handle text and number type of data
150
+ const dataSort = (data, key, order, type) => {
151
+ if (type === 'text') {
152
+ return data.sort((a, b) =>
153
+ order === 'asc'
154
+ ? a[key].localeCompare(b[key])
155
+ : b[key].localeCompare(a[key])
156
+ )
157
+ } else {
158
+ return data.sort((a, b) =>
159
+ order === 'asc' ? a[key] - b[key] : b[key] - a[key]
160
+ )
161
+ }
162
+ }
163
+
164
+ //getting the chip color accroding to value of chip from row
165
+ const getChipColor = (data, value) => {
166
+ const foundObject = data.find((chip) => chip.value === value)
167
+ if (foundObject) {
168
+ return foundObject['color']
169
+ } else {
170
+ return 'neutral-3'
171
+ }
172
+ }
173
+
120
174
  //getting the sorted icon according the order
121
175
  const getSortingIcon = (col) => {
122
176
  if (col) {
@@ -128,59 +182,19 @@ const getSortingIcon = (col) => {
128
182
  }
129
183
  }
130
184
 
131
- // it is giving the selected row and setting the new selection
132
- const isRowSelected = (row) => {
133
- if (row) {
134
- const index = selectedRows.value.findIndex((item) => item._id === row._id)
135
- return computed({
136
- get: () => {
137
- return index === -1 ? false : true
138
- },
139
- set: () => {
140
- if (index === -1) {
141
- selectedRows.value.push(row)
142
- } else {
143
- selectedRows.value.splice(index, 1)
144
- }
145
- },
146
- })
147
- } else {
148
- return computed({
149
- get: () => {
150
- let dataLength = props.virtualScroll
151
- ? pagination.value.rowsPerPage
152
- : props.rows.length
153
- return selectedRows.value.length === dataLength
154
- ? true
155
- : selectedRows.value.length === 0
156
- ? false
157
- : null
158
- },
159
- set: (value) => {
160
- if (value !== null) {
161
- selectedRows.value.splice(0, selectedRows.value.length)
162
- } else {
163
- if (props.virtualScroll) {
164
- handleSelectAllData().then((res) => {
165
- if (res === 200) {
166
- customLoading.value = false
167
- }
168
- })
169
- } else {
170
- props.rows.forEach((element) => {
171
- if (
172
- selectedRows.value.findIndex(
173
- (item) => item._id === element._id
174
- ) === -1
175
- ) {
176
- selectedRows.value.push(element)
177
- }
178
- })
179
- }
180
- }
181
- },
182
- })
183
- }
185
+ const handleActionColClick = (e) => {
186
+ e.preventDefault()
187
+ e.stopPropagation()
188
+ }
189
+
190
+ //if user want to add custom sort on data
191
+ const handleCustomSort = (key, sortOrder, type) => {
192
+ emit('onCustomSort', key, sortOrder, type)
193
+ }
194
+
195
+ const handleMenuEventStop = (e) => {
196
+ e.preventDefault()
197
+ e.stopPropagation()
184
198
  }
185
199
 
186
200
  // handling the large selection data in chunks
@@ -189,7 +203,7 @@ const handleSelectAllData = () => {
189
203
  return new Promise((resolve, reject) => {
190
204
  let index = 0
191
205
  function processChunk() {
192
- if (index >= pagination.value.rowsPerPage) {
206
+ if (index >= rows.value.length) {
193
207
  resolve(200)
194
208
  return
195
209
  }
@@ -213,23 +227,6 @@ const handleSelectAllData = () => {
213
227
  })
214
228
  }
215
229
 
216
- const emit = defineEmits(['onCustomSort'])
217
-
218
- // sorting funtion to handle text and number type of data
219
- const dataSort = (data, key, order, type) => {
220
- if (type === 'text') {
221
- return data.sort((a, b) =>
222
- order === 'asc'
223
- ? a[key].localeCompare(b[key])
224
- : b[key].localeCompare(a[key])
225
- )
226
- } else {
227
- return data.sort((a, b) =>
228
- order === 'asc' ? a[key] - b[key] : b[key] - a[key]
229
- )
230
- }
231
- }
232
-
233
230
  //it is sorting the data according to type like text or number type of data
234
231
  const handleSort = (key, sortOrder, type) => {
235
232
  rows.value = dataSort(rows.value, key, sortOrder, type)
@@ -240,22 +237,64 @@ const handleSort = (key, sortOrder, type) => {
240
237
  })
241
238
  }
242
239
 
243
- //if user want to add custom sort on data
244
- const handleCustomSort = (key, sortOrder, type) => {
245
- emit('onCustomSort', key, sortOrder, type)
240
+ //adding a new row to selectedRows
241
+ const handleToSelectRow = (row) => {
242
+ if (row) {
243
+ const index = selectedRows.value.findIndex((item) => item._id === row._id)
244
+ if (index === -1) {
245
+ selectedRows.value.push(row)
246
+ } else {
247
+ selectedRows.value.splice(index, 1)
248
+ }
249
+ }
246
250
  }
247
251
 
248
- //adding a new row to selectedRows
249
- const onRowClick = (event, row) => {
250
- if (props.multiSelection) {
251
- event.stopPropagation()
252
- handleToSelectRow(row)
252
+ // it is giving the selected row and setting the new selection
253
+ const isRowSelected = (row) => {
254
+ if (row) {
255
+ const index = selectedRows.value.findIndex((item) => item._id === row._id)
256
+ return computed({
257
+ get: () => {
258
+ return index === -1 ? false : true
259
+ },
260
+ set: () => {
261
+ if (index === -1) {
262
+ selectedRows.value.push(row)
263
+ } else {
264
+ selectedRows.value.splice(index, 1)
265
+ }
266
+ },
267
+ })
268
+ } else {
269
+ return computed({
270
+ get: () => {
271
+ let dataLength = props.rows.length
272
+ return selectedRows.value.length === dataLength
273
+ ? true
274
+ : selectedRows.value.length === 0
275
+ ? false
276
+ : null
277
+ },
278
+ set: (value) => {
279
+ if (value !== null) {
280
+ selectedRows.value.splice(0, selectedRows.value.length)
281
+ } else {
282
+ handleSelectAllData().then((res) => {
283
+ if (res === 200) {
284
+ customLoading.value = false
285
+ }
286
+ })
287
+ }
288
+ },
289
+ })
253
290
  }
254
291
  }
255
292
 
256
- //getting the chip color accroding to value of chip from row
257
- const getChipColor = (data, value) => {
258
- return data.find((chip) => chip.value === value)['color']
293
+ // removing console error for show dialog
294
+ const onMoreActionButtonClick = (id) => {
295
+ if (moreActionDialogData.value) {
296
+ moreActionDialogData.value.showDialog[id] = false
297
+ }
259
298
  }
260
299
 
261
300
  // handle to change the page, and if virtual scroll is enabled and user scrolled to bottom it will take user to the top
@@ -263,149 +302,189 @@ const onPageChange = (value) => {
263
302
  pagination.value.page = value
264
303
  }
265
304
 
305
+ //adding a new row to selectedRows
306
+ const onRowClick = (event, row) => {
307
+ if (props.multiSelection) {
308
+ event.stopPropagation()
309
+ handleToSelectRow(row)
310
+ }
311
+ }
312
+
266
313
  //handle to change the rows per page
267
314
  const onRowPerPageChange = (value) => {
268
315
  pagination.value.rowsPerPage = value
269
316
  }
317
+
318
+ const updatedRows = computed(() => {
319
+ if (props.filter.length && filteredRows.value?.length)
320
+ return filteredRows.value
321
+ else if (props.filter.length) return []
322
+ return rows.value
323
+ })
270
324
  </script>
271
325
 
272
326
  <template>
273
327
  <UTable
274
- :title="title"
275
- v-model:rows="rows"
276
328
  v-model:columns="columns"
277
- :separator="separator"
278
- :loading="loading"
279
- :flat="flat"
280
- :bordered="bordered"
329
+ v-model:filteredRows="filteredRows"
281
330
  v-model:pagination="pagination"
331
+ v-model:rows="rows"
332
+ :class="customClass"
333
+ :bordered="bordered"
334
+ :dataTestId="dataTestId"
335
+ :filter="filter"
336
+ :flat="flat"
282
337
  :grid="grid"
283
- :virtualScroll="virtualScroll"
338
+ :loading="loading"
339
+ :separator="separator"
340
+ :showPagination="showPagination"
284
341
  :stickyHeader="stickyHeader"
285
- :class="customClass"
342
+ :title="title"
343
+ :virtualScroll="virtualScroll"
286
344
  >
287
345
  <!-- custom header slot to add customized header -->
288
346
  <template v-slot:header="props">
289
347
  <UTr :props="props" :tableRowHover="tableRowHover">
290
348
  <UTh
291
349
  v-if="multiSelection"
350
+ :separator="separator"
292
351
  style="width: 3% !important"
293
- :tableHeaderAutoWidth="false"
294
352
  tableHeadAlignment="left"
295
- :separator="separator"
353
+ :tableHeaderAutoWidth="false"
296
354
  >
297
355
  <UCheckboxStd
298
- :id="`u-checkbox-table-header`"
299
356
  v-model="isRowSelected(null).value"
300
- name="Table Header"
357
+ id="u-checkbox-table-header"
301
358
  :indeterminate="true"
359
+ name="Table Header"
302
360
  />
303
361
  </UTh>
304
- <UTh
305
- v-for="(col, key) in props.cols"
306
- :class="`${col.sortable ? 'cursor-pointer' : ''} ${
307
- col.headerClasses
308
- }`"
309
- :key="key"
310
- :tableHeaderAutoWidth="col.autoWidth"
311
- :tableHeadAlignment="col.field === 'action' ? col.align : col.align"
312
- @click="
313
- col.sortable
314
- ? isCustomSort
315
- ? handleCustomSort(col.field, col.sortOrder, col.type)
316
- : handleSort(col.field, col.sortOrder, col.type)
317
- : null
318
- "
319
- :separator="separator"
320
- :style="col.headerStyle"
321
- >
322
- {{ col.label }}
323
- <span v-if="col.sortable && col.field !== 'action'">
324
- <q-btn label="Sorting Button" :ripple="false" class="u-sorting-btn">
325
- <q-icon
326
- size="xs"
327
- :class="`${getSortingIcon(col)} sorting-icon`"
328
- :aria-label="col.label"
329
- alt="sort-desc"
330
- tabindex="0"
331
- ></q-icon>
332
- </q-btn>
333
- </span>
334
- </UTh>
362
+ <template v-for="(col, key) in props.cols">
363
+ <UTh
364
+ v-if="typeof col.show === 'undefined' || col.show"
365
+ :class="`${col.sortable ? 'cursor-pointer' : ''} ${
366
+ col.headerClasses
367
+ }`"
368
+ :key="key"
369
+ :separator="separator"
370
+ :style="col.headerStyle"
371
+ :tableHeadAlignment="col.field === 'action' ? col.align : col.align"
372
+ :tableHeaderAutoWidth="col.autoWidth"
373
+ @click="
374
+ col.sortable
375
+ ? isCustomSort
376
+ ? handleCustomSort(col.field, col.sortOrder, col.type)
377
+ : handleSort(col.field, col.sortOrder, col.type)
378
+ : null
379
+ "
380
+ >
381
+ <span
382
+ :class="`${col.field === 'action' ? 'hidden-header-label' : ''}`"
383
+ >
384
+ {{ col.field === 'action' ? 'Action' : col.label }}
385
+ </span>
386
+
387
+ <span v-if="col.sortable && col.field !== 'action'">
388
+ <UBtnIcon
389
+ :class="`more-action-icon cursor-pointer`"
390
+ :iconClass="`${getSortingIcon(col)}`"
391
+ :aria-label="`Sort ${col.label}`"
392
+ ref="btn-icon"
393
+ size="sm"
394
+ />
395
+ </span>
396
+ </UTh>
397
+ </template>
335
398
  </UTr>
336
399
  </template>
337
400
  <!-- custom body slots to add customized cell data -->
338
401
  <template v-slot:body="props">
339
402
  <UTr
403
+ :class="`${isRowSelected(props.row).value ? 'selected-data-row' : ''}`"
340
404
  :props="props"
341
- @click="onRowClick($event, props.row)"
342
405
  :tableRowHover="tableRowHover"
343
- :class="`${isRowSelected(props.row).value ? 'selected-data-row' : ''}`"
406
+ @click="onRowClick($event, props.row)"
344
407
  >
345
408
  <UTd
346
409
  v-if="multiSelection"
347
410
  :index="-1"
411
+ :separator="separator"
348
412
  tableDataAlignment="left"
349
413
  :tableHeaderAutoWidth="false"
350
- :separator="separator"
351
414
  >
352
415
  <UCheckboxStd
353
- :id="`u-checkbox-${props.row._id}`"
354
416
  v-model="isRowSelected(props.row).value"
417
+ :id="`u-checkbox-${props.row._id}`"
355
418
  :name="props.row._id"
356
419
  />
357
420
  </UTd>
421
+
358
422
  <template v-for="(col, index) in props.cols" :key="index">
359
423
  <!-- to show the cell data without the action cell -->
360
424
  <UTd
361
- v-if="col.field !== 'action'"
362
- :row="props.row"
425
+ v-if="
426
+ col.field !== 'action' &&
427
+ (typeof col.show === 'undefined' || col.show)
428
+ "
429
+ :class="col.classes"
363
430
  :col="col"
364
431
  :index="index"
365
- :tableDataAlignment="col.align"
366
- :tableDataAutoWidth="col.autoWidth"
432
+ :row="props.row"
367
433
  :separator="separator"
368
434
  :style="col.style"
369
- :class="col.classes"
435
+ :tableDataAlignment="col.align"
436
+ :tableDataAutoWidth="col.autoWidth"
370
437
  >
371
438
  <!-- to show the chips with different variant -->
372
439
  <template v-if="col.chipValues && col.chipValues.length > 0">
373
440
  <UChip
374
441
  v-model="tableDataChip"
375
- :type="getChipColor(col.chipValues, props.row[col.field])"
442
+ class="u-table-chip"
443
+ :anchor="col.anchor"
376
444
  avatarLabel=""
377
445
  :chipLabel="props.row[col.field].toString()"
446
+ :dense="col.denseChip"
447
+ :is-show-tooltip="col.showChipTooltip"
448
+ :offset="col.offset"
378
449
  :removable="false"
379
- class="u-table-chip"
450
+ :type="getChipColor(col.chipValues, props.row[col.field])"
380
451
  />
381
452
  </template>
382
453
  <!-- to show the avatar of user image with name and other details -->
383
454
  <template v-else-if="col.avatarKey">
384
455
  <div class="flex justify-start items-center">
385
456
  <div
386
- :class="`${
387
- !multiSelection
388
- ? 'table-data-avatar-right'
389
- : 'table-data-avatar-left'
390
- }`"
457
+ v-if="
458
+ props.row[col.avatarKey] &&
459
+ typeof props.row[col.avatarKey] === 'object'
460
+ "
461
+ class="table-data-avatar"
391
462
  >
392
463
  <UAvatar
464
+ v-if="props.row[col.avatarKey]?.type === 'initials'"
465
+ :name="`${props.row[col.avatarKey]?.value}`"
466
+ size="md"
467
+ />
468
+ <UAvatar
469
+ v-else-if="props.row[col.avatarKey]?.type === 'image'"
470
+ :image="`${props.row[col.avatarKey]?.value}`"
393
471
  :name="
394
- props.row[col.avatarKey]
395
- ? props.row[col.avatarKey]
396
- : 'Need to get initials of Name, replace with initial common function'
472
+ props.row[col.avatarKey]?.name ??
473
+ props.row[col.avatarKey]?.value
397
474
  "
398
475
  size="md"
399
- :image="`${
400
- props.row[col.avatarKey]
401
- ? props.row[col.avatarKey]
402
- : 'https://cdn.quasar.dev/img/avatar1.jpg'
403
- }`"
476
+ />
477
+ </div>
478
+ <div v-else class="table-data-avatar">
479
+ <UAvatar
480
+ :image="`${props.row[col.avatarKey]}`"
481
+ :name="`${props.row[col.avatarKey]}`"
482
+ size="md"
404
483
  />
405
484
  </div>
406
485
  <div class="td-grid-content">
407
486
  <div>{{ props.row[col.field] }}</div>
408
- <div class="td-caption text-body-xs" v-if="col.captionKey">
487
+ <div v-if="col.captionKey" class="td-caption text-body-xs">
409
488
  {{ props.row[col.captionKey] }}
410
489
  </div>
411
490
  </div>
@@ -413,76 +492,290 @@ const onRowPerPageChange = (value) => {
413
492
  </template>
414
493
  <!-- to show other cell data -->
415
494
  <template v-else>
416
- <div>
417
- <div>{{ props.row[col.field] }}</div>
418
- <div class="td-caption text-body-xs" v-if="col.captionKey">
495
+ <div class="td-grid-content">
496
+ <div v-if="col.type !== 'icon'">
497
+ {{ props.row[col.field] }}
498
+ </div>
499
+ <div v-else-if="col.type === 'icon'">
500
+ <template v-if="props.row[col.field]">
501
+ <q-icon
502
+ :class="props.row[col.field]"
503
+ :alt="props.row.ariaLabel"
504
+ :aria-label="props.row.ariaLabel"
505
+ :color="props?.row?.iconColor ?? 'primary'"
506
+ size="1.5rem"
507
+ />
508
+ </template>
509
+ </div>
510
+ <div
511
+ v-if="col.captionKey && col.type !== 'icon'"
512
+ class="td-caption text-body-xs"
513
+ >
419
514
  {{ props.row[col.captionKey] }}
420
515
  </div>
421
516
  </div>
422
517
  </template>
423
518
  </UTd>
424
519
  <!-- to the action cell, it can have single and multiple -->
520
+
425
521
  <UTd
426
- v-else
522
+ v-else-if="typeof col.show === 'undefined' || col.show"
523
+ :class="col.classes"
427
524
  :index="index"
428
- :tableDataAlignment="col.align"
429
- :tableDataAutoWidth="false"
430
- style="width: 3%"
431
525
  :separator="separator"
432
526
  :style="col.style"
433
- :class="col.classes"
527
+ style="width: 3%"
528
+ :tableDataAlignment="col.align"
529
+ :tableDataAutoWidth="false"
530
+ @click="handleActionColClick"
434
531
  >
435
532
  <template v-if="col.actions && col.actions.length === 1">
436
- <UBtnStd
437
- v-for="(action, key) in col.actions"
438
- :key="key"
439
- :size="action.size"
440
- :flat="action.flat"
441
- :label="action.label"
442
- :color="action.color"
443
- :leftIcon="action.icon"
444
- @onClick="action.handler(props.row)"
445
- />
533
+ <template v-for="(action, key) in col.actions" :key="key">
534
+ <UBtnStd
535
+ v-if="
536
+ typeof action.hide === 'function'
537
+ ? !action.hide(props.row)
538
+ : true
539
+ "
540
+ :color="
541
+ typeof action.color === 'function'
542
+ ? action.color(props.row)
543
+ : action.color
544
+ "
545
+ :disable="
546
+ typeof action.disable === 'function' &&
547
+ action.disable(props.row)
548
+ "
549
+ :flat="
550
+ typeof action.flat === 'function'
551
+ ? action.flat(props.row)
552
+ : action.flat
553
+ "
554
+ :key="key"
555
+ :label="
556
+ typeof action.label === 'function'
557
+ ? action.label(props.row)
558
+ : action.label
559
+ "
560
+ :leftIcon="
561
+ typeof action.icon === 'function'
562
+ ? action.icon(props.row)
563
+ : action.icon
564
+ "
565
+ :loading="
566
+ typeof action.loading === 'function'
567
+ ? action.loading(props.row)
568
+ : action.loading
569
+ "
570
+ :outline="
571
+ typeof action.outline === 'function'
572
+ ? action.outline(props.row)
573
+ : action.outline
574
+ "
575
+ :size="action.size"
576
+ @on-click="action.handler(props.row)"
577
+ >
578
+ <template #tooltip>
579
+ <UTooltip
580
+ v-if="
581
+ typeof action.tooltip === 'function'
582
+ ? action.tooltip(props.row)
583
+ : action.tooltip
584
+ "
585
+ :anchor="action.anchor"
586
+ :description="
587
+ typeof action.tooltip === 'function'
588
+ ? action.tooltip(props.row)
589
+ : action.tooltip
590
+ "
591
+ :offset="action.offset ? action.offset : [10, 40]"
592
+ :self="action.anchor"
593
+ />
594
+ </template>
595
+ </UBtnStd>
596
+ </template>
446
597
  </template>
447
598
  <!-- to show the actions list if the actions are multiple -->
448
599
  <template v-else>
449
- <q-icon
450
- size="xs"
451
- class="fa-kit fa-ellipsis-vertical action-icon cursor-pointer"
452
- aria-label="more-action-icon"
453
- alt="action-icon-more"
454
- tabindex="0"
600
+ <q-menu
601
+ v-if="
602
+ moreActionDialogData &&
603
+ moreActionDialogData.showDialog[props.row.id]
604
+ "
605
+ v-model="moreActionDialogData.showDialog[props.row.id]"
606
+ :class="`more-action-popup q-px-ba q-py-ba`"
607
+ anchor="top left"
608
+ :cover="false"
609
+ :fit="true"
610
+ :id="`actionPopupRef-${props.row.id}`"
611
+ :offset="[85, 0]"
612
+ role="list"
613
+ self="bottom middle"
614
+ transition-hide="scale"
615
+ transition-show="scale"
616
+ @show="checkMenuPosition(props.row.id)"
455
617
  >
456
- <UTooltip description="More Actions" />
457
- <q-menu
458
- transition-show="slide-left"
459
- transition-hide="slide-right"
460
- anchor="center start"
461
- self="center left"
462
- >
463
- <div
464
- class="flex justify-end items-center more-action-wrapper"
465
- >
466
- <template v-for="(action, key) in col.actions" :key="key">
467
- <UTooltip
468
- :target="`#more-action-${key}`"
469
- :description="action.tooltip"
470
- />
471
- <q-icon
472
- :size="action.size"
473
- :id="`more-action-${key}`"
474
- :class="`more-action-icon ${
475
- action.icon
476
- } cursor-pointer ${key > 0 ? 'q-ml-sm' : ''}`"
477
- :aria-label="action.label"
478
- :alt="`${action.label}-key`"
479
- @click="action.handler(props.row)"
480
- tabindex="0"
481
- />
482
- </template>
483
- </div>
484
- </q-menu>
485
- </q-icon>
618
+ <div :class="tailClass"></div>
619
+ <q-card class="more-action-popup-wrapper">
620
+ <q-card-section>
621
+ <div class="content-wrapper text-center">
622
+ <div class="q-pb-ba flex justify-center items-center">
623
+ <div
624
+ :class="`remove-icon-wrapper ${
625
+ moreActionDialogData.row.iconColor === 'accent'
626
+ ? 'icon-bg-accent'
627
+ : 'icon-bg-primary'
628
+ }`"
629
+ >
630
+ <q-icon
631
+ :class="`${moreActionDialogData.row.icon} ${
632
+ moreActionDialogData.row.iconColor === 'accent'
633
+ ? 'icon-text-accent'
634
+ : 'icon-text-primary'
635
+ }`"
636
+ alt="confirmation icon"
637
+ aria-label="confirmation icon"
638
+ size="1.5rem"
639
+ />
640
+ </div>
641
+ </div>
642
+
643
+ <div
644
+ class="text-heading-xxs primary-content-text q-pb-xxs"
645
+ >
646
+ {{ moreActionDialogData.row.title }}
647
+ </div>
648
+ <div
649
+ v-if="moreActionDialogData.row.description"
650
+ class="text-body-sm secondary-content-text q-pb-xs"
651
+ >
652
+ {{ moreActionDialogData.row.description }}
653
+ </div>
654
+ </div>
655
+ <!-- <p class="hidden-scope-value">{{ scope.value }}</p> -->
656
+ </q-card-section>
657
+
658
+ <q-card-actions align="right">
659
+ <UBtnStd
660
+ v-if="moreActionDialogData.secondaryAction"
661
+ :color="moreActionDialogData.secondaryAction.color"
662
+ :disable="moreActionDialogData.secondaryAction.disable"
663
+ :flat="moreActionDialogData.secondaryAction.flat"
664
+ :label="moreActionDialogData.secondaryAction.label"
665
+ :loading="moreActionDialogData.secondaryAction.loading"
666
+ :outline="moreActionDialogData.secondaryAction.outline"
667
+ :size="moreActionDialogData.secondaryAction.size"
668
+ @on-click="
669
+ moreActionDialogData.secondaryAction.handler(props.row)
670
+ "
671
+ />
672
+ <UBtnStd
673
+ v-if="moreActionDialogData.primaryAction"
674
+ class="confirm-primary-action"
675
+ :color="moreActionDialogData.primaryAction.color"
676
+ :disable="moreActionDialogData.primaryAction.disable"
677
+ :flat="moreActionDialogData.primaryAction.flat"
678
+ :label="moreActionDialogData.primaryAction.label"
679
+ :loading="moreActionDialogData.primaryAction.loading"
680
+ :outline="moreActionDialogData.primaryAction.outline"
681
+ :size="moreActionDialogData.primaryAction.size"
682
+ @on-click="
683
+ moreActionDialogData.primaryAction.handler(props.row)
684
+ "
685
+ />
686
+ </q-card-actions>
687
+ </q-card>
688
+ </q-menu>
689
+
690
+ <UBtnIcon
691
+ :class="`action-icon cursor-pointer`"
692
+ iconClass="fa-kit fa-ellipsis-vertical"
693
+ ariaLabel="More action"
694
+ :id="`actionPopupRefBtn-${props.row.id}`"
695
+ :key="index"
696
+ ref="btn-icon"
697
+ @click.stop="handleMenuEventStop"
698
+ @on-click="onMoreActionButtonClick(props.row.id)"
699
+ >
700
+ <template #menu>
701
+ <q-menu v-if="!verticalMoreActions" auto-close role="list">
702
+ <div
703
+ :class="`${
704
+ verticalMoreActions ? 'vertical' : 'horizontal'
705
+ }-more-action-wrapper more-action-common`"
706
+ >
707
+ <template v-for="(action, key) in col.actions" :key="key">
708
+ <UBtnIcon
709
+ v-if="
710
+ typeof action.hide === 'function'
711
+ ? !action.hide(props.row)
712
+ : true && !verticalMoreActions
713
+ "
714
+ :class="`more-action-icon cursor-pointer table-more-action`"
715
+ :iconClass="
716
+ typeof action.icon === 'function'
717
+ ? action.icon(props.row)
718
+ : action.icon
719
+ "
720
+ :anchor="action.anchor"
721
+ :ariaLabel="
722
+ typeof action.label === 'function'
723
+ ? action.label(props.row)
724
+ : action.label
725
+ "
726
+ :disable="
727
+ typeof action.disable === 'function' &&
728
+ action.disable(props.row)
729
+ "
730
+ :id="`more-action-${key}`"
731
+ :offset="action.offset ? action.offset : [10, 40]"
732
+ ref="btn-icon"
733
+ :self="action.self"
734
+ :size="action.size"
735
+ :tooltip="
736
+ typeof action.tooltip === 'function'
737
+ ? action.tooltip(props.row)
738
+ : action.tooltip
739
+ "
740
+ @click.stop="handleMenuEventStop"
741
+ @on-click="action.handler(props.row)"
742
+ />
743
+ </template>
744
+ </div>
745
+ </q-menu>
746
+
747
+ <q-menu v-else auto-close role="list">
748
+ <UMenuDropdown
749
+ v-if="verticalMoreActions"
750
+ :data="
751
+ col.actions.map((action) => {
752
+ return {
753
+ destructive: action.destructive,
754
+ disable:
755
+ typeof action.disable === 'function' &&
756
+ action.disable(props.row),
757
+ hide: !(typeof action.hide === 'function'
758
+ ? !action.hide(props.row)
759
+ : true),
760
+ label:
761
+ typeof action.label === 'function'
762
+ ? action.label(props.row)
763
+ : action.label,
764
+ leftIcon:
765
+ typeof action.icon === 'function'
766
+ ? action.icon(props.row)
767
+ : action.icon,
768
+ positive: action.positive,
769
+ handler: function () {
770
+ return action.handler(props.row)
771
+ },
772
+ }
773
+ })
774
+ "
775
+ />
776
+ </q-menu>
777
+ </template>
778
+ </UBtnIcon>
486
779
  </template>
487
780
  </UTd>
488
781
  </template>
@@ -500,16 +793,24 @@ const onRowPerPageChange = (value) => {
500
793
  </template> -->
501
794
  </UTable>
502
795
  <!-- customized pagination with the vitual scroll functionality and rows per page selection -->
503
- <div class="row justify-end items-center pagination-wrapper">
796
+ <div
797
+ v-if="showPagination"
798
+ class="row justify-end items-center pagination-wrapper"
799
+ dataTestId="table-pagination"
800
+ >
504
801
  <UPagination
505
- v-if="rows.length >= 6"
802
+ v-if="updatedRows.length >= 6"
506
803
  v-model="pagination.page"
507
- :rowPerPage="pagination.rowsPerPage"
804
+ :maxPageLink="
805
+ Number(
806
+ Math.ceil(updatedRows.length / pagination.rowsPerPage > 10 ? 6 : 3)
807
+ )
808
+ "
809
+ :maxPages="Number(Math.ceil(updatedRows.length / pagination.rowsPerPage))"
508
810
  :perPageOptions="getRowsPerPageOptions"
811
+ :rowPerPage="pagination.rowsPerPage"
509
812
  @onPageChange="onPageChange"
510
813
  @onRowChange="onRowPerPageChange"
511
- :maxPageLink="Number(maxPageLink)"
512
- :maxPages="Number(Math.ceil(rows.length / pagination.rowsPerPage))"
513
814
  />
514
815
  </div>
515
816
  <q-inner-loading :showing="customLoading" class="custom-table-loader" />
@@ -517,21 +818,25 @@ const onRowPerPageChange = (value) => {
517
818
 
518
819
  <style lang="sass">
519
820
  .action-icon
520
- height: $md
521
- width: $md
522
- color: $neutral-9
821
+ &:hover
822
+ background: $blue-1
523
823
 
524
- .table-data-avatar-left
525
- padding: $xs
526
- padding-left: 0px
824
+ .q-icon
825
+ height: $md !important
826
+ width: $md !important
827
+ color: $neutral-9 !important
828
+ font-size: $ba !important
829
+ &:hover
830
+ color: $primary !important
527
831
 
528
- .table-data-avatar-right
832
+ .table-data-avatar
529
833
  padding: $xs
834
+ padding-left: 0px
530
835
 
531
836
  .td-caption
532
837
  color: $description
533
838
 
534
- .more-action-wrapper
839
+ .more-action-common
535
840
  min-width: 3.125rem
536
841
  padding: $xs
537
842
  min-height: $xl
@@ -540,13 +845,42 @@ const onRowPerPageChange = (value) => {
540
845
  background: $surface-bg-1
541
846
  box-shadow: 0px 0px 4px 0px rgba(16, 17, 20, 0.08)
542
847
 
543
- .more-action-icon
544
- color: $neutral-9
848
+ .vertical-more-action-wrapper
849
+ display: grid
850
+ place-items: flex-start
851
+ place-content: center
852
+ padding: $xxs
853
+ gap: $xxs
545
854
 
546
- .sorting-icon
547
- color: $description
548
- padding-left: $xs
549
- font-size: $ba !important
855
+ .horizontal-more-action-wrapper
856
+ display: flex
857
+ align-items: center
858
+ justify-content: start
859
+
860
+ .vertical-single-action
861
+ width: 100%
862
+ gap: $xs
863
+ padding: 0 $xs
864
+ height: $lg
865
+
866
+ .q-item__section--main
867
+ color: $neutral-9
868
+ white-space: nowrap
869
+
870
+ .q-item__section--avatar
871
+ min-width: 0px
872
+
873
+ .q-item__section--side
874
+ padding-right: 0
875
+
876
+ .q-icon
877
+ color: $neutral-9
878
+ font-size: $ba
879
+
880
+ .more-action-icon
881
+ .q-icon
882
+ color: $neutral-9 !important
883
+ font-size: $ba !important
550
884
 
551
885
  .selected-data-row
552
886
  background: #F7F7F7
@@ -586,10 +920,10 @@ const onRowPerPageChange = (value) => {
586
920
  top: 0
587
921
 
588
922
  &.q-table--loading thead tr:last-child th
589
- top: 48px
923
+ top: 3rem
590
924
 
591
925
  tbody
592
- scroll-margin-top: 48px
926
+ scroll-margin-top: 3rem
593
927
 
594
928
  .u-sorting-btn
595
929
  padding: 0
@@ -616,4 +950,90 @@ const onRowPerPageChange = (value) => {
616
950
  .custom-table-loader
617
951
  .q-spinner
618
952
  color: $primary
953
+
954
+ .td-grid-content
955
+ text-wrap: balance
956
+ word-break: break-all
957
+
958
+ .hidden-header-label
959
+ visibility: hidden
960
+
961
+ .more-action-popup
962
+ border-radius: $border-radius-sm
963
+ background: $neutral-1
964
+ box-shadow: 0rem 0rem 0.75rem 0rem rgba(16, 17, 20, 0.16)
965
+ max-width: 18.375rem !important
966
+ width: 100%
967
+ position: relative
968
+ overflow: visible
969
+
970
+ .more-action-popup-wrapper
971
+ box-shadow: none
972
+ max-width: 18.375rem !important
973
+ width: 100%
974
+ .hidden-scope-value
975
+ display: none
976
+ .q-card__section
977
+ padding: 0
978
+ .q-card__actions
979
+ flex-wrap: nowrap
980
+ padding: $ba 0 0 0
981
+ .q-btn
982
+ width: 50%
983
+
984
+ .content-wrapper
985
+ display: grid
986
+ place-content: center
987
+ place-items: center
988
+
989
+ .secondary-content-text
990
+ color: $description
991
+
992
+ .primary-content-text
993
+ color: $dark
994
+
995
+ .remove-icon-wrapper
996
+ border-radius: 7.75rem
997
+ padding: $sm
998
+ width: 3.125rem
999
+ height: 3.125rem
1000
+ .q-icon
1001
+ color: $accent
1002
+
1003
+ .confirm-primary-action
1004
+ margin-left: $ba !important
1005
+
1006
+ .icon-bg-accent
1007
+ background-color: $red-1
1008
+
1009
+ .icon-bg-primary
1010
+ background-color: $blue-1
1011
+
1012
+ .icon-text-accent
1013
+ color: $accent
1014
+
1015
+ .icon-text-primary
1016
+ color: $primary !important
1017
+
1018
+ .tail-top
1019
+ width: 1.25rem
1020
+ height: 1.25rem
1021
+ transform: rotate(45deg)
1022
+ position: absolute
1023
+ right: 1.304rem
1024
+ top: -0.604rem
1025
+ border-top-left-radius: $border-radius-xs
1026
+ background: $neutral-1
1027
+ box-shadow: -3px -2px 5px -3px rgba(0, 0, 0, 0.2)
1028
+
1029
+ .tail-bottom
1030
+ width: 1.25rem
1031
+ height: 1.25rem
1032
+ transform: rotate(45deg)
1033
+ position: absolute
1034
+ right: 1.304rem
1035
+ bottom: -0.604rem
1036
+ border-bottom-right-radius: $border-radius-xs
1037
+ background: $neutral-1
1038
+ box-shadow: 3px 4px 5px -3px rgba(0, 0, 0, 0.2)
619
1039
  </style>