From 93faadabbda9f26657ff4b500bc8e68b5e044d25 Mon Sep 17 00:00:00 2001 From: Josef Bredreck Date: Tue, 28 Apr 2020 20:53:56 +0200 Subject: [PATCH 1/5] Add support for viewport resizing - Via manual input - Via shortcuts --- packages/uikit-workshop/src/icons/hay.svg | 3 + .../scripts/components/modal-styleguide.js | 22 +- .../src/scripts/components/modal-viewer.js | 24 +- .../src/scripts/components/pl-nav/pl-nav.js | 24 +- .../scripts/components/pl-search/pl-search.js | 24 +- .../lit-components/pl-header/pl-header.js | 23 +- .../pl-tools-menu/pl-tools-menu.js | 23 +- .../pl-viewport-size-list.iframe-helper.js | 24 ++ .../pl-viewport-size-list.js | 297 +++++++++++++++--- .../pl-viewport-size/pl-viewport-size.js | 49 ++- .../pl-viewport-size/pl-viewport-size.scss | 12 +- .../lit-components/pl-viewport/pl-viewport.js | 43 ++- .../pl-viewport/pl-viewport.scss | 3 + .../src/scripts/patternlab-pattern.modern.js | 1 + .../utils/iframe-msg-data-extraction.js | 21 ++ .../uikit-workshop/src/scripts/utils/index.js | 1 + 16 files changed, 417 insertions(+), 177 deletions(-) create mode 100644 packages/uikit-workshop/src/icons/hay.svg create mode 100644 packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.iframe-helper.js create mode 100644 packages/uikit-workshop/src/scripts/utils/iframe-msg-data-extraction.js diff --git a/packages/uikit-workshop/src/icons/hay.svg b/packages/uikit-workshop/src/icons/hay.svg new file mode 100644 index 000000000..466695e0d --- /dev/null +++ b/packages/uikit-workshop/src/icons/hay.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/uikit-workshop/src/scripts/components/modal-styleguide.js b/packages/uikit-workshop/src/scripts/components/modal-styleguide.js index c63907a8b..09e217139 100644 --- a/packages/uikit-workshop/src/scripts/components/modal-styleguide.js +++ b/packages/uikit-workshop/src/scripts/components/modal-styleguide.js @@ -5,6 +5,7 @@ import { panelsUtil } from './panels-util'; import './pl-copy-to-clipboard/pl-copy-to-clipboard'; +import { iframeMsgDataExtraction } from '../utils'; export const modalStyleguide = { // set up some defaults @@ -195,24 +196,11 @@ export const modalStyleguide = { /** * toggle the comment pop-up based on a user clicking on the pattern * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info + * + * @param {MessageEvent} e A message received by a target object. */ - receiveIframeMessage(event) { - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + receiveIframeMessage(e) { + const data = iframeMsgDataExtraction(e); // see if it got a path to replace if (data.event !== undefined && data.event === 'patternLab.patternQuery') { diff --git a/packages/uikit-workshop/src/scripts/components/modal-viewer.js b/packages/uikit-workshop/src/scripts/components/modal-viewer.js index cb29fd21e..3b9b736f0 100644 --- a/packages/uikit-workshop/src/scripts/components/modal-viewer.js +++ b/packages/uikit-workshop/src/scripts/components/modal-viewer.js @@ -4,7 +4,7 @@ */ import { scrollTo } from 'scroll-js'; -import { urlHandler, Dispatcher } from '../utils'; +import { urlHandler, Dispatcher, iframeMsgDataExtraction } from '../utils'; import { panelsViewer } from './panels-viewer'; import { store } from '../store.js'; // These are the actions needed by this element. @@ -283,25 +283,11 @@ export const modalViewer = { /** * toggle the comment pop-up based on a user clicking on the pattern * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info + * + * @param {MessageEvent} e A message received by a target object. */ - receiveIframeMessage(event) { - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + receiveIframeMessage(e) { + const data = iframeMsgDataExtraction(e); if (data.event !== undefined && data.event === 'patternLab.pageLoad') { // @todo: refactor to better handle async iframe loading diff --git a/packages/uikit-workshop/src/scripts/components/pl-nav/pl-nav.js b/packages/uikit-workshop/src/scripts/components/pl-nav/pl-nav.js index 7311307b9..0f4026a29 100644 --- a/packages/uikit-workshop/src/scripts/components/pl-nav/pl-nav.js +++ b/packages/uikit-workshop/src/scripts/components/pl-nav/pl-nav.js @@ -7,6 +7,7 @@ const classNames = require('classnames'); import { getParents } from './get-parents'; import { store } from '../../store.js'; // redux store import { BaseComponent } from '../base-component.js'; +import { iframeMsgDataExtraction } from '../../utils'; import Mousetrap from 'mousetrap'; import 'url-search-params-polyfill'; @@ -89,24 +90,13 @@ class Nav extends BaseComponent { } } - receiveIframeMessage(event) { + /** + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { const self = this; - - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + const data = iframeMsgDataExtraction(e); if (data.event !== undefined && data.event === 'patternLab.pageClick') { try { diff --git a/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js index 02fe99f72..502999acc 100644 --- a/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js +++ b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js @@ -10,7 +10,7 @@ import Mousetrap from 'mousetrap'; import VisuallyHidden from '@reach/visually-hidden'; import Autosuggest from 'react-autosuggest'; -import { urlHandler } from '../../utils'; +import { urlHandler, iframeMsgDataExtraction } from '../../utils'; import { BaseComponent } from '../base-component'; @define @@ -118,22 +118,12 @@ class Search extends BaseComponent { document.activeElement.blur(); } - receiveIframeMessage(event) { - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + /** + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { + const data = iframeMsgDataExtraction(e); if (data.event !== undefined && data.event === 'patternLab.keyPress') { if (data.key === 'f' && data.metaKey === true) { diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-header/pl-header.js b/packages/uikit-workshop/src/scripts/lit-components/pl-header/pl-header.js index a6f1addce..404fe38de 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-header/pl-header.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-header/pl-header.js @@ -3,6 +3,7 @@ import { store } from '../../store.js'; // connect to redux import { ifDefined } from 'lit-html/directives/if-defined'; import { html } from 'lit-html'; import { BaseLitComponent } from '../../components/base-component'; +import { iframeMsgDataExtraction } from '../../utils'; import { customElement } from 'lit-element'; import Mousetrap from 'mousetrap'; import styles from './pl-header.scss?external'; @@ -142,24 +143,14 @@ class Header extends BaseLitComponent { `; } - receiveIframeMessage(event) { + /** + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { const self = this; - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + const data = iframeMsgDataExtraction(e); if (data.event !== undefined && data.event === 'patternLab.pageClick') { try { diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-tools-menu/pl-tools-menu.js b/packages/uikit-workshop/src/scripts/lit-components/pl-tools-menu/pl-tools-menu.js index 07cd2f9cc..9dcb89e49 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-tools-menu/pl-tools-menu.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-tools-menu/pl-tools-menu.js @@ -2,7 +2,7 @@ import { define, props } from 'skatejs'; import Mousetrap from 'mousetrap'; import { h } from 'preact'; -import { urlHandler, patternName } from '../../utils'; +import { urlHandler, patternName, iframeMsgDataExtraction } from '../../utils'; import { store } from '../../store'; // redux store import styles from './pl-tools-menu.scss?external'; @@ -99,23 +99,14 @@ class ToolsMenu extends BaseLitComponent { this.toggle(); } - receiveIframeMessage(event) { + /** + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { const self = this; - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + const data = iframeMsgDataExtraction(e); if (data.event !== undefined && data.event === 'patternLab.pageClick') { try { diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.iframe-helper.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.iframe-helper.js new file mode 100644 index 000000000..16c0e36b1 --- /dev/null +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.iframe-helper.js @@ -0,0 +1,24 @@ +import { targetOrigin } from '../../utils'; + +function sendPatternLabKeyEvent(e, name) { + try { + window.parent.postMessage( + JSON.stringify({ + event: `patternLab.${name}`, + key: e.key, + code: e.code, + }), + targetOrigin + ); + } catch (error) { + // @todo: how do we want to handle exceptions here? + } +} + +document.addEventListener('keydown', e => { + sendPatternLabKeyEvent(e, 'iframeKeyDownEvent'); +}); + +document.addEventListener('keyup', e => { + sendPatternLabKeyEvent(e, 'iframeKeyUpEvent'); +}); diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js index a93c5fb0e..8b09ad428 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js @@ -7,15 +7,38 @@ import { store } from '../../store.js'; // connect to redux import { Tooltip } from '../../components/pl-tooltip/pl-tooltip'; import VisuallyHidden from '@reach/visually-hidden'; -import { minViewportWidth, maxViewportWidth, getRandom } from '../../utils'; +import { + minViewportWidth, + maxViewportWidth, + getRandom, + iframeMsgDataExtraction, +} from '../../utils'; import styles from './pl-viewport-size-list.scss?external'; -// @todo: re-add keyboard shortcuts to these @define class ViewportSizes extends BaseComponent { static is = 'pl-viewport-sizes'; + sizes = Object.freeze({ + ZERO: 'zero', + SMALL: 'small', + MEDIUM: 'medium', + LARGE: 'large', + FULL: 'full', + RANDOM: 'random', + DISCO: 'disco', + HAY: 'hay', + }); + + discomode = false; + doscoId = null; + hayMode = false; + hayId = null; + + controlIsPressed = false; + altIsPressed = false; + _stateChanged(state) { this.triggerUpdate(); } @@ -33,6 +56,16 @@ class ViewportSizes extends BaseComponent { const state = store.getState(); const { ishControlsHide } = window.ishControls; this.ishControlsHide = ishControlsHide; + + // Remove EventListener or they will be added multiple times when reloading in serve mode + document.removeEventListener('keydown', this.handleKeyDownEvent); + document.removeEventListener('keyup', this.handleKeyCombination); + document.addEventListener('keydown', this.handleKeyDownEvent.bind(this)); + document.addEventListener('keyup', this.handleKeyCombination.bind(this)); + self.receiveIframeMessage = this.receiveIframeMessage.bind(self); + + window.removeEventListener('message', this.receiveIframeMessage); + window.addEventListener('message', this.receiveIframeMessage, false); } disconnectedCallback() { @@ -46,8 +79,14 @@ class ViewportSizes extends BaseComponent { resizeViewport(size) { if (this.iframe) { + this.killDisco(); + this.killHay(); + switch (size) { - case 'small': + case this.sizes.ZERO: + this.iframe.fullMode = false; + this.iframe.sizeiframe(0, true); + case this.sizes.SMALL: this.iframe.fullMode = false; this.iframe.sizeiframe( getRandom( @@ -59,7 +98,7 @@ class ViewportSizes extends BaseComponent { true ); break; - case 'medium': + case this.sizes.MEDIUM: this.iframe.fullMode = false; this.iframe.sizeiframe( getRandom( @@ -73,26 +112,187 @@ class ViewportSizes extends BaseComponent { true ); break; - case 'large': + case this.sizes.LARGE: this.iframe.fullMode = false; this.iframe.sizeiframe( getRandom( window.config.ishViewportRange !== undefined ? parseInt(window.config.ishViewportRange.l[0], 10) : 800, - maxViewportWidth + window.config.ishViewportRange !== undefined + ? parseInt(window.config.ishViewportRange.l[1], 10) + : 1000 ), true ); break; - case 'full': + case this.sizes.FULL: this.iframe.fullMode = true; this.iframe.sizeiframe(maxViewportWidth, true); break; + case this.sizes.RANDOM: + this.fullMode = false; + this.iframe.sizeiframe(this.getRangeRandomNumber(), true); + break; + case this.sizes.DISCO: + this.fullMode = false; + this.startDisco(); + break; + case this.sizes.HAY: + this.fullMode = false; + this.iframe.sizeiframe(minViewportWidth, true); + this.startHay(); + break; } } } + /** + * Get a random number between minViewportWidth and maxViewportWidth + */ + getRangeRandomNumber() { + return getRandom( + minViewportWidth, + // Do not evaluate a number higher than the clientWidth of the Iframe + // to prevent having max size multiple times + maxViewportWidth > this.iframe.clientWidth + ? this.iframe.clientWidth + : maxViewportWidth + ); + } + + /** + * Start the disco mode, which means in a specific interval resize + * the iframe random between minViewportWidth and maxViewportWidth + */ + startDisco() { + this.discoMode = true; + this.discoId = setInterval(this.disco.bind(this), 1000); + } + + /** + * Stop the disco mode + */ + killDisco() { + this.discoMode = false; + clearInterval(this.discoId); + this.discoID = null; + } + + /** + * Action to resize the Iframe in disco mode + */ + disco() { + this.iframe.sizeiframe(this.getRangeRandomNumber(), true); + } + + /** + * Start the Hay! mode, which means the iframe is growing slowly + * from minViewportWidth to maxViewportWidth + */ + startHay() { + this.hayMode = true; + this.hayId = setInterval(this.hay.bind(this), 100); + } + + /** + * Stop the Hay! Mode + */ + killHay() { + this.hayMode = false; + clearInterval(this.hayId); + this.hayId = null; + } + + /** + * Action to resize the Iframe in Hay! mode + */ + hay() { + this.iframe.sizeiframe(store.getState().app.viewportPx + 1, true); + } + + /** + * Litte workaround for Firefox Bug. + * + * On QWERTZ keyboards the e.altKey and e.ctrlKey will + * not be set if you click on a key that has a specific + * secondary or third char at ALT + ... + * + * @param {KeyboardEvent} e the keyevent + */ + handleKeyDownEvent(e) { + if (e.key === 'Control') { + this.controlIsPressed = true; + } + if (e.key === 'Alt') { + this.altIsPressed = true; + } + } + + /** + * https://patternlab.io/docs/advanced-keyboard-shortcuts.html + * + * Why use these specific key combinations? + * Works on QUERTZ, QUERTY and AZERTY keyboard and they are no + * reserved browser functionality key combinations. + * + * QUERTY https://en.wikipedia.org/wiki/QWERTY + * QUERTZ https://en.wikipedia.org/wiki/QWERTZ + * AZERTY https://en.wikipedia.org/wiki/AZERTY + * + * Chromium + * https://support.google.com/chrome/answer/157179?hl=en + * + * Firefox + * https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly + * + * @param {KeyboardEvent} e the keyevent + */ + handleKeyCombination(e) { + const ctrlKey = this.controlIsPressed; + const altKey = this.altIsPressed; + + if (ctrlKey && altKey && (e.code === 'Digit0' || e.code === 'Numpad0')) { + this.resizeViewport(this.sizes.ZERO); + } else if (ctrlKey && altKey && e.code === 'KeyS') { + this.resizeViewport(this.sizes.SMALL); + } else if (ctrlKey && altKey && e.code === 'KeyM') { + this.resizeViewport(this.sizes.MEDIUM); + } else if (ctrlKey && altKey && e.code === 'KeyL') { + this.resizeViewport(this.sizes.LARGE); + } else if (ctrlKey && altKey && e.code === 'KeyF') { + this.resizeViewport(this.sizes.FULL); + } else if (ctrlKey && altKey && e.code === 'KeyR') { + this.resizeViewport(this.sizes.RANDOM); + } else if (ctrlKey && altKey && e.code === 'KeyD') { + this.resizeViewport(this.sizes.DISCO); + } else if (ctrlKey && altKey && e.code === 'KeyH') { + this.resizeViewport(this.sizes.HAY); + } + + if (e.key === 'Control') { + this.controlIsPressed = false; + } + if (e.key === 'Alt') { + this.altIsPressed = false; + } + } + + /** + * Interpret and handle the received message input + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { + const data = iframeMsgDataExtraction(e); + + if (data.event && data.event === 'patternLab.iframeKeyDownEvent') { + this.handleKeyDownEvent(data); + } else if (data.event && data.event === 'patternLab.iframeKeyUpEvent') { + this.handleKeyCombination(data); + } + } + rendered() { this.iframe = document.querySelector('pl-iframe'); this.iframeElem = document.querySelector('pl-iframe iframe'); @@ -116,7 +316,7 @@ class ViewportSizes extends BaseComponent { id: 'pl-size-s', ref: triggerRef, })} - onClick={e => this.resizeViewport('small')} + onClick={e => this.resizeViewport(this.sizes.SMALL)} dangerouslySetInnerHTML={{ __html: ` Resize viewport to small @@ -143,7 +343,7 @@ class ViewportSizes extends BaseComponent { id: 'pl-size-m', ref: triggerRef, })} - onClick={e => this.resizeViewport('medium')} + onClick={e => this.resizeViewport(this.sizes.MEDIUM)} dangerouslySetInnerHTML={{ __html: ` Resize viewport to medium @@ -170,7 +370,7 @@ class ViewportSizes extends BaseComponent { id: 'pl-size-l', ref: triggerRef, })} - onClick={e => this.resizeViewport('large')} + onClick={e => this.resizeViewport(this.sizes.LARGE)} dangerouslySetInnerHTML={{ __html: ` Resize viewport to large @@ -197,7 +397,7 @@ class ViewportSizes extends BaseComponent { id: 'pl-size-full', ref: triggerRef, })} - onClick={e => this.resizeViewport('full')} + onClick={e => this.resizeViewport(this.sizes.FULL)} dangerouslySetInnerHTML={{ __html: ` Resize viewport to full @@ -209,7 +409,7 @@ class ViewportSizes extends BaseComponent { )} - {/* {!this.ishControlsHide.random && ( + {!this.ishControlsHide.random && (
  • this.resizeViewport('random')} - > - Resize viewport to random - - + onClick={e => this.resizeViewport(this.sizes.RANDOM)} + dangerouslySetInnerHTML={{ + __html: ` + Resize viewport to random + + `, + }} + /> )}
  • - )} */} - {/* {!this.ishControlsHide.disco && ( + )} + {!this.ishControlsHide.disco && (
  • this.resizeViewport('disco')} - > - - Resize viewport using disco mode! - - - + onClick={e => this.resizeViewport(this.sizes.DISCO)} + dangerouslySetInnerHTML={{ + __html: ` + Resize viewport using disco mode! + + `, + }} + /> )}
  • - )} */} + )} {!this.ishControlsHide.hay && (
  • - + + {({ getTriggerProps, triggerRef }) => ( +
  • )} diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js index 9ca37136c..5637d4a70 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js @@ -19,12 +19,17 @@ class ViewportSize extends BaseLitComponent { }; } + constructor() { + super(); + this.state = { inputPixelValue: '' }; + } + connectedCallback() { super.connectedCallback && super.connectedCallback(); styles.use(); const state = store.getState(); - this.px = round(state.app.viewportPx, 0); - this.em = round(state.app.viewportEm, 1); + this.setPxEm(state); + this.iframe = document.querySelector('pl-iframe'); } disconnectedCallback() { @@ -33,8 +38,13 @@ class ViewportSize extends BaseLitComponent { } _stateChanged(state) { + this.setPxEm(state); + } + + setPxEm(state) { if (state.app.viewportPx !== this.px) { this.px = round(state.app.viewportPx, 0); + this.setState({ inputPixelValue: this.px }); } if (round(state.app.viewportEm, 1) !== this.em) { @@ -52,13 +62,40 @@ class ViewportSize extends BaseLitComponent { } } + handlePixelUpdate(e) { + // Prevent inserting letters or symbols + if (e.key.match(/\D+/g) && !(e.charCode === 13 || e.keyCode === 13)) { + e.preventDefault(); + } else { + this.setState({ inputPixelValue: e.target.value.replace(/\D+/g, '') }); + } + } + + handleSubmit(event) { + event.preventDefault(); + this.iframe.sizeiframe(this.state.inputPixelValue, true); + } + + handlePixelBlur(e) { + this.setState({ inputPixelValue: this.px }); + e.target.value = this.state.inputPixelValue; + } + render() { if (!window.__PRERENDER_INJECTED) { return html` -
    - ${this.px}px /  - ${this.em}em + +
    `; } diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss index be81c570a..cdf823dd6 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss @@ -18,6 +18,7 @@ pl-viewport-size { .pl-c-viewport-size { margin: 0; border: 0; + font-size: 0.85rem; padding: 0.3rem 0.25rem; line-height: 1; display: flex; @@ -35,13 +36,18 @@ pl-viewport-size { border: 0; border-radius: $pl-border-radius; background-color: transparent; - font-size: 0.75rem; color: inherit; width: auto; text-align: right; transition: all $pl-animate-quick ease-out; pointer-events: none; text-overflow: ellipsis; +} + +.pl-c-viewport-size__input-action { + max-width: 35px; + font-size: inherit; + pointer-events: auto; &::-moz-focus-inner { padding: 0; @@ -61,7 +67,6 @@ pl-viewport-size { outline-offset: -1px; } } - /** * Size input labels */ @@ -69,7 +74,6 @@ pl-viewport-size { display: block; margin: 0; padding: 0; - font-size: 0.7rem; - pointer-events: none; + cursor: pointer; } diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js index 99a456810..3e814d870 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js @@ -4,8 +4,13 @@ import { ifDefined } from 'lit-html/directives/if-defined'; import { store } from '../../store.js'; // connect to redux import { updateCurrentPattern, updateCurrentUrl } from '../../actions/app.js'; // redux actions import { updateViewportPx, updateViewportEm } from '../../actions/app.js'; // redux actions needed -import { minViewportWidth, maxViewportWidth } from '../../utils'; -import { urlHandler, patternName } from '../../utils'; +import { + minViewportWidth, + maxViewportWidth, + urlHandler, + patternName, + iframeMsgDataExtraction, +} from '../../utils'; import { html } from 'lit-html'; import { BaseLitComponent } from '../../components/base-component.js'; @@ -197,14 +202,16 @@ class IFrame extends BaseLitComponent { if (size < maxViewportWidth) { theSize = size; - } else if (size < minViewportWidth) { - //If the entered size is less than the minimum allowed viewport size, cap value at min vp size - theSize = minViewportWidth; } else { //If the entered size is larger than the max allowed viewport size, cap value at max vp size theSize = maxViewportWidth; } + if (size < minViewportWidth) { + //If the entered size is less than the minimum allowed viewport size, cap value at min vp size + theSize = minViewportWidth; + } + if (theSize > this.clientWidth) { theSize = this.clientWidth; } @@ -465,30 +472,20 @@ class IFrame extends BaseLitComponent { return false; } - // updates the nav after the iframed page tells the iframe it's done loading - receiveIframeMessage(event) { - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } + /** + * updates the nav after the iframed page tells the iframe it's done loading + * + * @param {MessageEvent} e A message received by a target object. + */ + receiveIframeMessage(e) { + const data = iframeMsgDataExtraction(e); // try to auto-correct for currentPattern data that doesn't always match with url // workaround for certain pages (especially view all pages) not always matching up internally with the expected current pattern key if (data.event !== undefined && data.event === 'patternLab.pageLoad') { try { const currentPattern = - this.sanitizePatternName(event.data.patternpartial) || + this.sanitizePatternName(data.patternpartial) || this.getPatternParam(); document.title = 'Pattern Lab - ' + currentPattern; diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.scss b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.scss index c6ea632e2..c2fc119f6 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.scss +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.scss @@ -39,6 +39,9 @@ pl-iframe { z-index: 0; flex-grow: 1; transition: height 0.3s ease; + // Prevent mobile overflow of pl-c-viewport__resizer-handle + overflow-x: hidden; + // @supports (position: sticky) { // top: 0; // } diff --git a/packages/uikit-workshop/src/scripts/patternlab-pattern.modern.js b/packages/uikit-workshop/src/scripts/patternlab-pattern.modern.js index fe0901868..08b09ad61 100755 --- a/packages/uikit-workshop/src/scripts/patternlab-pattern.modern.js +++ b/packages/uikit-workshop/src/scripts/patternlab-pattern.modern.js @@ -1,4 +1,5 @@ import './components/modal-styleguide'; import './components/pl-search/pl-search.iframe-helper'; import './components/pl-nav/pl-nav.iframe-helper'; +import './lit-components/pl-viewport-size-list/pl-viewport-size-list.iframe-helper'; import './utils/share-inner-iframe-data'; diff --git a/packages/uikit-workshop/src/scripts/utils/iframe-msg-data-extraction.js b/packages/uikit-workshop/src/scripts/utils/iframe-msg-data-extraction.js new file mode 100644 index 000000000..aa68d688b --- /dev/null +++ b/packages/uikit-workshop/src/scripts/utils/iframe-msg-data-extraction.js @@ -0,0 +1,21 @@ +/** + * Does the origin sending the message match the current host? + * if not dev/null the request + * + * @param {MessageEvent} e A message received by a target object. + */ +export function iframeMsgDataExtraction(e) { + if ( + window.location.protocol !== 'file:' && + event.origin !== window.location.protocol + '//' + window.location.host + ) { + return; + } + + try { + return typeof event.data !== 'string' ? event.data : JSON.parse(event.data); + } catch (e) { + // @todo: how do we want to handle exceptions here? + return {}; + } +} diff --git a/packages/uikit-workshop/src/scripts/utils/index.js b/packages/uikit-workshop/src/scripts/utils/index.js index d0a0a9b49..42c0c1491 100755 --- a/packages/uikit-workshop/src/scripts/utils/index.js +++ b/packages/uikit-workshop/src/scripts/utils/index.js @@ -9,3 +9,4 @@ export { targetOrigin } from './get-target-origin'; export { patternName } from './pattern-name'; export { minViewportWidth, maxViewportWidth } from './viewport-sizes'; export { getRandom } from './get-random'; +export { iframeMsgDataExtraction } from './iframe-msg-data-extraction'; From ad59246e9d2f6756e421e5d1e2ad78e5aacee0bf Mon Sep 17 00:00:00 2001 From: Josef Bredreck Date: Tue, 28 Apr 2020 21:09:45 +0200 Subject: [PATCH 2/5] #1167: Fix wrong event binding --- .../pl-viewport-size-list/pl-viewport-size-list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js index 8b09ad428..6b5110354 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size-list/pl-viewport-size-list.js @@ -62,7 +62,7 @@ class ViewportSizes extends BaseComponent { document.removeEventListener('keyup', this.handleKeyCombination); document.addEventListener('keydown', this.handleKeyDownEvent.bind(this)); document.addEventListener('keyup', this.handleKeyCombination.bind(this)); - self.receiveIframeMessage = this.receiveIframeMessage.bind(self); + this.receiveIframeMessage = this.receiveIframeMessage.bind(this); window.removeEventListener('message', this.receiveIframeMessage); window.addEventListener('message', this.receiveIframeMessage, false); From c7cf394d6874573c4d1c6b031b669303f4b0c8da Mon Sep 17 00:00:00 2001 From: Josef Bredreck Date: Sat, 2 May 2020 17:42:33 +0200 Subject: [PATCH 3/5] #1167: Add em value edit - Revertet overflow-x value previously setted because it causes an error for the resize bar --- .../pl-viewport-size/pl-viewport-size.js | 134 +++++++++++++++--- .../pl-viewport-size/pl-viewport-size.scss | 2 +- .../lit-components/pl-viewport/pl-viewport.js | 26 ++-- .../pl-viewport/pl-viewport.scss | 4 +- 4 files changed, 136 insertions(+), 30 deletions(-) diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js index 5637d4a70..611b0814d 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.js @@ -21,7 +21,7 @@ class ViewportSize extends BaseLitComponent { constructor() { super(); - this.state = { inputPixelValue: '' }; + this.state = { inputPixelValue: 0, inputEmValue: 0 }; } connectedCallback() { @@ -44,11 +44,12 @@ class ViewportSize extends BaseLitComponent { setPxEm(state) { if (state.app.viewportPx !== this.px) { this.px = round(state.app.viewportPx, 0); - this.setState({ inputPixelValue: this.px }); + this.setState({ inputPixelValue: this.px }, () => {}); } if (round(state.app.viewportEm, 1) !== this.em) { this.em = round(state.app.viewportEm, 1); + this.setState({ inputEmValue: this.em }, () => {}); } } @@ -62,39 +63,140 @@ class ViewportSize extends BaseLitComponent { } } - handlePixelUpdate(e) { - // Prevent inserting letters or symbols - if (e.key.match(/\D+/g) && !(e.charCode === 13 || e.keyCode === 13)) { + handlePixelUpdatePress(e) { + const nRegex = /\D+/g; + + if (e.key.match(nRegex)) { + console.log(e); + + // Prevent inserting letters or symbols e.preventDefault(); } else { - this.setState({ inputPixelValue: e.target.value.replace(/\D+/g, '') }); + this.setState( + { inputPixelValue: e.target.value.replace(nRegex, '') }, + () => {} + ); } } - handleSubmit(event) { - event.preventDefault(); - this.iframe.sizeiframe(this.state.inputPixelValue, true); + handlePixelUpdateUp(e) { + const nRegex = /\D+/g; + + if (e.key === 'Enter') { + event.preventDefault(); + this.iframe.sizeiframe(this.state.inputPixelValue, true); + } else if (e.key === 'ArrowUp') { + this.setState( + { + inputPixelValue: + Number(e.target.value.replace(nRegex, '')) + (e.shiftKey ? 10 : 1), + }, + () => {} + ); + this.iframe.sizeiframe(this.state.inputPixelValue, true); + } else if (e.key === 'ArrowDown') { + this.setState( + { + inputPixelValue: + Number(e.target.value.replace(nRegex, '')) - (e.shiftKey ? 10 : 1), + }, + () => {} + ); + this.iframe.sizeiframe(this.state.inputPixelValue, true); + } } handlePixelBlur(e) { - this.setState({ inputPixelValue: this.px }); + this.setState({ inputPixelValue: this.px }, () => {}); e.target.value = this.state.inputPixelValue; } + handleEmUpdatePress(e) { + const fpRegex = /[^0-9\.]+/g; + + if (e.key.match(fpRegex)) { + console.log(e); + + // Prevent inserting letters or symbols + e.preventDefault(); + } else { + this.setState( + { inputEmValue: e.target.value.replace(fpRegex, '') }, + () => {} + ); + } + } + + handleEmUpdateUp(e) { + const fpRegex = /[^0-9\.]+/g; + + if (e.key === 'Enter') { + event.preventDefault(); + this.iframe.sizeiframe(this.toPixelValue(), true); + } else if (e.key === 'ArrowUp') { + this.setState( + { + inputEmValue: round( + Number(e.target.value.replace(fpRegex, '')) + + (e.shiftKey ? 0.5 : 0.1), + 1 + ), + }, + () => {} + ); + this.iframe.sizeiframe(this.toPixelValue(), true); + } else if (e.key === 'ArrowDown') { + this.setState( + { + inputEmValue: round( + Number(e.target.value.replace(fpRegex, '')) - + (e.shiftKey ? 0.5 : 0.1), + 1 + ), + }, + () => {} + ); + this.iframe.sizeiframe(this.toPixelValue(), true); + } + } + + handleEmBlur(e) { + this.setState({ inputEmValue: this.em }, () => {}); + e.target.value = this.state.inputEmValue; + } + + toPixelValue() { + return Math.floor(this.state.inputEmValue * this.iframe.bodySize); + } + render() { if (!window.__PRERENDER_INJECTED) { return html`
    -
     /  From 0f44b3a3a3dfb07a465495a4e0463ab7891c734e Mon Sep 17 00:00:00 2001 From: Josef Bredreck Date: Sat, 9 May 2020 16:56:21 +0200 Subject: [PATCH 5/5] #1167: Fix zooming in size input when on phone --- .../docs/src/docs/advanced-keyboard-shortcuts.md | 12 ++++++------ .../pl-viewport-size/pl-viewport-size.scss | 9 +++++++-- .../lit-components/pl-viewport/pl-viewport.js | 9 +++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/docs/src/docs/advanced-keyboard-shortcuts.md b/packages/docs/src/docs/advanced-keyboard-shortcuts.md index f338df46c..e2d099c50 100644 --- a/packages/docs/src/docs/advanced-keyboard-shortcuts.md +++ b/packages/docs/src/docs/advanced-keyboard-shortcuts.md @@ -15,12 +15,12 @@ Pattern Lab comes with support for a number of special keyboard shortcuts to mak Modifying the viewport: -- **ctrl+shift+0**: set the viewport to 320px -- **ctrl+shift+s**: set the viewport to "small" -- **ctrl+shift+m**: set the viewport to "medium" -- **ctrl+shift+l**: set the viewport to "large" -- **ctrl+shift+h**: toggle Hay mode -- **ctrl+shift+d**: toggle disco mode +- **ctrl+alt+0**: set the viewport to 320px +- **ctrl+alt+s**: set the viewport to "small" +- **ctrl+alt+m**: set the viewport to "medium" +- **ctrl+alt+l**: set the viewport to "large" +- **ctrl+alt+h**: toggle Hay mode +- **ctrl+alt+d**: toggle disco mode Modifying the views: diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss index aa1354535..106817cf8 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport-size/pl-viewport-size.scss @@ -18,7 +18,12 @@ pl-viewport-size { .pl-c-viewport-size { margin: 0; border: 0; - font-size: 0.85rem; + + // Prevent zooming on phone browser when the font size is smaller than 16px + // As it would break the visualization the field itself will be scaled to 85% + font-size: 1rem; + transform: scale(0.85); + padding: 0.3rem 0.25rem; line-height: 1; display: flex; @@ -45,7 +50,7 @@ pl-viewport-size { } .pl-c-viewport-size__input-action { - max-width: 40px; + max-width: 47px; font-size: inherit; pointer-events: auto; diff --git a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js index d690e39f1..ace961f3c 100644 --- a/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js +++ b/packages/uikit-workshop/src/scripts/lit-components/pl-viewport/pl-viewport.js @@ -55,6 +55,7 @@ class IFrame extends BaseLitComponent { this.themeMode = state.app.themeMode || 'dark'; this.isViewallPage = state.app.isViewallPage || false; this.currentPattern = state.app.currentPattern || ''; + this.layoutMode = state.app.layoutMode; if (state.app.viewportPx) { this.sizeiframe(state.app.viewportPx, false); @@ -312,6 +313,14 @@ class IFrame extends BaseLitComponent { this.sizeiframe(this.iframe.clientWidth, false); } } + + // Update size when layout is changed + if (this.layoutMode !== state.app.layoutMode) { + this.layoutMode = state.app.layoutMode; + if (this.iframeContainer) { + this.updateSizeReading(this.iframeContainer.clientWidth); + } + } } navigateTo(pattern = patternName) {