@coreui/data-grid 0.1.0-rc.0
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/LICENSE +3 -0
- package/README.md +37 -0
- package/dist/css/data-grid.css +444 -0
- package/dist/css/data-grid.css.map +1 -0
- package/dist/css/data-grid.min.css +5 -0
- package/dist/css/data-grid.min.css.map +1 -0
- package/dist/js/csv.cjs +68 -0
- package/dist/js/csv.cjs.map +1 -0
- package/dist/js/csv.mjs +63 -0
- package/dist/js/csv.mjs.map +1 -0
- package/dist/js/data-grid.esm.js +9618 -0
- package/dist/js/data-grid.esm.js.map +1 -0
- package/dist/js/data-grid.esm.min.js +8 -0
- package/dist/js/data-grid.esm.min.js.map +1 -0
- package/dist/js/data-grid.js +9635 -0
- package/dist/js/data-grid.js.map +1 -0
- package/dist/js/data-grid.min.js +8 -0
- package/dist/js/data-grid.min.js.map +1 -0
- package/dist/js/data-grid.mjs +9618 -0
- package/dist/js/data-grid.mjs.map +1 -0
- package/dist/js/filters.cjs +143 -0
- package/dist/js/filters.cjs.map +1 -0
- package/dist/js/filters.mjs +137 -0
- package/dist/js/filters.mjs.map +1 -0
- package/dist/js/labels.cjs +76 -0
- package/dist/js/labels.cjs.map +1 -0
- package/dist/js/labels.mjs +71 -0
- package/dist/js/labels.mjs.map +1 -0
- package/dist/types/base-component.d.ts +19 -0
- package/dist/types/data-grid.d.ts +128 -0
- package/dist/types/dom/event-handler.d.ts +11 -0
- package/dist/types/dom/manipulator.d.ts +9 -0
- package/dist/types/engine/active-cell.d.ts +32 -0
- package/dist/types/engine/data-source.d.ts +19 -0
- package/dist/types/engine/scheduler.d.ts +11 -0
- package/dist/types/engine/table.d.ts +9 -0
- package/dist/types/engine/virtualizer.d.ts +21 -0
- package/dist/types/filters.d.mts +1 -0
- package/dist/types/filters.d.ts +13 -0
- package/dist/types/icons.d.ts +21 -0
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/labels.d.mts +1 -0
- package/dist/types/labels.d.ts +9 -0
- package/dist/types/types.d.ts +262 -0
- package/dist/types/ui/body.d.ts +34 -0
- package/dist/types/ui/column-chooser.d.ts +27 -0
- package/dist/types/ui/editor.d.ts +8 -0
- package/dist/types/ui/filter-menu.d.ts +21 -0
- package/dist/types/ui/header.d.ts +50 -0
- package/dist/types/ui/loading.d.ts +12 -0
- package/dist/types/ui/menu.d.ts +17 -0
- package/dist/types/ui/pagination.d.ts +17 -0
- package/dist/types/ui/pinning.d.ts +17 -0
- package/dist/types/ui/toolbar.d.ts +34 -0
- package/dist/types/ui/tooltip.d.ts +10 -0
- package/dist/types/util/config.d.ts +11 -0
- package/dist/types/util/csv.d.mts +1 -0
- package/dist/types/util/csv.d.ts +19 -0
- package/dist/types/util/debounce.d.ts +7 -0
- package/dist/types/util/index.d.ts +10 -0
- package/dist/types/util/sanitizer.d.ts +11 -0
- package/package.json +147 -0
- package/scss/data-grid.scss +559 -0
- package/src/base-component.d.ts +19 -0
- package/src/base-component.js +90 -0
- package/src/data-grid.ts +2208 -0
- package/src/dom/data.js +59 -0
- package/src/dom/event-handler.d.ts +11 -0
- package/src/dom/event-handler.js +331 -0
- package/src/dom/manipulator.d.ts +9 -0
- package/src/dom/manipulator.js +75 -0
- package/src/engine/active-cell.ts +195 -0
- package/src/engine/data-source.ts +63 -0
- package/src/engine/scheduler.ts +42 -0
- package/src/engine/table.ts +122 -0
- package/src/engine/virtualizer.ts +57 -0
- package/src/filters.ts +176 -0
- package/src/icons.ts +60 -0
- package/src/index.ts +56 -0
- package/src/labels.ts +70 -0
- package/src/types.ts +334 -0
- package/src/ui/body.ts +348 -0
- package/src/ui/column-chooser.ts +183 -0
- package/src/ui/editor.ts +98 -0
- package/src/ui/filter-menu.ts +398 -0
- package/src/ui/header.ts +441 -0
- package/src/ui/loading.ts +39 -0
- package/src/ui/menu.ts +218 -0
- package/src/ui/pagination.ts +150 -0
- package/src/ui/pinning.ts +53 -0
- package/src/ui/toolbar.ts +144 -0
- package/src/ui/tooltip.ts +113 -0
- package/src/util/config.d.ts +11 -0
- package/src/util/config.js +69 -0
- package/src/util/csv.ts +85 -0
- package/src/util/debounce.ts +18 -0
- package/src/util/index.d.ts +10 -0
- package/src/util/index.js +310 -0
- package/src/util/sanitizer.d.ts +11 -0
- package/src/util/sanitizer.js +162 -0
package/src/dom/data.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Vendored from https://github.com/coreui/coreui-pro/blob/bc4daf1cd79d527bf665d615c0a31f53ab0c3d4e/js/src/dom/data.js — sync manually
|
|
2
|
+
/**
|
|
3
|
+
* --------------------------------------------------------------------------
|
|
4
|
+
* CoreUI dom/data.js
|
|
5
|
+
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
|
|
6
|
+
*
|
|
7
|
+
* This is a modified version of the Bootstrap's dom/data.js
|
|
8
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
9
|
+
* --------------------------------------------------------------------------
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constants
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const elementMap = new Map()
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
set(element, key, instance) {
|
|
20
|
+
if (!elementMap.has(element)) {
|
|
21
|
+
elementMap.set(element, new Map())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const instanceMap = elementMap.get(element)
|
|
25
|
+
|
|
26
|
+
// make it clear we only want one instance per element
|
|
27
|
+
// can be removed later when multiple key/instances are fine to be used
|
|
28
|
+
if (!instanceMap.has(key) && instanceMap.size !== 0) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
instanceMap.set(key, instance)
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
get(element, key) {
|
|
38
|
+
if (elementMap.has(element)) {
|
|
39
|
+
return elementMap.get(element).get(key) || null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
remove(element, key) {
|
|
46
|
+
if (!elementMap.has(element)) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const instanceMap = elementMap.get(element)
|
|
51
|
+
|
|
52
|
+
instanceMap.delete(key)
|
|
53
|
+
|
|
54
|
+
// free up element references if there are no instances left for an element
|
|
55
|
+
if (instanceMap.size === 0) {
|
|
56
|
+
elementMap.delete(element)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Hand-written types for the vendored event-handler.js (see the provenance header there)
|
|
2
|
+
type EventHandlerFunction = (event: Event & Record<string, unknown>) => void
|
|
3
|
+
|
|
4
|
+
declare const EventHandler: {
|
|
5
|
+
on(element: Element | Document | Window, event: string, handler: string | EventHandlerFunction, delegationFunction?: EventHandlerFunction): void
|
|
6
|
+
one(element: Element | Document | Window, event: string, handler: string | EventHandlerFunction, delegationFunction?: EventHandlerFunction): void
|
|
7
|
+
off(element: Element | Document | Window, originalTypeEvent: string, handler?: string | EventHandlerFunction, delegationFunction?: EventHandlerFunction): void
|
|
8
|
+
trigger(element: Element | Document | Window, event: string, args?: Record<string, unknown>): (Event & Record<string, unknown>) | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default EventHandler
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// Vendored from https://github.com/coreui/coreui-pro/blob/bc4daf1cd79d527bf665d615c0a31f53ab0c3d4e/js/src/dom/event-handler.js — sync manually
|
|
2
|
+
/**
|
|
3
|
+
* --------------------------------------------------------------------------
|
|
4
|
+
* CoreUI dom/event-handler.js
|
|
5
|
+
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
|
|
6
|
+
*
|
|
7
|
+
* This is a modified version of the Bootstrap's dom/event-handler.js
|
|
8
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
9
|
+
* --------------------------------------------------------------------------
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getjQuery } from '../util/index.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Constants
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
|
|
19
|
+
const stripNameRegex = /\..*/
|
|
20
|
+
const stripUidRegex = /::\d+$/
|
|
21
|
+
const eventRegistry = {} // Events storage
|
|
22
|
+
let uidEvent = 1
|
|
23
|
+
const customEvents = {
|
|
24
|
+
mouseenter: 'mouseover',
|
|
25
|
+
mouseleave: 'mouseout'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const nativeEvents = new Set([
|
|
29
|
+
'click',
|
|
30
|
+
'dblclick',
|
|
31
|
+
'mouseup',
|
|
32
|
+
'mousedown',
|
|
33
|
+
'contextmenu',
|
|
34
|
+
'mousewheel',
|
|
35
|
+
'DOMMouseScroll',
|
|
36
|
+
'mouseover',
|
|
37
|
+
'mouseout',
|
|
38
|
+
'mousemove',
|
|
39
|
+
'selectstart',
|
|
40
|
+
'selectend',
|
|
41
|
+
'keydown',
|
|
42
|
+
'keypress',
|
|
43
|
+
'keyup',
|
|
44
|
+
'orientationchange',
|
|
45
|
+
'touchstart',
|
|
46
|
+
'touchmove',
|
|
47
|
+
'touchend',
|
|
48
|
+
'touchcancel',
|
|
49
|
+
'pointerdown',
|
|
50
|
+
'pointermove',
|
|
51
|
+
'pointerup',
|
|
52
|
+
'pointerleave',
|
|
53
|
+
'pointercancel',
|
|
54
|
+
// Local addition over the coreui-pro source: HTML5 drag & drop events used
|
|
55
|
+
// by the data grid's column reordering.
|
|
56
|
+
'drag',
|
|
57
|
+
'dragstart',
|
|
58
|
+
'dragend',
|
|
59
|
+
'dragenter',
|
|
60
|
+
'dragleave',
|
|
61
|
+
'dragover',
|
|
62
|
+
'drop',
|
|
63
|
+
'gesturestart',
|
|
64
|
+
'gesturechange',
|
|
65
|
+
'gestureend',
|
|
66
|
+
'focus',
|
|
67
|
+
'blur',
|
|
68
|
+
'change',
|
|
69
|
+
'input',
|
|
70
|
+
'reset',
|
|
71
|
+
'select',
|
|
72
|
+
'submit',
|
|
73
|
+
'focusin',
|
|
74
|
+
'focusout',
|
|
75
|
+
'load',
|
|
76
|
+
'unload',
|
|
77
|
+
'beforeunload',
|
|
78
|
+
'resize',
|
|
79
|
+
'move',
|
|
80
|
+
'DOMContentLoaded',
|
|
81
|
+
'readystatechange',
|
|
82
|
+
'error',
|
|
83
|
+
'abort',
|
|
84
|
+
'scroll'
|
|
85
|
+
])
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Private methods
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
function makeEventUid(element, uid) {
|
|
92
|
+
return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getElementEvents(element) {
|
|
96
|
+
const uid = makeEventUid(element)
|
|
97
|
+
|
|
98
|
+
element.uidEvent = uid
|
|
99
|
+
eventRegistry[uid] = eventRegistry[uid] || {}
|
|
100
|
+
|
|
101
|
+
return eventRegistry[uid]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function bootstrapHandler(element, fn) {
|
|
105
|
+
return function handler(event) {
|
|
106
|
+
hydrateObj(event, { delegateTarget: element })
|
|
107
|
+
|
|
108
|
+
if (handler.oneOff) {
|
|
109
|
+
EventHandler.off(element, event.type, fn)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return fn.apply(element, [event])
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function bootstrapDelegationHandler(element, selector, fn) {
|
|
117
|
+
return function handler(event) {
|
|
118
|
+
const domElements = element.querySelectorAll(selector)
|
|
119
|
+
|
|
120
|
+
for (let { target } = event; target && target !== this; target = target.parentNode) {
|
|
121
|
+
for (const domElement of domElements) {
|
|
122
|
+
if (domElement !== target) {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
hydrateObj(event, { delegateTarget: target })
|
|
127
|
+
|
|
128
|
+
if (handler.oneOff) {
|
|
129
|
+
EventHandler.off(element, event.type, selector, fn)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return fn.apply(target, [event])
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function findHandler(events, callable, delegationSelector = null) {
|
|
139
|
+
return Object.values(events)
|
|
140
|
+
.find(event => event.callable === callable && event.delegationSelector === delegationSelector)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
|
|
144
|
+
const isDelegated = typeof handler === 'string'
|
|
145
|
+
// TODO: tooltip passes `false` instead of selector, so we need to check
|
|
146
|
+
const callable = isDelegated ? delegationFunction : (handler || delegationFunction)
|
|
147
|
+
let typeEvent = getTypeEvent(originalTypeEvent)
|
|
148
|
+
|
|
149
|
+
if (!nativeEvents.has(typeEvent)) {
|
|
150
|
+
typeEvent = originalTypeEvent
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return [isDelegated, callable, typeEvent]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
|
|
157
|
+
if (typeof originalTypeEvent !== 'string' || !element) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
|
|
162
|
+
|
|
163
|
+
// in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
|
|
164
|
+
// this prevents the handler from being dispatched the same way as mouseover or mouseout does
|
|
165
|
+
if (originalTypeEvent in customEvents) {
|
|
166
|
+
const wrapFunction = fn => {
|
|
167
|
+
return function (event) {
|
|
168
|
+
if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
|
|
169
|
+
return fn.call(this, event)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
callable = wrapFunction(callable)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const events = getElementEvents(element)
|
|
178
|
+
const handlers = events[typeEvent] || (events[typeEvent] = {})
|
|
179
|
+
const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)
|
|
180
|
+
|
|
181
|
+
if (previousFunction) {
|
|
182
|
+
previousFunction.oneOff = previousFunction.oneOff && oneOff
|
|
183
|
+
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))
|
|
188
|
+
const fn = isDelegated ?
|
|
189
|
+
bootstrapDelegationHandler(element, handler, callable) :
|
|
190
|
+
bootstrapHandler(element, callable)
|
|
191
|
+
|
|
192
|
+
fn.delegationSelector = isDelegated ? handler : null
|
|
193
|
+
fn.callable = callable
|
|
194
|
+
fn.oneOff = oneOff
|
|
195
|
+
fn.uidEvent = uid
|
|
196
|
+
handlers[uid] = fn
|
|
197
|
+
|
|
198
|
+
element.addEventListener(typeEvent, fn, isDelegated)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
|
|
202
|
+
const fn = findHandler(events[typeEvent], handler, delegationSelector)
|
|
203
|
+
|
|
204
|
+
if (!fn) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
|
|
209
|
+
delete events[typeEvent][fn.uidEvent]
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
|
|
213
|
+
const storeElementEvent = events[typeEvent] || {}
|
|
214
|
+
|
|
215
|
+
for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
|
|
216
|
+
if (handlerKey.includes(namespace)) {
|
|
217
|
+
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function getTypeEvent(event) {
|
|
223
|
+
// allow to get the native events from namespaced events ('click.coreui.button' --> 'click')
|
|
224
|
+
event = event.replace(stripNameRegex, '')
|
|
225
|
+
return customEvents[event] || event
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const EventHandler = {
|
|
229
|
+
on(element, event, handler, delegationFunction) {
|
|
230
|
+
addHandler(element, event, handler, delegationFunction, false)
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
one(element, event, handler, delegationFunction) {
|
|
234
|
+
addHandler(element, event, handler, delegationFunction, true)
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
off(element, originalTypeEvent, handler, delegationFunction) {
|
|
238
|
+
if (typeof originalTypeEvent !== 'string' || !element) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
|
|
243
|
+
const inNamespace = typeEvent !== originalTypeEvent
|
|
244
|
+
const events = getElementEvents(element)
|
|
245
|
+
const storeElementEvent = events[typeEvent] || {}
|
|
246
|
+
const isNamespace = originalTypeEvent.startsWith('.')
|
|
247
|
+
|
|
248
|
+
if (typeof callable !== 'undefined') {
|
|
249
|
+
// Simplest case: handler is passed, remove that listener ONLY.
|
|
250
|
+
if (!Object.keys(storeElementEvent).length) {
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (isNamespace) {
|
|
259
|
+
for (const elementEvent of Object.keys(events)) {
|
|
260
|
+
removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
|
|
265
|
+
const handlerKey = keyHandlers.replace(stripUidRegex, '')
|
|
266
|
+
|
|
267
|
+
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
|
|
268
|
+
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
trigger(element, event, args) {
|
|
274
|
+
if (typeof event !== 'string' || !element) {
|
|
275
|
+
return null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const $ = getjQuery()
|
|
279
|
+
const typeEvent = getTypeEvent(event)
|
|
280
|
+
const inNamespace = event !== typeEvent
|
|
281
|
+
|
|
282
|
+
let jQueryEvent = null
|
|
283
|
+
let bubbles = true
|
|
284
|
+
let nativeDispatch = true
|
|
285
|
+
let defaultPrevented = false
|
|
286
|
+
|
|
287
|
+
if (inNamespace && $) {
|
|
288
|
+
jQueryEvent = $.Event(event, args)
|
|
289
|
+
|
|
290
|
+
$(element).trigger(jQueryEvent)
|
|
291
|
+
bubbles = !jQueryEvent.isPropagationStopped()
|
|
292
|
+
nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
|
|
293
|
+
defaultPrevented = jQueryEvent.isDefaultPrevented()
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)
|
|
297
|
+
|
|
298
|
+
if (defaultPrevented) {
|
|
299
|
+
evt.preventDefault()
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (nativeDispatch) {
|
|
303
|
+
element.dispatchEvent(evt)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (evt.defaultPrevented && jQueryEvent) {
|
|
307
|
+
jQueryEvent.preventDefault()
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return evt
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function hydrateObj(obj, meta = {}) {
|
|
315
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
316
|
+
try {
|
|
317
|
+
obj[key] = value
|
|
318
|
+
} catch {
|
|
319
|
+
Object.defineProperty(obj, key, {
|
|
320
|
+
configurable: true,
|
|
321
|
+
get() {
|
|
322
|
+
return value
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return obj
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export default EventHandler
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Hand-written types for the vendored manipulator.js (see the provenance header there)
|
|
2
|
+
declare const Manipulator: {
|
|
3
|
+
setDataAttribute(element: Element, key: string, value: unknown): void
|
|
4
|
+
removeDataAttribute(element: Element, key: string): void
|
|
5
|
+
getDataAttributes(element: Element): Record<string, unknown>
|
|
6
|
+
getDataAttribute(element: Element, key: string): unknown
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default Manipulator
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Vendored from https://github.com/coreui/coreui-pro/blob/bc4daf1cd79d527bf665d615c0a31f53ab0c3d4e/js/src/dom/manipulator.js — sync manually
|
|
2
|
+
/**
|
|
3
|
+
* --------------------------------------------------------------------------
|
|
4
|
+
* CoreUI dom/manipulator.js
|
|
5
|
+
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
|
|
6
|
+
*
|
|
7
|
+
* This is a modified version of the Bootstrap's dom/manipulator.js
|
|
8
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
9
|
+
* --------------------------------------------------------------------------
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function normalizeData(value) {
|
|
13
|
+
if (value === 'true') {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (value === 'false') {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (value === Number(value).toString()) {
|
|
22
|
+
return Number(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (value === '' || value === 'null') {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof value !== 'string') {
|
|
30
|
+
return value
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(decodeURIComponent(value))
|
|
35
|
+
} catch {
|
|
36
|
+
return value
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeDataKey(key) {
|
|
41
|
+
return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Manipulator = {
|
|
45
|
+
setDataAttribute(element, key, value) {
|
|
46
|
+
element.setAttribute(`data-coreui-${normalizeDataKey(key)}`, value)
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
removeDataAttribute(element, key) {
|
|
50
|
+
element.removeAttribute(`data-coreui-${normalizeDataKey(key)}`)
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
getDataAttributes(element) {
|
|
54
|
+
if (!element) {
|
|
55
|
+
return {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const attributes = {}
|
|
59
|
+
const coreuiKeys = Object.keys(element.dataset).filter(key => key.startsWith('coreui') && !key.startsWith('coreuiConfig'))
|
|
60
|
+
|
|
61
|
+
for (const key of coreuiKeys) {
|
|
62
|
+
let pureKey = key.replace(/^coreui/, '')
|
|
63
|
+
pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)
|
|
64
|
+
attributes[pureKey] = normalizeData(element.dataset[key])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return attributes
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
getDataAttribute(element, key) {
|
|
71
|
+
return normalizeData(element.getAttribute(`data-coreui-${normalizeDataKey(key)}`))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default Manipulator
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------------------------------------
|
|
3
|
+
* CoreUI PRO data-grid engine/active-cell.ts
|
|
4
|
+
* License (https://coreui.io/pro/license/)
|
|
5
|
+
* --------------------------------------------------------------------------
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// The cellNavigation substrate: one grid-owned active cell (roving tabindex).
|
|
9
|
+
// rowIndex is an index into the current row model (page-local under
|
|
10
|
+
// pagination); -1 addresses the header label row. Focus is derived from this
|
|
11
|
+
// state, never from the DOM - the body applies it after every render pass.
|
|
12
|
+
|
|
13
|
+
export interface ActiveCell {
|
|
14
|
+
rowIndex: number
|
|
15
|
+
columnId: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ActiveCellContext {
|
|
19
|
+
// Visible leaf column ids in visual order (pinning + order + visibility).
|
|
20
|
+
columnIds: string[]
|
|
21
|
+
rowCount: number
|
|
22
|
+
// Rows per PageUp/PageDown step: visible viewport rows (virtualized) or the
|
|
23
|
+
// page size (paginated).
|
|
24
|
+
pageStep: number
|
|
25
|
+
paginated: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface NavigationResult {
|
|
29
|
+
cell: ActiveCell
|
|
30
|
+
pageDelta?: -1 | 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const HEADER_ROW_INDEX = -1
|
|
34
|
+
|
|
35
|
+
const clampColumn = (columnId: string, columnIds: string[]): string | null => {
|
|
36
|
+
if (columnIds.includes(columnId)) {
|
|
37
|
+
return columnId
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return columnIds.length > 0 ? columnIds[0] : null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Nulls the cell when the grid has no addressable cells; otherwise snaps it
|
|
44
|
+
// back into the current column set and row range.
|
|
45
|
+
export const clampActiveCell = (cell: ActiveCell | null, context: ActiveCellContext): ActiveCell | null => {
|
|
46
|
+
if (!cell) {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const columnId = clampColumn(cell.columnId, context.columnIds)
|
|
51
|
+
if (columnId === null) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const maxRow = context.rowCount - 1
|
|
56
|
+
const rowIndex = cell.rowIndex < 0 ?
|
|
57
|
+
HEADER_ROW_INDEX :
|
|
58
|
+
Math.max(0, Math.min(cell.rowIndex, Math.max(maxRow, 0)))
|
|
59
|
+
|
|
60
|
+
if (rowIndex >= 0 && maxRow < 0) {
|
|
61
|
+
return { rowIndex: HEADER_ROW_INDEX, columnId }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { rowIndex, columnId }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The APG key decision table - a pure function so every edition asserts
|
|
68
|
+
// identical outcomes. Returns null for keys the grid does not handle.
|
|
69
|
+
export const resolveNavigation = (
|
|
70
|
+
key: string,
|
|
71
|
+
ctrlKey: boolean,
|
|
72
|
+
current: ActiveCell,
|
|
73
|
+
context: ActiveCellContext
|
|
74
|
+
): NavigationResult | null => {
|
|
75
|
+
const { columnIds, rowCount, pageStep, paginated } = context
|
|
76
|
+
const columnIndex = columnIds.indexOf(current.columnId)
|
|
77
|
+
if (columnIndex === -1 || columnIds.length === 0) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lastRow = rowCount - 1
|
|
82
|
+
const lastColumn = columnIds.length - 1
|
|
83
|
+
const cell = (rowIndex: number, index: number): NavigationResult => ({
|
|
84
|
+
cell: { rowIndex, columnId: columnIds[index] }
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
switch (key) {
|
|
88
|
+
case 'ArrowLeft': {
|
|
89
|
+
return cell(current.rowIndex, Math.max(columnIndex - 1, 0))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'ArrowRight': {
|
|
93
|
+
return cell(current.rowIndex, Math.min(columnIndex + 1, lastColumn))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'ArrowUp': {
|
|
97
|
+
return cell(Math.max(current.rowIndex - 1, HEADER_ROW_INDEX), columnIndex)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'ArrowDown': {
|
|
101
|
+
return cell(Math.min(current.rowIndex + 1, lastRow), columnIndex)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'Home': {
|
|
105
|
+
if (ctrlKey) {
|
|
106
|
+
return cell(rowCount > 0 ? 0 : HEADER_ROW_INDEX, 0)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return cell(current.rowIndex, 0)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case 'End': {
|
|
113
|
+
if (ctrlKey) {
|
|
114
|
+
return cell(rowCount > 0 ? lastRow : HEADER_ROW_INDEX, lastColumn)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return cell(current.rowIndex, lastColumn)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
case 'PageUp': {
|
|
121
|
+
if (paginated && current.rowIndex === 0) {
|
|
122
|
+
return { ...cell(0, columnIndex), pageDelta: -1 }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return cell(Math.max(current.rowIndex - pageStep, 0), columnIndex)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case 'PageDown': {
|
|
129
|
+
if (paginated && current.rowIndex === lastRow) {
|
|
130
|
+
return { ...cell(lastRow, columnIndex), pageDelta: 1 }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return cell(Math.min(current.rowIndex + pageStep, lastRow), columnIndex)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
default: {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ActiveCellManager {
|
|
143
|
+
get: () => ActiveCell | null
|
|
144
|
+
// Stores the cell; focus: true marks a pending focus request the next
|
|
145
|
+
// render pass consumes (set false for pointer-driven activation, where the
|
|
146
|
+
// browser already moved focus).
|
|
147
|
+
set: (cell: ActiveCell, options?: { focus?: boolean }) => void
|
|
148
|
+
clamp: (context: ActiveCellContext) => void
|
|
149
|
+
reset: () => void
|
|
150
|
+
consumeFocusRequest: () => boolean
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const createActiveCellManager = (onChange: () => void): ActiveCellManager => {
|
|
154
|
+
let current: ActiveCell | null = null
|
|
155
|
+
let focusRequested = false
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
get: () => current,
|
|
159
|
+
set(cell, options = {}) {
|
|
160
|
+
const changed = !current || current.rowIndex !== cell.rowIndex || current.columnId !== cell.columnId
|
|
161
|
+
current = cell
|
|
162
|
+
if (options.focus) {
|
|
163
|
+
focusRequested = true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (changed || options.focus) {
|
|
167
|
+
onChange()
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
clamp(context) {
|
|
171
|
+
const next = clampActiveCell(current, context)
|
|
172
|
+
const changed = Boolean(current) !== Boolean(next) ||
|
|
173
|
+
(current && next && (current.rowIndex !== next.rowIndex || current.columnId !== next.columnId))
|
|
174
|
+
current = next
|
|
175
|
+
if (changed) {
|
|
176
|
+
onChange()
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
reset() {
|
|
180
|
+
if (current) {
|
|
181
|
+
current = null
|
|
182
|
+
focusRequested = false
|
|
183
|
+
onChange()
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
consumeFocusRequest() {
|
|
187
|
+
if (!focusRequested) {
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
focusRequested = false
|
|
192
|
+
return true
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|