From 9c8a80066e89c06d36fcf4319a3f88e393a38b27 Mon Sep 17 00:00:00 2001 From: Kevin Chappell Date: Sat, 7 Mar 2020 17:57:20 -0800 Subject: [PATCH] fix: column resizing feat: disable form action buttons --- docs/options/controls/README.md | 18 ++++--- src/js/common/dom.js | 6 +-- src/js/common/helpers.js | 2 - src/js/components/columns/column.js | 3 +- src/js/components/columns/events.js | 76 +++++++++++++-------------- src/js/components/controls/index.js | 18 ++++--- src/js/components/controls/options.js | 31 +++++++++++ src/js/components/rows/row.js | 5 +- src/js/constants.js | 2 + 9 files changed, 98 insertions(+), 63 deletions(-) create mode 100644 src/js/components/controls/options.js diff --git a/docs/options/controls/README.md b/docs/options/controls/README.md index 54416a74..c378a56e 100644 --- a/docs/options/controls/README.md +++ b/docs/options/controls/README.md @@ -2,14 +2,15 @@ Control options can be used to disable, extend and modify the Formeo's control panel. -| Option | Type | Description | Example | Default | -| ----------------------------- | ------ | ----------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------ | -| [sortable](#sortable) | String | allow controls to be reordered by users | `true` | `false` | -| [disable](#disable) | Object | disable built-in elements or groups | `{elements: ['number']}` | `{}` | -| [elements](#elements) | Array | define custom elements | [see below](#elements) | `[]` | -| [elementOrder](#elementOrder) | Object | set order of elements in a group | `{html: ['header', 'paragraph', 'divider']}` | `[]` | -| [groups](#groups) | Array | define custom [control groups](../../controls/#control-groups) beyond the default 3 | [see below](#groups) | `[]` | -| [groupOrder](#groupOrder) | Array | set order of [control groups](../../controls/#control-groups) | `['html', 'layout']` | `['common', 'html', 'layout']` | +| Option | Type | Description | Example | Default | +| ----------------------------- | ------- | ----------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------ | +| [sortable](#sortable) | String | allow controls to be reordered by users | `true` | `false` | +| [disable](#disable) | Object | disable built-in elements or groups | `{elements: ['number']}` | `{}` | +| [elements](#elements) | Array | define custom elements | [see below](#elements) | `[]` | +| [elementOrder](#elementOrder) | Object | set order of elements in a group | `{html: ['header', 'paragraph', 'divider']}` | `[]` | +| [groups](#groups) | Array | define custom [control groups](../../controls/#control-groups) beyond the default 3 | [see below](#groups) | `[]` | +| [groupOrder](#groupOrder) | Array | set order of [control groups](../../controls/#control-groups) | `['html', 'layout']` | `['common', 'html', 'layout']` | +| [ghostPreview](#ghostPreview) | Boolean | use a live preview of the control when dragging | `true` | `false` | ## sortable @@ -34,6 +35,7 @@ const controlOptions = { disable: { elements: ['number'], groups: ['layout'], + formActions: true, // cancel and save buttons will not be shown }, } diff --git a/src/js/common/dom.js b/src/js/common/dom.js index 07a70cbc..fe878a12 100644 --- a/src/js/common/dom.js +++ b/src/js/common/dom.js @@ -11,6 +11,7 @@ import { FIELD_CLASSNAME, CONTROL_GROUP_CLASSNAME, CHILD_CLASSNAME_MAP, + bsColRegExp, } from '../constants' /** @@ -110,7 +111,7 @@ class DOM { element.innerHTML += children }, object: children => { - return element.appendChild(_this.create(children, isPreview)) + return children && element.appendChild(_this.create(children, isPreview)) }, node: children => { return element.appendChild(children) @@ -765,9 +766,8 @@ class DOM { return } const width = parseFloat((100 / columns.length).toFixed(1)) / 1 - const bsGridRegEx = /\bcol-\w+-\d+/g - this.removeClasses(columns, bsGridRegEx) + this.removeClasses(columns, bsColRegExp) h.forEach(columns, column => { Columns.get(column.id).refreshFieldPanels() diff --git a/src/js/common/helpers.js b/src/js/common/helpers.js index 3e6ec610..f3899370 100644 --- a/src/js/common/helpers.js +++ b/src/js/common/helpers.js @@ -5,8 +5,6 @@ import lodashGet from 'lodash/get' import lodashCamelCase from 'lodash/camelCase' import lodashLowerCase from 'lodash/lowerCase' -export const bsGridRegEx = /\bcol-\w+-\d+/g - /** * Tests if is whole number. returns false if n is Float * @param {String|Number} n diff --git a/src/js/components/columns/column.js b/src/js/components/columns/column.js index 4db43cfa..a611d91a 100644 --- a/src/js/components/columns/column.js +++ b/src/js/components/columns/column.js @@ -20,8 +20,7 @@ const DOM_CONFIGS = { resizeHandle: () => ({ className: 'resize-x-handle', action: { - mousedown: resize, - touchstart: resize, + pointerdown: resize, }, content: [dom.icon('triangle-down'), dom.icon('triangle-up')], }), diff --git a/src/js/components/columns/events.js b/src/js/components/columns/events.js index c270cb63..187021de 100644 --- a/src/js/components/columns/events.js +++ b/src/js/components/columns/events.js @@ -1,11 +1,12 @@ import dom from '../../common/dom' import Components from '..' import { percent, numToPercent } from '../../common/utils' -import { ROW_CLASSNAME } from '../../constants' +import { ROW_CLASSNAME, bsColRegExp } from '../../constants' import { map } from '../../common/helpers' const CUSTOM_COLUMN_OPTION_CLASSNAME = 'custom-column-widths' const COLUMN_PRESET_CLASSNAME = 'column-preset' +const COLUMN_RESIZE_CLASSNAME = 'resizing-columns' /** * Handle column resizing @@ -18,18 +19,17 @@ export function resize(evt) { const row = column.closest(`.${ROW_CLASSNAME}`) const rowStyle = dom.getStyle(row) const rowPadding = parseFloat(rowStyle.paddingLeft) + parseFloat(rowStyle.paddingRight) + evt.target.removeEventListener('pointerdown', resize) /** * Set the width before resizing so the column * does not resize near window edges * @param {Object} evt */ - resize.move = evt => { - let clientX - if (evt.type === 'touchmove') { - clientX = evt.touches[0].clientX - } else { - clientX = evt.clientX + resize.move = ({ touches, type, clientX }) => { + if (type === 'touchmove') { + const [firstTouch] = touches + clientX = firstTouch.clientX } const newColWidth = resize.colStartWidth + clientX - resize.startX const newSibWidth = resize.sibStartWidth - clientX + resize.startX @@ -37,17 +37,19 @@ export function resize(evt) { const colWidthPercent = parseFloat(percent(newColWidth, resize.rowWidth)) const sibWidthPercent = parseFloat(percent(newSibWidth, resize.rowWidth)) - column.dataset.colWidth = numToPercent(colWidthPercent.toFixed(1)) - sibling.dataset.colWidth = numToPercent(sibWidthPercent.toFixed(1)) + const colWidth = numToPercent(colWidthPercent.toFixed(1)) + const siblingColWidth = numToPercent(sibWidthPercent.toFixed(1)) - column.style.width = numToPercent(colWidthPercent) - sibling.style.width = numToPercent(sibWidthPercent) + column.dataset.colWidth = colWidth + sibling.dataset.colWidth = siblingColWidth + column.style.width = colWidth + sibling.style.width = siblingColWidth resize.resized = true } resize.stop = function() { - window.removeEventListener('mousemove', resize.move) - window.removeEventListener('mouseup', resize.stop) + window.removeEventListener('pointermove', resize.move) + window.removeEventListener('pointerup', resize.stop) window.removeEventListener('touchmove', resize.move) window.removeEventListener('touchend', resize.stop) if (!resize.resized) { @@ -55,37 +57,35 @@ export function resize(evt) { } setCustomWidthValue(row, resize.rowWidth) - row.classList.remove('resizing-columns') + row.classList.remove(COLUMN_RESIZE_CLASSNAME) Components.setAddress(`columns.${column.id}.config.width`, column.dataset.colWidth) Components.setAddress(`columns.${sibling.id}.config.width`, sibling.dataset.colWidth) resize.resized = false } - resize.start = (function(evt) { - if (evt.type === 'touchstart') { - resize.startX = evt.touches[0].clientX - } else { - resize.startX = evt.clientX - } - row.classList.add('resizing-columns') - - // remove bootstrap column classes since we are custom sizing - const reg = /\bcol-\w+-\d+/g - column.className.replace(reg, '') - sibling.className.replace(reg, '') - - // eslint-disable-next-line - resize.colStartWidth = column.offsetWidth || dom.getStyle(column, 'width') - // eslint-disable-next-line - resize.sibStartWidth = sibling.offsetWidth || dom.getStyle(sibling, 'width') - resize.rowWidth = row.offsetWidth - rowPadding // compensate for padding - - window.addEventListener('mouseup', resize.stop, false) - window.addEventListener('mousemove', resize.move, false) - window.addEventListener('touchend', resize.stop, false) - window.addEventListener('touchmove', resize.move, false) - })(evt) + if (evt.type === 'touchstart') { + const [firstTouch] = evt.touches + resize.startX = firstTouch.clientX + } else { + resize.startX = evt.clientX + } + row.classList.add(COLUMN_RESIZE_CLASSNAME) + + // remove bootstrap column classes since we are custom sizing + column.className.replace(bsColRegExp, '') + sibling.className.replace(bsColRegExp, '') + + // eslint-disable-next-line + resize.colStartWidth = column.offsetWidth || dom.getStyle(column, 'width') + // eslint-disable-next-line + resize.sibStartWidth = sibling.offsetWidth || dom.getStyle(sibling, 'width') + resize.rowWidth = row.offsetWidth - rowPadding // compensate for padding + + window.addEventListener('pointerup', resize.stop, false) + window.addEventListener('pointermove', resize.move, false) + window.addEventListener('touchend', resize.stop, false) + window.addEventListener('touchmove', resize.move, false) } /** diff --git a/src/js/components/controls/index.js b/src/js/components/controls/index.js index cbe1a02e..ce240d29 100644 --- a/src/js/components/controls/index.js +++ b/src/js/components/controls/index.js @@ -180,6 +180,9 @@ export class Controls { * @return {Object} form action buttons config */ formActions() { + if (this.options.disable.formActions === true) { + return null + } const clearBtn = { ...dom.btnTemplate({ content: [dom.icon('bin'), i18n.get('clear')], title: i18n.get('clearAll') }), className: ['clear-form'], @@ -227,9 +230,15 @@ export class Controls { }, }, } + const formActions = { className: 'form-actions f-btn-group', - content: [clearBtn, saveBtn], + content: Object.entries({ clearBtn, saveBtn }).reduce((acc, [key, value]) => { + if (!this.options.disable.formActions.includes(key)) { + acc.push(value) + } + return acc + }, []), } return formActions @@ -255,14 +264,9 @@ export class Controls { controlClass += ' formeo-sticky' } - const content = [groupsWrap] - if (!this.options.disable.formActions) { - content.push(formActions) - } - const element = dom.create({ className: controlClass, - content, + content: [groupsWrap, formActions], }) const groups = element.getElementsByClassName('control-group') diff --git a/src/js/components/controls/options.js b/src/js/components/controls/options.js new file mode 100644 index 00000000..06df9a3a --- /dev/null +++ b/src/js/components/controls/options.js @@ -0,0 +1,31 @@ +const defaultOptions = Object.freeze({ + sortable: true, + elementOrder: {}, + groupOrder: [], + groups: [ + { + id: 'layout', + label: 'controls.groups.layout', + elementOrder: ['row', 'column'], + }, + { + id: 'common', + label: 'controls.groups.form', + elementOrder: ['button', 'checkbox'], + }, + { + id: 'html', + label: 'controls.groups.html', + elementOrder: ['header', 'block-text'], + }, + ], + disable: { + groups: [], + elements: [], + formActions: true, + }, + elements: [], + container: null, +}) + +export default defaultOptions diff --git a/src/js/components/rows/row.js b/src/js/components/rows/row.js index ed170881..b0407099 100644 --- a/src/js/components/rows/row.js +++ b/src/js/components/rows/row.js @@ -3,9 +3,8 @@ import Sortable from 'sortablejs' import Component from '../component' import dom from '../../common/dom' import events from '../../common/events' -import { bsGridRegEx } from '../../common/helpers' import { numToPercent } from '../../common/utils' -import { ROW_CLASSNAME, COLUMN_TEMPLATES, ANIMATION_SPEED_FAST, COLUMN_CLASSNAME } from '../../constants' +import { ROW_CLASSNAME, COLUMN_TEMPLATES, ANIMATION_SPEED_FAST, COLUMN_CLASSNAME, bsColRegExp } from '../../constants' import { removeCustomOption } from '../columns/events' const DEFAULT_DATA = () => @@ -187,7 +186,7 @@ export default class Row extends Component { const width = parseFloat((100 / columns.length).toFixed(1)) / 1 columns.forEach(column => { - column.removeClasses(bsGridRegEx) + column.removeClasses(bsColRegExp) const colDom = column.dom const newColWidth = numToPercent(width) diff --git a/src/js/constants.js b/src/js/constants.js index 1f2047e3..c9ea5dc3 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -177,3 +177,5 @@ export const CONDITION_TEMPLATE = () => ({ }) export const UUID_REGEXP = /(\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b)/gi + +export const bsColRegExp = /\bcol-\w+-\d+/g