├── .gitignore ├── .npmignore ├── .DS_Store ├── docs ├── .DS_Store ├── index.html └── resizedrag.js ├── package.json ├── LICENSE ├── .github └── workflows │ └── npm-publish.yml ├── README.md └── resizedrag.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs 2 | .github 3 | node_modules 4 | __tests__ -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MurugappanVR/resizedrag.js/HEAD/.DS_Store -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MurugappanVR/resizedrag.js/HEAD/docs/.DS_Store -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resizedrag", 3 | "version": "1.0.4", 4 | "description": "Tiny javascript library to make DOM elements movable & resizable", 5 | "main": "resizedrag.min.js", 6 | "scripts": { 7 | "build": "uglifyjs resizedrag.js -o resizedrag.min.js -c" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/MurugappanVR/resizedrag.js.git" 12 | }, 13 | "keywords": [ 14 | "resize", 15 | "draggable", 16 | "resizable", 17 | "drag", 18 | "drag-and-drop", 19 | "dom-manipulation" 20 | ], 21 | "author": "Murugappan.VR", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/MurugappanVR/resizedrag.js/issues" 25 | }, 26 | "homepage": "https://github.com/MurugappanVR/resizedrag.js#readme", 27 | "devDependencies": { 28 | "uglify-js": "^3.13.9" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2021 Murugappan VR 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 12 18 | - run: npm install 19 | # - run: npm test 20 | - run: npm build 21 | 22 | publish-npm: 23 | needs: build 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: 12 30 | registry-url: https://registry.npmjs.org/ 31 | - run: npm install 32 | - run: npm publish 33 | env: 34 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 35 | 36 | # Commented publish-gpr as it works only for scoped packages , eg @MurugappanVR/resizedrag 37 | # publish-gpr: 38 | # needs: build 39 | # runs-on: ubuntu-latest 40 | # permissions: 41 | # contents: read 42 | # packages: write 43 | # steps: 44 | # - uses: actions/checkout@v2 45 | # - uses: actions/setup-node@v2 46 | # with: 47 | # node-version: 12 48 | # registry-url: https://npm.pkg.github.com/ 49 | # - run: npm install 50 | # - run: npm publish 51 | # env: 52 | # NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # resizedrag.js 2 | 3 |  4 | 5 | Tiny js library to make DOM elements movable and resizable . [Demo Here](https://murugappanvr.github.io/resizedrag.js/) . This library has added resizing functionalities to the existing [dragdrop.js](https://github.com/knadh/dragmove.js) . 6 | 7 | ## Getting started 8 | 9 | Installing resize drag, 10 | 11 | ```sh 12 | npm install resizedrag 13 | ``` 14 | ```sh 15 | import { resizedrag } from 'resizedrag'; 16 | 17 | // (target, handler, onStart(target, x, y), onEnd(target, x, y)). 18 | // onStart and onEnd are optional callbacks that receive target element, and x, y coordinates. 19 | resizedrag(document.querySelector("#box"), document.querySelector("#box .drag-handle")); 20 | ``` 21 | ## Usage 22 | Sample element in the html which has enabled resizedrag 23 | ```sh 24 |
26 | ``` 27 | Various attributes defined in the element above are explained below, 28 | 29 | #### data-rd-drag-enabled : 30 | enable|disable drag for the dom element , default option is true . 31 | ```sh 32 | data-rd-drag-enabled="false" 33 | ``` 34 | #### data-rd-resize-enabled : 35 | enable|disable resize for the dom element , default option is true . 36 | ```sh 37 | data-rd-resize-enabled="false" 38 | ``` 39 | #### data-rd-min-width : 40 | Minimum resizeble width of the the dom element, default value is 5(in pixels) . 41 | ```sh 42 | data-rd-min-width=25 43 | ``` 44 | #### data-rd-min-height : 45 | Minimum resizeble height of the the dom element, default value is 5(in pixels) . 46 | ```sh 47 | data-rd-min-height=25 48 | ``` 49 | #### data-rd-drag-boder-enabled : 50 | Option to show border in the element which is being dragged , default value is true . 51 | ```sh 52 | data-rd-drag-boder-enabled="false" 53 | ``` 54 | ## License 55 | 56 | MIT 57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 40 | 41 | 42 | 44 | 46 | 48 | 49 | 87 | -------------------------------------------------------------------------------- /docs/resizedrag.js: -------------------------------------------------------------------------------- 1 | let _loaded = false; 2 | let _callbacks = []; 3 | const _isTouch = window.ontouchstart !== undefined; 4 | const resizedrag = function(target, handler, onStart, onEnd) { 5 | let config = { 6 | dragEnabled : target.dataset.rdDragEnabled !== "false", 7 | resizeEnabled : target.dataset.rdResizeEnabled !== "false", 8 | dragBorderEnabled : target.dataset.rdDragBorderEnabled !== "false", 9 | rdDragBoundary : target.dataset.rdDragBoundary === "true", 10 | minWidth : target.dataset.rdMinWidth ? target.dataset.rdMinWidth : 5 , 11 | minHeight : target.dataset.rdMinHeight ? target.dataset.rdMinHeight : 5 12 | } 13 | let MARGINS = 4; 14 | let edges = { 15 | top : false, 16 | bottom : false, 17 | left : false, 18 | right : false, 19 | } 20 | let targetElement = document.getElementById(target.id); 21 | let clickedInstance ; 22 | let eventHandlers = { 23 | mousemove : function(e) { 24 | let c = e; 25 | if(e.touches) { 26 | c = e.touches[0]; 27 | } 28 | // On mouse move, dispatch the coords to all registered callbacks. 29 | for (var i = 0; i < _callbacks.length; i++) { 30 | _callbacks[i](c.clientX, c.clientY); 31 | } 32 | }, 33 | mousedown : function(e) { 34 | e.stopPropagation(); 35 | e.preventDefault(); 36 | if (!config.dragEnabled && !config.resizeEnabled) { 37 | return; 38 | } 39 | 40 | let c = e; 41 | if (e.touches) { 42 | c = e.touches[0]; 43 | } 44 | isMoving = true; 45 | let bObj = attachResizeDragCursorStyle(c.clientX,c.clientY); 46 | clickedInstance = { 47 | cx : c.clientX, 48 | cy : c.clientY, 49 | w: bObj.b.width, 50 | h: bObj.b.height 51 | } 52 | isResizing = edges.right || edges.bottom || edges.top || edges.left; 53 | if(!isResizing){ 54 | target.style["border-style"]="dashed"; 55 | target.style["border-color"]="grey"; 56 | target.style["border-width"]="2px"; 57 | } 58 | startX = target.offsetLeft - c.clientX; 59 | startY = target.offsetTop - c.clientY; 60 | }, 61 | mouseup : function(e) { 62 | if (onEnd && hasStarted) { 63 | onEnd(target, parseInt(target.style.left), parseInt(target.style.top)); 64 | } 65 | let c = e; 66 | attachResizeDragCursorStyle(c.clientX,c.clientY); 67 | isResizing = false; 68 | isMoving = false; 69 | hasStarted = false; 70 | target.style["border"]="none"; 71 | } 72 | } 73 | // Register a global event to capture mouse moves (once). 74 | if (!_loaded) { 75 | document.addEventListener(_isTouch ? "touchmove" : "mousemove", eventHandlers.mousemove); 76 | } 77 | 78 | _loaded = true; 79 | let isMoving = false, hasStarted = false, isResizing = false; 80 | let startX = 0, startY = 0, lastX = 0, lastY = 0; 81 | 82 | // On the first click and hold, record the offset of the pointer in relation 83 | // to the point of click inside the element. 84 | handler.addEventListener(_isTouch ? "touchstart" : "mousedown", eventHandlers.mousedown); 85 | 86 | // On leaving click, stop moving. 87 | document.addEventListener(_isTouch ? "touchend" : "mouseup", eventHandlers.mouseup); 88 | 89 | // Register mouse-move callback to move the element. 90 | _callbacks.push(function move(x, y) { 91 | if(targetElement && !isResizing){ 92 | attachResizeDragCursorStyle(x,y); 93 | } 94 | if (!isMoving) { 95 | return; 96 | } 97 | 98 | if (!hasStarted) { 99 | hasStarted = true; 100 | if (onStart) { 101 | onStart(target, lastX, lastY); 102 | } 103 | } 104 | 105 | lastX = x + startX; 106 | lastY = y + startY; 107 | // If boundary checking is on, don't let the element cross the viewport. 108 | if (config.rdDragBoundary) { 109 | if (lastX < 1 || lastX >= window.innerWidth - target.offsetWidth) { 110 | return; 111 | } 112 | if (lastY < 1 || lastY >= window.innerHeight - target.offsetHeight) { 113 | return; 114 | } 115 | } 116 | if(isMoving){ 117 | if(!isResizing && config.dragEnabled){ 118 | target.style.left = lastX + "px"; 119 | target.style.top = lastY + "px"; 120 | }else{ 121 | if(config.resizeEnabled){ 122 | let b = target.getBoundingClientRect(); 123 | let bx = x - b.left; 124 | let by = y - b.top; 125 | if (edges.right) { 126 | target.style.width = Math.max(bx, config.minWidth) + 'px'; 127 | } 128 | if (edges.bottom) { 129 | target.style.height = Math.max(by, config.minHeight) + 'px'; 130 | } 131 | if (edges.left) { 132 | var currentWidth = Math.max(clickedInstance.cx - x + clickedInstance.w, config.minWidth); 133 | if (currentWidth > config.minWidth) { 134 | target.style.width = currentWidth + 'px'; 135 | target.style.left = x + 'px'; 136 | } 137 | } 138 | if (edges.top) { 139 | var currentHeight = Math.max(clickedInstance.cy - y + clickedInstance.h, config.minHeight); 140 | if (currentHeight > config.minHeight) { 141 | targetElement.style.height = currentHeight + 'px'; 142 | targetElement.style.top = y + 'px'; 143 | } 144 | } 145 | } 146 | } 147 | } 148 | }); 149 | 150 | let attachResizeDragCursorStyle = function(x,y){ 151 | let b = target.getBoundingClientRect(); 152 | let eX = x - b.left; 153 | let eY = y - b.top; 154 | edges.top = eY < MARGINS; 155 | edges.left = eX < MARGINS; 156 | edges.right = eX >= b.width - MARGINS; 157 | edges.bottom = eY >= b.height - MARGINS; 158 | 159 | if (edges.right && edges.bottom || edges.left && edges.top) { 160 | targetElement.style.cursor = 'nwse-resize'; 161 | } else if (edges.right && edges.top || edges.bottom && edges.left) { 162 | targetElement.style.cursor = 'nesw-resize'; 163 | } else if (edges.right || edges.left) { 164 | targetElement.style.cursor = 'ew-resize'; 165 | } else if (edges.bottom || edges.top) { 166 | targetElement.style.cursor = 'ns-resize'; 167 | } else if(eX > 0 && eX < b.width && eY > 0 && eY < b.height){ 168 | targetElement.style.cursor = config.dragEnabled ?'move':'not-allowed'; 169 | }else { 170 | targetElement.style.cursor = 'default'; 171 | } 172 | let boundaryObj = { 173 | b:b, bX : eX, bY : eY 174 | } 175 | return boundaryObj 176 | } 177 | 178 | } 179 | export {resizedrag} ; 180 | -------------------------------------------------------------------------------- /resizedrag.js: -------------------------------------------------------------------------------- 1 | let _loaded = false; 2 | let _callbacks = []; 3 | const _isTouch = window.ontouchstart !== undefined; 4 | const resizedrag = function(target, handler, onStart, onEnd) { 5 | let config = { 6 | dragEnabled : target.dataset.rdDragEnabled !== "false", 7 | resizeEnabled : target.dataset.rdResizeEnabled !== "false", 8 | dragBorderEnabled : target.dataset.rdDragBorderEnabled !== "false", 9 | rdDragBoundary : target.dataset.rdDragBoundary === "true", 10 | minWidth : target.dataset.rdMinWidth ? target.dataset.rdMinWidth : 5 , 11 | minHeight : target.dataset.rdMinHeight ? target.dataset.rdMinHeight : 5 12 | } 13 | let MARGINS = 4; 14 | let edges = { 15 | top : false, 16 | bottom : false, 17 | left : false, 18 | right : false, 19 | } 20 | let targetElement = document.getElementById(target.id); 21 | let clickedInstance ; 22 | let eventHandlers = { 23 | mousemove : function(e) { 24 | let c = e; 25 | if(e.touches) { 26 | c = e.touches[0]; 27 | } 28 | // On mouse move, dispatch the coords to all registered callbacks. 29 | for (var i = 0; i < _callbacks.length; i++) { 30 | _callbacks[i](c.clientX, c.clientY); 31 | } 32 | }, 33 | mousedown : function(e) { 34 | e.stopPropagation(); 35 | e.preventDefault(); 36 | if (!config.dragEnabled && !config.resizeEnabled) { 37 | return; 38 | } 39 | 40 | let c = e; 41 | if (e.touches) { 42 | c = e.touches[0]; 43 | } 44 | isMoving = true; 45 | let bObj = attachResizeDragCursorStyle(c.clientX,c.clientY); 46 | clickedInstance = { 47 | cx : c.clientX, 48 | cy : c.clientY, 49 | w: bObj.b.width, 50 | h: bObj.b.height 51 | } 52 | isResizing = edges.right || edges.bottom || edges.top || edges.left; 53 | if(!isResizing){ 54 | target.style["border-style"]="dashed"; 55 | target.style["border-color"]="grey"; 56 | target.style["border-width"]="2px"; 57 | } 58 | startX = target.offsetLeft - c.clientX; 59 | startY = target.offsetTop - c.clientY; 60 | }, 61 | mouseup : function(e) { 62 | if (onEnd && hasStarted) { 63 | onEnd(target, parseInt(target.style.left), parseInt(target.style.top)); 64 | } 65 | let c = e; 66 | attachResizeDragCursorStyle(c.clientX,c.clientY); 67 | isResizing = false; 68 | isMoving = false; 69 | hasStarted = false; 70 | target.style["border"]="none"; 71 | } 72 | } 73 | // Register a global event to capture mouse moves (once). 74 | if (!_loaded) { 75 | document.addEventListener(_isTouch ? "touchmove" : "mousemove", eventHandlers.mousemove); 76 | } 77 | 78 | _loaded = true; 79 | let isMoving = false, hasStarted = false, isResizing = false; 80 | let startX = 0, startY = 0, lastX = 0, lastY = 0; 81 | 82 | // On the first click and hold, record the offset of the pointer in relation 83 | // to the point of click inside the element. 84 | handler.addEventListener(_isTouch ? "touchstart" : "mousedown", eventHandlers.mousedown); 85 | 86 | // On leaving click, stop moving. 87 | document.addEventListener(_isTouch ? "touchend" : "mouseup", eventHandlers.mouseup); 88 | 89 | // Register mouse-move callback to move the element. 90 | _callbacks.push(function move(x, y) { 91 | if(targetElement && !isResizing){ 92 | attachResizeDragCursorStyle(x,y); 93 | } 94 | if (!isMoving) { 95 | return; 96 | } 97 | 98 | if (!hasStarted) { 99 | hasStarted = true; 100 | if (onStart) { 101 | onStart(target, lastX, lastY); 102 | } 103 | } 104 | 105 | lastX = x + startX; 106 | lastY = y + startY; 107 | // If boundary checking is on, don't let the element cross the viewport. 108 | if (config.rdDragBoundary) { 109 | if (lastX < 1 || lastX >= window.innerWidth - target.offsetWidth) { 110 | return; 111 | } 112 | if (lastY < 1 || lastY >= window.innerHeight - target.offsetHeight) { 113 | return; 114 | } 115 | } 116 | if(isMoving){ 117 | if(!isResizing && config.dragEnabled){ 118 | target.style.left = lastX + "px"; 119 | target.style.top = lastY + "px"; 120 | }else{ 121 | if(config.resizeEnabled){ 122 | let b = target.getBoundingClientRect(); 123 | let bx = x - b.left; 124 | let by = y - b.top; 125 | if (edges.right) { 126 | target.style.width = Math.max(bx, config.minWidth) + 'px'; 127 | } 128 | if (edges.bottom) { 129 | target.style.height = Math.max(by, config.minHeight) + 'px'; 130 | } 131 | if (edges.left) { 132 | var currentWidth = Math.max(clickedInstance.cx - x + clickedInstance.w, config.minWidth); 133 | if (currentWidth > config.minWidth) { 134 | target.style.width = currentWidth + 'px'; 135 | target.style.left = x + 'px'; 136 | } 137 | } 138 | if (edges.top) { 139 | var currentHeight = Math.max(clickedInstance.cy - y + clickedInstance.h, config.minHeight); 140 | if (currentHeight > config.minHeight) { 141 | targetElement.style.height = currentHeight + 'px'; 142 | targetElement.style.top = y + 'px'; 143 | } 144 | } 145 | } 146 | } 147 | } 148 | }); 149 | 150 | let attachResizeDragCursorStyle = function(x,y){ 151 | let b = target.getBoundingClientRect(); 152 | let eX = x - b.left; 153 | let eY = y - b.top; 154 | edges.top = eY < MARGINS; 155 | edges.left = eX < MARGINS; 156 | edges.right = eX >= b.width - MARGINS; 157 | edges.bottom = eY >= b.height - MARGINS; 158 | 159 | if (edges.right && edges.bottom || edges.left && edges.top) { 160 | targetElement.style.cursor = 'nwse-resize'; 161 | } else if (edges.right && edges.top || edges.bottom && edges.left) { 162 | targetElement.style.cursor = 'nesw-resize'; 163 | } else if (edges.right || edges.left) { 164 | targetElement.style.cursor = 'ew-resize'; 165 | } else if (edges.bottom || edges.top) { 166 | targetElement.style.cursor = 'ns-resize'; 167 | } else if(eX > 0 && eX < b.width && eY > 0 && eY < b.height){ 168 | targetElement.style.cursor = config.dragEnabled ?'move':'not-allowed'; 169 | }else { 170 | targetElement.style.cursor = 'default'; 171 | } 172 | let boundaryObj = { 173 | b:b, bX : eX, bY : eY 174 | } 175 | return boundaryObj 176 | } 177 | 178 | } 179 | export { resizedrag }; 180 | --------------------------------------------------------------------------------