alchemy-form 0.2.8 → 0.2.10

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,873 @@
1
+ const DOM_ELEMENT = Symbol('dom_element'),
2
+ HAS_APPEARS_LISTENER = Symbol('has_appears_listener');
3
+
4
+ /**
5
+ * The al-virtual-scroll element:
6
+ * A scrollable container that only renders the visible elements.
7
+ *
8
+ * @author Jelle De Loecker <jelle@elevenways.be>
9
+ * @since 0.2.10
10
+ * @version 0.2.10
11
+ */
12
+ const VirtualScroll = Function.inherits('Alchemy.Element.Form.WithDataprovider', 'VirtualScroll');
13
+
14
+ /**
15
+ * The template to use to render an entry
16
+ *
17
+ * @author Jelle De Loecker <jelle@elevenways.be>
18
+ * @since 0.2.10
19
+ * @version 0.2.10
20
+ */
21
+ VirtualScroll.setAttribute('item-template');
22
+
23
+ /**
24
+ * Which sides can be used for remote loading?
25
+ *
26
+ * @author Jelle De Loecker <jelle@elevenways.be>
27
+ * @since 0.2.10
28
+ * @version 0.2.10
29
+ */
30
+ VirtualScroll.setAttribute('remote-load-sides', function getRemoteLoadSides(val) {
31
+
32
+ if (val == null) {
33
+ val = ['top', 'bottom'];
34
+ } else {
35
+ val = val.split(',');
36
+ }
37
+
38
+ return val;
39
+ });
40
+
41
+ /**
42
+ * On what side are new items added?
43
+ *
44
+ * @author Jelle De Loecker <jelle@elevenways.be>
45
+ * @since 0.2.10
46
+ * @version 0.2.10
47
+ */
48
+ VirtualScroll.setAttribute('insert-side', function getInsertSide(val) {
49
+
50
+ if (!val) {
51
+ val = 'bottom';
52
+ }
53
+
54
+ return val;
55
+ });
56
+
57
+ /**
58
+ * How should the records be sorted?
59
+ * This is only used to inform the remote host.
60
+ *
61
+ * @author Jelle De Loecker <jelle@elevenways.be>
62
+ * @since 0.2.10
63
+ * @version 0.2.10
64
+ */
65
+ VirtualScroll.setAttribute('sort', function getSort(val) {
66
+
67
+ if (val == -1) {
68
+ val = 'desc';
69
+ } else if (val == 1) {
70
+ val = 'asc';
71
+ } else if (typeof val == 'string') {
72
+ val = val.toLowerCase();
73
+ }
74
+
75
+ if (!val) {
76
+ val = 'asc';
77
+ }
78
+
79
+ return val;
80
+ });
81
+
82
+ /**
83
+ * The batch size
84
+ *
85
+ * @author Jelle De Loecker <jelle@elevenways.be>
86
+ * @since 0.2.10
87
+ * @version 0.2.10
88
+ */
89
+ VirtualScroll.setAttribute('batch-size', function getBatchSize(value) {
90
+
91
+ if (!value || !isFinite(value)) {
92
+ value = 10;
93
+ }
94
+
95
+ return value;
96
+
97
+ }, {type: 'number'});
98
+
99
+ /**
100
+ * The amount of allowed-invisible-elements to remain in the dom
101
+ *
102
+ * @author Jelle De Loecker <jelle@elevenways.be>
103
+ * @since 0.2.10
104
+ * @version 0.2.10
105
+ */
106
+ VirtualScroll.setAttribute('allowed-invisible-elements', function getAllowedInvisibleElements(value) {
107
+
108
+ if (!value || !isFinite(value) || value < 0) {
109
+ value = this.batch_size;
110
+ }
111
+
112
+ if (!value || !isFinite(value)) {
113
+ value = 10;
114
+ }
115
+
116
+ // Keep at least 5 elements at each side
117
+ if (value < 5) {
118
+ value = 5;
119
+ }
120
+
121
+ return value;
122
+
123
+ }, {type: 'number'});
124
+
125
+ /**
126
+ * The expected height of an entry (in pixels)
127
+ *
128
+ * @author Jelle De Loecker <jelle@elevenways.be>
129
+ * @since 0.2.10
130
+ * @version 0.2.10
131
+ */
132
+ VirtualScroll.setAttribute('expected-item-height', {type: 'number'});
133
+
134
+ /**
135
+ * The expected width of an entry (in pixels)
136
+ *
137
+ * @author Jelle De Loecker <jelle@elevenways.be>
138
+ * @since 0.2.10
139
+ * @version 0.2.10
140
+ */
141
+ VirtualScroll.setAttribute('expected-item-width', {type: 'number'});
142
+
143
+ /**
144
+ * Getter for the top-trigger element
145
+ *
146
+ * @author Jelle De Loecker <jelle@elevenways.be>
147
+ * @since 0.2.10
148
+ * @version 0.2.10
149
+ */
150
+ VirtualScroll.addElementGetter('top_trigger', '.top-trigger');
151
+
152
+ /**
153
+ * Getter for the bottom-trigger element
154
+ *
155
+ * @author Jelle De Loecker <jelle@elevenways.be>
156
+ * @since 0.2.10
157
+ * @version 0.2.10
158
+ */
159
+ VirtualScroll.addElementGetter('bottom_trigger', '.bottom-trigger');
160
+
161
+ /**
162
+ * The bidrectional array
163
+ *
164
+ * @author Jelle De Loecker <jelle@elevenways.be>
165
+ * @since 0.2.10
166
+ * @version 0.2.10
167
+ */
168
+ VirtualScroll.setAssignedProperty('loaded_entries', function getLoadedEntries(val) {
169
+
170
+ if (!val) {
171
+ val = new Classes.Develry.LinkedMap();
172
+ this.loaded_entries = val;
173
+ }
174
+
175
+ return val;
176
+ });
177
+
178
+ /**
179
+ * Get the expected item height in pixels
180
+ *
181
+ * @author Jelle De Loecker <jelle@elevenways.be>
182
+ * @since 0.2.10
183
+ * @version 0.2.10
184
+ */
185
+ VirtualScroll.setMethod(function getExpectedItemHeight() {
186
+ return this.expected_item_height || 100;
187
+ });
188
+
189
+ /**
190
+ * Construct the config object used to fetch data
191
+ *
192
+ * @author Jelle De Loecker <jelle@elevenways.be>
193
+ * @since 0.2.10
194
+ * @version 0.2.10
195
+ */
196
+ VirtualScroll.setMethod(function getRemoteFetchConfig(config) {
197
+
198
+ if (!config) {
199
+ config = {};
200
+ }
201
+
202
+ if (!config.direction) {
203
+ config.initial_data = true;
204
+ }
205
+
206
+ config.insert_side = this.insert_side;
207
+ config.sort = this.sort;
208
+ config.batch_size = this.batch_size;
209
+
210
+ this.ensureTriggerElements();
211
+
212
+ if (this.page_size) {
213
+ config.page_size = this.page_size;
214
+ config.page = this.getWantedPage();
215
+ }
216
+
217
+ let head = this.loaded_entries.head,
218
+ tail = this.loaded_entries.tail;
219
+
220
+ if (config.direction == 'top') {
221
+ if (head) {
222
+ config.before = head.value?.record;
223
+ }
224
+ } else if (config.direction == 'bottom') {
225
+ if (tail) {
226
+ config.after = tail.value?.record;
227
+ }
228
+ }
229
+
230
+ return config;
231
+ });
232
+
233
+ /**
234
+ * Make sure the elements that should trigger new elements
235
+ * to render are added to the DOM
236
+ *
237
+ * @author Jelle De Loecker <jelle@elevenways.be>
238
+ * @since 0.2.10
239
+ * @version 0.2.10
240
+ */
241
+ VirtualScroll.setMethod(function ensureTriggerElements() {
242
+
243
+ let top_trigger = this.top_trigger,
244
+ bottom_trigger = this.bottom_trigger;
245
+
246
+ if (!top_trigger) {
247
+ top_trigger = this.createElement('div');
248
+ top_trigger.classList.add('top-trigger');
249
+ this.prepend(top_trigger);
250
+ }
251
+
252
+ if (!bottom_trigger) {
253
+ bottom_trigger = this.createElement('div');
254
+ bottom_trigger.classList.add('bottom-trigger');
255
+ this.append(bottom_trigger);
256
+ }
257
+
258
+ if (!Blast.isBrowser || this[HAS_APPEARS_LISTENER]) {
259
+ return;
260
+ }
261
+
262
+ this[HAS_APPEARS_LISTENER] = true;
263
+
264
+ this.addEventListener('scroll', e => {
265
+ this.handleScrollEvent();
266
+ });
267
+
268
+ const onElementVisible = (element) => {
269
+
270
+ if (element == top_trigger) {
271
+ this.showEntries('top');
272
+ } else if (element == bottom_trigger) {
273
+ this.showEntries('bottom');
274
+ }
275
+ }
276
+
277
+ const observer = new IntersectionObserver((entries, observer) => {
278
+ for (let entry of entries) {
279
+ if (entry.isIntersecting) {
280
+ onElementVisible(entry.target);
281
+ }
282
+ }
283
+ }, {
284
+ threshold: 0,
285
+ rootMargin: '50px 0px 50px 0px',
286
+ });
287
+
288
+ observer.observe(top_trigger);
289
+ observer.observe(bottom_trigger);
290
+ });
291
+
292
+ /**
293
+ * Apply the fetched data
294
+ *
295
+ * @author Jelle De Loecker <jelle@elevenways.be>
296
+ * @since 0.2.10
297
+ * @version 0.2.10
298
+ */
299
+ VirtualScroll.setMethod(function applyFetchedData(err, result, config) {
300
+
301
+ if (err) {
302
+ console.error(err);
303
+ return;
304
+ }
305
+
306
+ if (!result) {
307
+ return;
308
+ }
309
+
310
+ let records;
311
+
312
+ if (Array.isArray(result)) {
313
+ records = result;
314
+ } else if (result.length) {
315
+ // This way we keep `DocumentList` instances as they are!
316
+ records = result;
317
+ } else {
318
+ records = result.records;
319
+ }
320
+
321
+ let append;
322
+
323
+ if (config.initial_data || config.append) {
324
+ append = true;
325
+ } else if (config.append != null) {
326
+ append = config.append;
327
+ } else {
328
+ append = config.direction == 'bottom';
329
+ }
330
+
331
+ if (append) {
332
+ //records = Array.cast(records).reverse();
333
+ for (let record of records) {
334
+ let entry = this.createEntryFor(record);
335
+ this.loaded_entries.set(entry.key, entry);
336
+ }
337
+ } else {
338
+ for (let record of records) {
339
+ let entry = this.createEntryFor(record);
340
+ this.loaded_entries.unshift(entry.key, entry);
341
+ }
342
+ }
343
+
344
+ this.addMissingElementsFromLoadedEntries();
345
+ });
346
+
347
+ /**
348
+ * Get the highest index of a dom element that has been added
349
+ *
350
+ * @author Jelle De Loecker <jelle@elevenways.be>
351
+ * @since 0.2.10
352
+ * @version 0.2.10
353
+ */
354
+ VirtualScroll.setMethod(function getDomKeyRange() {
355
+
356
+ let elements = this.querySelectorAll('[data-loaded-entry-key]'),
357
+ highest = null,
358
+ lowest = null,
359
+ element;
360
+
361
+ element = elements[0];
362
+
363
+ if (element) {
364
+ lowest = element.dataset.loadedEntryKey;
365
+ }
366
+
367
+ element = elements[elements.length - 1];
368
+
369
+ if (element) {
370
+ highest = element.dataset.loadedEntryKey;
371
+ }
372
+
373
+ return [lowest, highest];
374
+ });
375
+
376
+ /**
377
+ * Handle the scroll event
378
+ *
379
+ * @author Jelle De Loecker <jelle@elevenways.be>
380
+ * @since 0.2.10
381
+ * @version 0.2.10
382
+ */
383
+ VirtualScroll.setMethod(function handleScrollEvent() {
384
+ // @TODO
385
+ });
386
+
387
+ /**
388
+ * Add missing elements from loaded entries
389
+ *
390
+ * @author Jelle De Loecker <jelle@elevenways.be>
391
+ * @since 0.2.10
392
+ * @version 0.2.10
393
+ */
394
+ VirtualScroll.setMethod(function showEntries(side) {
395
+
396
+ let [lowest_key, highest_key] = this.getDomKeyRange();
397
+ let key_to_check,
398
+ direction;
399
+
400
+ if (side == 'bottom') {
401
+ direction = 'getNodeAfter';
402
+ key_to_check = highest_key;
403
+ } else {
404
+ direction = 'getNodeBefore';
405
+ key_to_check = lowest_key;
406
+ }
407
+
408
+ this.addMissingElementsFromLoadedEntries();
409
+
410
+ // If the id to check is already loaded, don't do anything
411
+ if (this.loaded_entries[direction](key_to_check, this.batch_size)) {
412
+ return;
413
+ }
414
+
415
+ if (this.remote_load_sides.includes(side)) {
416
+ this.loadRemoteData({direction: side});
417
+ }
418
+ });
419
+
420
+ /**
421
+ * Get the key of something
422
+ *
423
+ * @author Jelle De Loecker <jelle@elevenways.be>
424
+ * @since 0.2.10
425
+ * @version 0.2.10
426
+ */
427
+ VirtualScroll.setMethod(function getKeyOf(record) {
428
+
429
+ if (record._id != null) {
430
+ return '' + record._id;
431
+ }
432
+
433
+ if (record.id != null) {
434
+ return '' + record.id;
435
+ }
436
+
437
+ if (record.key != null) {
438
+ return '' + record.key;
439
+ }
440
+
441
+ throw new Error('Could not get key of record');
442
+ });
443
+
444
+ /**
445
+ * Create an entry object for the given record
446
+ *
447
+ * @author Jelle De Loecker <jelle@elevenways.be>
448
+ * @since 0.2.10
449
+ * @version 0.2.10
450
+ */
451
+ VirtualScroll.setMethod(function createEntryFor(record) {
452
+
453
+ let entry = {
454
+ record,
455
+ key : this.getKeyOf(record),
456
+ };
457
+
458
+ return entry;
459
+ });
460
+
461
+ /**
462
+ * Insert a new record from somewhere
463
+ *
464
+ * @author Jelle De Loecker <jelle@elevenways.be>
465
+ * @since 0.2.10
466
+ * @version 0.2.10
467
+ */
468
+ VirtualScroll.setMethod(function insertRecord(record) {
469
+
470
+ let entry = this.createEntryFor(record);
471
+
472
+ if (this.insert_side == 'bottom') {
473
+ this.loaded_entries.set(entry.key, entry);
474
+ } else {
475
+ this.loaded_entries.unshift(entry.key, entry);
476
+ }
477
+
478
+ let is_at_bottom = this.scrollTop + this.clientHeight >= this.scrollHeight;
479
+
480
+ // Add possible new elements
481
+ let add_count = this.addMissingElementsFromLoadedEntries();
482
+
483
+ if (is_at_bottom && add_count > 0) {
484
+ this.scrollTop = this.scrollHeight;
485
+ }
486
+ });
487
+
488
+ /**
489
+ * Add missing elements from loaded entries
490
+ *
491
+ * @author Jelle De Loecker <jelle@elevenways.be>
492
+ * @since 0.2.10
493
+ * @version 0.2.10
494
+ */
495
+ VirtualScroll.setMethod(function addMissingElementsFromLoadedEntries() {
496
+
497
+ let [lowest_key, highest_key] = this.getDomKeyRange();
498
+
499
+ let top_result,
500
+ bottom_result;
501
+
502
+ let add_to_bottom = 0,
503
+ add_to_top = 0;
504
+
505
+ // We add even more padding to this isVisible check, just to make sure
506
+ if (Blast.isNode) {
507
+ add_to_top = 10;
508
+ } else if (this.top_trigger?.isVisible?.(100)) {
509
+ add_to_top = 10;
510
+ }
511
+
512
+ if (this.bottom_trigger?.isVisible?.(100)) {
513
+ add_to_bottom = 10;
514
+ }
515
+
516
+ let added = [];
517
+ let add_count = 0;
518
+
519
+ let top_node = this.loaded_entries.getNode(lowest_key),
520
+ bottom_node = this.loaded_entries.getNode(highest_key);
521
+
522
+ if (!top_node && !bottom_node) {
523
+ top_node = this.loaded_entries.tail;
524
+ }
525
+
526
+ while (true) {
527
+
528
+ if (add_to_top > 0 && top_node) {
529
+ add_to_top--;
530
+ top_node = top_node.prev;
531
+
532
+ top_result = this._addAdjacent(this.top_trigger, top_node);
533
+
534
+ if (top_result) {
535
+ added.push(top_node.key);
536
+ add_count++;
537
+ }
538
+ } else {
539
+ top_result = false;
540
+ }
541
+
542
+ if (add_to_bottom > 0 && bottom_node) {
543
+ add_to_bottom--;
544
+ bottom_node = bottom_node.next;
545
+ bottom_result = this._addAdjacent(this.bottom_trigger, bottom_node);
546
+
547
+ if (bottom_result) {
548
+ added.push(bottom_node.key);
549
+ add_count++;
550
+ }
551
+ } else {
552
+ bottom_result = false;
553
+ }
554
+
555
+ if (!top_result && !bottom_result) {
556
+ break;
557
+ }
558
+ }
559
+
560
+ this.cullInvisibleElements(added);
561
+
562
+ return add_count;
563
+ });
564
+
565
+ /**
566
+ * Cull invisible elements from the DOM.
567
+ *
568
+ * @author Jelle De Loecker <jelle@elevenways.be>
569
+ * @since 0.2.10
570
+ * @version 0.2.10
571
+ */
572
+ VirtualScroll.setMethod(function cullInvisibleElements(added) {
573
+
574
+ if (!Blast.isBrowser) {
575
+ return;
576
+ }
577
+
578
+ if (!added) {
579
+ added = [];
580
+ }
581
+
582
+ // Get the lowest and highest dom index
583
+ let [lowest_key, highest_key] = this.getDomKeyRange();
584
+
585
+ let lowest_element = this.querySelector('[data-loaded-entry-key="' + lowest_key + '"]'),
586
+ highest_element = this.querySelector('[data-loaded-entry-key="' + highest_key + '"]');
587
+
588
+ let lowest_to_remove = [],
589
+ highest_to_remove = [];
590
+
591
+ const isNear = (element) => {
592
+
593
+ if (added.includes(element)) {
594
+ return true;
595
+ }
596
+
597
+ if (element == this.top_trigger) {
598
+ return true;
599
+ }
600
+
601
+ if (element == this.bottom_trigger) {
602
+ return true;
603
+ }
604
+
605
+ if (element.isVisible()) {
606
+ return true;
607
+ }
608
+
609
+ return false;
610
+ };
611
+
612
+ while (highest_element) {
613
+
614
+ if (isNear(highest_element)) {
615
+ break;
616
+ }
617
+
618
+ highest_to_remove.push(highest_element);
619
+ highest_element = highest_element.previousElementSibling;
620
+ }
621
+
622
+ while (lowest_element) {
623
+
624
+ if (isNear(lowest_element)) {
625
+ break;
626
+ }
627
+
628
+ lowest_to_remove.push(lowest_element);
629
+ lowest_element = lowest_element.nextElementSibling;
630
+ }
631
+
632
+ let allowed_invisible_elements = this.allowed_invisible_elements;
633
+
634
+ if (highest_to_remove.length > allowed_invisible_elements) {
635
+ highest_to_remove = highest_to_remove.slice(0, highest_to_remove.length - allowed_invisible_elements);
636
+
637
+ for (let element of highest_to_remove) {
638
+ this._hideElement(element, 'bottom');
639
+ }
640
+ }
641
+
642
+ if (lowest_to_remove.length > allowed_invisible_elements) {
643
+ lowest_to_remove = lowest_to_remove.slice(0, lowest_to_remove.length - allowed_invisible_elements);
644
+
645
+ for (let element of lowest_to_remove) {
646
+ this._hideElement(element, 'top');
647
+ }
648
+ }
649
+ });
650
+
651
+ /**
652
+ * Hide the given element
653
+ *
654
+ * @author Jelle De Loecker <jelle@elevenways.be>
655
+ * @since 0.2.10
656
+ * @version 0.2.10
657
+ */
658
+ VirtualScroll.setMethod(function _hideElement(element, removed_from_side) {
659
+ this.increaseScrollPaddingBecauseRemoved(removed_from_side, element);
660
+ element.remove();
661
+ });
662
+
663
+ /**
664
+ * Get the scrollpadding for the given side
665
+ *
666
+ * @author Jelle De Loecker <jelle@elevenways.be>
667
+ * @since 0.2.10
668
+ * @version 0.2.10
669
+ */
670
+ VirtualScroll.setMethod(function getScrollPadding(side) {
671
+
672
+ if (typeof side != 'string') {
673
+ side = this.bottom_trigger == side ? 'bottom' : 'top';
674
+ }
675
+
676
+ let result = this[side + '_scroll_padding'];
677
+
678
+ if (!result) {
679
+ result = 0;
680
+ }
681
+
682
+ return result;
683
+ });
684
+
685
+ /**
686
+ * Set the scrollpadding for the given side
687
+ *
688
+ * @author Jelle De Loecker <jelle@elevenways.be>
689
+ * @since 0.2.10
690
+ * @version 0.2.10
691
+ */
692
+ VirtualScroll.setMethod(function setScrollPadding(side, amount) {
693
+
694
+ if (!amount || amount < 0) {
695
+ amount = 0;
696
+ }
697
+
698
+ this[side + '_scroll_padding'] = amount;
699
+
700
+ let trigger = this[side + '_trigger'];
701
+
702
+ if (!trigger) {
703
+ return;
704
+ }
705
+
706
+ let style = trigger.style;
707
+
708
+ if (amount > 0) {
709
+ style.height = amount + 'px';
710
+ } else {
711
+ style.height = '';
712
+ }
713
+ });
714
+
715
+ /**
716
+ * Increase the scroll padding of the given side with the given element height
717
+ * because it has been removed from the given side
718
+ *
719
+ * @author Jelle De Loecker <jelle@elevenways.be>
720
+ * @since 0.2.10
721
+ * @version 0.2.10
722
+ *
723
+ * @param {String} side
724
+ * @param {Element} element
725
+ */
726
+ VirtualScroll.setMethod(function increaseScrollPaddingBecauseRemoved(side, element) {
727
+ element.dataset.removedFromSide = side;
728
+ this.increaseScrollPadding(side, element);
729
+ });
730
+
731
+ /**
732
+ * Increase the scroll padding of the given side with the given element height.
733
+ *
734
+ * @author Jelle De Loecker <jelle@elevenways.be>
735
+ * @since 0.2.10
736
+ * @version 0.2.10
737
+ */
738
+ VirtualScroll.setMethod(function increaseScrollPadding(side, element) {
739
+
740
+ let amount = element.offsetHeight;
741
+
742
+ let current = this.getScrollPadding(side);
743
+ this.setScrollPadding(side, current + amount);
744
+ });
745
+
746
+ /**
747
+ * Decrease the scroll padding of the given side
748
+ *
749
+ * @author Jelle De Loecker <jelle@elevenways.be>
750
+ * @since 0.2.10
751
+ * @version 0.2.10
752
+ *
753
+ * @param {String} side
754
+ */
755
+ VirtualScroll.setMethod(function decreaseScrollPadding(side, element) {
756
+ let amount = element.offsetHeight;
757
+ let current = this.getScrollPadding(side);
758
+ this.setScrollPadding(side, current - amount);
759
+ });
760
+
761
+ /**
762
+ * Decrease the scroll padding of the given side
763
+ *
764
+ * @author Jelle De Loecker <jelle@elevenways.be>
765
+ * @since 0.2.10
766
+ * @version 0.2.10
767
+ *
768
+ * @param {Element} side_trigger
769
+ * @param {Element} element
770
+ */
771
+ VirtualScroll.setMethod(function decreaseScrollPaddingBecauseInserted(side_trigger, element) {
772
+
773
+ let side = this.bottom_trigger == side_trigger ? 'bottom' : 'top';
774
+ let previous_removed_from_side = element.dataset.removedFromSide;
775
+
776
+ if (previous_removed_from_side) {
777
+ this.decreaseScrollPadding(previous_removed_from_side, element);
778
+ } else {
779
+ this.decreaseScrollPadding(side, element);
780
+ }
781
+
782
+ element.removeAttribute('data-removed-from-side');
783
+ });
784
+
785
+ /**
786
+ * Add an element adjacent to the given trigger
787
+ *
788
+ * @author Jelle De Loecker <jelle@elevenways.be>
789
+ * @since 0.2.10
790
+ * @version 0.2.10
791
+ *
792
+ * @param {Element} trigger_element
793
+ * @param {KeyedNode} node
794
+ *
795
+ * @return {boolean}
796
+ */
797
+ VirtualScroll.setMethod(function _addAdjacent(trigger_element, node) {
798
+
799
+ let position = trigger_element == this.bottom_trigger ? 'beforebegin' : 'afterend';
800
+ let is_top = trigger_element == this.top_trigger;
801
+
802
+ let entry = node?.value;
803
+
804
+ if (!entry) {
805
+ return false;
806
+ }
807
+
808
+ let element = this.getEntryElement(entry);
809
+
810
+ if (!element) {
811
+ return false;
812
+ }
813
+
814
+ let current_scroll = this.scrollTop,
815
+ first_element = this.top_trigger.nextElementSibling,
816
+ first_offset = first_element?.offsetTop;
817
+
818
+ trigger_element.insertAdjacentElement(position, element);
819
+ this.decreaseScrollPaddingBecauseInserted(trigger_element, element);
820
+
821
+ if (is_top && first_offset != null) {
822
+ let new_offset = first_element.offsetTop;
823
+ let difference = new_offset - first_offset;
824
+ let new_scroll = difference + current_scroll;
825
+ this.scrollTop = new_scroll;
826
+ }
827
+
828
+ return true;
829
+ });
830
+
831
+ /**
832
+ * Get the entry element
833
+ *
834
+ * @author Jelle De Loecker <jelle@elevenways.be>
835
+ * @since 0.2.10
836
+ * @version 0.2.10
837
+ */
838
+ VirtualScroll.setMethod(function getEntryElement(entry) {
839
+
840
+ if (!entry) {
841
+ return;
842
+ }
843
+
844
+ let result = entry[DOM_ELEMENT];
845
+
846
+ if (!result) {
847
+ result = this.createElement('div');
848
+ entry[DOM_ELEMENT] = result;
849
+ result.textContent = entry.record.name;
850
+ result.dataset.loadedEntryKey = entry.key;
851
+
852
+ if (typeof hawkejs !== 'undefined') {
853
+ let placeholder = hawkejs.scene.general_renderer.partial(this.item_template, {
854
+ record: entry.record
855
+ });
856
+
857
+ result.append(placeholder);
858
+ }
859
+ }
860
+
861
+ return result;
862
+ });
863
+
864
+ /**
865
+ * Added to the DOM for the first time
866
+ *
867
+ * @author Jelle De Loecker <jelle@elevenways.be>
868
+ * @since 0.2.10
869
+ * @version 0.2.10
870
+ */
871
+ VirtualScroll.setMethod(function introduced() {
872
+ this.ensureTriggerElements();
873
+ });