@coreui/vue-pro 4.7.0 → 4.8.0-next.1
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/components/calendar/utils.d.ts +23 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/modal/CModal.d.ts +4 -20
- package/dist/components/multi-select/CMultiSelect.d.ts +35 -44
- package/dist/components/multi-select/CMultiSelectNativeSelect.d.ts +1 -1
- package/dist/components/multi-select/CMultiSelectOptions.d.ts +13 -11
- package/dist/components/multi-select/CMultiSelectSelection.d.ts +1 -1
- package/dist/components/multi-select/types.d.ts +14 -0
- package/dist/components/multi-select/utils.d.ts +6 -0
- package/dist/components/offcanvas/COffcanvas.d.ts +35 -18
- package/dist/components/smart-table/CSmartTable.d.ts +65 -87
- package/dist/components/smart-table/CSmartTableBody.d.ts +16 -40
- package/dist/components/smart-table/CSmartTableHead.d.ts +17 -58
- package/dist/components/smart-table/CSmartTableInterface.d.ts +1 -1
- package/dist/components/smart-table/types.d.ts +50 -0
- package/dist/components/smart-table/utils.d.ts +17 -0
- package/dist/components/table/CTable.d.ts +1 -1
- package/dist/components/time-picker/types.d.ts +15 -0
- package/dist/components/time-picker/utils.d.ts +23 -0
- package/dist/components/virtual-scroller/CVirtualScroller.d.ts +23 -0
- package/dist/components/virtual-scroller/index.d.ts +6 -0
- package/dist/index.es.js +943 -885
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +943 -883
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -3
- package/dist/utils/isObjectInArray.d.ts +2 -0
- package/package.json +6 -6
- package/src/components/calendar/CCalendar.ts +1 -1
- package/src/{utils/calendar.ts → components/calendar/utils.ts} +1 -1
- package/src/components/date-range-picker/CDateRangePicker.ts +1 -1
- package/src/components/element-cover/CElementCover.ts +14 -14
- package/src/components/index.ts +1 -0
- package/src/components/modal/CModal.ts +10 -10
- package/src/components/multi-select/CMultiSelect.ts +33 -99
- package/src/components/multi-select/CMultiSelectNativeSelect.ts +2 -1
- package/src/components/multi-select/CMultiSelectOptions.ts +31 -17
- package/src/components/multi-select/CMultiSelectSelection.ts +2 -1
- package/src/components/multi-select/types.ts +15 -0
- package/src/components/multi-select/utils.ts +92 -0
- package/src/components/offcanvas/COffcanvas.ts +50 -28
- package/src/components/smart-table/CSmartTable.ts +365 -268
- package/src/components/smart-table/CSmartTableBody.ts +126 -137
- package/src/components/smart-table/CSmartTableHead.ts +53 -138
- package/src/components/smart-table/CSmartTableInterface.ts +1 -1
- package/src/components/smart-table/types.ts +61 -0
- package/src/components/smart-table/utils.ts +212 -0
- package/src/components/time-picker/CTimePicker.ts +49 -27
- package/src/components/time-picker/types.ts +15 -0
- package/src/{utils/time.ts → components/time-picker/utils.ts} +43 -2
- package/src/components/virtual-scroller/CVirtualScroller.ts +109 -0
- package/src/components/virtual-scroller/index.ts +10 -0
- package/src/utils/index.ts +1 -3
- package/src/utils/getNextSibling.ts +0 -18
- package/src/utils/getPreviousSibling.ts +0 -18
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Column,
|
|
3
|
+
ColumnFilter,
|
|
4
|
+
ColumnFilterValue,
|
|
5
|
+
Item,
|
|
6
|
+
Sorter,
|
|
7
|
+
SorterValue,
|
|
8
|
+
TableFilter,
|
|
9
|
+
} from './types'
|
|
10
|
+
|
|
11
|
+
export const filterColumns = (
|
|
12
|
+
items: Item[],
|
|
13
|
+
columnFilter: boolean | ColumnFilter | undefined,
|
|
14
|
+
columnFilterState: ColumnFilterValue,
|
|
15
|
+
itemsDataColumns: string[],
|
|
16
|
+
) => {
|
|
17
|
+
if (columnFilter && typeof columnFilter === 'object' && columnFilter.external) {
|
|
18
|
+
return items
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Object.entries(columnFilterState).forEach(([key, value]) => {
|
|
22
|
+
if (value instanceof Function) {
|
|
23
|
+
items = items.filter((item) => value(item[key]))
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const columnFilter = String(value).toLowerCase()
|
|
28
|
+
if (columnFilter && itemsDataColumns.includes(key)) {
|
|
29
|
+
items = items.filter((item) => {
|
|
30
|
+
return String(item[key]).toLowerCase().includes(columnFilter)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return items
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const filterTable = (
|
|
39
|
+
items: Item[],
|
|
40
|
+
tableFilter: boolean | TableFilter | undefined,
|
|
41
|
+
tableFilterState: string,
|
|
42
|
+
itemsDataColumns: string[],
|
|
43
|
+
) => {
|
|
44
|
+
if (
|
|
45
|
+
!tableFilterState ||
|
|
46
|
+
(tableFilter && typeof tableFilter === 'object' && tableFilter.external)
|
|
47
|
+
) {
|
|
48
|
+
return items
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const filter = tableFilterState.toLowerCase()
|
|
52
|
+
const valueContainFilter = (val: any) => String(val).toLowerCase().includes(filter)
|
|
53
|
+
items = items.filter((item) => {
|
|
54
|
+
return !!itemsDataColumns.find((key) => valueContainFilter(item[key]))
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return items
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const getClickedColumnName = (
|
|
61
|
+
target: HTMLTextAreaElement,
|
|
62
|
+
columnNames: string[],
|
|
63
|
+
): string => {
|
|
64
|
+
const closest = target.closest('tr')
|
|
65
|
+
const children = closest ? Array.from(closest.children) : []
|
|
66
|
+
const clickedCell = children.filter((child) => child.contains(target))[0]
|
|
67
|
+
return columnNames[children.indexOf(clickedCell)]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const getColumnKey = (column: Column | string) =>
|
|
71
|
+
typeof column === 'object' ? column.key : column
|
|
72
|
+
|
|
73
|
+
export const getColumnLabel = (column: Column | string) =>
|
|
74
|
+
typeof column === 'object'
|
|
75
|
+
? column.label !== undefined
|
|
76
|
+
? column.label
|
|
77
|
+
: pretifyName(column.key)
|
|
78
|
+
: pretifyName(column)
|
|
79
|
+
|
|
80
|
+
export const getColumnNames = (columns: (string | Column)[] | undefined, items: Item[]) =>
|
|
81
|
+
columns
|
|
82
|
+
? columns.map((column: Column | string) => {
|
|
83
|
+
if (typeof column === 'object') return column.key
|
|
84
|
+
else return column
|
|
85
|
+
})
|
|
86
|
+
: getColumnNamesFromItems(items)
|
|
87
|
+
|
|
88
|
+
export const getColumnNamesFromItems = (items: Item[]) =>
|
|
89
|
+
Object.keys(items[0] || {}).filter((el) => el.charAt(0) !== '_')
|
|
90
|
+
|
|
91
|
+
export const getColumnSorterState = (
|
|
92
|
+
key: string,
|
|
93
|
+
sorterState: SorterValue | undefined,
|
|
94
|
+
): string | number => {
|
|
95
|
+
if (sorterState && sorterState.column === key) {
|
|
96
|
+
if (sorterState.state) {
|
|
97
|
+
return sorterState.state
|
|
98
|
+
}
|
|
99
|
+
return 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const getColumnValues = (items: Item[], key: string) => {
|
|
106
|
+
return items.map((item) => item[key])
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const getTableDataCellProps = (item: Item, colName: string) => {
|
|
110
|
+
const props = item._cellProps && {
|
|
111
|
+
...(item._cellProps['all'] && { ...item._cellProps['all'] }),
|
|
112
|
+
...(item._cellProps[colName] && { ...item._cellProps[colName] }),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return props
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const getTableHeaderCellProps = (column: Column | string) => {
|
|
119
|
+
if (typeof column === 'object' && column._props) {
|
|
120
|
+
return column._props
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const getTableHeaderCellStyles = (
|
|
127
|
+
column: Column | string,
|
|
128
|
+
columnSorter: boolean | Sorter | undefined,
|
|
129
|
+
) => {
|
|
130
|
+
const style = {}
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
columnSorter &&
|
|
134
|
+
(typeof column !== 'object' ||
|
|
135
|
+
(typeof column === 'object' && (typeof column.sorter === 'undefined' || column.sorter)))
|
|
136
|
+
) {
|
|
137
|
+
style['cursor'] = 'pointer'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof column === 'object' && column._style) {
|
|
141
|
+
return { ...style, ...column._style }
|
|
142
|
+
}
|
|
143
|
+
return style
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const isObjectInArray = <T>(array: T[], item: T, ignore: string[] = []) =>
|
|
147
|
+
array.some((_item: T) => {
|
|
148
|
+
let result = true
|
|
149
|
+
for (const key in item) {
|
|
150
|
+
if (!ignore.includes(key) && item[key] !== _item[key]) {
|
|
151
|
+
result = false
|
|
152
|
+
break
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return result
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
export const isSortable = (
|
|
160
|
+
i: number,
|
|
161
|
+
columns: (string | Column)[] | undefined,
|
|
162
|
+
columnSorter: Sorter | boolean | undefined,
|
|
163
|
+
itemsDataColumns: string[],
|
|
164
|
+
columnNames: string[],
|
|
165
|
+
): boolean | undefined => {
|
|
166
|
+
const isDataColumn = itemsDataColumns.includes(columnNames[i])
|
|
167
|
+
let column
|
|
168
|
+
if (columns) column = columns[i]
|
|
169
|
+
return (
|
|
170
|
+
columnSorter &&
|
|
171
|
+
(!columns ||
|
|
172
|
+
typeof column !== 'object' ||
|
|
173
|
+
(typeof column === 'object' && (typeof column.sorter === 'undefined' || column.sorter))) &&
|
|
174
|
+
isDataColumn
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const pretifyName = (name: string) => {
|
|
179
|
+
return name
|
|
180
|
+
.replace(/[-_.]/g, ' ')
|
|
181
|
+
.replace(/ +/g, ' ')
|
|
182
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
183
|
+
.split(' ')
|
|
184
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
185
|
+
.join(' ')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const sortItems = (
|
|
189
|
+
columnSorter: boolean | Sorter | undefined,
|
|
190
|
+
items: Item[],
|
|
191
|
+
itemsDataColumns: string[],
|
|
192
|
+
sorterState: SorterValue,
|
|
193
|
+
) => {
|
|
194
|
+
const column = sorterState.column
|
|
195
|
+
if (
|
|
196
|
+
!column ||
|
|
197
|
+
!itemsDataColumns.includes(column) ||
|
|
198
|
+
(columnSorter && typeof columnSorter === 'object' && columnSorter.external)
|
|
199
|
+
) {
|
|
200
|
+
return items
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const flip = sorterState.state === 'asc' ? 1 : sorterState.state === 'desc' ? -1 : 0
|
|
204
|
+
const sorted = items.slice().sort((item, item2) => {
|
|
205
|
+
const value = item[column]
|
|
206
|
+
const value2 = item2[column]
|
|
207
|
+
const a = typeof value === 'number' ? value : String(value).toLowerCase()
|
|
208
|
+
const b = typeof value2 === 'number' ? value2 : String(value2).toLowerCase()
|
|
209
|
+
return a > b ? 1 * flip : b > a ? -1 * flip : 0
|
|
210
|
+
})
|
|
211
|
+
return sorted
|
|
212
|
+
}
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { defineComponent, h, ref, watch } from 'vue'
|
|
2
2
|
|
|
3
3
|
import { CFormInput, CFormSelect, CInputGroup, CInputGroupText } from '../form/'
|
|
4
|
-
import { CFormControlWrapper } from '
|
|
4
|
+
import { CFormControlWrapper } from '../form/CFormControlWrapper'
|
|
5
5
|
import { CPicker } from '../picker'
|
|
6
|
+
|
|
6
7
|
import { CTimePickerRollCol } from './CTimePickerRollCol'
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
convert12hTo24h,
|
|
10
11
|
convertTimeToDate,
|
|
11
12
|
getAmPm,
|
|
12
|
-
|
|
13
|
-
getListOfMinutes,
|
|
14
|
-
getListOfSeconds,
|
|
13
|
+
getLocalizedTimePartials,
|
|
15
14
|
getSelectedHour,
|
|
16
15
|
getSelectedMinutes,
|
|
17
16
|
getSelectedSeconds,
|
|
18
|
-
isAmPm,
|
|
19
17
|
isValidTime,
|
|
20
|
-
} from '
|
|
18
|
+
} from './utils'
|
|
21
19
|
|
|
22
20
|
import { Color } from '../props'
|
|
21
|
+
import type { LocalizedTimePartials } from './types'
|
|
23
22
|
|
|
24
23
|
const CTimePicker = defineComponent({
|
|
25
24
|
name: 'CTimePicker',
|
|
@@ -281,10 +280,16 @@ const CTimePicker = defineComponent({
|
|
|
281
280
|
'update:time',
|
|
282
281
|
],
|
|
283
282
|
setup(props, { emit, attrs, slots }) {
|
|
284
|
-
const visible = ref(props.visible)
|
|
285
283
|
const date = ref<Date | null>(convertTimeToDate(props.time))
|
|
286
|
-
const initialDate = ref<Date | null>(null)
|
|
287
284
|
const ampm = ref<'am' | 'pm'>(date.value ? getAmPm(new Date(date.value), props.locale) : 'am')
|
|
285
|
+
const initialDate = ref<Date | null>(null)
|
|
286
|
+
const visible = ref(props.visible)
|
|
287
|
+
const localizedTimePartials = ref<LocalizedTimePartials>({
|
|
288
|
+
listOfHours: [],
|
|
289
|
+
listOfMinutes: [],
|
|
290
|
+
listOfSeconds: [],
|
|
291
|
+
hour12: false,
|
|
292
|
+
})
|
|
288
293
|
|
|
289
294
|
watch(
|
|
290
295
|
() => props.time,
|
|
@@ -294,6 +299,8 @@ const CTimePicker = defineComponent({
|
|
|
294
299
|
)
|
|
295
300
|
|
|
296
301
|
watch(date, () => {
|
|
302
|
+
localizedTimePartials.value = getLocalizedTimePartials(props.locale, props.ampm)
|
|
303
|
+
|
|
297
304
|
if (date.value) {
|
|
298
305
|
ampm.value = getAmPm(new Date(date.value), props.locale)
|
|
299
306
|
}
|
|
@@ -313,13 +320,14 @@ const CTimePicker = defineComponent({
|
|
|
313
320
|
if (value === 'am') {
|
|
314
321
|
_date.setHours(_date.getHours() - 12)
|
|
315
322
|
}
|
|
323
|
+
|
|
316
324
|
if (value === 'pm') {
|
|
317
325
|
_date.setHours(_date.getHours() + 12)
|
|
318
326
|
}
|
|
319
327
|
}
|
|
320
328
|
|
|
321
329
|
if (set === 'hours') {
|
|
322
|
-
if (
|
|
330
|
+
if (localizedTimePartials.value && localizedTimePartials.value.hour12) {
|
|
323
331
|
_date.setHours(convert12hTo24h(ampm.value, parseInt(value)))
|
|
324
332
|
} else {
|
|
325
333
|
_date.setHours(parseInt(value))
|
|
@@ -353,9 +361,7 @@ const CTimePicker = defineComponent({
|
|
|
353
361
|
readonly: props.inputReadOnly,
|
|
354
362
|
value: date.value
|
|
355
363
|
? date.value.toLocaleTimeString(props.locale, {
|
|
356
|
-
hour12:
|
|
357
|
-
(props.ampm === 'auto' && isAmPm(props.locale)) ||
|
|
358
|
-
(props.ampm === 'boolean' && props.ampm),
|
|
364
|
+
hour12: localizedTimePartials.value && localizedTimePartials.value.hour12,
|
|
359
365
|
...(!props.seconds && { timeStyle: 'short' }),
|
|
360
366
|
})
|
|
361
367
|
: '',
|
|
@@ -391,35 +397,50 @@ const CTimePicker = defineComponent({
|
|
|
391
397
|
h('span', { class: 'time-picker-inline-icon' }),
|
|
392
398
|
h(CFormSelect, {
|
|
393
399
|
disabled: props.disabled,
|
|
394
|
-
options:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
+
options:
|
|
401
|
+
localizedTimePartials.value &&
|
|
402
|
+
localizedTimePartials.value.listOfHours?.map((option) => {
|
|
403
|
+
return {
|
|
404
|
+
value: option.value.toString(),
|
|
405
|
+
label: option.label,
|
|
406
|
+
}
|
|
407
|
+
}),
|
|
400
408
|
onChange: (event) => handleTimeChange('hours', event.target.value),
|
|
401
409
|
...(date.value && { value: getSelectedHour(date.value, props.locale) }),
|
|
402
410
|
}),
|
|
403
411
|
':',
|
|
404
|
-
// @ts-expect-error the getListOfMinutes function returns corect type
|
|
405
412
|
h(CFormSelect, {
|
|
406
413
|
disabled: props.disabled,
|
|
407
|
-
options:
|
|
414
|
+
options:
|
|
415
|
+
localizedTimePartials.value &&
|
|
416
|
+
localizedTimePartials.value.listOfMinutes.map((option) => {
|
|
417
|
+
return {
|
|
418
|
+
value: option.value.toString(),
|
|
419
|
+
label: option.label,
|
|
420
|
+
}
|
|
421
|
+
}),
|
|
408
422
|
onChange: (event: Event) =>
|
|
409
423
|
handleTimeChange('minutes', (event.target as HTMLSelectElement).value),
|
|
410
424
|
...(date.value && { value: getSelectedMinutes(date.value) }),
|
|
411
425
|
}),
|
|
412
426
|
props.seconds && ':',
|
|
413
427
|
props.seconds &&
|
|
414
|
-
// @ts-expect-error the getListOfMinutes function returns corect type
|
|
415
428
|
h(CFormSelect, {
|
|
416
429
|
disabled: props.disabled,
|
|
417
|
-
options:
|
|
430
|
+
options:
|
|
431
|
+
localizedTimePartials.value &&
|
|
432
|
+
localizedTimePartials.value.listOfSeconds.map((option) => {
|
|
433
|
+
return {
|
|
434
|
+
value: option.value.toString(),
|
|
435
|
+
label: option.label,
|
|
436
|
+
}
|
|
437
|
+
}),
|
|
418
438
|
onChange: (event: Event) =>
|
|
419
439
|
handleTimeChange('seconds', (event.target as HTMLSelectElement).value),
|
|
420
440
|
...(date.value && { value: getSelectedSeconds(date.value) }),
|
|
421
441
|
}),
|
|
422
|
-
|
|
442
|
+
localizedTimePartials.value &&
|
|
443
|
+
localizedTimePartials.value.hour12 &&
|
|
423
444
|
h(CFormSelect, {
|
|
424
445
|
disabled: props.disabled,
|
|
425
446
|
options: [
|
|
@@ -433,22 +454,23 @@ const CTimePicker = defineComponent({
|
|
|
433
454
|
|
|
434
455
|
const TimePickerRoll = () => [
|
|
435
456
|
h(CTimePickerRollCol, {
|
|
436
|
-
elements:
|
|
457
|
+
elements: localizedTimePartials.value && localizedTimePartials.value.listOfHours,
|
|
437
458
|
onClick: (index: number) => handleTimeChange('hours', index.toString()),
|
|
438
459
|
selected: getSelectedHour(date.value, props.locale, props.ampm),
|
|
439
460
|
}),
|
|
440
461
|
h(CTimePickerRollCol, {
|
|
441
|
-
elements:
|
|
462
|
+
elements: localizedTimePartials.value && localizedTimePartials.value.listOfMinutes,
|
|
442
463
|
onClick: (index: number) => handleTimeChange('minutes', index.toString()),
|
|
443
464
|
selected: getSelectedMinutes(date.value),
|
|
444
465
|
}),
|
|
445
466
|
props.seconds &&
|
|
446
467
|
h(CTimePickerRollCol, {
|
|
447
|
-
elements:
|
|
468
|
+
elements: localizedTimePartials.value && localizedTimePartials.value.listOfSeconds,
|
|
448
469
|
onClick: (index: number) => handleTimeChange('seconds', index.toString()),
|
|
449
470
|
selected: getSelectedSeconds(date.value),
|
|
450
471
|
}),
|
|
451
|
-
|
|
472
|
+
localizedTimePartials.value &&
|
|
473
|
+
localizedTimePartials.value.hour12 &&
|
|
452
474
|
h(CTimePickerRollCol, {
|
|
453
475
|
elements: [
|
|
454
476
|
{ value: 'am', label: 'AM' },
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LocalizedTimePartials } from './types'
|
|
2
|
+
|
|
1
3
|
export const convert12hTo24h = (abbr: 'am' | 'pm', hour: number) => {
|
|
2
4
|
if (abbr === 'am' && hour === 12) {
|
|
3
5
|
return 0
|
|
@@ -26,6 +28,7 @@ export const getAmPm = (date: Date, locale: string) => {
|
|
|
26
28
|
return date.getHours() >= 12 ? 'pm' : 'am'
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
// TODO: clean-up
|
|
29
32
|
export const getListOfHours = (locale: string, ampm: 'auto' | boolean = 'auto') =>
|
|
30
33
|
Array.from({ length: (ampm === 'auto' && isAmPm(locale)) || ampm === true ? 12 : 24 }, (_, i) => {
|
|
31
34
|
return {
|
|
@@ -36,6 +39,7 @@ export const getListOfHours = (locale: string, ampm: 'auto' | boolean = 'auto')
|
|
|
36
39
|
}
|
|
37
40
|
})
|
|
38
41
|
|
|
42
|
+
// TODO: clean-up
|
|
39
43
|
export const getListOfMinutes = (locale: string, valueAsString = false) =>
|
|
40
44
|
Array.from({ length: 60 }, (_, i) => {
|
|
41
45
|
const d = new Date()
|
|
@@ -47,10 +51,11 @@ export const getListOfMinutes = (locale: string, valueAsString = false) =>
|
|
|
47
51
|
minute: '2-digit',
|
|
48
52
|
second: '2-digit',
|
|
49
53
|
})
|
|
50
|
-
.split(/[^A-Za-z0-9]/)[0],
|
|
54
|
+
.split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
|
|
51
55
|
}
|
|
52
56
|
})
|
|
53
57
|
|
|
58
|
+
// TODO: clean-up
|
|
54
59
|
export const getListOfSeconds = (locale: string, valueAsString = false) =>
|
|
55
60
|
Array.from({ length: 60 }, (_, i) => {
|
|
56
61
|
const d = new Date()
|
|
@@ -62,10 +67,46 @@ export const getListOfSeconds = (locale: string, valueAsString = false) =>
|
|
|
62
67
|
minute: '2-digit',
|
|
63
68
|
second: '2-digit',
|
|
64
69
|
})
|
|
65
|
-
.split(/[^A-Za-z0-9]/)[
|
|
70
|
+
.split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const getLocalizedTimePartials = (
|
|
75
|
+
locale: string,
|
|
76
|
+
ampm: 'auto' | boolean = 'auto',
|
|
77
|
+
): LocalizedTimePartials => {
|
|
78
|
+
const date = new Date()
|
|
79
|
+
const hour12 = ['am', 'AM', 'pm', 'PM'].some((el) => date.toLocaleString(locale).includes(el))
|
|
80
|
+
const listOfHours = Array.from(
|
|
81
|
+
{ length: (ampm === 'auto' && hour12) || ampm === true ? 12 : 24 },
|
|
82
|
+
(_, i) => {
|
|
83
|
+
return {
|
|
84
|
+
value: (ampm === 'auto' && hour12) || ampm === true ? i + 1 : i,
|
|
85
|
+
label: ((ampm === 'auto' && hour12) || ampm === true ? i + 1 : i).toLocaleString(locale),
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
const listOfMinutesSeconds = Array.from({ length: 60 }, (_, i) => {
|
|
90
|
+
date.setMinutes(i)
|
|
91
|
+
return {
|
|
92
|
+
value: i,
|
|
93
|
+
label: date
|
|
94
|
+
.toLocaleTimeString(locale, {
|
|
95
|
+
minute: '2-digit',
|
|
96
|
+
second: '2-digit',
|
|
97
|
+
})
|
|
98
|
+
.split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
|
|
66
99
|
}
|
|
67
100
|
})
|
|
68
101
|
|
|
102
|
+
return {
|
|
103
|
+
listOfHours,
|
|
104
|
+
listOfMinutes: listOfMinutesSeconds,
|
|
105
|
+
listOfSeconds: listOfMinutesSeconds,
|
|
106
|
+
hour12,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
69
110
|
export const getSelectedHour = (
|
|
70
111
|
date: Date | null,
|
|
71
112
|
locale: string,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { cloneVNode, computed, defineComponent, h, onMounted, ref, VNode } from 'vue'
|
|
2
|
+
|
|
3
|
+
const CVirtualScroller = defineComponent({
|
|
4
|
+
name: 'CVirtualScroller',
|
|
5
|
+
props: {
|
|
6
|
+
/**
|
|
7
|
+
* Amount of visible items
|
|
8
|
+
*/
|
|
9
|
+
visibleItems: {
|
|
10
|
+
type: Number,
|
|
11
|
+
default: 10,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
setup(props, { slots }) {
|
|
15
|
+
const virtualScrollRef = ref<HTMLDivElement>()
|
|
16
|
+
const virtualScrollContentRef = ref<HTMLDivElement>()
|
|
17
|
+
|
|
18
|
+
const currentItemIndex = ref(1)
|
|
19
|
+
const itemHeight = ref<number>(0)
|
|
20
|
+
const itemsNumber = ref<number>(0)
|
|
21
|
+
const viewportPadding = ref(0)
|
|
22
|
+
|
|
23
|
+
const buffer = computed(() => Math.floor(props.visibleItems / 2))
|
|
24
|
+
|
|
25
|
+
const maxHeight = computed(
|
|
26
|
+
() => itemsNumber.value * itemHeight.value + 2 * viewportPadding.value,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const viewportHeight = computed(
|
|
30
|
+
() => props.visibleItems * itemHeight.value + 2 * viewportPadding.value,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
onMounted(() => {
|
|
34
|
+
if (virtualScrollRef.value) {
|
|
35
|
+
viewportPadding.value = parseFloat(getComputedStyle(virtualScrollRef.value).paddingTop)
|
|
36
|
+
// It's necessary to calculate heights of items
|
|
37
|
+
virtualScrollRef.value.dispatchEvent(new CustomEvent('scroll'))
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const handleScroll = (scrollTop: number) => {
|
|
42
|
+
currentItemIndex.value =
|
|
43
|
+
itemHeight.value && Math.max(Math.ceil(scrollTop / itemHeight.value), 1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
const children: any = slots.default
|
|
48
|
+
? Array.isArray(slots.default()[0].children)
|
|
49
|
+
? slots.default()[0].children
|
|
50
|
+
: slots.default()
|
|
51
|
+
: []
|
|
52
|
+
itemsNumber.value = children && children.length ? children.length : 0
|
|
53
|
+
return h(
|
|
54
|
+
'div',
|
|
55
|
+
{
|
|
56
|
+
class: ['virtual-scroller'],
|
|
57
|
+
onScroll: (event: any) => handleScroll((event.target as HTMLDivElement).scrollTop),
|
|
58
|
+
style: {
|
|
59
|
+
height: `${
|
|
60
|
+
maxHeight.value > viewportHeight.value ? viewportHeight.value : maxHeight.value
|
|
61
|
+
}px`,
|
|
62
|
+
overflowY: 'auto',
|
|
63
|
+
},
|
|
64
|
+
ref: virtualScrollRef,
|
|
65
|
+
},
|
|
66
|
+
h(
|
|
67
|
+
'div',
|
|
68
|
+
{
|
|
69
|
+
class: 'virtual-scroller-content',
|
|
70
|
+
style: {
|
|
71
|
+
height: `${maxHeight.value}px`,
|
|
72
|
+
},
|
|
73
|
+
ref: virtualScrollContentRef,
|
|
74
|
+
},
|
|
75
|
+
children.map(
|
|
76
|
+
(slot: VNode, index: number) =>
|
|
77
|
+
index + 1 > Math.max(currentItemIndex.value - buffer.value, 0) &&
|
|
78
|
+
index + 1 <= currentItemIndex.value + props.visibleItems + buffer.value &&
|
|
79
|
+
cloneVNode(slot, {
|
|
80
|
+
class: [
|
|
81
|
+
{
|
|
82
|
+
'virtual-scroller-item-preload':
|
|
83
|
+
index + 1 > currentItemIndex.value + props.visibleItems ||
|
|
84
|
+
index + 1 < currentItemIndex.value,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
style: {
|
|
88
|
+
...(currentItemIndex.value > buffer.value && {
|
|
89
|
+
transform: `translateY(${
|
|
90
|
+
(currentItemIndex.value - buffer.value) * itemHeight.value
|
|
91
|
+
}px)`,
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
ref: (node) => {
|
|
95
|
+
if (node && (node as HTMLElement).offsetHeight) {
|
|
96
|
+
itemHeight.value =
|
|
97
|
+
(node as HTMLElement).offsetHeight +
|
|
98
|
+
parseFloat(getComputedStyle(node as HTMLElement).marginTop) +
|
|
99
|
+
parseFloat(getComputedStyle(node as HTMLElement).marginBottom)
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
export { CVirtualScroller }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { App } from 'vue'
|
|
2
|
+
import { CVirtualScroller } from './CVirtualScroller'
|
|
3
|
+
|
|
4
|
+
const CVirtualScrollerPlugin = {
|
|
5
|
+
install: (app: App): void => {
|
|
6
|
+
app.component(CVirtualScroller.name, CVirtualScroller)
|
|
7
|
+
},
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { CVirtualScroller, CVirtualScrollerPlugin }
|
package/src/utils/index.ts
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const getNextSibling = (elem: HTMLElement, selector?: string) => {
|
|
2
|
-
// Get the next sibling element
|
|
3
|
-
let sibling = elem.nextElementSibling
|
|
4
|
-
|
|
5
|
-
// If there's no selector, return the first sibling
|
|
6
|
-
if (!selector) return sibling
|
|
7
|
-
|
|
8
|
-
// If the sibling matches our selector, use it
|
|
9
|
-
// If not, jump to the next sibling and continue the loop
|
|
10
|
-
while (sibling) {
|
|
11
|
-
if (sibling.matches(selector)) return sibling
|
|
12
|
-
sibling = sibling.nextElementSibling
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default getNextSibling
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const getPreviousSibling = (elem: HTMLElement, selector?: string) => {
|
|
2
|
-
// Get the next sibling element
|
|
3
|
-
let sibling = elem.previousElementSibling
|
|
4
|
-
|
|
5
|
-
// If there's no selector, return the first sibling
|
|
6
|
-
if (!selector) return sibling
|
|
7
|
-
|
|
8
|
-
// If the sibling matches our selector, use it
|
|
9
|
-
// If not, jump to the next sibling and continue the loop
|
|
10
|
-
while (sibling) {
|
|
11
|
-
if (sibling.matches(selector)) return sibling
|
|
12
|
-
sibling = sibling.previousElementSibling
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default getPreviousSibling
|