UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

871 lines (857 loc) 24 kB
import { extend, colorRgbToHex, colorRgbToHsl, colorHslToHsb, colorHslToRgb, colorHsbToHsl, colorHexToRgb, nextTick, deleteProps } from '../../shared/utils.js'; import Framework7Class from '../../shared/class.js'; import $ from '../../shared/dom7.js'; import { getDevice } from '../../shared/get-device.js'; import moduleAlphaSlider from './modules/alpha-slider.js'; import moduleCurrentColor from './modules/current-color.js'; import moduleHex from './modules/hex.js'; import moduleHsbSliders from './modules/hsb-sliders.js'; import moduleHueSlider from './modules/hue-slider.js'; import moduleBrightnessSlider from './modules/brightness-slider.js'; import modulePalette from './modules/palette.js'; import moduleInitialCurrentColors from './modules/initial-current-colors.js'; import moduleRgbBars from './modules/rgb-bars.js'; import moduleRgbSliders from './modules/rgb-sliders.js'; import moduleSbSpectrum from './modules/sb-spectrum.js'; import moduleHsSpectrum from './modules/hs-spectrum.js'; import moduleWheel from './modules/wheel.js'; /** @jsx $jsx */ import $jsx from '../../shared/$jsx.js'; class ColorPicker extends Framework7Class { constructor(app, params) { if (params === void 0) { params = {}; } super(params, [app]); const self = this; self.params = extend({}, app.params.colorPicker, params); let $containerEl; if (self.params.containerEl) { $containerEl = $(self.params.containerEl); if ($containerEl.length === 0) return self; } let $inputEl; if (self.params.inputEl) { $inputEl = $(self.params.inputEl); } let $targetEl; if (self.params.targetEl) { $targetEl = $(self.params.targetEl); } extend(self, { app, $containerEl, containerEl: $containerEl && $containerEl[0], inline: $containerEl && $containerEl.length > 0, $inputEl, inputEl: $inputEl && $inputEl[0], $targetEl, targetEl: $targetEl && $targetEl[0], initialized: false, opened: false, url: self.params.url, modules: { 'alpha-slider': moduleAlphaSlider, 'current-color': moduleCurrentColor, hex: moduleHex, // eslint-disable-line 'hsb-sliders': moduleHsbSliders, 'hue-slider': moduleHueSlider, 'brightness-slider': moduleBrightnessSlider, palette: modulePalette, // eslint-disable-line 'initial-current-colors': moduleInitialCurrentColors, 'rgb-bars': moduleRgbBars, 'rgb-sliders': moduleRgbSliders, 'sb-spectrum': moduleSbSpectrum, 'hs-spectrum': moduleHsSpectrum, wheel: moduleWheel // eslint-disable-line } }); function onInputClick() { self.open(); } function onInputFocus(e) { e.preventDefault(); } function onTargetClick() { self.open(); } function onHtmlClick(e) { if (self.destroyed || !self.params) return; if (self.params.openIn === 'page') return; const $clickTargetEl = $(e.target); if (!self.opened || self.closing) return; if ($clickTargetEl.closest('[class*="backdrop"]').length) return; if ($clickTargetEl.closest('.color-picker-popup, .color-picker-popover').length) return; if ($inputEl && $inputEl.length > 0) { if ($clickTargetEl[0] !== $inputEl[0] && $clickTargetEl.closest('.sheet-modal').length === 0) { self.close(); } } else if ($(e.target).closest('.sheet-modal').length === 0) { self.close(); } } // Events extend(self, { attachInputEvents() { self.$inputEl.on('click', onInputClick); if (self.params.inputReadOnly) { self.$inputEl.on('focus mousedown', onInputFocus); if (self.$inputEl[0]) { self.$inputEl[0].f7ValidateReadonly = true; } } }, detachInputEvents() { self.$inputEl.off('click', onInputClick); if (self.params.inputReadOnly) { self.$inputEl.off('focus mousedown', onInputFocus); if (self.$inputEl[0]) { delete self.$inputEl[0].f7ValidateReadonly; } } }, attachTargetEvents() { self.$targetEl.on('click', onTargetClick); }, detachTargetEvents() { self.$targetEl.off('click', onTargetClick); }, attachHtmlEvents() { app.on('click', onHtmlClick); }, detachHtmlEvents() { app.off('click', onHtmlClick); } }); self.init(); return self; } get view() { const { $inputEl, $targetEl, app, params } = this; let view; if (params.view) { view = params.view; } else { if ($inputEl) { view = $inputEl.parents('.view').length && $inputEl.parents('.view')[0].f7View; } if (!view && $targetEl) { view = $targetEl.parents('.view').length && $targetEl.parents('.view')[0].f7View; } } if (!view) view = app.views.main; return view; } attachEvents() { const self = this; self.centerModules = self.centerModules.bind(self); if (self.params.centerModules) { self.app.on('resize', self.centerModules); } } detachEvents() { const self = this; if (self.params.centerModules) { self.app.off('resize', self.centerModules); } } centerModules() { const self = this; if (!self.opened || !self.$el || self.inline) return; const $pageContentEl = self.$el.find('.page-content'); if (!$pageContentEl.length) return; const { scrollHeight, offsetHeight } = $pageContentEl[0]; if (scrollHeight <= offsetHeight) { $pageContentEl.addClass('justify-content-center'); } else { $pageContentEl.removeClass('justify-content-center'); } } initInput() { const self = this; if (!self.$inputEl) return; if (self.params.inputReadOnly) self.$inputEl.prop('readOnly', true); } getModalType() { const self = this; const { app, modal, params } = self; const { openIn, openInPhone } = params; const device = getDevice(); if (modal && modal.type) return modal.type; if (openIn !== 'auto') return openIn; if (self.inline) return null; if (device.ios) { return device.ipad ? 'popover' : openInPhone; } if (app.width >= 768) { return 'popover'; } return openInPhone; } formatValue() { const self = this; const { value } = self; if (self.params.formatValue) { return self.params.formatValue.call(self, value); } return value.hex; } // eslint-disable-next-line normalizeHsValues(arr) { return [Math.floor(arr[0] * 10) / 10, Math.floor(arr[1] * 1000) / 1000, Math.floor(arr[2] * 1000) / 1000]; } setValue(value, updateModules) { if (value === void 0) { value = {}; } if (updateModules === void 0) { updateModules = true; } const self = this; if (typeof value === 'undefined') return; let { hex, rgb, hsl, hsb, alpha = 1, hue, rgba, hsla } = self.value || {}; const needChangeEvent = self.value || !self.value && !self.params.value; let valueChanged; Object.keys(value).forEach(k => { if (!self.value || typeof self.value[k] === 'undefined') { valueChanged = true; return; } const v = value[k]; if (Array.isArray(v)) { v.forEach((subV, subIndex) => { if (subV !== self.value[k][subIndex]) { valueChanged = true; } }); } else if (v !== self.value[k]) { valueChanged = true; } }); if (!valueChanged) return; if (value.rgb || value.rgba) { const [r, g, b, a = alpha] = value.rgb || value.rgba; rgb = [r, g, b]; hex = colorRgbToHex(...rgb); hsl = colorRgbToHsl(...rgb); hsb = colorHslToHsb(...hsl); hsl = self.normalizeHsValues(hsl); hsb = self.normalizeHsValues(hsb); hue = hsb[0]; alpha = a; rgba = [rgb[0], rgb[1], rgb[2], a]; hsla = [hsl[0], hsl[1], hsl[2], a]; } if (value.hsl || value.hsla) { const [h, s, l, a = alpha] = value.hsl || value.hsla; hsl = [h, s, l]; rgb = colorHslToRgb(...hsl); hex = colorRgbToHex(...rgb); hsb = colorHslToHsb(...hsl); hsl = self.normalizeHsValues(hsl); hsb = self.normalizeHsValues(hsb); hue = hsb[0]; alpha = a; rgba = [rgb[0], rgb[1], rgb[2], a]; hsla = [hsl[0], hsl[1], hsl[2], a]; } if (value.hsb) { const [h, s, b, a = alpha] = value.hsb; hsb = [h, s, b]; hsl = colorHsbToHsl(...hsb); rgb = colorHslToRgb(...hsl); hex = colorRgbToHex(...rgb); hsl = self.normalizeHsValues(hsl); hsb = self.normalizeHsValues(hsb); hue = hsb[0]; alpha = a; rgba = [rgb[0], rgb[1], rgb[2], a]; hsla = [hsl[0], hsl[1], hsl[2], a]; } if (value.hex) { rgb = colorHexToRgb(value.hex); hex = colorRgbToHex(...rgb); hsl = colorRgbToHsl(...rgb); hsb = colorHslToHsb(...hsl); hsl = self.normalizeHsValues(hsl); hsb = self.normalizeHsValues(hsb); hue = hsb[0]; rgba = [rgb[0], rgb[1], rgb[2], alpha]; hsla = [hsl[0], hsl[1], hsl[2], alpha]; } if (typeof value.alpha !== 'undefined') { alpha = value.alpha; if (typeof rgb !== 'undefined') { rgba = [rgb[0], rgb[1], rgb[2], alpha]; } if (typeof hsl !== 'undefined') { hsla = [hsl[0], hsl[1], hsl[2], alpha]; } } if (typeof value.hue !== 'undefined') { const [h, s, l] = hsl; // eslint-disable-line hsl = [value.hue, s, l]; hsb = colorHslToHsb(...hsl); rgb = colorHslToRgb(...hsl); hex = colorRgbToHex(...rgb); hsl = self.normalizeHsValues(hsl); hsb = self.normalizeHsValues(hsb); hue = hsb[0]; rgba = [rgb[0], rgb[1], rgb[2], alpha]; hsla = [hsl[0], hsl[1], hsl[2], alpha]; } self.value = { hex, alpha, hue, rgb, hsl, hsb, rgba, hsla }; if (!self.initialValue) self.initialValue = extend({}, self.value); self.updateValue(needChangeEvent); if (self.opened && updateModules) { self.updateModules(); } } getValue() { const self = this; return self.value; } updateValue(fireEvents) { if (fireEvents === void 0) { fireEvents = true; } const self = this; const { $inputEl, value, $targetEl } = self; if ($targetEl && self.params.targetElSetBackgroundColor) { const { rgba } = value; $targetEl.css('background-color', `rgba(${rgba.join(', ')})`); } if (fireEvents) { self.emit('local::change colorPickerChange', self, value); } if ($inputEl && $inputEl.length) { const inputValue = self.formatValue(value); if ($inputEl && $inputEl.length) { $inputEl.val(inputValue); if (fireEvents) { $inputEl.trigger('change'); } } } } updateModules() { const self = this; const { modules } = self; self.params.modules.forEach(m => { if (typeof m === 'string' && modules[m] && modules[m].update) { modules[m].update(self); } else if (m && m.update) { m.update(self); } }); } update() { const self = this; self.updateModules(); } renderPicker() { const self = this; const { params, modules } = self; let html = ''; params.modules.forEach(m => { if (typeof m === 'string' && modules[m] && modules[m].render) { html += modules[m].render(self); } else if (m && m.render) { html += m.render(self); } }); return html; } renderNavbar() { const self = this; if (self.params.renderNavbar) { return self.params.renderNavbar.call(self, self); } const { openIn, navbarTitleText, navbarBackLinkText, navbarCloseText } = self.params; return $jsx("div", { class: "navbar" }, $jsx("div", { class: "navbar-bg" }), $jsx("div", { class: "navbar-inner sliding" }, openIn === 'page' && $jsx("div", { class: "left" }, $jsx("a", { class: "link back" }, $jsx("i", { class: "icon icon-back" }), $jsx("span", { class: "if-not-md" }, navbarBackLinkText))), $jsx("div", { class: "title" }, navbarTitleText), openIn !== 'page' && $jsx("div", { class: "right" }, $jsx("a", { class: "link popup-close", "data-popup": ".color-picker-popup" }, navbarCloseText)))); } renderToolbar() { const self = this; if (self.params.renderToolbar) { return self.params.renderToolbar.call(self, self); } 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", "data-sheet": ".color-picker-sheet-modal", "data-popover": ".color-picker-popover" }, self.params.toolbarCloseText)))); } renderInline() { const self = this; const { cssClass, groupedModules } = self.params; return $jsx("div", { class: `color-picker color-picker-inline ${groupedModules ? 'color-picker-grouped-modules' : ''} ${cssClass || ''}` }, self.renderPicker()); } renderSheet() { const self = this; const { cssClass, toolbarSheet, groupedModules } = self.params; return $jsx("div", { class: `sheet-modal color-picker color-picker-sheet-modal ${groupedModules ? 'color-picker-grouped-modules' : ''} ${cssClass || ''}` }, toolbarSheet && self.renderToolbar(), $jsx("div", { class: "sheet-modal-inner" }, $jsx("div", { class: "page-content" }, self.renderPicker()))); } renderPopover() { const self = this; const { cssClass, toolbarPopover, groupedModules } = self.params; return $jsx("div", { class: `popover color-picker-popover ${cssClass || ''}` }, $jsx("div", { class: "popover-inner" }, $jsx("div", { class: `color-picker ${groupedModules ? 'color-picker-grouped-modules' : ''}` }, toolbarPopover && self.renderToolbar(), $jsx("div", { class: "page-content" }, self.renderPicker())))); } renderPopup() { const self = this; const { cssClass, navbarPopup, groupedModules } = self.params; return $jsx("div", { class: `popup color-picker-popup ${cssClass || ''}` }, $jsx("div", { class: "page" }, navbarPopup && self.renderNavbar(), $jsx("div", { class: `color-picker ${groupedModules ? 'color-picker-grouped-modules' : ''}` }, $jsx("div", { class: "page-content" }, self.renderPicker())))); } renderPage() { const self = this; const { cssClass, groupedModules } = self.params; return $jsx("div", { class: `page color-picker-page ${cssClass || ''}`, "data-name": "color-picker-page" }, self.renderNavbar(), $jsx("div", { class: `color-picker ${groupedModules ? 'color-picker-grouped-modules' : ''}` }, $jsx("div", { class: "page-content" }, self.renderPicker()))); } // eslint-disable-next-line render() { const self = this; const { params } = self; if (params.render) return params.render.call(self); if (self.inline) return self.renderInline(); if (params.openIn === 'page') { return self.renderPage(); } const modalType = self.getModalType(); if (modalType === 'popover') return self.renderPopover(); if (modalType === 'sheet') return self.renderSheet(); if (modalType === 'popup') return self.renderPopup(); } onOpen() { const self = this; const { initialized, $el, app, $inputEl, inline, value, params, modules } = self; self.closing = false; self.opened = true; self.opening = true; // Init main events self.attachEvents(); params.modules.forEach(m => { if (typeof m === 'string' && modules[m] && modules[m].init) { modules[m].init(self); } else if (m && m.init) { m.init(self); } }); const updateValue = !value && params.value; // Set value if (!initialized) { if (value) self.setValue(value);else if (params.value) { self.setValue(params.value, false); } else if (!params.value) { self.setValue({ hex: '#ff0000' }, false); } } else if (value) { self.initialValue = extend({}, value); self.setValue(value, false); } // Update input value if (updateValue) self.updateValue(); self.updateModules(); // Center modules if (params.centerModules) { self.centerModules(); } // Extra focus if (!inline && $inputEl && $inputEl.length && app.theme === 'md') { $inputEl.trigger('focus'); } self.initialized = true; // Trigger events if ($el) { $el.trigger('colorpicker:open'); } if ($inputEl) { $inputEl.trigger('colorpicker:open'); } self.emit('local::open colorPickerOpen', self); } onOpened() { const self = this; self.opening = false; if (self.$el) { self.$el.trigger('colorpicker:opened'); } if (self.$inputEl) { self.$inputEl.trigger('colorpicker:opened'); } self.emit('local::opened colorPickerOpened', self); } onClose() { const self = this; const { app, params, modules } = self; self.opening = false; self.closing = true; // Detach events self.detachEvents(); if (self.$inputEl) { if (app.theme === 'md') { self.$inputEl.trigger('blur'); } else { const validate = self.$inputEl.attr('validate'); const required = self.$inputEl.attr('required'); if (validate && required) { app.input.validate(self.$inputEl); } } } params.modules.forEach(m => { if (typeof m === 'string' && modules[m] && modules[m].destroy) { modules[m].destroy(self); } else if (m && m.destroy) { m.destroy(self); } }); if (self.$el) { self.$el.trigger('colorpicker:close'); } if (self.$inputEl) { self.$inputEl.trigger('colorpicker:close'); } self.emit('local::close colorPickerClose', self); } onClosed() { const self = this; self.opened = false; self.closing = false; if (!self.inline) { nextTick(() => { if (self.modal && self.modal.el && self.modal.destroy) { if (!self.params.routableModals) { self.modal.destroy(); } } delete self.modal; }); } if (self.$el) { self.$el.trigger('colorpicker:closed'); } if (self.$inputEl) { self.$inputEl.trigger('colorpicker:closed'); } self.emit('local::closed colorPickerClosed', self); } open() { const self = this; const { app, opened, inline, $inputEl, $targetEl, params } = self; if (opened) return; if (inline) { self.$el = $(self.render()); self.$el[0].f7ColorPicker = self; self.$containerEl.append(self.$el); self.onOpen(); self.onOpened(); return; } const colorPickerContent = self.render(); if (params.openIn === 'page') { self.view.router.navigate({ url: self.url, route: { content: colorPickerContent, path: self.url, on: { pageBeforeIn(e, page) { self.$el = page.$el.find('.color-picker'); self.$el[0].f7ColorPicker = self; self.onOpen(); }, pageAfterIn() { self.onOpened(); }, pageBeforeOut() { self.onClose(); }, pageAfterOut() { self.onClosed(); if (self.$el && self.$el[0]) { self.$el[0].f7ColorPicker = null; delete self.$el[0].f7ColorPicker; } } } } }); } else { const modalType = self.getModalType(); let backdrop = params.backdrop; if (backdrop === null || typeof backdrop === 'undefined') { if (modalType === 'popover' && app.params.popover.backdrop !== false) backdrop = true; if (modalType === 'popup') backdrop = true; } const modalParams = { targetEl: $targetEl || $inputEl, scrollToEl: params.scrollToInput ? $targetEl || $inputEl : undefined, content: colorPickerContent, backdrop, closeByBackdropClick: params.closeByBackdropClick, on: { open() { const modal = this; self.modal = modal; self.$el = modalType === 'popover' || modalType === 'popup' ? modal.$el.find('.color-picker') : modal.$el; self.$el[0].f7ColorPicker = self; self.onOpen(); }, opened() { self.onOpened(); }, close() { self.onClose(); }, closed() { self.onClosed(); if (self.$el && self.$el[0]) { self.$el[0].f7ColorPicker = null; delete self.$el[0].f7ColorPicker; } } } }; if (modalType === 'popup') { modalParams.push = params.popupPush; modalParams.swipeToClose = params.popupSwipeToClose; } if (modalType === 'sheet') { modalParams.push = params.sheetPush; modalParams.swipeToClose = params.sheetSwipeToClose; } if (params.routableModals && self.view) { self.view.router.navigate({ url: self.url, route: { path: self.url, [modalType]: modalParams } }); } else { self.modal = app[modalType].create(modalParams); self.modal.open(); } } } close() { const self = this; const { opened, inline } = self; if (!opened) return; if (inline) { self.onClose(); self.onClosed(); return; } if (self.params.routableModals && self.view || self.params.openIn === 'page') { self.view.router.back(); } else { self.modal.close(); } } init() { const self = this; self.initInput(); if (self.inline) { self.open(); self.emit('local::init colorPickerInit', self); return; } if (!self.initialized && self.params.value) { self.setValue(self.params.value); } // Attach input Events if (self.$inputEl) { self.attachInputEvents(); } if (self.$targetEl) { self.attachTargetEvents(); } if (self.params.closeByOutsideClick) { self.attachHtmlEvents(); } self.emit('local::init colorPickerInit', self); } destroy() { const self = this; if (self.destroyed) return; const { $el } = self; self.emit('local::beforeDestroy colorPickerBeforeDestroy', self); if ($el) $el.trigger('colorpicker:beforedestroy'); self.close(); // Detach Events self.detachEvents(); if (self.$inputEl) { self.detachInputEvents(); } if (self.$targetEl) { self.detachTargetEvents(); } if (self.params.closeByOutsideClick) { self.detachHtmlEvents(); } if ($el && $el.length) delete self.$el[0].f7ColorPicker; deleteProps(self); self.destroyed = true; } } export default ColorPicker;