diff --git a/editor.js b/editor.js index 082a3ba..abc8f98 100644 --- a/editor.js +++ b/editor.js @@ -146,6 +146,8 @@ kind === "footer" ? "th" : "td"); + cell.innerHTML = " "; + newRow.appendChild(cell); } @@ -260,6 +262,8 @@ rowKind === "header" || rowKind === "footer" ? "th" : "td")); + newCell.innerHTML = " "; + if (position === self.colIndex.length) { row.appendChild(newCell); } else { diff --git a/index.html b/index.html index 4c054cd..995ed07 100644 --- a/index.html +++ b/index.html @@ -12,53 +12,8 @@ body { font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; } - - table[tableedit] { - border-spacing: 0px; - border-bottom: solid 1px #999; - border-right: solid 1px #999; - margin: 2em; - } - - [tableedit] td, [tableedit] th { - border-top: solid 1px #999; - border-left: solid 1px #999; - min-height: 1.5em; - line-height: 1.5em; - min-width: 80px; - padding-left: 0.2em; - padding-right: 0.2em; - } - - [tableedit] th { - background-color: #EEE; - } - - .tableedit-rowguide, - .tableedit-colguide { - border: solid #CCC 1px; - border-top-color: #EEE; - border-left-color: #DDD; - border-right-color: #DDD; - -webkit-border-radius: 3px; - list-style-type: none; - margin: 0px; - padding: 0px; - background-color: white; - - -webkit-box-shadow: rgba(0,0,0,0.1) 0px 1px 1px; - } - - .tableedit-rowguide { - margin-left: -2em; - width: 1.8em; - } - - .tableedit-colguide { - margin-top: -2em; - height: 1.8em; - } +

Table Editor

diff --git a/style.css b/style.css new file mode 100644 index 0000000..14c2d78 --- /dev/null +++ b/style.css @@ -0,0 +1,147 @@ +.tableedit-active, +.tableedit-active *, +.tableedit-rowguide, +.tableedit-rowguide *, +.tableedit-colguide, +.tableedit-colguide * { + box-sizing: border-box; + -moz-box-sizing: border-box; + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; +} + +.tableedit-active { + border-spacing: 0px; + border-bottom: solid 1px #999; + border-right: solid 1px #999; + margin: 2em; +} + +.tableedit-active td, +.tableedit-active th { + border-top: solid 1px #999; + border-left: solid 1px #999; + min-height: 1.5em; + line-height: 1.5em; + min-width: 80px; + padding-left: 0.2em; + padding-right: 0.2em; + min-height: 1.5em; +} + +.tableedit-active th { + background-color: #EEE; +} + +.tableedit-rowguide, +.tableedit-colguide { + border: solid #CCC 1px; + border-top-color: #EEE; + border-left-color: #DDD; + border-right-color: #DDD; + -webkit-border-radius: 3px; + list-style-type: none; + margin: 0px; + padding: 0px; + background-color: white; + overflow: hidden; + + -webkit-box-shadow: rgba(0,0,0,0.1) 0px 1px 1px; +} + +.tableedit-rowguide { + margin-left: -2em; + width: 1.8em; +} + +.tableedit-colguide { + margin-top: -2em; + height: 1.8em; +} + +.tableedit-colguide > li { + position: absolute; + border-right: solid #EAEAEA 1px; + height: 100%; + line-height: 1.7em; +} + +.tableedit-rowguide > li { + position: absolute; + border-bottom: solid #EAEAEA 1px; + width: 100%; + line-height: 1.7em; +} + +.tableedit-colguide label, +.tableedit-rowguide label { + width: 100%; + height: 100%; + text-align: center; + display: block; + font-size: 9pt; + color: #999; +} + +.tableedit-rowguide > li ul, +.tableedit-colguide > li ul { + display: none; + position: absolute; + top: 0px; + left: 0px; + margin: 0px; + padding: 0px; + list-style-type: none; +} + +.tableedit-rowguide > li ul { + margin-left: 100%; + padding-left: 0.4em; +} + +.tableedit-rowguide > li ul:before { + position: absolute; + display: block; + content: "."; + color: transparent; + border: solid transparent 10px; + border-right-color: rgba(0,0,0,0.8); + width: 0px; + height: 0px; + margin-top: 0.3em; + left: -14px; +} + +.tableedit-rowguide > li ul li, +.tableedit-colguide > li ul li { + display: block; + padding-left: 0.5em; + padding-right: 0.5em; + background-color: rgba(0,0,0,0.8); + min-width: 150px; + color: white; + font-size: 10pt; + + cursor: pointer; +} + +.tableedit-rowguide > li ul li:hover, +.tableedit-colguide > li ul li:hover { + background-color: black; +} + +.tableedit-rowguide > li ul li:first-child, +.tableedit-colguide > li ul li:first-child { + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; +} + +.tableedit-rowguide > li ul li:last-child, +.tableedit-colguide > li ul li:last-child { + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; +} + +.tableedit-rowguide > li:hover ul, +.tableedit-colguide > li:hover ul { + display: block; +} \ No newline at end of file diff --git a/ui.js b/ui.js new file mode 100644 index 0000000..266fbb4 --- /dev/null +++ b/ui.js @@ -0,0 +1,233 @@ +(function(glob) { + + var TableEditUI = function(editor) { + var self = this; + + if (!(editor instanceof TableEdit)) + throw new Error("UI must be constructed with an instanceof TableEdit."); + + self.editor = editor; + self.table = editor.table; + self.rowGuide = document.createElement("ul"); + self.colGuide = document.createElement("ul"); + + // Assign some basic classes... + self.rowGuide.className = "tableedit-rowguide"; + self.colGuide.className = "tableedit-colguide"; + self.colGuide.style.position = self.rowGuide.style.position = "fixed"; + + // Assign a class to the table being edited... + self.table.className += " tableedit-active"; + + // Listen to table update events... + self.editor.on("update",self.update.bind(self)); + + // And to any document or interaction events we might need to keep our UI looking nice... + var posUpdate = self.updatePositioning.bind(self); + window.addEventListener("resize",posUpdate); + document.addEventListener("scroll",posUpdate); + self.table.addEventListener("keydown",posUpdate); + self.table.addEventListener("keyup",posUpdate); + + for (var i = 0; i < 25; i++) + self.editor.addRow(); + + return self; + }; + + TableEditUI.prototype.getElementOffset = function(element) { + var offset = {x: 0, y: 0, width: 0, height: 0}; + + if (element instanceof HTMLElement) { + + offset.width = element.offsetWidth; + offset.height = element.offsetHeight; + + do { + offset.x += element.offsetLeft; + offset.y += element.offsetTop; + + } while ((element = element.offsetParent)); + + return offset; + + } else { + throw new Error(""); + } + }; + + TableEditUI.prototype.update = function() { + var self = this; + + self.rowGuide.innerHTML = ""; + self.colGuide.innerHTML = ""; + + function menuItem(text,handler) { + var menuItem = document.createElement("li"); + menuItem.innerHTML = text; + + menuItem.addEventListener("click",handler); + return menuItem; + } + + // Generate list items for rows and cols. + self.editor.rowIndex.forEach(function(row,index) { + var rowTab = document.createElement("li"), + rowTabLabel = document.createElement("label"), + rowTabMenu = document.createElement("ul"); + + rowTabLabel.innerHTML = index+1; + rowTab.appendChild(rowTabLabel); + rowTab.appendChild(rowTabMenu); + self.rowGuide.appendChild(rowTab); + + rowTabMenu.appendChild(menuItem( + "Delete Row", + function() { + self.editor.removeRow(row); + } + )); + + rowTabMenu.appendChild(menuItem( + "Insert Header Before", + function() { + self.editor.addRow("header",index); + } + )); + + rowTabMenu.appendChild(menuItem( + "Insert Header After", + function() { + self.editor.addRow("header",index+1); + } + )); + + rowTabMenu.appendChild(menuItem( + "Insert Row Before", + function() { + self.editor.addRow("normal",index); + } + )); + + rowTabMenu.appendChild(menuItem( + "Insert Row After", + function() { + self.editor.addRow("normal",index+1); + } + )); + }); + + self.editor.colIndex.forEach(function(col,index) { + var colTab = document.createElement("li"), + colTabLabel = document.createElement("label"), + colTabMenu = document.createElement("ul"); + + var unitAlpha = index % 26, + globalAlpha = (index / 26) | 0, + alphaString = String.fromCharCode(unitAlpha+65); + + if (globalAlpha > 0) alphaString = String.fromCharCode(globalAlpha+64) + alphaString; + + colTabLabel.innerHTML = alphaString; + colTab.appendChild(colTabLabel); + colTab.appendChild(colTabMenu); + self.colGuide.appendChild(colTab); + }); + + self.updatePositioning(); + + return self; + }; + + TableEditUI.prototype.updatePositioning = function() { + var self = this; + + // The UI centers around the table. So get the table offset... + var tableOffset = self.getElementOffset(self.table); + + var scrollTop = document.body.scrollTop >= 0 ? document.body.scrollTop : 0, + scrollLeft = document.body.scrollLeft >= 0 ? document.body.scrollLeft : 0, + wheight = window.innerHeight, + wwidth = window.innerWidth, + top = (tableOffset.y - scrollTop), + left = (tableOffset.x - scrollLeft), + height = tableOffset.height, + width = tableOffset.width, + viewportBase = scrollTop + wheight, + viewportRight = scrollLeft + wwidth, + tableBase = tableOffset.height + tableOffset.y, + tableRight = tableOffset.width + tableOffset.x; + + // Check to ensure our values are in bounds... + scrollTop = scrollTop + wheight > document.body.scrollHeight ? + document.body.scrollHeight - wheight : scrollTop; + + scrollLeft = scrollLeft + wwidth > document.body.scrollWidth ? + document.body.scrollLeft - wwidth : scrollLeft; + + var rowGuideScrollTop = top-40 <= 0 ? (top-40)*-1 : 0, + colGuideScrollLeft = left-40 <= 0 ? (left-40)*-1 : 0; + + // And compute the final dimensions... + top = top <= 40 ? 40 : top; + left = left <= 40 ? 40 : left; + height = top + height > wheight - 3 ? wheight - top - 3: height; + width = left + width > wwidth - 3 ? wwidth - left - 3: width; + height = ((tableBase-scrollTop)-top) < height ? ((tableBase-scrollTop)-top) : height; + width = ((tableRight-scrollLeft)-left) < width ? ((tableRight-scrollLeft)-left) : width; + + // First of all - position row and colguides... + self.rowGuide.style.height = height + "px"; + self.rowGuide.style.top = top + "px"; + self.rowGuide.style.left = left + "px"; + self.rowGuide.scrollTop = rowGuideScrollTop; + + self.colGuide.style.width = width + "px"; + self.colGuide.style.top = top + "px"; + self.colGuide.style.left = left + "px"; + self.colGuide.scrollLeft = colGuideScrollLeft; + + // Set the dimensions of children in the row and col guides + var cumulativeRowHeight = 0; + [].slice.call(self.rowGuide.childNodes) + .forEach(function(node,index) { + if (self.editor.rowIndex[index]) { + var rowDimensions = + self.getElementOffset(self.editor.rowIndex[index]); + + node.style.height = rowDimensions.height + "px"; + node.style.top = cumulativeRowHeight + "px"; + cumulativeRowHeight += rowDimensions.height; + } + }); + + var cumulativeColWidth = 0; + [].slice.call(self.colGuide.childNodes) + .forEach(function(node,index) { + if (self.editor.colIndex[index]) { + var col = self.editor.colIndex[index][0], + colDimensions = + self.getElementOffset(col); + + node.style.width = colDimensions.width + "px"; + node.style.left = cumulativeColWidth + "px"; + cumulativeColWidth += colDimensions.width; + } + }); + + return self; + }; + + TableEditUI.prototype.render = function() { + var self = this; + + document.body.appendChild(self.rowGuide); + document.body.appendChild(self.colGuide); + self.update(); + + return self; + }; + + glob.TableEditUI = TableEditUI; + +})(this); \ No newline at end of file