datatables.net-columncontrol 0.9.2

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.
@@ -0,0 +1,2496 @@
1
+ /*! ColumnControl 0.9.2
2
+ * Copyright (c) SpryMedia Ltd - datatables.net/license
3
+ *
4
+ * SVG icons: ISC License
5
+ * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
6
+ * All other copyright (c) for Lucide are held by Lucide Contributors 2022.
7
+ */
8
+
9
+ import jQuery from 'jquery';
10
+ import DataTable from 'datatables.net';
11
+
12
+ // Allow reassignment of the $ variable
13
+ let $ = jQuery;
14
+
15
+
16
+ function createElement(type, classes, text, children) {
17
+ if (classes === void 0) { classes = []; }
18
+ if (text === void 0) { text = null; }
19
+ if (children === void 0) { children = []; }
20
+ var el = document.createElement(type);
21
+ addClass(el, classes);
22
+ if (text) {
23
+ el.innerHTML = text;
24
+ }
25
+ children.forEach(function (child) {
26
+ el.appendChild(child);
27
+ });
28
+ return el;
29
+ }
30
+ function addClass(el, classes) {
31
+ if (!classes) {
32
+ return;
33
+ }
34
+ if (!Array.isArray(classes)) {
35
+ classes = [classes];
36
+ }
37
+ classes.forEach(function (className) {
38
+ if (className) {
39
+ el.classList.add(className);
40
+ }
41
+ });
42
+ }
43
+
44
+ // The SVG for many of these icons are from Lucide ( https://lucide.dev ), which are available
45
+ // under the ISC License. There are a number of custom icons as well. These are optimised through
46
+ // https://optimize.svgomg.net/
47
+ function wrap(paths) {
48
+ return ('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
49
+ paths +
50
+ '</svg>');
51
+ }
52
+ var icons = {
53
+ chevronRight: wrap('<path d="m9 18 6-6-6-6"/>'),
54
+ // columns-3
55
+ columns: wrap('<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="M15 3v18"/>'),
56
+ // Custom
57
+ contains: wrap('<path d="M10 3h4v18h-4z"/><path d="M18 8h3v9h-3"/><path d="M6 17H3V8h3"/>'),
58
+ empty: wrap('<circle cx="12" cy="12" r="10"/>'),
59
+ ends: wrap('<path d="M21 3h-4v18h4z"/><path d="M13 8H3v9h10"/>'),
60
+ // Customised
61
+ equal: wrap('<line x1="5" x2="19" y1="9" y2="9"/><line x1="5" x2="19" y1="15" y2="15"/>'),
62
+ greater: wrap('<path d="m9 18 6-6-6-6"/>'),
63
+ // Custom
64
+ greaterOrEqual: wrap('<path d="m9 16 6-6-6-6"/><path d="m9 21 6-6"/>'),
65
+ less: wrap('<path d="m15 18-6-6 6-6"/>'),
66
+ // Custom
67
+ lessOrEqual: wrap('<path d="m15 16-6-6 6-6"/><path d="m15 21-6-6"/>'),
68
+ menu: wrap('<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>'),
69
+ // move-horizontal
70
+ move: wrap('<line x1="12" x2="12" y1="3" y2="21"/><polyline points="8 8 4 12 8 16"/><polyline points="16 16 20 12 16 8"/>'),
71
+ // arrow-left-from-line
72
+ moveLeft: wrap('<path d="m9 6-6 6 6 6"/><path d="M3 12h14"/><path d="M21 19V5"/>'),
73
+ // arrow-right-from-line
74
+ moveRight: wrap('<path d="M3 5v14"/><path d="M21 12H7"/><path d="m15 18 6-6-6-6"/>'),
75
+ // Custom
76
+ notContains: wrap('<path d="M15 4 9 20"/><path d="M3 8h18v9H3z"/>'),
77
+ notEmpty: wrap('<circle cx="12" cy="12" r="10"/><line x1="9" x2="15" y1="15" y2="9"/>'),
78
+ notEqual: wrap('<path d="M5 9h14"/><path d="M5 15h14"/><path d="M15 5 9 19"/>'),
79
+ // Custom
80
+ orderAddAsc: wrap('<path d="M17 21v-8"/><path d="M3 4h6"/><path d="M3 8h9"/><path d="M3 12h10"/><path d="M13 17h8"/>'),
81
+ // Custom
82
+ orderAddDesc: wrap('<path d="M17 21v-8"/><path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="M13 17h8"/>'),
83
+ orderAsc: wrap('<path d="m3 8 4-4 4 4"/><path d="M7 4v16"/><path d="M11 12h4"/><path d="M11 16h7"/><path d="M11 20h10"/>'),
84
+ // Custom
85
+ orderClear: wrap('<path d="m21 21-8-8"/><path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="m13 21 8-8"/>'),
86
+ orderDesc: wrap('<path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M11 4h10"/><path d="M11 8h7"/><path d="M11 12h4"/>'),
87
+ // Custom
88
+ orderRemove: wrap('<path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="M13 17h8"/>'),
89
+ // Custom
90
+ orderNone: wrap('<path d="m3 8 4-4 4 4"/><path d="m11 16-4 4-4-4"/><path d="M7 4v16"/><path d="M15 8h6"/><path d="M15 16h6"/><path d="M13 12h8"/>'),
91
+ // search
92
+ search: wrap('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>'),
93
+ // search-x
94
+ searchClear: wrap('<path d="m13.5 8.5-5 5"/><path d="m8.5 8.5 5 5"/><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>'),
95
+ // Custom
96
+ starts: wrap('<path d="M3 3h4v18H3z"/><path d="M11 8h10v9H11"/>'),
97
+ // tick
98
+ tick: wrap('<path d="M20 6 9 17l-5-5"/>'),
99
+ // x
100
+ x: wrap('<path d="M18 6 6 18"/><path d="m6 6 12 12"/>')
101
+ };
102
+
103
+ /**
104
+ * Close all or only other dropdowns
105
+ *
106
+ * @param e Event or null to close all others
107
+ */
108
+ function close(e) {
109
+ if (e === void 0) { e = null; }
110
+ document.querySelectorAll('div.dtcc-dropdown').forEach(function (el) {
111
+ if (e === null || !el.contains(e.target)) {
112
+ el._close();
113
+ if (!e._closed) {
114
+ e._closed = [];
115
+ }
116
+ e._closed.push(el);
117
+ }
118
+ });
119
+ }
120
+ /**
121
+ * Position the dropdown relative to the button that activated it, with possible corrections
122
+ * to make sure it is visible on the page.
123
+ *
124
+ * @param dropdown Dropdown element
125
+ * @param dt Container DataTable
126
+ * @param btn Button the dropdown emanates from
127
+ */
128
+ function positionDropdown(dropdown, dt, btn) {
129
+ var dtContainer = dt.table().container();
130
+ var header = btn.closest('div.dt-column-header');
131
+ var headerStyle = getComputedStyle(header);
132
+ var dropdownWidth = dropdown.offsetWidth;
133
+ var position = relativePosition(dtContainer, btn);
134
+ var left, top;
135
+ top = position.top + btn.offsetHeight;
136
+ if (headerStyle.flexDirection === 'row-reverse') {
137
+ // Icon is on the left of the header - align the left hand sides
138
+ left = position.left;
139
+ }
140
+ else {
141
+ // Icon is on the right of the header - align the right hand sides
142
+ left = position.left - dropdownWidth + btn.offsetWidth;
143
+ }
144
+ // Corrections - don't extend past the DataTable to the left and right
145
+ var containerOffsetLeft = dtContainer.offsetLeft;
146
+ var containerWidth = dtContainer.offsetWidth;
147
+ if (left + dropdownWidth > containerWidth) {
148
+ left -= left + dropdownWidth - containerWidth;
149
+ }
150
+ if (left < containerOffsetLeft) {
151
+ left = containerOffsetLeft;
152
+ }
153
+ dropdown.style.top = top + 'px';
154
+ dropdown.style.left = left + 'px';
155
+ }
156
+ /**
157
+ * Display the dropdown in the document
158
+ *
159
+ * @param dropdown Dropdown element
160
+ * @param dt Container DataTable
161
+ * @param btn Button the dropdown emanates from
162
+ * @returns Function to call when the dropdown should be removed from the document
163
+ */
164
+ function attachDropdown(dropdown, dt, btn) {
165
+ var dtContainer = dt.table().container();
166
+ dropdown._shown = true;
167
+ dtContainer.append(dropdown);
168
+ positionDropdown(dropdown, dt, btn.element());
169
+ // Note that this could be called when the dropdown has already been removed from the document
170
+ // via another dropdown being shown. This will clean up the event on the next body click.
171
+ var removeDropdown = function (e) {
172
+ // Not in document, so just clean up the event handler
173
+ if (!dropdown._shown) {
174
+ document.body.removeEventListener('click', removeDropdown);
175
+ return;
176
+ }
177
+ // If the click is inside the dropdown, ignore it - we don't want to immediately close
178
+ if (e.target === dropdown || dropdown.contains(e.target)) {
179
+ return;
180
+ }
181
+ dropdown._close();
182
+ document.body.removeEventListener('click', removeDropdown);
183
+ };
184
+ document.body.addEventListener('click', removeDropdown);
185
+ return removeDropdown;
186
+ }
187
+ /**
188
+ * Get the position of an element, relative to a given parent. The origin MUST be under the
189
+ * parent's tree.
190
+ *
191
+ * @param parent Parent element to get position relative to
192
+ * @param origin Target element
193
+ */
194
+ function relativePosition(parent, origin) {
195
+ var top = 0;
196
+ var left = 0;
197
+ while (origin && origin !== parent && origin !== document.body) {
198
+ top += origin.offsetTop;
199
+ left += origin.offsetLeft;
200
+ origin = origin.offsetParent;
201
+ }
202
+ return {
203
+ top: top,
204
+ left: left
205
+ };
206
+ }
207
+ var dropdownContent = {
208
+ classes: {
209
+ container: 'dtcc-dropdown',
210
+ liner: 'dtcc-dropdown-liner'
211
+ },
212
+ defaults: {
213
+ className: 'dropdown',
214
+ content: [],
215
+ icon: 'menu',
216
+ text: 'More...'
217
+ },
218
+ init: function (config) {
219
+ var dt = this.dt();
220
+ var dropdown = createElement('div', dropdownContent.classes.container, '', [
221
+ createElement('div', dropdownContent.classes.liner)
222
+ ]);
223
+ dropdown._shown = false;
224
+ dropdown._close = function () {
225
+ dropdown.remove();
226
+ dropdown._shown = false;
227
+ };
228
+ // A liner element allows more styling options, so the contents go inside this
229
+ var liner = dropdown.childNodes[0];
230
+ var btn = new Button(dt)
231
+ .text(dt.i18n('columnControl.dropdown', config.text))
232
+ .icon(config.icon)
233
+ .className(config.className)
234
+ .dropdownDisplay(liner)
235
+ .handler(function (e) {
236
+ // Do nothing if our dropdown was just closed as part of the event (i.e. allow
237
+ // the button to toggle it closed)
238
+ if (e._closed && e._closed.includes(dropdown)) {
239
+ return;
240
+ }
241
+ attachDropdown(dropdown, dt, config._parents ? config._parents[0] : btn);
242
+ });
243
+ // Add the content for the dropdown
244
+ for (var i = 0; i < config.content.length; i++) {
245
+ var content = this.resolve(config.content[i]);
246
+ // For nested items we need to keep a reference to the top level so the sub-levels
247
+ // can communicate back - e.g. active or positioned relative to that top level.
248
+ if (!content.config._parents) {
249
+ content.config._parents = [];
250
+ }
251
+ content.config._parents.push(btn);
252
+ var el = content.plugin.init.call(this, content.config);
253
+ liner.appendChild(el);
254
+ }
255
+ // For nested dropdowns, add an extra icon element to show that it will dropdown further
256
+ if (config._parents && config._parents.length) {
257
+ btn.extra('chevronRight');
258
+ }
259
+ // Reposition if needed
260
+ dt.on('columns-reordered', function () {
261
+ positionDropdown(dropdown, dt, btn.element());
262
+ });
263
+ return btn.element();
264
+ }
265
+ };
266
+
267
+ var _namespace = 0;
268
+ var Button = /** @class */ (function () {
269
+ /**
270
+ * Create a new button for use in ColumnControl contents. Buttons created by this class can be
271
+ * used at the top level in the header or in a dropdown.
272
+ */
273
+ function Button(dt) {
274
+ this._s = {
275
+ active: false,
276
+ activeList: [],
277
+ buttonClick: null,
278
+ dt: null,
279
+ enabled: true,
280
+ label: '',
281
+ namespace: '',
282
+ value: null
283
+ };
284
+ this._s.dt = dt;
285
+ this._dom = {
286
+ button: createElement('button', Button.classes.container),
287
+ dropdownDisplay: null,
288
+ extra: createElement('span', 'dtcc-button-extra'),
289
+ icon: createElement('span', 'dtcc-button-icon'),
290
+ state: createElement('span', 'dtcc-button-state'),
291
+ text: createElement('span', 'dtcc-button-text')
292
+ };
293
+ this._dom.button.append(this._dom.icon);
294
+ this._dom.button.append(this._dom.text);
295
+ this._dom.button.append(this._dom.state);
296
+ this._dom.button.append(this._dom.extra);
297
+ // Default state is enabled
298
+ this.enable(true);
299
+ }
300
+ Button.prototype.active = function (active) {
301
+ if (active === undefined) {
302
+ return this._s.active;
303
+ }
304
+ this._s.active = active;
305
+ this._checkActive();
306
+ return this;
307
+ };
308
+ /**
309
+ * A button can be marked as active by any of its sub-buttons (i.e. if it is a dropdown)
310
+ * and each one needs to be able to enable this button without effecting the active state
311
+ * trigged by any other sub-buttons. This method provides a way to do that.
312
+ *
313
+ * @param unique Unique id for the activate state
314
+ * @param active If it is active
315
+ * @returns Button instance
316
+ */
317
+ Button.prototype.activeList = function (unique, active) {
318
+ this._s.activeList[unique] = active;
319
+ this._checkActive();
320
+ return this;
321
+ };
322
+ /**
323
+ * Scan over the dropdown element looking for any visible content. If there isn't any then
324
+ * we hide this button.
325
+ *
326
+ * @returns Button instance
327
+ */
328
+ Button.prototype.checkDisplay = function () {
329
+ var visible = 0;
330
+ var children = this._dom.dropdownDisplay.childNodes;
331
+ for (var i = 0; i < children.length; i++) {
332
+ // No need to getComputedStyle since if a button is hidden, it was done with JS writing
333
+ // to style.display, so we can check against that.
334
+ if (children[i].style.display !== 'none') {
335
+ visible++;
336
+ }
337
+ }
338
+ if (visible === 0) {
339
+ this._dom.button.style.display = 'none';
340
+ }
341
+ return this;
342
+ };
343
+ /**
344
+ * Set the class name for the button
345
+ *
346
+ * @param className Class name
347
+ * @returns Button instance
348
+ */
349
+ Button.prototype.className = function (className) {
350
+ this._dom.button.classList.add('dtcc-button_' + className);
351
+ return this;
352
+ };
353
+ /**
354
+ * Destroy the button, cleaning up event listeners
355
+ */
356
+ Button.prototype.destroy = function () {
357
+ if (this._s.buttonClick) {
358
+ this._dom.button.removeEventListener('click', this._s.buttonClick);
359
+ }
360
+ if (this._s.namespace) {
361
+ this._s.dt.off('destroy.' + this._s.namespace);
362
+ }
363
+ };
364
+ /**
365
+ * Relevant for drop downs only. When a button in a dropdown is hidden, we might want to
366
+ * hide the host button as well (if it has nothing else to show). For that we need to know
367
+ * what the dropdown element is.
368
+ *
369
+ * @param el Element that can be used for telling us about drop down elements.
370
+ * @returns Button instance
371
+ */
372
+ Button.prototype.dropdownDisplay = function (el) {
373
+ this._dom.dropdownDisplay = el;
374
+ return this;
375
+ };
376
+ /**
377
+ * Get the DOM Button element to attach into the document
378
+ *
379
+ * @returns The Button element
380
+ */
381
+ Button.prototype.element = function () {
382
+ return this._dom.button;
383
+ };
384
+ /**
385
+ * Set if the button should be enabled or not.
386
+ *
387
+ * @param enable Toggle the enable state
388
+ * @returns Button instance
389
+ */
390
+ Button.prototype.enable = function (enable) {
391
+ this._dom.button.classList.toggle('dtcc-button_disabled', !enable);
392
+ this._s.enabled = enable;
393
+ return this;
394
+ };
395
+ /**
396
+ * Set the extra information icon
397
+ *
398
+ * @param icon Icon name
399
+ * @returns Button instance
400
+ */
401
+ Button.prototype.extra = function (icon) {
402
+ this._dom.extra.innerHTML = icon ? icons[icon] : '';
403
+ return this;
404
+ };
405
+ /**
406
+ * Set the event handler for when the button is activated
407
+ *
408
+ * @param fn Event handler
409
+ * @returns Button instance
410
+ */
411
+ Button.prototype.handler = function (fn) {
412
+ var _this = this;
413
+ var buttonClick = function (e) {
414
+ // Close any dropdowns which are already open
415
+ close(e);
416
+ // Stop bubbling to the DataTables default header, which might still be enabled
417
+ e.stopPropagation();
418
+ e.preventDefault();
419
+ if (_this._s.enabled) {
420
+ fn(e);
421
+ }
422
+ };
423
+ this._s.buttonClick = buttonClick;
424
+ this._s.namespace = 'dtcc-' + _namespace++;
425
+ this._dom.button.addEventListener('click', buttonClick);
426
+ // Use a unique namespace to be able to easily remove per button
427
+ this._s.dt.on('destroy.' + this._s.namespace, function () {
428
+ _this.destroy();
429
+ });
430
+ return this;
431
+ };
432
+ /**
433
+ * Set the icon to display in the button
434
+ *
435
+ * @param icon Icon name
436
+ * @returns Button instance
437
+ */
438
+ Button.prototype.icon = function (icon) {
439
+ this._dom.icon.innerHTML = icon ? icons[icon] : '';
440
+ return this;
441
+ };
442
+ Button.prototype.text = function (text) {
443
+ if (text === undefined) {
444
+ return this._s.label;
445
+ }
446
+ this._dom.text.innerHTML = text;
447
+ this._s.label = text; // for fast retrieval
448
+ return this;
449
+ };
450
+ Button.prototype.value = function (val) {
451
+ if (val === undefined) {
452
+ return this._s.value;
453
+ }
454
+ this._s.value = val;
455
+ return this;
456
+ };
457
+ /**
458
+ * Check if anything is making this button active
459
+ *
460
+ * @returns Self for chaining
461
+ */
462
+ Button.prototype._checkActive = function () {
463
+ if (this._s.active === true || Object.values(this._s.activeList).includes(true)) {
464
+ this._dom.state.innerHTML = icons.tick;
465
+ this._dom.button.classList.add('dtcc-button_active');
466
+ }
467
+ else {
468
+ this._dom.state.innerHTML = '';
469
+ this._dom.button.classList.remove('dtcc-button_active');
470
+ }
471
+ return this;
472
+ };
473
+ Button.classes = {
474
+ container: 'dtcc-button'
475
+ };
476
+ return Button;
477
+ }());
478
+
479
+ var CheckList = /** @class */ (function () {
480
+ /**
481
+ * Container for a list of buttons
482
+ */
483
+ function CheckList(dt, opts) {
484
+ var _this = this;
485
+ this._s = {
486
+ buttons: [],
487
+ dt: null,
488
+ handler: function () { },
489
+ search: ''
490
+ };
491
+ this._s.dt = dt;
492
+ this._dom = {
493
+ buttons: createElement('div', 'dtcc-list-buttons'),
494
+ container: createElement('div', CheckList.classes.container),
495
+ controls: createElement('div', 'dtcc-list-controls'),
496
+ title: createElement('div', 'dtcc-list-title'),
497
+ selectAll: createElement('button', 'dtcc-list-selectAll', dt.i18n('columnControl.list.all', 'Select all')),
498
+ selectAllCount: createElement('span'),
499
+ selectNone: createElement('button', 'dtcc-list-selectNone', dt.i18n('columnControl.list.none', 'Deselect')),
500
+ selectNoneCount: createElement('span'),
501
+ search: createElement('input', CheckList.classes.input)
502
+ };
503
+ var dom = this._dom;
504
+ dom.search.setAttribute('type', 'text');
505
+ dom.container.append(dom.title);
506
+ dom.container.append(dom.controls);
507
+ dom.container.append(dom.buttons);
508
+ if (opts.select) {
509
+ dom.controls.append(dom.selectAll);
510
+ dom.controls.append(dom.selectNone);
511
+ dom.selectAll.append(dom.selectAllCount);
512
+ dom.selectNone.append(dom.selectNoneCount);
513
+ }
514
+ // Events
515
+ var searchInput = function () {
516
+ _this._s.search = dom.search.value;
517
+ _this._redraw();
518
+ };
519
+ var selectAllClick = function (e) {
520
+ _this.selectAll();
521
+ _this._s.handler(e, null, _this._s.buttons);
522
+ _this._updateCount();
523
+ };
524
+ var selectNoneClick = function (e) {
525
+ _this.selectNone();
526
+ _this._s.handler(e, null, _this._s.buttons);
527
+ _this._updateCount();
528
+ };
529
+ if (opts.search) {
530
+ dom.controls.append(dom.search);
531
+ dom.search.setAttribute('placeholder', dt.i18n('columnControl.list.search', 'Search...'));
532
+ dom.search.addEventListener('input', searchInput);
533
+ }
534
+ dom.selectAll.addEventListener('click', selectAllClick);
535
+ dom.selectNone.addEventListener('click', selectNoneClick);
536
+ dt.on('destroy', function () {
537
+ dom.selectAll.removeEventListener('click', selectAllClick);
538
+ dom.selectNone.removeEventListener('click', selectNoneClick);
539
+ dom.search.removeEventListener('input', searchInput);
540
+ });
541
+ }
542
+ /**
543
+ * Add one or more buttons to the list
544
+ *
545
+ * @param options Configuration for the button(s) to add
546
+ * @returns Self for chaining
547
+ */
548
+ CheckList.prototype.add = function (options, update) {
549
+ var _this = this;
550
+ if (!Array.isArray(options)) {
551
+ options = [options];
552
+ }
553
+ var _loop_1 = function (i) {
554
+ var option = options[i];
555
+ var btn = new Button(this_1._s.dt)
556
+ .active(option.active || false)
557
+ .handler(function (e) {
558
+ _this._s.handler(e, btn, _this._s.buttons);
559
+ _this._updateCount();
560
+ })
561
+ .icon(option.icon || '')
562
+ .text(option.label)
563
+ .value(option.value);
564
+ this_1._s.buttons.push(btn);
565
+ };
566
+ var this_1 = this;
567
+ for (var i = 0; i < options.length; i++) {
568
+ _loop_1(i);
569
+ }
570
+ var count = this._s.buttons.length;
571
+ if (update === true || update === undefined) {
572
+ this._dom.selectAllCount.innerHTML = count ? '(' + count + ')' : '';
573
+ this._redraw();
574
+ }
575
+ return this;
576
+ };
577
+ /**
578
+ * Find a button with a given value
579
+ *
580
+ * @param val Value to search for
581
+ * @returns Found button
582
+ */
583
+ CheckList.prototype.button = function (val) {
584
+ var buttons = this._s.buttons;
585
+ for (var i = 0; i < buttons.length; i++) {
586
+ if (buttons[i].value() === val) {
587
+ return buttons[i];
588
+ }
589
+ }
590
+ return null;
591
+ };
592
+ /**
593
+ * Remove all buttons from the list
594
+ *
595
+ * @returns Self for chaining
596
+ */
597
+ CheckList.prototype.clear = function () {
598
+ // Clean up the buttons
599
+ for (var i = 0; i < this._s.buttons.length; i++) {
600
+ this._s.buttons[i].destroy();
601
+ }
602
+ // Then empty them out
603
+ this._dom.buttons.replaceChildren();
604
+ this._s.buttons.length = 0;
605
+ return this;
606
+ };
607
+ /**
608
+ * Get the DOM container element to attach into the document
609
+ *
610
+ * @returns Container
611
+ */
612
+ CheckList.prototype.element = function () {
613
+ return this._dom.container;
614
+ };
615
+ /**
616
+ * Set the event handler for what happens when a button is clicked
617
+ *
618
+ * @param fn Event handler
619
+ */
620
+ CheckList.prototype.handler = function (fn) {
621
+ this._s.handler = fn;
622
+ return this;
623
+ };
624
+ /**
625
+ * Indicate that this is a search control and should listen for corresponding events
626
+ *
627
+ * @param dt DataTable instance
628
+ * @param idx Column index
629
+ */
630
+ CheckList.prototype.searchListener = function (dt, parent) {
631
+ var _this = this;
632
+ // Column control search clearing (column().ccSearchClear() method)
633
+ dt.on('cc-search-clear', function (e, colIdx) {
634
+ if (colIdx === parent.idx()) {
635
+ _this.selectNone();
636
+ _this._s.handler(e, null, _this._s.buttons);
637
+ _this._s.search = '';
638
+ _this._dom.search.value = '';
639
+ _this._redraw();
640
+ _this._updateCount();
641
+ }
642
+ });
643
+ return this;
644
+ };
645
+ /**
646
+ * Select all buttons
647
+ *
648
+ * @returns Self for chaining
649
+ */
650
+ CheckList.prototype.selectAll = function () {
651
+ for (var i = 0; i < this._s.buttons.length; i++) {
652
+ this._s.buttons[i].active(true);
653
+ }
654
+ return this;
655
+ };
656
+ /**
657
+ * Deselect all buttons
658
+ *
659
+ * @returns Self for chaining
660
+ */
661
+ CheckList.prototype.selectNone = function () {
662
+ for (var i = 0; i < this._s.buttons.length; i++) {
663
+ this._s.buttons[i].active(false);
664
+ }
665
+ return this;
666
+ };
667
+ /**
668
+ * Set the list's title
669
+ *
670
+ * @param title Display title
671
+ * @returns Button instance
672
+ */
673
+ CheckList.prototype.title = function (title) {
674
+ this._dom.title.innerHTML = title;
675
+ return this;
676
+ };
677
+ CheckList.prototype.values = function (values) {
678
+ var i;
679
+ var result = [];
680
+ var buttons = this._s.buttons;
681
+ if (values !== undefined) {
682
+ for (i = 0; i < buttons.length; i++) {
683
+ if (values.includes(buttons[i].value())) {
684
+ buttons[i].active(true);
685
+ }
686
+ }
687
+ this._updateCount();
688
+ return this;
689
+ }
690
+ for (i = 0; i < buttons.length; i++) {
691
+ if (buttons[i].active()) {
692
+ result.push(buttons[i].value());
693
+ }
694
+ }
695
+ return result;
696
+ };
697
+ /**
698
+ * Update the deselect counter
699
+ */
700
+ CheckList.prototype._updateCount = function () {
701
+ var count = this.values().length;
702
+ this._dom.selectNoneCount.innerHTML = count ? '(' + count + ')' : '';
703
+ };
704
+ /**
705
+ * Add the buttons to the page - taking into account filtering
706
+ */
707
+ CheckList.prototype._redraw = function () {
708
+ var buttons = this._s.buttons;
709
+ var el = this._dom.buttons;
710
+ var searchTerm = this._s.search.toLowerCase();
711
+ el.replaceChildren();
712
+ for (var i = 0; i < buttons.length; i++) {
713
+ var btn = buttons[i];
714
+ if (!searchTerm ||
715
+ btn
716
+ .text()
717
+ .toLowerCase()
718
+ .includes(searchTerm)) {
719
+ el.appendChild(btn.element());
720
+ }
721
+ }
722
+ };
723
+ CheckList.classes = {
724
+ container: 'dtcc-list',
725
+ input: 'dtcc-list-search'
726
+ };
727
+ return CheckList;
728
+ }());
729
+
730
+ var colVis = {
731
+ defaults: {
732
+ className: 'colVis',
733
+ columns: '',
734
+ search: false,
735
+ select: false,
736
+ title: 'Column visibility'
737
+ },
738
+ init: function (config) {
739
+ var dt = this.dt();
740
+ var checkList = new CheckList(dt, {
741
+ search: config.search,
742
+ select: config.select
743
+ })
744
+ .title(dt.i18n('columnControl.colVis', config.title))
745
+ .handler(function (e, btn, buttons) {
746
+ if (btn) {
747
+ btn.active(!btn.active());
748
+ }
749
+ apply(buttons);
750
+ });
751
+ // Need to apply in a loop to allow for select all / select none
752
+ var apply = function (buttons) {
753
+ for (var i = 0; i < buttons.length; i++) {
754
+ var btn = buttons[i];
755
+ var idx = btn.value();
756
+ var col = dt.column(idx);
757
+ if (btn.active() && !col.visible()) {
758
+ col.visible(true);
759
+ }
760
+ else if (!btn.active() && col.visible()) {
761
+ col.visible(false);
762
+ }
763
+ }
764
+ };
765
+ var rebuild = function () {
766
+ var columns = dt.columns(config.columns);
767
+ columns.every(function () {
768
+ checkList.add({
769
+ active: this.visible(),
770
+ label: this.title(),
771
+ value: this.index()
772
+ });
773
+ });
774
+ };
775
+ rebuild();
776
+ dt.on('column-visibility', function (e, s, colIdx, state) {
777
+ var btn = checkList.button(colIdx);
778
+ if (btn) {
779
+ btn.active(state);
780
+ }
781
+ });
782
+ dt.on('columns-reordered', function (e, details) {
783
+ checkList.clear();
784
+ rebuild();
785
+ });
786
+ return checkList.element();
787
+ }
788
+ };
789
+
790
+ var colVisDropdown = {
791
+ defaults: {
792
+ className: 'colVis',
793
+ columns: '',
794
+ search: false,
795
+ select: false,
796
+ text: 'Column visibility',
797
+ title: 'Column visibility'
798
+ },
799
+ extend: function (config) {
800
+ var dt = this.dt();
801
+ return {
802
+ extend: 'dropdown',
803
+ icon: 'columns',
804
+ text: dt.i18n('columnControl.colVisDropdown', config.text),
805
+ content: [
806
+ Object.assign(config, {
807
+ extend: 'colVis'
808
+ })
809
+ ]
810
+ };
811
+ }
812
+ };
813
+
814
+ var reorder = {
815
+ defaults: {
816
+ className: 'reorder',
817
+ icon: 'move',
818
+ text: 'Reorder columns'
819
+ },
820
+ init: function (config) {
821
+ var _this = this;
822
+ var dt = this.dt();
823
+ var btn = new Button(dt)
824
+ .text(dt.i18n('columnControl.reorder', config.text))
825
+ .icon(config.icon)
826
+ .className(config.className)
827
+ .handler(function () {
828
+ var idx = _this.idx();
829
+ if (idx > 0) {
830
+ dt.colReorder.move(idx, idx - 1);
831
+ }
832
+ });
833
+ if (this.idx() === 0) {
834
+ btn.enable(false);
835
+ }
836
+ dt.on('columns-reordered', function (e, details) {
837
+ btn.enable(_this.idx() > 0);
838
+ });
839
+ // If ColReorder wasn't initialised on this DataTable, then we need to add it
840
+ if (!dt.init().colReorder) {
841
+ new DataTable.ColReorder(dt, {});
842
+ }
843
+ return btn.element();
844
+ }
845
+ };
846
+
847
+ var reorderLeft = {
848
+ defaults: {
849
+ className: 'reorderLeft',
850
+ icon: 'moveLeft',
851
+ text: 'Move column left'
852
+ },
853
+ init: function (config) {
854
+ var _this = this;
855
+ var dt = this.dt();
856
+ var btn = new Button(dt)
857
+ .text(dt.i18n('columnControl.reorderLeft', config.text))
858
+ .icon(config.icon)
859
+ .className(config.className)
860
+ .handler(function () {
861
+ var idx = _this.idx();
862
+ // TODO account for visibility
863
+ if (idx > 0) {
864
+ dt.colReorder.move(idx, idx - 1);
865
+ }
866
+ });
867
+ if (this.idx() === 0) {
868
+ btn.enable(false);
869
+ }
870
+ dt.on('columns-reordered', function (e, details) {
871
+ btn.enable(_this.idx() > 0);
872
+ });
873
+ return btn.element();
874
+ }
875
+ };
876
+
877
+ var reorderRight = {
878
+ defaults: {
879
+ className: 'reorderRight',
880
+ icon: 'moveRight',
881
+ text: 'Move column right'
882
+ },
883
+ init: function (config) {
884
+ var _this = this;
885
+ var dt = this.dt();
886
+ var btn = new Button(dt)
887
+ .text(dt.i18n('columnControl.reorderRight', config.text))
888
+ .icon(config.icon)
889
+ .className(config.className)
890
+ .handler(function () {
891
+ var idx = _this.idx();
892
+ if (idx < dt.columns().count() - 1) {
893
+ dt.colReorder.move(idx, idx + 1);
894
+ }
895
+ });
896
+ if (this.idx() === dt.columns().count() - 1) {
897
+ btn.enable(false);
898
+ }
899
+ dt.on('columns-reordered', function (e, details) {
900
+ btn.enable(_this.idx() < dt.columns().count() - 1);
901
+ });
902
+ return btn.element();
903
+ }
904
+ };
905
+
906
+ var order = {
907
+ defaults: {
908
+ className: 'order',
909
+ iconAsc: 'orderAsc',
910
+ iconDesc: 'orderDesc',
911
+ iconNone: 'orderNone',
912
+ statusOnly: false,
913
+ text: 'Toggle ordering'
914
+ },
915
+ init: function (config) {
916
+ var _this = this;
917
+ var dt = this.dt();
918
+ var btn = new Button(dt)
919
+ .text(dt.i18n('columnControl.order', config.text))
920
+ .icon('orderAsc')
921
+ .className(config.className);
922
+ if (!config.statusOnly) {
923
+ dt.order.listener(btn.element(), this.idx(), function () { });
924
+ }
925
+ dt.on('order', function (e, s, order) {
926
+ var found = order.find(function (o) { return o.col === _this.idx(); });
927
+ if (!found) {
928
+ btn.active(false).icon(config.iconNone);
929
+ }
930
+ else if (found.dir === 'asc') {
931
+ btn.active(true).icon(config.iconAsc);
932
+ }
933
+ else if (found.dir === 'desc') {
934
+ btn.active(true).icon(config.iconDesc);
935
+ }
936
+ });
937
+ return btn.element();
938
+ }
939
+ };
940
+
941
+ var orderAddAsc = {
942
+ defaults: {
943
+ className: 'orderAddAsc',
944
+ icon: 'orderAddAsc',
945
+ text: 'Add Sort Ascending'
946
+ },
947
+ init: function (config) {
948
+ var _this = this;
949
+ var dt = this.dt();
950
+ var btn = new Button(dt)
951
+ .text(dt.i18n('columnControl.orderAddAsc', config.text))
952
+ .icon(config.icon)
953
+ .className(config.className)
954
+ .handler(function () {
955
+ var order = dt.order();
956
+ order.push([_this.idx(), 'asc']);
957
+ dt.draw();
958
+ });
959
+ dt.on('order', function (e, s, order) {
960
+ var found = order.some(function (o) { return o.col === _this.idx(); });
961
+ btn.enable(!found);
962
+ });
963
+ return btn.element();
964
+ }
965
+ };
966
+
967
+ var orderAddDesc = {
968
+ defaults: {
969
+ className: 'orderAddDesc',
970
+ icon: 'orderAddDesc',
971
+ text: 'Add Sort Descending'
972
+ },
973
+ init: function (config) {
974
+ var _this = this;
975
+ var dt = this.dt();
976
+ var btn = new Button(dt)
977
+ .text(dt.i18n('columnControl.orderAddDesc', config.text))
978
+ .icon(config.icon)
979
+ .className(config.className)
980
+ .handler(function () {
981
+ var order = dt.order();
982
+ order.push([_this.idx(), 'desc']);
983
+ dt.draw();
984
+ });
985
+ dt.on('order', function (e, s, order) {
986
+ var found = order.some(function (o) { return o.col === _this.idx(); });
987
+ btn.enable(!found);
988
+ });
989
+ return btn.element();
990
+ }
991
+ };
992
+
993
+ var orderAsc = {
994
+ defaults: {
995
+ className: 'orderAsc',
996
+ icon: 'orderAsc',
997
+ text: 'Sort Ascending'
998
+ },
999
+ init: function (config) {
1000
+ var _this = this;
1001
+ var dt = this.dt();
1002
+ var btn = new Button(dt)
1003
+ .text(dt.i18n('columnControl.orderAsc', config.text))
1004
+ .icon(config.icon)
1005
+ .className(config.className)
1006
+ .handler(function () {
1007
+ _this.dt()
1008
+ .order([
1009
+ {
1010
+ idx: _this.idx(),
1011
+ dir: 'asc'
1012
+ }
1013
+ ])
1014
+ .draw();
1015
+ });
1016
+ dt.on('order', function (e, s, order) {
1017
+ var found = order.some(function (o) { return o.col === _this.idx() && o.dir === 'asc'; });
1018
+ btn.active(found);
1019
+ });
1020
+ return btn.element();
1021
+ }
1022
+ };
1023
+
1024
+ var orderClear = {
1025
+ defaults: {
1026
+ className: 'orderClear',
1027
+ icon: 'orderClear',
1028
+ text: 'Clear sort'
1029
+ },
1030
+ init: function (config) {
1031
+ var dt = this.dt();
1032
+ var btn = new Button(dt)
1033
+ .text(dt.i18n('columnControl.orderClear', config.text))
1034
+ .icon(config.icon)
1035
+ .className(config.className)
1036
+ .handler(function () {
1037
+ dt.order([]).draw();
1038
+ });
1039
+ dt.on('order', function (e, s, order) {
1040
+ btn.enable(order.length > 0);
1041
+ });
1042
+ if (dt.order().length === 0) {
1043
+ btn.enable(false);
1044
+ }
1045
+ return btn.element();
1046
+ }
1047
+ };
1048
+
1049
+ var orderDesc = {
1050
+ defaults: {
1051
+ className: 'orderDesc',
1052
+ icon: 'orderDesc',
1053
+ text: 'Sort Descending'
1054
+ },
1055
+ init: function (config) {
1056
+ var _this = this;
1057
+ var dt = this.dt();
1058
+ var btn = new Button(dt)
1059
+ .text(dt.i18n('columnControl.orderDesc', config.text))
1060
+ .icon(config.icon)
1061
+ .className(config.className)
1062
+ .handler(function () {
1063
+ _this.dt()
1064
+ .order([
1065
+ {
1066
+ idx: _this.idx(),
1067
+ dir: 'desc'
1068
+ }
1069
+ ])
1070
+ .draw();
1071
+ });
1072
+ dt.on('order', function (e, s, order) {
1073
+ var found = order.some(function (o) { return o.col === _this.idx() && o.dir === 'desc'; });
1074
+ btn.active(found);
1075
+ });
1076
+ return btn.element();
1077
+ }
1078
+ };
1079
+
1080
+ var orderRemove = {
1081
+ defaults: {
1082
+ className: 'orderRemove',
1083
+ icon: 'orderRemove',
1084
+ text: 'Remove from sort'
1085
+ },
1086
+ init: function (config) {
1087
+ var _this = this;
1088
+ var dt = this.dt();
1089
+ var btn = new Button(dt)
1090
+ .text(dt.i18n('columnControl.orderRemove', config.text))
1091
+ .icon(config.icon)
1092
+ .className(config.className)
1093
+ .handler(function () {
1094
+ // Remove the current column from the ordering array, then reorder the table
1095
+ var order = dt.order();
1096
+ var idx = order.findIndex(function (o) { return o[0] === _this.idx(); });
1097
+ order.splice(idx, 1);
1098
+ dt.order(order).draw();
1099
+ });
1100
+ dt.on('order', function (e, s, order) {
1101
+ var found = order.some(function (o) { return o.col === _this.idx(); });
1102
+ btn.enable(found);
1103
+ });
1104
+ btn.enable(false);
1105
+ return btn.element();
1106
+ }
1107
+ };
1108
+
1109
+ var orderStatus = {
1110
+ defaults: {
1111
+ className: 'order',
1112
+ iconAsc: 'orderAsc',
1113
+ iconDesc: 'orderDesc',
1114
+ iconNone: 'orderNone',
1115
+ statusOnly: true,
1116
+ text: 'Sort status'
1117
+ },
1118
+ extend: function (config) {
1119
+ return Object.assign(config, { extend: 'order' });
1120
+ }
1121
+ };
1122
+
1123
+ var SearchInput = /** @class */ (function () {
1124
+ /**
1125
+ * Create a container element, for consistent DOM structure and styling
1126
+ */
1127
+ function SearchInput(dt, idx) {
1128
+ var _this = this;
1129
+ this._type = 'text';
1130
+ this._dt = dt;
1131
+ this._idx = idx;
1132
+ this._dom = {
1133
+ clear: createElement('span', 'dtcc-search-clear', icons['x']),
1134
+ container: createElement('div', SearchInput.classes.container),
1135
+ typeIcon: createElement('div', 'dtcc-search-type-icon'),
1136
+ searchIcon: createElement('div', 'dtcc-search-icon', icons['search']),
1137
+ input: createElement('input', SearchInput.classes.input),
1138
+ inputs: createElement('div'),
1139
+ select: createElement('select', SearchInput.classes.select),
1140
+ title: createElement('div', 'dtcc-search-title')
1141
+ };
1142
+ var dom = this._dom;
1143
+ var originalIdx = idx;
1144
+ dom.input.setAttribute('type', 'text');
1145
+ dom.container.append(dom.title, dom.inputs);
1146
+ dom.inputs.append(dom.typeIcon, dom.select, dom.searchIcon, dom.clear, dom.input);
1147
+ // Listeners
1148
+ var inputInput = function () {
1149
+ _this.runSearch();
1150
+ };
1151
+ var selectInput = function () {
1152
+ dom.typeIcon.innerHTML = icons[dom.select.value];
1153
+ _this.runSearch();
1154
+ };
1155
+ var clearClick = function () {
1156
+ _this.clear();
1157
+ };
1158
+ dom.input.addEventListener('input', inputInput);
1159
+ dom.select.addEventListener('input', selectInput);
1160
+ dom.clear.addEventListener('click', clearClick);
1161
+ dt.on('destroy', function () {
1162
+ dom.input.removeEventListener('input', inputInput);
1163
+ dom.select.removeEventListener('input', selectInput);
1164
+ dom.clear.removeEventListener('click', clearClick);
1165
+ });
1166
+ // State handling - all components that use this class have the same state saving structure
1167
+ // so shared handling can be performed here.
1168
+ dt.on('stateSaveParams', function (e, s, data) {
1169
+ if (!data.columnControl) {
1170
+ data.columnControl = {};
1171
+ }
1172
+ if (!data.columnControl[idx]) {
1173
+ data.columnControl[idx] = {};
1174
+ }
1175
+ data.columnControl[idx].searchInput = {
1176
+ logic: dom.select.value,
1177
+ type: _this._type,
1178
+ value: dom.input.value
1179
+ };
1180
+ });
1181
+ dt.on('stateLoaded', function (e, s, state) {
1182
+ _this._stateLoad(state);
1183
+ });
1184
+ // Same as for ColumnControl - reassign a column index if needed.
1185
+ dt.on('columns-reordered', function (e, details) {
1186
+ _this._idx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
1187
+ });
1188
+ // Column control search clearing (column().ccSearchClear() method)
1189
+ dt.on('cc-search-clear', function (e, colIdx) {
1190
+ if (colIdx === _this._idx) {
1191
+ _this.clear();
1192
+ }
1193
+ });
1194
+ }
1195
+ /**
1196
+ * Add a class to the container
1197
+ *
1198
+ * @param name Class name to add
1199
+ * @returns Self for chaining
1200
+ */
1201
+ SearchInput.prototype.addClass = function (name) {
1202
+ this._dom.container.classList.add(name);
1203
+ return this;
1204
+ };
1205
+ /**
1206
+ * Clear any applied search
1207
+ *
1208
+ * @returns Self for chaining
1209
+ */
1210
+ SearchInput.prototype.clear = function () {
1211
+ this.set(this._dom.select.children[0].getAttribute('value'), '');
1212
+ return this;
1213
+ };
1214
+ /**
1215
+ * Set the clear icon feature can be used or not
1216
+ *
1217
+ * @param set Flag
1218
+ * @returns Self for chaining
1219
+ */
1220
+ SearchInput.prototype.clearable = function (set) {
1221
+ // Note there is no add here as it is added by default and never used after setup, so
1222
+ // no need.
1223
+ if (!set) {
1224
+ this._dom.clear.remove();
1225
+ }
1226
+ return this;
1227
+ };
1228
+ /**
1229
+ * Get the container element
1230
+ *
1231
+ * @returns The container element
1232
+ */
1233
+ SearchInput.prototype.element = function () {
1234
+ return this._dom.container;
1235
+ };
1236
+ /**
1237
+ * Get the HTML input element for this control
1238
+ *
1239
+ * @returns HTML Input element
1240
+ */
1241
+ SearchInput.prototype.input = function () {
1242
+ return this._dom.input;
1243
+ };
1244
+ /**
1245
+ * Set the list of options for the dropdown
1246
+ *
1247
+ * @param opts List of options
1248
+ * @returns Self for chaining
1249
+ */
1250
+ SearchInput.prototype.options = function (opts) {
1251
+ var select = this._dom.select;
1252
+ for (var i = 0; i < opts.length; i++) {
1253
+ select.add(new Option(opts[i].label, opts[i].value));
1254
+ }
1255
+ // Initial icon
1256
+ this._dom.typeIcon.innerHTML = icons[opts[0].value];
1257
+ return this;
1258
+ };
1259
+ /**
1260
+ * Set the placeholder attribute for the input element
1261
+ *
1262
+ * @param placeholder Placeholder string
1263
+ * @returns Self for chaining
1264
+ */
1265
+ SearchInput.prototype.placeholder = function (placeholder) {
1266
+ if (placeholder) {
1267
+ var columnTitle = this._dt.column(this._idx).title();
1268
+ this._dom.input.placeholder = placeholder.replace('[title]', columnTitle);
1269
+ }
1270
+ return this;
1271
+ };
1272
+ /**
1273
+ * Run the search method
1274
+ */
1275
+ SearchInput.prototype.runSearch = function () {
1276
+ var dom = this._dom;
1277
+ var isActive = dom.select.value === 'empty' ||
1278
+ dom.select.value === 'notEmpty' ||
1279
+ dom.input.value !== '';
1280
+ dom.container.classList.toggle('dtcc-search_active', isActive);
1281
+ if (this._search &&
1282
+ (this._lastValue !== dom.input.value || this._lastType !== dom.select.value)) {
1283
+ this._search(dom.select.value, dom.input.value, this._loadingState);
1284
+ this._lastValue = dom.input.value;
1285
+ this._lastType = dom.select.value;
1286
+ }
1287
+ };
1288
+ /**
1289
+ * Set the function that will be run when a search operation is required. Note that this can
1290
+ * trigger the function to run if there is a saved state.
1291
+ *
1292
+ * @param fn Search callback
1293
+ * @returns Self for chaining
1294
+ */
1295
+ SearchInput.prototype.search = function (fn) {
1296
+ this._search = fn;
1297
+ // If there is a saved state, load it now that set up is done.
1298
+ this._stateLoad(this._dt.state.loaded());
1299
+ return this;
1300
+ };
1301
+ /**
1302
+ * Set a value for the search input
1303
+ *
1304
+ * @param logic Logic type
1305
+ * @param val Value
1306
+ * @returns Self for chaining
1307
+ */
1308
+ SearchInput.prototype.set = function (logic, val) {
1309
+ var dom = this._dom;
1310
+ dom.input.value = val;
1311
+ dom.select.value = logic;
1312
+ dom.typeIcon.innerHTML = icons[dom.select.value];
1313
+ this.runSearch();
1314
+ return this;
1315
+ };
1316
+ /**
1317
+ * Set the text that will be shown as the title for the control
1318
+ *
1319
+ * @param text Set the title text
1320
+ * @returns Self for chaining
1321
+ */
1322
+ SearchInput.prototype.title = function (text) {
1323
+ if (text) {
1324
+ var columnTitle = this._dt.column(this._idx).title();
1325
+ this._dom.title.innerHTML = text.replace('[title]', columnTitle);
1326
+ }
1327
+ return this;
1328
+ };
1329
+ /**
1330
+ * Set the title attribute for the input element
1331
+ *
1332
+ * @param title Title attribute string
1333
+ * @returns Self for chaining
1334
+ */
1335
+ SearchInput.prototype.titleAttr = function (title) {
1336
+ if (title) {
1337
+ var columnTitle = this._dt.column(this._idx).title();
1338
+ this._dom.input.title = title.replace('[title]', columnTitle);
1339
+ }
1340
+ return this;
1341
+ };
1342
+ SearchInput.prototype.type = function (t) {
1343
+ this._type = t;
1344
+ return this;
1345
+ };
1346
+ /**
1347
+ * Load a DataTables state
1348
+ *
1349
+ * @param state State object being loaded
1350
+ */
1351
+ SearchInput.prototype._stateLoad = function (state) {
1352
+ var _a, _b;
1353
+ var dom = this._dom;
1354
+ var idx = this._idx;
1355
+ var loadedState = (_b = (_a = state === null || state === void 0 ? void 0 : state.columnControl) === null || _a === void 0 ? void 0 : _a[idx]) === null || _b === void 0 ? void 0 : _b.searchInput;
1356
+ if (loadedState) {
1357
+ // The search callback needs to know if we are loading an existing state or not
1358
+ // so it can determine if it needs to draw the table. If it was a user input, then
1359
+ // it redraws, if it was a state load, then there should be no redraw.
1360
+ this._loadingState = true;
1361
+ dom.select.value = loadedState.logic;
1362
+ dom.input.value = loadedState.value;
1363
+ dom.select.dispatchEvent(new Event('input'));
1364
+ this._loadingState = false;
1365
+ }
1366
+ };
1367
+ SearchInput.classes = {
1368
+ container: ['dtcc-content', 'dtcc-search'],
1369
+ input: '',
1370
+ select: ''
1371
+ };
1372
+ return SearchInput;
1373
+ }());
1374
+
1375
+ var searchDateTime = {
1376
+ defaults: {
1377
+ clear: true,
1378
+ placeholder: '',
1379
+ title: '',
1380
+ titleAttr: ''
1381
+ },
1382
+ init: function (config) {
1383
+ var _this = this;
1384
+ var fromPicker = false;
1385
+ var moment = DataTable.use('moment');
1386
+ var luxon = DataTable.use('luxon');
1387
+ var dt = this.dt();
1388
+ var displayFormat = '';
1389
+ var dateTime;
1390
+ var searchInput = new SearchInput(dt, this.idx())
1391
+ .type('date')
1392
+ .addClass('dtcc-searchDate')
1393
+ .clearable(config.clear)
1394
+ .placeholder(config.placeholder)
1395
+ .title(config.title)
1396
+ .titleAttr(config.titleAttr)
1397
+ .options([
1398
+ { label: 'Equals', value: 'equal' },
1399
+ { label: 'Does not equal', value: 'notEqual' },
1400
+ { label: 'After', value: 'greater' },
1401
+ { label: 'Before', value: 'less' },
1402
+ { label: 'Empty', value: 'empty' },
1403
+ { label: 'Not empty', value: 'notEmpty' }
1404
+ ])
1405
+ .search(function (searchType, searchTerm, loadingState) {
1406
+ var column = dt.column(_this.idx());
1407
+ var search = searchTerm === ''
1408
+ ? ''
1409
+ : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), displayFormat, moment, luxon);
1410
+ if (searchType === 'empty') {
1411
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1412
+ }
1413
+ else if (searchType === 'notEmpty') {
1414
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1415
+ }
1416
+ else if (column.search.fixed('dtcc') === '' && search === '') {
1417
+ // No change - don't do anything
1418
+ return;
1419
+ }
1420
+ else if (!search) {
1421
+ // Clear search
1422
+ column.search.fixed('dtcc', '');
1423
+ }
1424
+ else if (searchType === 'equal') {
1425
+ // Use a function for matching - weak typing
1426
+ // Note that the haystack in the search function is the rendered date - it
1427
+ // might need to be converted back to a date
1428
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) == search; });
1429
+ }
1430
+ else if (searchType === 'notEqual') {
1431
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) != search; });
1432
+ }
1433
+ else if (searchType === 'greater') {
1434
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) > search; });
1435
+ }
1436
+ else if (searchType === 'less') {
1437
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) < search; });
1438
+ }
1439
+ // If in a dropdown, set the parent levels as active
1440
+ if (config._parents) {
1441
+ config._parents.forEach(function (btn) {
1442
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1443
+ });
1444
+ }
1445
+ if (!loadingState) {
1446
+ column.draw();
1447
+ }
1448
+ });
1449
+ // Once data has been loaded we can run DateTime with the specified format
1450
+ dt.ready(function () {
1451
+ var DateTime = DataTable.use('datetime');
1452
+ displayFormat = getFormat(dt, _this.idx());
1453
+ if (DateTime) {
1454
+ dateTime = new DateTime(searchInput.input(), {
1455
+ format: displayFormat,
1456
+ onChange: function () {
1457
+ fromPicker = true;
1458
+ searchInput.runSearch();
1459
+ fromPicker = false;
1460
+ }
1461
+ });
1462
+ }
1463
+ });
1464
+ return searchInput.element();
1465
+ }
1466
+ };
1467
+ /**
1468
+ * Determine the formatting string for the date time information in the colum
1469
+ *
1470
+ * @param dt DataTable instance
1471
+ * @param column Column index
1472
+ * @returns Date / time formatting string
1473
+ */
1474
+ function getFormat(dt, column) {
1475
+ var type = dt.column(column).type();
1476
+ if (!type) {
1477
+ // Assume that it is ISO unless otherwise specified - that is all DataTables can do anyway
1478
+ return 'YYYY-MM-DD';
1479
+ }
1480
+ else if (type === 'datetime') {
1481
+ // If no format was specified in the DT type, then we need to use Moment / Luxon's default
1482
+ // locale formatting.
1483
+ var moment = DataTable.use('moment');
1484
+ var luxon = DataTable.use('luxon');
1485
+ if (moment) {
1486
+ return moment().creationData().locale._longDateFormat.L;
1487
+ }
1488
+ if (luxon) {
1489
+ // Luxon doesn't appear to provide a way to let us get the default locale formatting
1490
+ // string, so we need to attempt to decode it.
1491
+ return luxon.DateTime.fromISO('1999-08-07')
1492
+ .toLocaleString()
1493
+ .replace('07', 'dd')
1494
+ .replace('7', 'd')
1495
+ .replace('08', 'MM')
1496
+ .replace('8', 'M')
1497
+ .replace('1999', 'yyyy')
1498
+ .replace('99', 'yy');
1499
+ }
1500
+ }
1501
+ else if (type.includes('datetime-')) {
1502
+ // Column was specified with a particular display format - we can extract that format from
1503
+ // the type, as it is part of the type name.
1504
+ return type.replace(/datetime-/g, '');
1505
+ }
1506
+ else if (type.includes('moment')) {
1507
+ return type.replace(/moment-/g, '');
1508
+ }
1509
+ else if (type.includes('luxon')) {
1510
+ return type.replace(/luxon-/g, '');
1511
+ }
1512
+ return 'YYYY-MM-DD';
1513
+ }
1514
+ /**
1515
+ * Convert from a source date / time value (usually a string) to a timestamp for comparisons.
1516
+ *
1517
+ * @param input Input value
1518
+ * @param srcFormat String format of the input
1519
+ * @param moment Moment instance, if it is available
1520
+ * @param luxon Luxon object, if it is available
1521
+ * @returns Time stamp - milliseconds
1522
+ */
1523
+ function dateToNum(input, srcFormat, moment, luxon) {
1524
+ if (input === '') {
1525
+ return '';
1526
+ }
1527
+ else if (input instanceof Date) {
1528
+ return input.getTime();
1529
+ }
1530
+ else if (srcFormat !== 'YYYY-MM-DD' && (moment || luxon)) {
1531
+ return moment
1532
+ ? moment(input, srcFormat).unix() * 1000
1533
+ : luxon.DateTime.fromFormat(input, srcFormat).toMillis();
1534
+ }
1535
+ return new Date(input).getTime();
1536
+ }
1537
+
1538
+ /** Set the options to show in the list */
1539
+ function setOptions(checkList, opts) {
1540
+ var existing = checkList.values();
1541
+ checkList.clear();
1542
+ for (var i = 0; i < opts.length; i++) {
1543
+ if (typeof opts[i] === 'object') {
1544
+ checkList.add({
1545
+ active: false,
1546
+ label: opts[i].label,
1547
+ value: opts[i].value
1548
+ }, i === opts.length - 1);
1549
+ }
1550
+ else {
1551
+ checkList.add({
1552
+ active: false,
1553
+ label: opts[i],
1554
+ value: opts[i]
1555
+ }, i === opts.length - 1);
1556
+ }
1557
+ }
1558
+ if (existing.length) {
1559
+ checkList.values(existing);
1560
+ }
1561
+ }
1562
+ /** Load a saved state */
1563
+ function getState(columnIdx, state) {
1564
+ var _a, _b;
1565
+ var loadedState = (_b = (_a = state === null || state === void 0 ? void 0 : state.columnControl) === null || _a === void 0 ? void 0 : _a[columnIdx]) === null || _b === void 0 ? void 0 : _b.searchList;
1566
+ if (loadedState) {
1567
+ return loadedState;
1568
+ }
1569
+ }
1570
+ /** Get the options for a column from a DT JSON object */
1571
+ function getJsonOptions(dt, idx) {
1572
+ var _a;
1573
+ var json = (_a = dt.ajax.json()) === null || _a === void 0 ? void 0 : _a.columnControl;
1574
+ var column = dt.column(idx);
1575
+ var name = column.name();
1576
+ var dataSrc = column.dataSrc();
1577
+ if (json && json[name]) {
1578
+ // Found options matching the column's name - top priority
1579
+ return json[name];
1580
+ }
1581
+ else if (json && typeof dataSrc === 'string' && json[dataSrc]) {
1582
+ // Found options matching the column's data source string
1583
+ return json[dataSrc];
1584
+ }
1585
+ else if (json && json[idx]) {
1586
+ // Found options matching the column's data index
1587
+ return json[idx];
1588
+ }
1589
+ return null;
1590
+ }
1591
+ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1592
+ var _a;
1593
+ // Was there options specified in the Ajax return?
1594
+ var json = (_a = dt.ajax.json()) === null || _a === void 0 ? void 0 : _a.columnControl;
1595
+ var options = [];
1596
+ var jsonOptions = getJsonOptions(dt, idx);
1597
+ if (jsonOptions) {
1598
+ options = jsonOptions;
1599
+ }
1600
+ else if (json && config.ajaxOnly) {
1601
+ // Ajax only options - need to hide the search list
1602
+ checkList.element().style.display = 'none';
1603
+ // Check if the parent buttons should be hidden as well (they will be if there
1604
+ // is no visible content in them)
1605
+ if (config._parents) {
1606
+ config._parents.forEach(function (btn) { return btn.checkDisplay(); });
1607
+ }
1608
+ // No point in doing any further processing here
1609
+ return;
1610
+ }
1611
+ else {
1612
+ // Either no ajax object (i.e. not an Ajax table), or no matching ajax options
1613
+ // for this column - get the values for the column, taking into account
1614
+ // orthogonal rendering
1615
+ var found_1 = {};
1616
+ dt.cells('*', idx, { order: idx }).every(function () {
1617
+ var filter = this.render('filter');
1618
+ if (!found_1[filter]) {
1619
+ found_1[filter] = true;
1620
+ options.push({
1621
+ label: this.render('display'),
1622
+ value: filter
1623
+ });
1624
+ }
1625
+ });
1626
+ }
1627
+ setOptions(checkList, options);
1628
+ // If there was a state loaded at start up, then we need to set the visual
1629
+ // appearance to match
1630
+ if (loadedValues) {
1631
+ checkList.values(loadedValues);
1632
+ }
1633
+ }
1634
+ var searchList = {
1635
+ defaults: {
1636
+ ajaxOnly: true,
1637
+ className: 'searchList',
1638
+ options: null,
1639
+ search: true,
1640
+ select: true,
1641
+ title: ''
1642
+ },
1643
+ init: function (config) {
1644
+ var _this = this;
1645
+ var loadedValues = null;
1646
+ var dt = this.dt();
1647
+ // The search can be applied from a stored start at start up before the options are
1648
+ // available. It can also be applied by user input, so it is generalised into this function.
1649
+ var applySearch = function (values) {
1650
+ var col = dt.column(_this.idx());
1651
+ if (!values) {
1652
+ return;
1653
+ }
1654
+ else if (values.length === 0) {
1655
+ // Nothing selected - clear the filter
1656
+ col.search.fixed('dtcc-list', '');
1657
+ }
1658
+ else {
1659
+ // Find all matching options from the list of values
1660
+ col.search.fixed('dtcc-list', function (val) {
1661
+ return values.includes(val);
1662
+ });
1663
+ }
1664
+ // If in a dropdown, set the parent levels as active
1665
+ if (config._parents) {
1666
+ config._parents.forEach(function (btn) { return btn.activeList(_this.unique(), !!values.length); });
1667
+ }
1668
+ };
1669
+ var checkList = new CheckList(dt, {
1670
+ search: config.search,
1671
+ select: config.select
1672
+ })
1673
+ .searchListener(dt, this)
1674
+ .title(dt
1675
+ .i18n('columnControl.searchList', config.title)
1676
+ .replace('[title]', dt.column(this.idx()).title()))
1677
+ .handler(function (e, btn) {
1678
+ if (btn) {
1679
+ btn.active(!btn.active());
1680
+ }
1681
+ applySearch(checkList.values());
1682
+ dt.draw();
1683
+ });
1684
+ if (config.options) {
1685
+ setOptions(checkList, config.options);
1686
+ }
1687
+ else {
1688
+ dt.ready(function () {
1689
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1690
+ });
1691
+ }
1692
+ // Xhr event listener for updates of options
1693
+ dt.on('xhr', function (e, s, json) {
1694
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1695
+ });
1696
+ // Unlike the SearchInput based search contents, CheckList does not handle state saving
1697
+ // (since the mechanism for column visibility is different), so state saving is handled
1698
+ // here.
1699
+ dt.on('stateLoaded', function (e, s, state) {
1700
+ var values = getState(_this.idx(), state);
1701
+ if (values) {
1702
+ checkList.values(values);
1703
+ }
1704
+ });
1705
+ dt.on('stateSaveParams', function (e, s, data) {
1706
+ var idx = _this.idx();
1707
+ if (!data.columnControl) {
1708
+ data.columnControl = {};
1709
+ }
1710
+ if (!data.columnControl[idx]) {
1711
+ data.columnControl[idx] = {};
1712
+ }
1713
+ // If the table isn't yet ready, then the options for the list won't have been
1714
+ // populated (above) and therefore there can't be an values. In such a case
1715
+ // use the last saved values and this will refresh on the next draw.
1716
+ data.columnControl[idx].searchList = dt.ready()
1717
+ ? checkList.values()
1718
+ : loadedValues;
1719
+ });
1720
+ loadedValues = getState(this.idx(), dt.state.loaded());
1721
+ applySearch(loadedValues);
1722
+ return checkList.element();
1723
+ }
1724
+ };
1725
+
1726
+ var searchNumber = {
1727
+ defaults: {
1728
+ clear: true,
1729
+ placeholder: '',
1730
+ title: '',
1731
+ titleAttr: ''
1732
+ },
1733
+ init: function (config) {
1734
+ var _this = this;
1735
+ var dt = this.dt();
1736
+ var searchInput = new SearchInput(dt, this.idx())
1737
+ .type('num')
1738
+ .addClass('dtcc-searchNumber')
1739
+ .clearable(config.clear)
1740
+ .placeholder(config.placeholder)
1741
+ .title(config.title)
1742
+ .titleAttr(config.titleAttr)
1743
+ .options([
1744
+ { label: 'Equals', value: 'equal' },
1745
+ { label: 'Does not equal', value: 'notEqual' },
1746
+ { label: 'Greater than', value: 'greater' },
1747
+ { label: 'Greater or equal', value: 'greaterOrEqual' },
1748
+ { label: 'Less than', value: 'less' },
1749
+ { label: 'Less or equal', value: 'lessOrEqual' },
1750
+ { label: 'Empty', value: 'empty' },
1751
+ { label: 'Not empty', value: 'notEmpty' }
1752
+ ])
1753
+ .search(function (searchType, searchTerm, loadingState) {
1754
+ var column = dt.column(_this.idx());
1755
+ if (searchType === 'empty') {
1756
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1757
+ }
1758
+ else if (searchType === 'notEmpty') {
1759
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1760
+ }
1761
+ else if (column.search.fixed('dtcc') === '' && searchTerm === '') {
1762
+ // No change - don't do anything
1763
+ return;
1764
+ }
1765
+ else if (searchTerm === '') {
1766
+ // Clear search
1767
+ column.search.fixed('dtcc', '');
1768
+ }
1769
+ else if (searchType === 'equal') {
1770
+ // Use a function for matching - weak typing
1771
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) == searchTerm; });
1772
+ }
1773
+ else if (searchType === 'notEqual') {
1774
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) != searchTerm; });
1775
+ }
1776
+ else if (searchType === 'greater') {
1777
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) > searchTerm; });
1778
+ }
1779
+ else if (searchType === 'greaterOrEqual') {
1780
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) >= searchTerm; });
1781
+ }
1782
+ else if (searchType === 'less') {
1783
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) < searchTerm; });
1784
+ }
1785
+ else if (searchType === 'lessOrEqual') {
1786
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) <= searchTerm; });
1787
+ }
1788
+ // If in a dropdown, set the parents as active
1789
+ if (config._parents) {
1790
+ config._parents.forEach(function (btn) {
1791
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1792
+ });
1793
+ }
1794
+ if (!loadingState) {
1795
+ column.draw();
1796
+ }
1797
+ });
1798
+ // Set a numeric input type, per BBC's guidelines
1799
+ searchInput.input().setAttribute('inputmode', 'numeric');
1800
+ searchInput.input().setAttribute('pattern', '[0-9]*');
1801
+ return searchInput.element();
1802
+ }
1803
+ };
1804
+ var _re_html = /<([^>]*>)/g;
1805
+ var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1806
+ function stringToNum(d) {
1807
+ if (d !== 0 && (!d || d === '-')) {
1808
+ return -Infinity;
1809
+ }
1810
+ var type = typeof d;
1811
+ if (type === 'number' || type === 'bigint') {
1812
+ return d;
1813
+ }
1814
+ if (d.replace) {
1815
+ d = d.replace(_re_html, '').replace(_re_formatted_numeric, '');
1816
+ }
1817
+ return d * 1;
1818
+ }
1819
+
1820
+ var searchText = {
1821
+ defaults: {
1822
+ clear: true,
1823
+ placeholder: '',
1824
+ title: '',
1825
+ titleAttr: ''
1826
+ },
1827
+ init: function (config) {
1828
+ var _this = this;
1829
+ var dt = this.dt();
1830
+ var searchInput = new SearchInput(dt, this.idx())
1831
+ .addClass('dtcc-searchText')
1832
+ .clearable(config.clear)
1833
+ .placeholder(config.placeholder)
1834
+ .title(config.title)
1835
+ .titleAttr(config.titleAttr)
1836
+ .options([
1837
+ { label: 'Contains', value: 'contains' },
1838
+ { label: 'Does not contain', value: 'notContains' },
1839
+ { label: 'Equals', value: 'equal' },
1840
+ { label: 'Does not equal', value: 'notEqual' },
1841
+ { label: 'Starts', value: 'starts' },
1842
+ { label: 'Ends', value: 'ends' },
1843
+ { label: 'Empty', value: 'empty' },
1844
+ { label: 'Not empty', value: 'notEmpty' }
1845
+ ])
1846
+ .search(function (searchType, searchTerm, loadingState) {
1847
+ var column = dt.column(_this.idx());
1848
+ searchTerm = searchTerm.toLowerCase();
1849
+ if (searchType === 'empty') {
1850
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1851
+ }
1852
+ else if (searchType === 'notEmpty') {
1853
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1854
+ }
1855
+ else if (column.search.fixed('dtcc') === '' && searchTerm === '') {
1856
+ // No change - don't do anything
1857
+ return;
1858
+ }
1859
+ else if (searchTerm === '') {
1860
+ // Clear search
1861
+ column.search.fixed('dtcc', '');
1862
+ }
1863
+ else if (searchType === 'equal') {
1864
+ // Use a function for exact matching
1865
+ column.search.fixed('dtcc', function (haystack) { return haystack.toLowerCase() == searchTerm; });
1866
+ }
1867
+ else if (searchType === 'notEqual') {
1868
+ column.search.fixed('dtcc', function (haystack) { return haystack.toLowerCase() != searchTerm; });
1869
+ }
1870
+ else if (searchType === 'contains') {
1871
+ // Use the built in smart search
1872
+ column.search.fixed('dtcc', searchTerm);
1873
+ }
1874
+ else if (searchType === 'notContains') {
1875
+ // Use the built in smart search
1876
+ column.search.fixed('dtcc', function (haystack) { return !haystack.toLowerCase().includes(searchTerm); });
1877
+ }
1878
+ else if (searchType === 'starts') {
1879
+ // Use a function for startsWith case insensitive search
1880
+ column.search.fixed('dtcc', function (haystack) {
1881
+ return haystack.toLowerCase().startsWith(searchTerm);
1882
+ });
1883
+ }
1884
+ else if (searchType === 'ends') {
1885
+ column.search.fixed('dtcc', function (haystack) {
1886
+ return haystack.toLowerCase().endsWith(searchTerm);
1887
+ });
1888
+ }
1889
+ // If in a dropdown, set the parent levels as active
1890
+ if (config._parents) {
1891
+ config._parents.forEach(function (btn) {
1892
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1893
+ });
1894
+ }
1895
+ if (!loadingState) {
1896
+ column.draw();
1897
+ }
1898
+ });
1899
+ return searchInput.element();
1900
+ }
1901
+ };
1902
+
1903
+ var search = {
1904
+ defaults: {
1905
+ allowSearchList: false
1906
+ },
1907
+ init: function (config) {
1908
+ var _this = this;
1909
+ var _a, _b, _c;
1910
+ var dt = this.dt();
1911
+ var idx = this.idx();
1912
+ var displayEl;
1913
+ var loadedState = (_c = (_b = (_a = dt.state.loaded()) === null || _a === void 0 ? void 0 : _a.columnControl) === null || _b === void 0 ? void 0 : _b[idx]) === null || _c === void 0 ? void 0 : _c.searchInput;
1914
+ var initType = function (type) {
1915
+ var json = getJsonOptions(dt, idx);
1916
+ // Attempt to match what type of search should be shown
1917
+ if (config.allowSearchList && json) {
1918
+ // We've got a list of JSON options, and are allowed to show the searchList
1919
+ return searchList.init.call(_this, Object.assign({}, searchList.defaults, config));
1920
+ }
1921
+ else if (type === 'date' || type.startsWith('datetime')) {
1922
+ // Date types
1923
+ return searchDateTime.init.call(_this, Object.assign({}, searchDateTime.defaults, config));
1924
+ }
1925
+ else if (type.includes('num')) {
1926
+ // Number types
1927
+ return searchNumber.init.call(_this, Object.assign({}, searchNumber.defaults, config));
1928
+ }
1929
+ else {
1930
+ // Everything else
1931
+ return searchText.init.call(_this, Object.assign({}, searchText.defaults, config));
1932
+ }
1933
+ };
1934
+ // If we know the type from the saved state, we can load it immediately. This is required
1935
+ // to allow the state to be applied to the table and the first draw to have a filter
1936
+ // applied (if it is needed).
1937
+ if (loadedState) {
1938
+ displayEl = initType(loadedState.type);
1939
+ }
1940
+ else {
1941
+ // Wait until we can get the data type for the column and the run the corresponding type
1942
+ displayEl = document.createElement('div');
1943
+ dt.ready(function () {
1944
+ var column = dt.column(idx);
1945
+ var display = initType(column.type());
1946
+ displayEl.replaceWith(display);
1947
+ });
1948
+ }
1949
+ return displayEl;
1950
+ }
1951
+ };
1952
+
1953
+ var searchClear = {
1954
+ defaults: {
1955
+ className: 'searchClear',
1956
+ icon: 'searchClear',
1957
+ text: 'Clear Search'
1958
+ },
1959
+ init: function (config) {
1960
+ var _this = this;
1961
+ var dt = this.dt();
1962
+ var btn = new Button(dt)
1963
+ .text(dt.i18n('columnControl.searchClear', config.text))
1964
+ .icon(config.icon)
1965
+ .className(config.className)
1966
+ .handler(function () {
1967
+ dt.column(_this.idx()).ccSearchClear();
1968
+ })
1969
+ .enable(false);
1970
+ dt.on('draw', function () {
1971
+ // change enable state
1972
+ var search = dt.column(_this.idx()).search.fixed('dtcc');
1973
+ var searchList = dt.column(_this.idx()).search.fixed('dtcc-list');
1974
+ btn.enable(!!(search || searchList));
1975
+ });
1976
+ return btn.element();
1977
+ }
1978
+ };
1979
+
1980
+ var searchDropdown = {
1981
+ defaults: {
1982
+ ajaxOnly: true,
1983
+ allowSearchList: true,
1984
+ className: 'searchDropdown',
1985
+ clear: true,
1986
+ columns: '',
1987
+ options: null,
1988
+ placeholder: '',
1989
+ search: true,
1990
+ select: true,
1991
+ text: 'Search',
1992
+ title: '',
1993
+ titleAttr: ''
1994
+ },
1995
+ extend: function (config) {
1996
+ var dt = this.dt();
1997
+ return {
1998
+ extend: 'dropdown',
1999
+ icon: 'search',
2000
+ text: dt.i18n('columnControl.searchDropdown', config.text),
2001
+ content: [
2002
+ Object.assign(config, {
2003
+ extend: 'search'
2004
+ })
2005
+ ]
2006
+ };
2007
+ }
2008
+ };
2009
+
2010
+ var spacer = {
2011
+ defaults: {
2012
+ className: 'dtcc-spacer',
2013
+ text: ''
2014
+ },
2015
+ init: function (config) {
2016
+ var dt = this.dt();
2017
+ var spacer = createElement('div', config.className, dt.i18n('columnControl.spacer', config.text));
2018
+ return spacer;
2019
+ }
2020
+ };
2021
+
2022
+ var title = {
2023
+ defaults: {
2024
+ className: 'dtcc-title',
2025
+ text: null
2026
+ },
2027
+ init: function (config) {
2028
+ var dt = this.dt();
2029
+ var title = dt.column(this.idx()).title();
2030
+ var text = config.text === null ? '[title]' : config.text;
2031
+ var el = createElement('div', config.className, text.replace('[title]', title));
2032
+ return el;
2033
+ }
2034
+ };
2035
+
2036
+ var contentTypes = {
2037
+ colVis: colVis,
2038
+ colVisDropdown: colVisDropdown,
2039
+ dropdown: dropdownContent,
2040
+ reorder: reorder,
2041
+ reorderLeft: reorderLeft,
2042
+ reorderRight: reorderRight,
2043
+ order: order,
2044
+ orderAddAsc: orderAddAsc,
2045
+ orderAddDesc: orderAddDesc,
2046
+ orderAsc: orderAsc,
2047
+ orderClear: orderClear,
2048
+ orderDesc: orderDesc,
2049
+ orderRemove: orderRemove,
2050
+ orderStatus: orderStatus,
2051
+ search: search,
2052
+ searchClear: searchClear,
2053
+ searchDropdown: searchDropdown,
2054
+ searchDateTime: searchDateTime,
2055
+ searchList: searchList,
2056
+ searchNumber: searchNumber,
2057
+ searchText: searchText,
2058
+ spacer: spacer,
2059
+ title: title
2060
+ };
2061
+
2062
+ /**
2063
+ *
2064
+ */
2065
+ var ColumnControl = /** @class */ (function () {
2066
+ /**
2067
+ * Create a new ColumnControl instance to control a DataTables column.
2068
+ *
2069
+ * @param dt DataTables API instance
2070
+ * @param columnIdx Column index to operation on
2071
+ * @param opts Configuration options
2072
+ */
2073
+ function ColumnControl(dt, columnIdx, opts) {
2074
+ var _this = this;
2075
+ this._dom = {
2076
+ target: null,
2077
+ wrapper: null
2078
+ };
2079
+ this._c = {};
2080
+ this._s = {
2081
+ columnIdx: null,
2082
+ unique: null
2083
+ };
2084
+ this._dt = dt;
2085
+ this._s.columnIdx = columnIdx;
2086
+ this._s.unique = Math.random();
2087
+ var originalIdx = columnIdx;
2088
+ Object.assign(this._c, ColumnControl.defaults, opts);
2089
+ this._dom.target = this._target();
2090
+ if (opts.className) {
2091
+ addClass(this._dom.target.closest('tr'), opts.className);
2092
+ }
2093
+ if (this._c.content) {
2094
+ // If column reordering can be done, we reassign the column index here, and before the
2095
+ // plugins can add their own listeners.
2096
+ dt.on('columns-reordered', function (e, details) {
2097
+ _this._s.columnIdx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
2098
+ });
2099
+ this._dom.wrapper = document.createElement('span');
2100
+ this._dom.wrapper.classList.add('dtcc');
2101
+ this._dom.target.appendChild(this._dom.wrapper);
2102
+ this._c.content.forEach(function (content) {
2103
+ var _a = _this.resolve(content), plugin = _a.plugin, config = _a.config;
2104
+ var el = plugin.init.call(_this, config);
2105
+ _this._dom.wrapper.appendChild(el);
2106
+ });
2107
+ dt.on('destroy', function () {
2108
+ _this._dom.wrapper.remove();
2109
+ });
2110
+ }
2111
+ }
2112
+ /**
2113
+ * Get the DataTables API instance that hosts this instance of ColumnControl
2114
+ *
2115
+ * @returns DataTables API instance
2116
+ */
2117
+ ColumnControl.prototype.dt = function () {
2118
+ return this._dt;
2119
+ };
2120
+ /**
2121
+ * Get what column index this instance of ColumnControl is operating on
2122
+ *
2123
+ * @returns Column index
2124
+ */
2125
+ ColumnControl.prototype.idx = function () {
2126
+ return this._s.columnIdx;
2127
+ };
2128
+ /**
2129
+ * Covert the options from `content` in the DataTable initialisation for this instance into a
2130
+ * resolved plugin and options.
2131
+ *
2132
+ * @param content The dev's supplied configuration for the content
2133
+ * @returns Resolved plugin information
2134
+ */
2135
+ ColumnControl.prototype.resolve = function (content) {
2136
+ var plugin = null;
2137
+ var config = null;
2138
+ var type = null;
2139
+ if (typeof content === 'string') {
2140
+ // Simple content - uses default options
2141
+ type = content;
2142
+ plugin = ColumnControl.content[type];
2143
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults);
2144
+ }
2145
+ else if (Array.isArray(content)) {
2146
+ // An array is a shorthand for a dropdown with its default options
2147
+ type = 'dropdown';
2148
+ plugin = ColumnControl.content[type];
2149
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults, {
2150
+ content: content
2151
+ });
2152
+ }
2153
+ else if (content.extend) {
2154
+ // Content with custom options
2155
+ type = content.extend;
2156
+ plugin = ColumnControl.content[type];
2157
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults, content);
2158
+ }
2159
+ if (!plugin) {
2160
+ throw new Error('Unknown ColumnControl content type: ' + type);
2161
+ }
2162
+ // If the plugin is a wrapper around another type - e.g. the colVisDropdown
2163
+ if (plugin.extend) {
2164
+ var self_1 = plugin.extend.call(this, config);
2165
+ return this.resolve(self_1);
2166
+ }
2167
+ return {
2168
+ config: config,
2169
+ type: type,
2170
+ plugin: plugin
2171
+ };
2172
+ };
2173
+ /**
2174
+ * Get the unique id for the instance
2175
+ *
2176
+ * @returns Instant unique id
2177
+ */
2178
+ ColumnControl.prototype.unique = function () {
2179
+ return this._s.unique;
2180
+ };
2181
+ /**
2182
+ * Resolve the configured target into a DOM element
2183
+ */
2184
+ ColumnControl.prototype._target = function () {
2185
+ var target = this._c.target;
2186
+ var column = this._dt.column(this._s.columnIdx);
2187
+ var node;
2188
+ var className = 'header';
2189
+ // Header row index
2190
+ if (typeof target === 'number') {
2191
+ node = column.header(target);
2192
+ }
2193
+ else {
2194
+ var parts = target.split(':');
2195
+ var isHeader = parts[0] === 'tfoot' ? false : true;
2196
+ var row = parts[1] ? parseInt(parts[1]) : 0;
2197
+ if (isHeader) {
2198
+ node = column.header(row);
2199
+ }
2200
+ else {
2201
+ node = column.footer(row);
2202
+ className = 'footer';
2203
+ }
2204
+ }
2205
+ return node.querySelector('div.dt-column-' + className);
2206
+ };
2207
+ // Classes for common UI
2208
+ ColumnControl.Button = Button;
2209
+ ColumnControl.CheckList = CheckList;
2210
+ ColumnControl.SearchInput = SearchInput;
2211
+ /** Content plugins */
2212
+ ColumnControl.content = contentTypes;
2213
+ /** Defaults for ColumnControl */
2214
+ ColumnControl.defaults = {
2215
+ className: '',
2216
+ content: null,
2217
+ target: 0,
2218
+ };
2219
+ /** SVG icons that can be used by the content plugins */
2220
+ ColumnControl.icons = icons;
2221
+ /** Version */
2222
+ ColumnControl.version = '0.9.2';
2223
+ return ColumnControl;
2224
+ }());
2225
+
2226
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2227
+ * DataTables API integration
2228
+ */
2229
+ DataTable.ColumnControl = ColumnControl;
2230
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2231
+ * DataTables listeners for initialisation
2232
+ */
2233
+ // Create header / footer rows that don't exist, but have been referenced in the ColumnControl
2234
+ // targets. This needs to be done _before_ the header / footer structure is detected.
2235
+ $(document).on('i18n.dt', function (e, settings) {
2236
+ if (e.namespace !== 'dt') {
2237
+ return;
2238
+ }
2239
+ var api = new DataTable.Api(settings);
2240
+ var thead = api.table().header();
2241
+ var tableInit = settings.oInit.columnControl;
2242
+ var defaultInit = ColumnControl.defaults;
2243
+ var baseTargets = [];
2244
+ var ackTargets = {};
2245
+ // Determine if there is only one header row initially. If there is, we might append more
2246
+ // after it. Mark the top row as the header row using `titleRow` in the DataTables configuration
2247
+ if (thead.querySelectorAll('tr').length <= 1 && settings.titleRow === null) {
2248
+ settings.titleRow = 0;
2249
+ }
2250
+ identifyTargets(baseTargets, tableInit);
2251
+ identifyTargets(baseTargets, defaultInit);
2252
+ api.columns().every(function (i) {
2253
+ var columnInit = this.init().columnControl;
2254
+ identifyTargets(baseTargets, columnInit);
2255
+ });
2256
+ for (var i = 0; i < baseTargets.length; i++) {
2257
+ assetTarget(ackTargets, baseTargets[i], api);
2258
+ }
2259
+ });
2260
+ // Initialisation of ColumnControl instances - has to be done _after_ the header / footer structure
2261
+ // is detected by DataTables.
2262
+ $(document).on('preInit.dt', function (e, settings) {
2263
+ if (e.namespace !== 'dt') {
2264
+ return;
2265
+ }
2266
+ var api = new DataTable.Api(settings);
2267
+ var tableInit = settings.oInit.columnControl;
2268
+ var defaultInit = ColumnControl.defaults;
2269
+ var baseTargets = [];
2270
+ identifyTargets(baseTargets, tableInit);
2271
+ identifyTargets(baseTargets, defaultInit);
2272
+ api.columns().every(function (i) {
2273
+ var columnInit = this.init().columnControl;
2274
+ var targets = identifyTargets(baseTargets.slice(), columnInit);
2275
+ for (var i_1 = 0; i_1 < targets.length; i_1++) {
2276
+ // Each of the column, table and defaults configuration can be an array of config
2277
+ // objects, an array of content, or a configuration object. There might be multiple
2278
+ // targets for each one, and they might not exist! Therefore this is more complex
2279
+ // than it might be desirable.
2280
+ var columnTargetInit = getOptionsForTarget(targets[i_1], columnInit);
2281
+ var tableTargetInit = getOptionsForTarget(targets[i_1], tableInit);
2282
+ var defaultTargetInit = getOptionsForTarget(targets[i_1], defaultInit);
2283
+ if (defaultTargetInit || tableTargetInit || columnTargetInit) {
2284
+ new ColumnControl(api, this.index(), Object.assign({}, defaultTargetInit || {}, tableTargetInit || {}, columnTargetInit || {}));
2285
+ }
2286
+ }
2287
+ });
2288
+ });
2289
+ DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', function () {
2290
+ var ctx = this;
2291
+ return this.iterator('column', function (settings, idx) {
2292
+ ctx.trigger('cc-search-clear', [idx]);
2293
+ });
2294
+ });
2295
+ DataTable.ext.buttons.ccSearchClear = {
2296
+ text: 'Clear search',
2297
+ init: function (dt, node, config) {
2298
+ var _this = this;
2299
+ dt.on('draw', function () {
2300
+ var enabled = false;
2301
+ var glob = !!dt.search();
2302
+ // No point in wasting clock cycles if we already know it will be enabled
2303
+ if (!glob) {
2304
+ dt.columns().every(function () {
2305
+ if (this.search.fixed('dtcc') || this.search.fixed('dtcc-list')) {
2306
+ enabled = true;
2307
+ }
2308
+ });
2309
+ }
2310
+ _this.enable(glob || enabled);
2311
+ });
2312
+ this.enable(false);
2313
+ },
2314
+ action: function (e, dt, node, config) {
2315
+ dt.search('');
2316
+ dt.columns().ccSearchClear();
2317
+ }
2318
+ };
2319
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2320
+ * Initialisation support - this is more involved than normal as targets might
2321
+ * need to be created, and also options needs to be resolved into a standard
2322
+ * ColumnControl configuration object, from the various forms allowed in the
2323
+ * DataTables configuration.
2324
+ */
2325
+ /**
2326
+ * Given a ColumnControl target, make sure that it exists. If not, create it.
2327
+ *
2328
+ * @param ackTargets Cache for list of targets that have already been found or created
2329
+ * @param target Current target
2330
+ * @param dt DataTable API
2331
+ * @returns Void
2332
+ */
2333
+ function assetTarget(ackTargets, target, dt) {
2334
+ // Check if we already know about the target - if so, we know that it must already be in place
2335
+ if (ackTargets[target]) {
2336
+ return;
2337
+ }
2338
+ var isHeader = true; // false for footer
2339
+ var row = 0;
2340
+ if (typeof target === 'number') {
2341
+ row = target;
2342
+ }
2343
+ else {
2344
+ var parts = target.split(':');
2345
+ if (parts[0] === 'tfoot') {
2346
+ isHeader = false;
2347
+ }
2348
+ if (parts[1]) {
2349
+ row = parseInt(parts[1]);
2350
+ }
2351
+ }
2352
+ // The header / footer have not yet had their structure read, so they aren't available via
2353
+ // the API. As such we need to do our own DOM tweaking
2354
+ var node = isHeader ? dt.table().header() : dt.table().footer();
2355
+ // If the node doesn't exist yet, we need to create it
2356
+ if (!node.querySelectorAll('tr')[row]) {
2357
+ var columns = dt.columns().count();
2358
+ var tr = createElement('tr');
2359
+ tr.setAttribute('data-dt-order', 'disable');
2360
+ for (var i = 0; i < columns; i++) {
2361
+ tr.appendChild(createElement('td'));
2362
+ }
2363
+ node.appendChild(tr);
2364
+ }
2365
+ ackTargets[target] = true;
2366
+ }
2367
+ /**
2368
+ * Given a target, get the config object for it from the parameter passed in
2369
+ *
2370
+ * @param target ColumnControl target
2371
+ * @param input The dev's configuration
2372
+ * @returns The resolved config object, if found
2373
+ */
2374
+ function getOptionsForTarget(target, input) {
2375
+ var defaultTarget = ColumnControl.defaults.target;
2376
+ var selfTarget;
2377
+ if (isIContentArray(input)) {
2378
+ // Top level content array - e.g. `columnControl: ['order']`
2379
+ if (defaultTarget === target) {
2380
+ return {
2381
+ target: defaultTarget,
2382
+ content: input
2383
+ };
2384
+ }
2385
+ }
2386
+ else if (Array.isArray(input)) {
2387
+ // Top level array, some items of which will be configuration objects (possibly not all)
2388
+ for (var i = 0; i < input.length; i++) {
2389
+ var item = input[i];
2390
+ if (isIContentArray(item)) {
2391
+ // A content array, e.g. the inner array from: `columnControl: [['order']]
2392
+ if (defaultTarget === target) {
2393
+ return {
2394
+ target: defaultTarget,
2395
+ content: item
2396
+ };
2397
+ }
2398
+ }
2399
+ else if (isIConfig(item)) {
2400
+ // A config object, e.g. the object from: `columnControl: [{content: []}]`
2401
+ selfTarget = item.target !== undefined ? item.target : defaultTarget;
2402
+ if (target === selfTarget) {
2403
+ return item;
2404
+ }
2405
+ }
2406
+ else {
2407
+ // A content object
2408
+ if (target === defaultTarget) {
2409
+ return {
2410
+ target: defaultTarget,
2411
+ content: input
2412
+ };
2413
+ }
2414
+ }
2415
+ }
2416
+ }
2417
+ else if (typeof input === 'object') {
2418
+ // An object can be either a config object, or an extending content object
2419
+ if (isIConfig(input)) {
2420
+ // Config object: columnControl: {content: []}
2421
+ selfTarget = input.target !== undefined ? input.target : defaultTarget;
2422
+ if (target === selfTarget) {
2423
+ return input;
2424
+ }
2425
+ }
2426
+ else {
2427
+ // content object: columnControl: [{extend: 'order'}]
2428
+ if (target === defaultTarget) {
2429
+ return {
2430
+ target: defaultTarget,
2431
+ content: input
2432
+ };
2433
+ }
2434
+ }
2435
+ }
2436
+ }
2437
+ /**
2438
+ * Get a list of all targets from the configuration objects / arrays
2439
+ *
2440
+ * @param targets Established list of targets - mutated
2441
+ * @param input Configuration object / array
2442
+ * @returns Updated array
2443
+ */
2444
+ function identifyTargets(targets, input) {
2445
+ function add(target) {
2446
+ if (!targets.includes(target)) {
2447
+ targets.push(target);
2448
+ }
2449
+ }
2450
+ if (Array.isArray(input)) {
2451
+ // Array of options, or an array of content
2452
+ input.forEach(function (item) {
2453
+ add(typeof item === 'object' && item.target !== undefined
2454
+ ? item.target
2455
+ : ColumnControl.defaults.target);
2456
+ });
2457
+ }
2458
+ else if (typeof input === 'object') {
2459
+ // Full options defined: { target: x, content: [] }
2460
+ add(input.target !== undefined ? input.target : ColumnControl.defaults.target);
2461
+ }
2462
+ return targets;
2463
+ }
2464
+ /**
2465
+ * Check if an item is a configuration object or not
2466
+ *
2467
+ * @param item Item to check
2468
+ * @returns true if it is a config object
2469
+ */
2470
+ function isIConfig(item) {
2471
+ return typeof item === 'object' && item.target !== undefined
2472
+ ? true
2473
+ : false;
2474
+ }
2475
+ /**
2476
+ * Determine if an array contains only content items or not
2477
+ *
2478
+ * @param arr Array to check
2479
+ * @returns true if is content only, false if not (i.e. is an array with configuration objects).
2480
+ */
2481
+ function isIContentArray(arr) {
2482
+ var detectedConfig = false;
2483
+ if (!Array.isArray(arr)) {
2484
+ return false;
2485
+ }
2486
+ for (var i = 0; i < arr.length; i++) {
2487
+ if (isIConfig(arr[i])) {
2488
+ detectedConfig = true;
2489
+ break;
2490
+ }
2491
+ }
2492
+ return !detectedConfig;
2493
+ }
2494
+
2495
+
2496
+ export default DataTable;