framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
588 lines (578 loc) • 16.4 kB
JavaScript
import { getWindow } from 'ssr-window';
import $ from '../../shared/dom7.js';
import { extend, nextTick, deleteProps } from '../../shared/utils.js';
import Framework7Class from '../../shared/class.js';
import { getDevice } from '../../shared/get-device.js';
import pickerColumn from './picker-column.js';
/** @jsx $jsx */
import $jsx from '../../shared/$jsx.js';
class Picker extends Framework7Class {
constructor(app, params) {
if (params === void 0) {
params = {};
}
super(params, [app]);
const picker = this;
const device = getDevice();
const window = getWindow();
picker.params = 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 $scrollToEl = picker.params.scrollToInput ? $inputEl : undefined;
if (picker.params.scrollToEl) {
const scrollToEl = $(picker.params.scrollToEl);
if (scrollToEl.length > 0) {
$scrollToEl = scrollToEl;
}
}
extend(picker, {
app,
$containerEl,
containerEl: $containerEl && $containerEl[0],
inline: $containerEl && $containerEl.length > 0,
needsOriginFix: device.ios || window.navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && window.navigator.userAgent.toLowerCase().indexOf('chrome') < 0 && !device.android,
cols: [],
$inputEl,
inputEl: $inputEl && $inputEl[0],
$scrollToEl,
initialized: false,
opened: false,
url: picker.params.url
});
function onResize() {
picker.resizeCols();
}
function onInputClick() {
picker.open();
}
function onInputFocus(e) {
e.preventDefault();
}
let htmlTouchStartTarget = null;
function onHtmlTouchStart(e) {
htmlTouchStartTarget = e.target;
}
function onHtmlClick(e) {
if (picker.destroyed || !picker.params) return;
const $targetEl = $(e.target);
if (picker.isPopover()) return;
if (!picker.opened || picker.closing) return;
if ($targetEl.closest('[class*="backdrop"]').length) return;
if ($inputEl && $inputEl.length > 0) {
if (htmlTouchStartTarget === e.target && $targetEl[0] !== $inputEl[0] && $targetEl.closest('.sheet-modal').length === 0) {
picker.close();
}
} else if ($(e.target).closest('.sheet-modal').length === 0) {
picker.close();
}
}
// Events
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);
if (picker.$inputEl[0]) {
picker.$inputEl[0].f7ValidateReadonly = true;
}
}
},
detachInputEvents() {
picker.$inputEl.off('click', onInputClick);
if (picker.params.inputReadOnly) {
picker.$inputEl.off('focus mousedown', onInputFocus);
if (picker.$inputEl[0]) {
delete picker.$inputEl[0].f7ValidateReadonly;
}
}
},
attachHtmlEvents() {
app.on('click', onHtmlClick);
app.on('touchstart', onHtmlTouchStart);
},
detachHtmlEvents() {
app.off('click', onHtmlClick);
app.off('touchstart', onHtmlTouchStart);
}
});
picker.init();
return picker;
}
get view() {
const {
app,
params,
$inputEl
} = this;
let view;
if (params.view) {
view = params.view;
} else if ($inputEl) {
view = $inputEl.parents('.view').length && $inputEl.parents('.view')[0].f7View;
}
if (!view) view = app.views.main;
return view;
}
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, false);
}
}
}
isPopover() {
const picker = this;
const {
app,
modal,
params
} = picker;
const device = getDevice();
if (params.openIn === 'sheet') return false;
if (modal && modal.type !== 'popover') return false;
if (!picker.inline && picker.inputEl) {
if (params.openIn === 'popover') return true;
if (device.ios) {
return !!device.ipad;
}
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) {
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]);
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;
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 $jsx("div", {
class: "toolbar toolbar-top"
}, $jsx("div", {
class: "toolbar-inner"
}, $jsx("div", {
class: "left"
}), $jsx("div", {
class: "right"
}, $jsx("a", {
class: "link sheet-close popover-close"
}, picker.params.toolbarCloseText))));
}
// 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) {
// prettier-ignore
columnHtml = `
<div class="${colClasses} picker-column-divider">${col.content}</div>
`;
} else {
// prettier-ignore
columnItemsHtml = col.values.map((value, index) => `
<div class="picker-item" data-picker-value="${value}">
<span>${col.displayValues ? col.displayValues[index] : value}</span>
</div>
`).join('');
// prettier-ignore
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 = $jsx("div", {
class: `picker picker-inline ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}`
}, toolbar && picker.renderToolbar(), $jsx("div", {
class: "picker-columns"
}, picker.cols.map(col => picker.renderColumn(col)), $jsx("div", {
class: "picker-center-highlight"
})));
return inlineHtml;
}
renderSheet() {
const picker = this;
const {
rotateEffect,
cssClass,
toolbar
} = picker.params;
const sheetHtml = $jsx("div", {
class: `sheet-modal picker picker-sheet ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}`
}, toolbar && picker.renderToolbar(), $jsx("div", {
class: "sheet-modal-inner picker-columns"
}, picker.cols.map(col => picker.renderColumn(col)), $jsx("div", {
class: "picker-center-highlight"
})));
return sheetHtml;
}
renderPopover() {
const picker = this;
const {
rotateEffect,
cssClass,
toolbar
} = picker.params;
const popoverHtml = $jsx("div", {
class: "popover picker-popover"
}, $jsx("div", {
class: "popover-inner"
}, $jsx("div", {
class: `picker ${rotateEffect ? 'picker-3d' : ''} ${cssClass || ''}`
}, toolbar && picker.renderToolbar(), $jsx("div", {
class: "picker-columns"
}, picker.cols.map(col => picker.renderColumn(col)), $jsx("div", {
class: "picker-center-highlight"
})))));
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;
picker.closing = false;
picker.opening = true;
// Init main events
picker.attachResizeEvent();
// Init cols
$el.find('.picker-column').each(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);else if (params.value) {
picker.setValue(params.value);
}
} else if (value) {
picker.setValue(value);
}
// Extra focus
if (!inline && $inputEl && $inputEl.length && app.theme === 'md') {
$inputEl.trigger('focus');
}
picker.initialized = true;
// Trigger events
if ($el) {
$el.trigger('picker:open');
}
if ($inputEl) {
$inputEl.trigger('picker:open');
}
picker.emit('local::open pickerOpen', picker);
}
onOpened() {
const picker = this;
picker.opening = false;
if (picker.$el) {
picker.$el.trigger('picker:opened');
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:opened');
}
picker.emit('local::opened pickerOpened', picker);
}
onClose() {
const picker = this;
const app = picker.app;
picker.opening = false;
picker.closing = true;
// Detach events
picker.detachResizeEvent();
picker.cols.forEach(col => {
if (col.destroy) col.destroy();
});
if (picker.$inputEl) {
if (app.theme === 'md') {
picker.$inputEl.trigger('blur');
} else {
const validate = picker.$inputEl.attr('validate');
const required = picker.$inputEl.attr('required');
if (validate && required) {
app.input.validate(picker.$inputEl);
}
}
}
if (picker.$el) {
picker.$el.trigger('picker:close');
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:close');
}
picker.emit('local::close pickerClose', picker);
}
onClosed() {
const picker = this;
picker.opened = false;
picker.closing = false;
if (!picker.inline) {
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');
}
if (picker.$inputEl) {
picker.$inputEl.trigger('picker:closed');
}
picker.emit('local::closed pickerClosed', picker);
}
open() {
const picker = this;
const {
app,
opened,
inline,
$inputEl,
$scrollToEl,
params
} = picker;
if (opened) return;
if (picker.cols.length === 0 && params.cols.length) {
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: $scrollToEl,
content: picker.render(),
backdrop: typeof params.backdrop !== 'undefined' ? params.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 (modalType === 'sheet') {
modalParams.push = params.sheetPush;
modalParams.swipeToClose = params.sheetSwipeToClose;
}
if (params.routableModals && picker.view) {
picker.view.router.navigate({
url: picker.url,
route: {
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) {
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.close();
// Detach Events
if (picker.$inputEl) {
picker.detachInputEvents();
}
if (picker.params.closeByOutsideClick) {
picker.detachHtmlEvents();
}
if ($el && $el.length) delete picker.$el[0].f7Picker;
deleteProps(picker);
picker.destroyed = true;
}
}
export default Picker;