@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
518 lines (481 loc) • 14.6 kB
JavaScript
import $ from 'dom7';
import Utils from '../../utils/utils';
import Framework7Class from '../../utils/class';
import pickerColumn from './picker-column';
class Picker extends Framework7Class {
constructor(app, params = {}) {
super(params, [app]);
const picker = this;
picker.params = Utils.extend({}, app.params.picker, params);
let $containerEl;
if (picker.params.containerEl) {
$containerEl = $(picker.params.containerEl);
if ($containerEl.length === 0) return picker;
}
let $inputEl;
if (picker.params.inputEl) {
$inputEl = $(picker.params.inputEl);
}
let view;
if ($inputEl) {
view = $inputEl.parents('.view').length && $inputEl.parents('.view')[0].f7View;
}
if (!view) view = app.views.main;
Utils.extend(picker, {
app,
$containerEl,
containerEl: $containerEl && $containerEl[0],
inline: $containerEl && $containerEl.length > 0,
needsOriginFix: app.device.ios || ((window.navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && window.navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !app.device.android),
cols: [],
$inputEl,
inputEl: $inputEl && $inputEl[0],
initialized: false,
opened: false,
url: picker.params.url,
view,
});
function onResize() {
picker.resizeCols();
}
function onInputClick() {
picker.open();
}
function onInputFocus(e) {
e.preventDefault();
}
function onHtmlClick(e) {
const $targetEl = $(e.target);
if (picker.isPopover()) return;
if (!picker.opened) return;
if ($targetEl.closest('[class*="backdrop"]').length) return;
if ($inputEl && $inputEl.length > 0) {
if ($targetEl[0] !== $inputEl[0] && $targetEl.closest('.sheet-modal').length === 0) {
picker.close();
}
} else if ($(e.target).closest('.sheet-modal').length === 0) {
picker.close();
}
}
// Events
Utils.extend(picker, {
attachResizeEvent() {
app.on('resize', onResize);
},
detachResizeEvent() {
app.off('resize', onResize);
},
attachInputEvents() {
picker.$inputEl.on('click', onInputClick);
if (picker.params.inputReadOnly) {
picker.$inputEl.on('focus mousedown', onInputFocus);
}
},
detachInputEvents() {
picker.$inputEl.off('click', onInputClick);
if (picker.params.inputReadOnly) {
picker.$inputEl.off('focus mousedown', onInputFocus);
}
},
attachHtmlEvents() {
app.on('click', onHtmlClick);
},
detachHtmlEvents() {
app.off('click', onHtmlClick);
},
});
picker.init();
return picker;
}
initInput() {
const picker = this;
if (!picker.$inputEl) return;
if (picker.params.inputReadOnly) picker.$inputEl.prop('readOnly', true);
}
resizeCols() {
const picker = this;
if (!picker.opened) return;
for (let i = 0; i < picker.cols.length; i += 1) {
if (!picker.cols[i].divider) {
picker.cols[i].calcSize();
picker.cols[i].setValue(picker.cols[i].value, 0, false);
}
}
}
isPopover() {
const picker = this;
const { app, modal, params } = picker;
if (params.openIn === 'sheet') return false;
if (modal && modal.type !== 'popover') return false;
if (!picker.inline && picker.inputEl) {
if (params.openIn === 'popover') return true;
else if (app.device.ios) {
return !!app.device.ipad;
} else if (app.width >= 768) {
return true;
}
}
return false;
}
formatValue() {
const picker = this;
const { value, displayValue } = picker;
if (picker.params.formatValue) {
return picker.params.formatValue.call(picker, value, displayValue);
}
return value.join(' ');
}
setValue(values, transition) {
const picker = this;
let valueIndex = 0;
if (picker.cols.length === 0) {
picker.value = values;
picker.updateValue(values);
return;
}
for (let i = 0; i < picker.cols.length; i += 1) {
if (picker.cols[i] && !picker.cols[i].divider) {
picker.cols[i].setValue(values[valueIndex], transition);
valueIndex += 1;
}
}
}
getValue() {
const picker = this;
return picker.value;
}
updateValue(forceValues) {
const picker = this;
const newValue = forceValues || [];
const newDisplayValue = [];
let column;
if (picker.cols.length === 0) {
const noDividerColumns = picker.params.cols.filter(c => !c.divider);
for (let i = 0; i < noDividerColumns.length; i += 1) {
column = noDividerColumns[i];
if (column.displayValues !== undefined && column.values !== undefined && column.values.indexOf(newValue[i]) !== -1) {
newDisplayValue.push(column.displayValues[column.values.indexOf(newValue[i])]);
} else {
newDisplayValue.push(newValue[i]);
}
}
} else {
for (let i = 0; i < picker.cols.length; i += 1) {
if (!picker.cols[i].divider) {
newValue.push(picker.cols[i].value);
newDisplayValue.push(picker.cols[i].displayValue);
}
}
}
if (newValue.indexOf(undefined) >= 0) {
return;
}
picker.value = newValue;
picker.displayValue = newDisplayValue;
if (picker.params.onChange) {
picker.emit('local::change pickerChange', picker, picker.value, picker.displayValue);
}
if (picker.inputEl) {
picker.$inputEl.val(picker.formatValue());
picker.$inputEl.trigger('change');
}
}
initColumn(colEl, updateItems) {
const picker = this;
pickerColumn.call(picker, colEl, updateItems);
}
// eslint-disable-next-line
destroyColumn(colEl) {
const picker = this;
const $colEl = $(colEl);
const index = $colEl.index();
if (picker.cols[index] && picker.cols[index].destroy) {
picker.cols[index].destroy();
}
}
renderToolbar() {
const picker = this;
if (picker.params.renderToolbar) return picker.params.renderToolbar.call(picker, picker);
return `
<div class="toolbar no-shadow">
<div class="toolbar-inner">
<div class="left"></div>
<div class="right">
<a href="#" class="link sheet-close popover-close">${picker.params.toolbarCloseText}</a>
</div>
</div>
</div>
`.trim();
}
// eslint-disable-next-line
renderColumn(col, onlyItems) {
const colClasses = `picker-column ${col.textAlign ? `picker-column-${col.textAlign}` : ''} ${col.cssClass || ''}`;
let columnHtml;
let columnItemsHtml;
if (col.divider) {
columnHtml = `
<div class="${colClasses} picker-column-divider">${col.content}</div>
`;
} else {
columnItemsHtml = col.values.map((value, index) => `
<div class="picker-item" data-picker-value="${value}">
<span>${col.displayValues ? col.displayValues[index] : value}</span>
</div>
`).join('');
columnHtml = `
<div class="${colClasses}">
<div class="picker-items">${columnItemsHtml}</div>
</div>
`;
}
return onlyItems ? columnItemsHtml.trim() : columnHtml.trim();
}
renderInline() {
const picker = this;
const { rotateEffect, cssClass, toolbar } = picker.params;
const inlineHtml = `
<div class="picker picker-inline ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}">
${toolbar ? picker.renderToolbar() : ''}
<div class="picker-columns">
${picker.cols.map(col => picker.renderColumn(col)).join('')}
<div class="picker-center-highlight"></div>
</div>
</div>
`.trim();
return inlineHtml;
}
renderSheet() {
const picker = this;
const { rotateEffect, cssClass, toolbar } = picker.params;
const sheetHtml = `
<div class="sheet-modal picker picker-sheet ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}">
${toolbar ? picker.renderToolbar() : ''}
<div class="sheet-modal-inner picker-columns">
${picker.cols.map(col => picker.renderColumn(col)).join('')}
<div class="picker-center-highlight"></div>
</div>
</div>
`.trim();
return sheetHtml;
}
renderPopover() {
const picker = this;
const { rotateEffect, cssClass, toolbar } = picker.params;
const popoverHtml = `
<div class="popover picker-popover">
<div class="popover-inner">
<div class="picker ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}">
${toolbar ? picker.renderToolbar() : ''}
<div class="picker-columns">
${picker.cols.map(col => picker.renderColumn(col)).join('')}
<div class="picker-center-highlight"></div>
</div>
</div>
</div>
</div>
`.trim();
return popoverHtml;
}
render() {
const picker = this;
if (picker.params.render) return picker.params.render.call(picker);
if (!picker.inline) {
if (picker.isPopover()) return picker.renderPopover();
return picker.renderSheet();
}
return picker.renderInline();
}
onOpen() {
const picker = this;
const { initialized, $el, app, $inputEl, inline, value, params } = picker;
picker.opened = true;
// Init main events
picker.attachResizeEvent();
// Init cols
$el.find('.picker-column').each((index, colEl) => {
let updateItems = true;
if (
(!initialized && params.value) ||
(initialized && value)
) {
updateItems = false;
}
picker.initColumn(colEl, updateItems);
});
// Set value
if (!initialized) {
if (value) picker.setValue(value, 0);
else if (params.value) {
picker.setValue(params.value, 0);
}
} else if (value) {
picker.setValue(value, 0);
}
// Extra focus
if (!inline && $inputEl.length && app.theme === 'md') {
$inputEl.trigger('focus');
}
picker.initialized = true;
// Trigger events
if ($el) {
$el.trigger('picker:open', picker);
}
if ($inputEl) {
$inputEl.trigger('picker:open', picker);
}
picker.emit('local::open pickerOpen', picker);
}
onOpened() {
const picker = this;
if (picker.$el) {
picker.$el.trigger('picker:opened', picker);
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:opened', picker);
}
picker.emit('local::opened pickerOpened', picker);
}
onClose() {
const picker = this;
const app = picker.app;
// Detach events
picker.detachResizeEvent();
picker.cols.forEach((col) => {
if (col.destroy) col.destroy();
});
if (picker.$inputEl && app.theme === 'md') {
picker.$inputEl.trigger('blur');
}
if (picker.$el) {
picker.$el.trigger('picker:close', picker);
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:close', picker);
}
picker.emit('local::close pickerClose', picker);
}
onClosed() {
const picker = this;
picker.opened = false;
if (!picker.inline) {
Utils.nextTick(() => {
if (picker.modal && picker.modal.el && picker.modal.destroy) {
if (!picker.params.routableModals) {
picker.modal.destroy();
}
}
delete picker.modal;
});
}
if (picker.$el) {
picker.$el.trigger('picker:closed', picker);
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:closed', picker);
}
picker.emit('local::closed pickerClosed', picker);
}
open() {
const picker = this;
const { app, opened, inline, $inputEl } = picker;
if (opened) return;
if (picker.cols.length === 0 && picker.params.cols.length) {
picker.params.cols.forEach((col) => {
picker.cols.push(col);
});
}
if (inline) {
picker.$el = $(picker.render());
picker.$el[0].f7Picker = picker;
picker.$containerEl.append(picker.$el);
picker.onOpen();
picker.onOpened();
return;
}
const isPopover = picker.isPopover();
const modalType = isPopover ? 'popover' : 'sheet';
const modalParams = {
targetEl: $inputEl,
scrollToEl: picker.params.scrollToInput ? $inputEl : undefined,
content: picker.render(),
backdrop: isPopover,
on: {
open() {
const modal = this;
picker.modal = modal;
picker.$el = isPopover ? modal.$el.find('.picker') : modal.$el;
picker.$el[0].f7Picker = picker;
picker.onOpen();
},
opened() { picker.onOpened(); },
close() { picker.onClose(); },
closed() { picker.onClosed(); },
},
};
if (picker.params.routableModals) {
picker.view.router.navigate(picker.url, {
createRoute: {
path: picker.url,
[modalType]: modalParams,
},
});
} else {
picker.modal = app[modalType].create(modalParams);
picker.modal.open();
}
}
close() {
const picker = this;
const { opened, inline } = picker;
if (!opened) return;
if (inline) {
picker.onClose();
picker.onClosed();
return;
}
if (picker.params.routableModals) {
picker.view.router.back();
} else {
picker.modal.close();
}
}
init() {
const picker = this;
picker.initInput();
if (picker.inline) {
picker.open();
picker.emit('local::init pickerInit', picker);
return;
}
if (!picker.initialized && picker.params.value) {
picker.setValue(picker.params.value);
}
// Attach input Events
if (picker.$inputEl) {
picker.attachInputEvents();
}
if (picker.params.closeByOutsideClick) {
picker.attachHtmlEvents();
}
picker.emit('local::init pickerInit', picker);
}
destroy() {
const picker = this;
if (picker.destroyed) return;
const { $el } = picker;
picker.emit('local::beforeDestroy pickerBeforeDestroy', picker);
if ($el) $el.trigger('picker:beforedestroy', picker);
picker.close();
// Detach Events
if (picker.$inputEl) {
picker.detachInputEvents();
}
if (picker.params.closeByOutsideClick) {
picker.detachHtmlEvents();
}
if ($el && $el.length) delete picker.$el[0].f7Picker;
Utils.deleteProps(picker);
picker.destroyed = true;
}
}
export default Picker;