framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
333 lines (330 loc) • 10.5 kB
JavaScript
import $ from '../../shared/dom7.js';
import { extend, deleteProps } from '../../shared/utils.js';
import Framework7Class from '../../shared/class.js';
import { getSupport } from '../../shared/get-support.js';
class ListIndex extends Framework7Class {
constructor(app, params) {
if (params === void 0) {
params = {};
}
super(params, [app]);
const index = this;
const defaults = {
el: null,
// where to render indexes
listEl: null,
// list el to generate indexes
indexes: 'auto',
// or array of indexes
iosItemHeight: 14,
mdItemHeight: 14,
scrollList: true,
label: false,
// eslint-disable-next-line
renderItem(itemContent, itemIndex) {
return `
<li>${itemContent}</li>
`.trim();
},
renderSkipPlaceholder() {
return '<li class="list-index-skip-placeholder"></li>';
},
on: {}
};
// Extend defaults with modules params
index.useModulesParams(defaults);
index.params = extend(defaults, params);
let $el;
let $listEl;
let $pageContentEl;
let $ul;
if (index.params.el) {
$el = $(index.params.el);
} else {
return index;
}
if ($el[0].f7ListIndex) {
return $el[0].f7ListIndex;
}
$ul = $el.find('ul');
if ($ul.length === 0) {
$ul = $('<ul></ul>');
$el.append($ul);
}
if (index.params.listEl) {
$listEl = $(index.params.listEl);
}
if (index.params.indexes === 'auto' && !$listEl) {
return index;
}
if ($listEl) {
$pageContentEl = $listEl.parents('.page-content').eq(0);
} else {
$pageContentEl = $el.siblings('.page-content').eq(0);
if ($pageContentEl.length === 0) {
$pageContentEl = $el.parents('.page').eq(0).find('.page-content').eq(0);
}
}
$el[0].f7ListIndex = index;
extend(index, {
app,
$el,
el: $el && $el[0],
$ul,
ul: $ul && $ul[0],
$listEl,
listEl: $listEl && $listEl[0],
$pageContentEl,
pageContentEl: $pageContentEl && $pageContentEl[0],
indexes: params.indexes,
height: 0,
skipRate: 0
});
// Install Modules
index.useModules();
// Attach events
function handleResize() {
const height = {
index
};
index.calcSize();
if (height !== index.height) {
index.render();
}
}
function handleClick(e) {
const $clickedLi = $(e.target).closest('li');
if (!$clickedLi.length) return;
let itemIndex = $clickedLi.index();
if (index.skipRate > 0) {
const percentage = itemIndex / ($clickedLi.siblings('li').length - 1);
itemIndex = Math.round((index.indexes.length - 1) * percentage);
}
const itemContent = index.indexes[itemIndex];
index.$el.trigger('listindex:click', {
content: itemContent,
index: itemIndex
});
index.emit('local::click listIndexClick', index, itemContent, itemIndex);
index.$el.trigger('listindex:select', {
content: itemContent,
index: itemIndex
});
index.emit('local::select listIndexSelect', index, itemContent, itemIndex);
if (index.$listEl && index.params.scrollList) {
index.scrollListToIndex(itemContent, itemIndex);
}
}
const touchesStart = {};
let isTouched;
let isMoved;
let topPoint;
let bottomPoint;
let $labelEl;
let previousIndex = null;
function handleTouchStart(e) {
const $children = $ul.children();
if (!$children.length) return;
topPoint = $children[0].getBoundingClientRect().top;
bottomPoint = $children[$children.length - 1].getBoundingClientRect().top + $children[0].offsetHeight;
touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
isTouched = true;
isMoved = false;
previousIndex = null;
}
function handleTouchMove(e) {
if (!isTouched) return;
if (!isMoved && index.params.label) {
$labelEl = $('<span class="list-index-label"></span>');
$el.append($labelEl);
}
isMoved = true;
const pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
e.preventDefault();
let percentage = (pageY - topPoint) / (bottomPoint - topPoint);
percentage = Math.min(Math.max(percentage, 0), 1);
const itemIndex = Math.round((index.indexes.length - 1) * percentage);
const itemContent = index.indexes[itemIndex];
const ulHeight = bottomPoint - topPoint;
const bubbleBottom = (index.height - ulHeight) / 2 + (1 - percentage) * ulHeight;
if (itemIndex !== previousIndex) {
if (index.params.label) {
$labelEl.html(itemContent).transform(`translateY(-${bubbleBottom}px)`);
}
if (index.$listEl && index.params.scrollList) {
index.scrollListToIndex(itemContent, itemIndex);
}
}
previousIndex = itemIndex;
index.$el.trigger('listindex:select');
index.emit('local::select listIndexSelect', index, itemContent, itemIndex);
}
function handleTouchEnd() {
if (!isTouched) return;
isTouched = false;
isMoved = false;
if (index.params.label) {
if ($labelEl) $labelEl.remove();
$labelEl = undefined;
}
}
const passiveListener = getSupport().passiveListener ? {
passive: true
} : false;
index.attachEvents = function attachEvents() {
$el.parents('.tab').on('tab:show', handleResize);
$el.parents('.page').on('page:reinit', handleResize);
$el.parents('.panel').on('panel:open', handleResize);
$el.parents('.sheet-modal, .actions-modal, .popup, .popover, .login-screen, .dialog, .toast').on('modal:open', handleResize);
app.on('resize', handleResize);
$el.on('click', handleClick);
$el.on(app.touchEvents.start, handleTouchStart, passiveListener);
app.on('touchmove:active', handleTouchMove);
app.on('touchend:passive', handleTouchEnd);
};
index.detachEvents = function attachEvents() {
$el.parents('.tab').off('tab:show', handleResize);
$el.parents('.page').off('page:reinit', handleResize);
$el.parents('.panel').off('panel:open', handleResize);
$el.parents('.sheet-modal, .actions-modal, .popup, .popover, .login-screen, .dialog, .toast').off('modal:open', handleResize);
app.off('resize', handleResize);
$el.off('click', handleClick);
$el.off(app.touchEvents.start, handleTouchStart, passiveListener);
app.off('touchmove:active', handleTouchMove);
app.off('touchend:passive', handleTouchEnd);
};
// Init
index.init();
return index;
}
// eslint-disable-next-line
scrollListToIndex(itemContent, itemIndex) {
const index = this;
const {
$listEl,
$pageContentEl,
app
} = index;
if (!$listEl || !$pageContentEl || $pageContentEl.length === 0) return index;
let $scrollToEl;
$listEl.find('.list-group-title').each(el => {
if ($scrollToEl) return;
const $el = $(el);
if ($el.text() === itemContent) {
$scrollToEl = $el;
}
});
if (!$scrollToEl || $scrollToEl.length === 0) return index;
const parentTop = $scrollToEl.parent().offset().top;
let paddingTop = parseInt($pageContentEl.css('padding-top'), 10);
const scrollTop = $pageContentEl[0].scrollTop;
const scrollToElTop = $scrollToEl.offset().top;
if ($pageContentEl.parents('.page-with-navbar-large').length) {
const navbarInnerEl = app.navbar.getElByPage($pageContentEl.parents('.page-with-navbar-large').eq(0));
const $titleLargeEl = $(navbarInnerEl).find('.title-large');
if ($titleLargeEl.length) {
paddingTop -= $titleLargeEl[0].offsetHeight || 0;
}
}
if (parentTop <= paddingTop) {
$pageContentEl.scrollTop(parentTop + scrollTop - paddingTop);
} else {
$pageContentEl.scrollTop(scrollToElTop + scrollTop - paddingTop);
}
return index;
}
renderSkipPlaceholder() {
const index = this;
return index.params.renderSkipPlaceholder.call(index);
}
renderItem(itemContent, itemIndex) {
const index = this;
return index.params.renderItem.call(index, itemContent, itemIndex);
}
render() {
const index = this;
const {
$ul,
indexes,
skipRate
} = index;
let wasSkipped;
const html = indexes.map((itemContent, itemIndex) => {
if (itemIndex % skipRate !== 0 && skipRate > 0) {
wasSkipped = true;
return '';
}
let itemHtml = index.renderItem(itemContent, itemIndex);
if (wasSkipped) {
itemHtml = index.renderSkipPlaceholder() + itemHtml;
}
wasSkipped = false;
return itemHtml;
}).join('');
$ul.html(html);
return index;
}
calcSize() {
const index = this;
const {
app,
params,
el,
indexes
} = index;
const height = el.offsetHeight;
const itemHeight = params[`${app.theme}ItemHeight`];
const maxItems = Math.floor(height / itemHeight);
const items = indexes.length;
let skipRate = 0;
if (items > maxItems) {
skipRate = Math.ceil((items * 2 - 1) / maxItems);
}
index.height = height;
index.skipRate = skipRate;
return index;
}
calcIndexes() {
const index = this;
if (index.params.indexes === 'auto') {
index.indexes = [];
index.$listEl.find('.list-group-title').each(el => {
const elContent = $(el).text();
if (index.indexes.indexOf(elContent) < 0) {
index.indexes.push(elContent);
}
});
} else {
index.indexes = index.params.indexes;
}
return index;
}
update() {
const index = this;
index.calcIndexes();
index.calcSize();
index.render();
return index;
}
init() {
const index = this;
index.calcIndexes();
index.calcSize();
index.render();
index.attachEvents();
}
destroy() {
let index = this;
index.$el.trigger('listindex:beforedestroy', index);
index.emit('local::beforeDestroy listIndexBeforeDestroy');
index.detachEvents();
if (index.$el[0]) {
index.$el[0].f7ListIndex = null;
delete index.$el[0].f7ListIndex;
}
deleteProps(index);
index = null;
}
}
export default ListIndex;