Skip to content

Instantly share code, notes, and snippets.

@Mohamed-Ali-SMA
Last active February 19, 2022 08:14
Show Gist options
  • Save Mohamed-Ali-SMA/784cfef68ecb148e66a9bb6af966873d to your computer and use it in GitHub Desktop.
Save Mohamed-Ali-SMA/784cfef68ecb148e66a9bb6af966873d to your computer and use it in GitHub Desktop.
POC : VD
import Component from '@ember/component';
import layout from '../templates/components/virtual-list-handler';
import object, { get, set, setProperties } from '@ember/object';
export default Component.extend({
layout,
wrapperElement: null,
elementIdentifier: null,
scrollBinded: false,
perElementHeight: 0,
topElementHeight: '0px',
bottomElementHeight: '0px',
listItems: null,
startIndexInfo: 0,
endIndexInfo: 0,
toggleItemLimit: 30,
toggleElementAtScrollPos: 0,
scrolledCount: 0,
computationDone: false,
visibleItems: 0,
debounceObject: object.create({ 'currentTimeout': 0, 'maxTimeout':1, 'isInited': false }),
didInsertElement(){
let self = this;
self._super(...arguments);
if(!self.scrollBinded){
self.bindScroll(self.elementIdentifier, null, function(wrapperElement){
wrapperElement = $(wrapperElement.target)[0];
self.scrollCallback(wrapperElement);
});
}
self._initProperties($(self.elementIdentifier)[0]);
},
_initProperties(wrapperElement){
let self = this;
set(self, 'perElementHeight', wrapperElement.scrollHeight/self.listItems.length);
set(self, 'topElementHeight', '0px');
set(self, 'bottomElementHeight', '0px');
set(self, 'startIndexInfo', 1);
set(self, 'endIndexInfo', 0);
set(self, 'toggleItemLimit', 30);
set(self, 'toggleElementAtScrollPos', 0);
set(self, 'scrolledCount', 0);
set(self, 'computationDone', true);
set(self, 'wrapperElement', wrapperElement);
self.listItems.setEach('canShowItem', false);
set(self, 'toggleItemLimit', self.toggleItemLimit*2);
self.toggleItem(true);
self.computeVirtualElementHeight();
set(self, 'toggleItemLimit', self.toggleItemLimit/2);
},
bindScroll(selector, namespace, scroll_function) {
let self = this,onScroll;
set(this, 'scrollBinded', true);
onScroll = function(event) {
// REASON FOR USING DEBOUNCE LOGIC : https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event#scroll_event_throttling
if (get(self, 'debounceObject.currentTimeout')) {
clearTimeout(get(self, 'debounceObject.currentTimeout'));
}
set(self, 'debounceObject.currentTimeout',
setTimeout(function() {
set(self, 'debounceObject.currentTimeout', 0);
set(self, 'debounceObject.isInited', true);
return scroll_function(event);
}, get(self, 'debounceObject.maxTimeout')));
};
let eventName = namespace ? 'scroll.' + namespace : 'scroll';
$(selector).on(eventName, onScroll)
},
unbindScroll(selector, namespace) {
set(this, 'scrollBinded', false);
let eventName = namespace ? 'scroll.' + namespace : 'scroll';
$(selector).off(eventName);
},
scrollCallback(wrapperElement){
let self = this,
toggleElementAtScrollPos = (self.perElementHeight*self.toggleItemLimit);
if(wrapperElement.scrollTop <= self.prevScrolledPos){
// console.log('>>>>>>>>>>>>>>>>> :::::::::: To Top');
set(self, 'scrolledCount', self.scrolledCount-1);
set(self, 'prevScrolledPos', (toggleElementAtScrollPos*self.scrolledCount));
self.toggleItem(true);
} else {
if (toggleElementAtScrollPos*(self.scrolledCount+1) <= wrapperElement.scrollTop){
// // console.log('<<<<<<<<<<<<<<<<< ::::::::: To Bottom');
set(self, 'scrolledCount', self.scrolledCount+1);
set(self, 'prevScrolledPos', (toggleElementAtScrollPos*self.scrolledCount));
self.toggleItem(false);
}
}
},
toggleItem(status){
// console.log(status, this.scrolledCount);
let self = this,
startIndex = 0,
endIndex = 0,
listItems = self.listItems,
visibleItems = listItems.filterBy('canShowItem', true).length;
if (status){
startIndex = self.scrolledCount*self.toggleItemLimit;
endIndex = (self.scrolledCount+1)*self.toggleItemLimit;
} else {
startIndex = (self.scrolledCount-1)*self.toggleItemLimit;
endIndex = self.scrolledCount*self.toggleItemLimit;
}
// console.log(startIndex, endIndex);
set(self, 'endIndexInfo', endIndex);
if (startIndex >= 0){
listItems.forEach((item, index)=>{
if (index < endIndex && index >= startIndex){
set(item, 'canShowItem', status);
}
});
set(self, 'listItems', listItems);
if(!self.computationDone){
set(self, 'startIndexInfo', startIndex+31);
$(self.wrapperElement)[0].scrollTop = $(self.wrapperElement)[0].scrollTop+1;
set(self, 'computationDone', true);
if (status){
set(self, 'scrolledCount', self.scrolledCount+3);
self.toggleItem(!status);
set(self, 'scrolledCount', self.scrolledCount-3);
} else {
set(self, 'scrolledCount', self.scrolledCount+1);
self.toggleItem(!status);
set(self, 'scrolledCount', self.scrolledCount-1);
}
self.computeVirtualElementHeight();
}
}
},
computeVirtualElementHeight(){
let self = this,
listItems = self.listItems,
toggleItemLimit = self.toggleItemLimit,
perElementHeight = self.perElementHeight,
visibleItems = listItems.filterBy('canShowItem', true).length,
wrapperElementHeight = self.wrapperElement.scrollHeight,
topElementHeight = (toggleItemLimit*self.scrolledCount) * perElementHeight,
bottomElementHeight = 0;
bottomElementHeight = wrapperElementHeight - topElementHeight;
bottomElementHeight -= (visibleItems*perElementHeight);
set(self, 'visibleItems', visibleItems);
// console.log(topElementHeight, bottomElementHeight);
// console.log(toggleItemLimit, self.scrolledCount)
if (topElementHeight >= 0){
set(this, 'topElementHeight', topElementHeight+'px');
}
// console.log(wrapperElementHeight , bottomElementHeight)
if (visibleItems >= toggleItemLimit){
set(this, 'bottomElementHeight', bottomElementHeight+'px');
} else {
set(this, 'bottomElementHeight', '0px');
}
set(self, 'computationDone', false);
},
willDestroyElement(){
let self = this;
setProperties(self.debounceObject, { 'currentTimeout': 0, 'maxTimeout': 1, 'isInited': false }); // No I18N
}
});
import Controller from '@ember/controller';
import { A } from '@ember/array';
import object, { set } from '@ember/object';
export default Controller.extend({
totalCount: 1000,
listItems: A(),
showList: false,
visibleItems: 0,
startIndexInfo: 1,
endIndexInfo: 0,
actions:{
toggleList(){
let self = this,
listItems = self.listItems;
listItems.clear();
if (!self.showList){
for (let i=0; i<self.totalCount; i++){
listItems.pushObject(object.create({'name': i+1, 'canShowItem': true}));
}
set(self, 'listItems', listItems);
}
set(self, 'showList', !self.showList);
}
}
});
<h2>
<u>Virtual DOM </u>
( <i>Elements count : {{input value=totalCount}} </i> )
<button {{action 'toggleList' true}}>
{{#if showList}} Hide {{else}} Show {{/if}} list
</button>
</h2>
{{outlet}}
{{#if showList}}
<h3 style='color:grey;background: #f7f5f5;height: 30px;margin: 0;padding: 18px;border: solid 0.1px #dce7a8bd;'>
Visible Items : <span style='color:#e372a1;'>{{visibleItems}}</span>
<span style="float: right;">
From : <span style='color:#e372a1;'>{{startIndexInfo}}</span>
&nbsp; &nbsp;
To : <span style='color:#e372a1;'>{{endIndexInfo}}</span>
</span>
</h3>
{{/if}}
{{#if showList}}
<div class='list-wrapper' style='max-height:475px;overflow-y:auto;border:ridge 1px;background:#ffecec;'>
<ul style="list-style-type: none;margin:0;">
{{#virtual-list-handler elementIdentifier='.list-wrapper' listItems=listItems visibleItems=visibleItems startIndexInfo=startIndexInfo endIndexInfo=endIndexInfo}}
{{#each listItems as |item index|}}
{{#if item.canShowItem}}
<li class='list-item {{item.name}}' style="border: solid 0.1px lightgrey;padding: 10px;">
<center> <b> {{item.name}} :: {{index}} </b> </center>
</li>
{{/if}}
{{/each}}
{{/virtual-list-handler}}
</ul>
</div>
<hr>
{{/if}}
<li style='border: solid 0.1px lightgrey;height:{{topElementHeight}}'></li>
{{yield}}
<li style='border: solid 0.1px lightgrey;height:{{bottomElementHeight}};'></li>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment