diff --git a/dist/template-app/js/resize.js b/dist/template-app/js/resize.js new file mode 100644 index 0000000..180290a --- /dev/null +++ b/dist/template-app/js/resize.js @@ -0,0 +1,246 @@ +// Global drag-related variables +var CURSOR_IN_DRAG_ZONE = false, + CAN_DRAG = false, + IS_DRAGGING = false; + +// Get the root .pane-group element +var rootPaneGroup = Array.from(document.getElementsByClassName('pane-group'))[0]; + +// Build pane tree +buildPaneTree(rootPaneGroup); + +// Rebuild tree on resize +window.onresize = function() { buildPaneTree(rootPaneGroup); } + +// Build pane tree recursively +function buildPaneTree(el) { + // Base case + if (isPane(el) && !isPaneGroup(el)) { + return ({ node: el }); + } else { + if (isPaneGroup(el)) { + var splitters; + + // Only allow .pane elements to pass through + var panes = objToArr(el.childNodes).filter(function(child) { + if (objToArr(child.classList).includes('pane')) return true; + else return false; + }); + + // Append splitters to DOM then split panes equally + appendSplitters(el, panes, isVerticalPaneGroup(el), function() { + var elementDimension, + splitterClass; + + // Set variables based on pane-group direction + if (isVerticalPaneGroup(el)) { + elementDimension = getExistingHeight(el); + splitterClass = 'splitter-vertical'; + } else { + elementDimension = getExistingWidth(el); + splitterClass = 'splitter'; + } + + // Set the flex-basis for each pane + panes.forEach(function(pane, i) { + if (pane.style.flexBasis === '') { + setFlexBasis(pane, (elementDimension / panes.length)); + } + }); + + // Push the splitters to an array + splitters = objToArr(el.childNodes).filter(function(child) { + if (objToArr(child.classList).includes(splitterClass)) return true; + else return false; + }); + }); + + splitters.forEach(function(s, i) { + s.onmousedown = resizer.bind(null, i, panes, isVerticalPaneGroup(el)); + }) + + return { + child_panes: panes.map(function(child) { + return buildPaneTree(child) + }), + node: el, + splitters: splitters, + vertical: isVerticalPaneGroup(el) + } + } + } +} + +function appendSplitters(el, panes, isVertical, setSplitters) { + // Appends a .splitter before every pane except for first child in the group + panes.forEach(function(pane, i) { + if (isVertical) { + if (i !== 0 && !objToArr(pane.previousSibling.classList).includes('splitter-vertical')) { + var splitter = document.createElement('div'), + zone = document.createElement('div'); + splitter.classList.add('splitter-vertical'); + zone.classList.add('splitter-zone-vertical'); + el.insertBefore(splitter, pane); + splitter.appendChild(zone); + } + } else { + if (i !== 0 && !objToArr(pane.previousSibling.classList).includes('splitter')) { + var splitter = document.createElement('div'), + zone = document.createElement('div'); + splitter.classList.add('splitter'); + zone.classList.add('splitter-zone'); + el.insertBefore(splitter, pane); + splitter.appendChild(zone); + } + } + }); + // Set each .pane's flex-basis property after splitters have + // been appended to the DOM + setSplitters(); +} + +function resizer(i, panes, vertical, e) { + CAN_DRAG = true; + + function resizePanes(prev, next, prevFlex, nextFlex) { + setFlexBasis(prev, prevFlex); + setFlexBasis(next, nextFlex); + } + + // Vertical .pane-group resizing + if (vertical) { + var initialCursorY = e.clientY, + topPane = panes[i], + bottomPane = panes[i + 1], + topPaneHeight = getExistingHeight(topPane), + bottomPaneHeight = getExistingHeight(bottomPane), + topPaneMaxHeight = getExistingMaxheight(topPane), + bottomPaneMaxHeight = getExistingMaxheight(bottomPane), + topPaneHasMaxHeight = !isNaN(topPaneMaxHeight), + bottomPaneHasMaxHeight = !isNaN(bottomPaneMaxHeight); + + document.onmousemove = function (e) { + if (CAN_DRAG) { + IS_DRAGGING = true; + var distanceTraveledY = initialCursorY - e.clientY, + newTopPaneHeight = topPaneHeight - distanceTraveledY, + newBottomPaneHeight = bottomPaneHeight + distanceTraveledY; + + // Allow resize only if new height < max height + if ((topPaneHasMaxHeight && !bottomPaneHasMaxHeight) && + (newTopPaneHeight <= topPaneMaxHeight)) { + resizePanes(topPane, bottomPane, newTopPaneHeight, newBottomPaneHeight); + } + else if ((!topPaneHasMaxHeight && bottomPaneHasMaxHeight) && + (newBottomPaneHeight <= bottomPaneMaxHeight)) { + resizePanes(topPane, bottomPane, newTopPaneHeight, newBottomPaneHeight); + } + else if ((topPaneHasMaxHeight && bottomPaneHasMaxHeight) && + (newTopPaneHeight <= topPaneMaxHeight) && + (newBottomPaneHeight <= bottomPaneMaxHeight)) { + resizePanes(topPane, bottomPane, newTopPaneHeight, newBottomPaneHeight); + } + else if (!topPaneHasMaxHeight && !bottomPaneHasMaxHeight) { + resizePanes(topPane, bottomPane, newTopPaneHeight, newBottomPaneHeight); + } + } + document.onmouseup = function (e) { + IS_DRAGGING = false; + CAN_DRAG = false; + } + } + } + // Horizontal .pane-group resizing + else { + var initialCursorX = e.clientX, + leftPane = panes[i], + rightPane = panes[i + 1], + leftPaneWidth = getExistingWidth(leftPane), + rightPaneWidth = getExistingWidth(rightPane), + leftPaneMaxWidth = getExistingMaxWidth(leftPane), + rightPaneMaxWidth = getExistingMaxWidth(rightPane), + leftPaneHasMaxWidth = !isNaN(leftPaneMaxWidth), + rightPaneHasMaxWidth = !isNaN(rightPaneMaxWidth); + + document.onmousemove = function (e) { + if (CAN_DRAG) { + IS_DRAGGING = true; + var distanceTraveledX = initialCursorX - e.clientX, + newLeftPaneWidth = leftPaneWidth - distanceTraveledX, + newRightPaneWidth = rightPaneWidth + distanceTraveledX; + + // Allow resize only if new width < max width + if ((leftPaneHasMaxWidth && !rightPaneHasMaxWidth) && + (newLeftPaneWidth <= leftPaneMaxWidth)) { + resizePanes(leftPane, rightPane, newLeftPaneWidth, newRightPaneWidth); + } + else if ((!leftPaneHasMaxWidth && rightPaneHasMaxWidth) && + (newRightPaneWidth <= rightPaneMaxWidth)) { + resizePanes(leftPane, rightPane, newLeftPaneWidth, newRightPaneWidth); + } + else if ((leftPaneHasMaxWidth && rightPaneHasMaxWidth) && + (newLeftPaneWidth <= leftPaneMaxWidth) && + (newRightPaneWidth <= rightPaneMaxWidth)) { + resizePanes(leftPane, rightPane, newLeftPaneWidth, newRightPaneWidth); + } + else if (!leftPaneHasMaxWidth && !rightPaneHasMaxWidth) { + resizePanes(leftPane, rightPane, newLeftPaneWidth, newRightPaneWidth); + } + } + document.onmouseup = function (e) { + IS_DRAGGING = false; + CAN_DRAG = false; + } + } + } +} + +// Utility Functions +// App-specific utilities +function isPane(el) { + return hasClass(el, 'pane'); +} +function isPaneGroup(el) { + return hasClass(el, 'pane-group'); +} +function isVerticalPaneGroup(el) { + return hasClass(el, 'pane-group-vertical'); +} +// General utilities +function objToArr(obj) { + if (Array.isArray(obj)) { + return obj; + } else { + var arr = []; + for (var key in obj) { + arr.push(obj[key]); + } + return arr; + } +} +function hasClass(el, className) { + if (objToArr(el.classList).includes(className)) { + return true; + } else { + return false; + } +} +// Style utilities +function getExistingStyle(el, prop) { + return parseInt(document.defaultView.getComputedStyle(el)[prop], 10); +} +function getExistingWidth(el) { + return getExistingStyle(el, 'width'); +} +function getExistingMaxWidth(el) { + return getExistingStyle(el, 'maxWidth'); +} +function getExistingHeight(el) { + return getExistingStyle(el, 'height'); +} +function getExistingMaxheight(el) { + return getExistingStyle(el, 'maxHeight'); +} +function setFlexBasis(el, flexBasis) { + el.setAttribute('style', 'flex-basis: ' + flexBasis + 'px'); +} diff --git a/dist/template-app/resize-demo.html b/dist/template-app/resize-demo.html new file mode 100644 index 0000000..94dce9a --- /dev/null +++ b/dist/template-app/resize-demo.html @@ -0,0 +1,193 @@ + + + + Photon + + + + + + + + +
+ + +
+

Photon

+
+ + +
+
+ +
+
+
Nest .panes and .pane-groups as deep as you want.
+
Nest .panes and .pane-groups as deep as you want.
+
+
Vertical .pane-groups
+
Click and drag to resize your panes.
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameKindDate ModifiedAuthor
bars.scssDocumentOct 13, 2015connors
base.scssDocumentOct 13, 2015connors
button-groups.scssDocumentOct 13, 2015connors
buttons.scssDocumentOct 13, 2015connors
docs.scssDocumentOct 13, 2015connors
forms.scssDocumentOct 13, 2015connors
grid.scssDocumentOct 13, 2015connors
icons.scssDocumentOct 13, 2015connors
images.scssDocumentOct 13, 2015connors
lists.scssDocumentOct 13, 2015connors
mixins.scssDocumentOct 13, 2015connors
navs.scssDocumentOct 13, 2015connors
normalize.scssDocumentOct 13, 2015connors
photon.scssDocumentOct 13, 2015connors
tables.scssDocumentOct 13, 2015connors
tabs.scssDocumentOct 13, 2015connors
utilities.scssDocumentOct 13, 2015connors
variables.scssDocumentOct 13, 2015connors
+
+
+
+
+
+ + + diff --git a/sass/grid.scss b/sass/grid.scss index 3760057..fca5252 100644 --- a/sass/grid.scss +++ b/sass/grid.scss @@ -2,13 +2,34 @@ // The Grid.css // -------------------------------------------------- -.pane-group { +// Only the root-level .pane-group is fixed positioned +.window-content > .pane-group { position: absolute; top: 0; right: 0; bottom: 0; left: 0; +} + +.pane-group { display: flex; + flex-flow: row nowrap; + justify-content: space-between; + flex: 1; +} + +// Vertical pane groups +.pane-group-vertical { + flex-direction: column; + justify-content: space-between; + // Only apply these styles to direct .pane descendents + > .pane { + border-left: 0; + border-top: 1px solid $border-color; + &:first-child { + border-top: 0; + } + } } .pane { diff --git a/sass/photon.scss b/sass/photon.scss index 0bfecce..b7dfdf6 100644 --- a/sass/photon.scss +++ b/sass/photon.scss @@ -20,5 +20,6 @@ @import "lists.scss"; @import "navs.scss"; @import "icons.scss"; +@import "splitters.scss"; @import "tables.scss"; @import "tabs.scss"; diff --git a/sass/splitters.scss b/sass/splitters.scss new file mode 100644 index 0000000..15ef6b3 --- /dev/null +++ b/sass/splitters.scss @@ -0,0 +1,37 @@ +// +// Splitters.css +// -------------------------------------------------- +$splitter-color: transparent; +$splitter-size: 1px; +$splitter-zone-size: 10px; + +// .splitter and .splitter elements are created automatically in resize.js + +.splitter { + background: $splitter-color; + width: $splitter-size; + position: relative; +} +.splitter-zone { + z-index: 1000; + cursor: col-resize; + position: absolute; + top: 0; + bottom: 0; + left: (-$splitter-zone-size / 2); + right: (-$splitter-zone-size / 2); +} +.splitter-vertical { + background: $splitter-color; + height: $splitter-size; + position: relative; +} +.splitter-zone-vertical { + z-index: 1000; + cursor: row-resize; + position: absolute; + top: (-$splitter-zone-size / 2); + bottom: (-$splitter-zone-size / 2); + left: 0; + right: 0; +}