Last active
February 24, 2023 15:00
-
-
Save xgqfrms/abbd95da478d3574febc9f5c6064838b to your computer and use it in GitHub Desktop.
Group Sortable H5-DnD Framework App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="zh-Hans"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<meta name="author" content="xgqfrms"> | |
<meta name="generator" content="VS code"> | |
<title>Group H5DnD Framework App</title> | |
<!-- css --> | |
<link rel="stylesheet" href="https://cdn.xgqfrms.xyz/sweetalert/sweetalert.css"> | |
<!-- <link rel="stylesheet" href="./libs/sweetalert.css"> | |
<link rel="stylesheet" href="./group.css"> --> | |
</head> | |
<body> | |
<section class="modules-boxes"> | |
<ul class="containers"> | |
<li><span data-uid="container-01" draggable="true">container-01</span></li> | |
<li><span data-uid="container-02" draggable="true">container-02</span></li> | |
<li><span data-uid="container-03" draggable="true">container-03</span></li> | |
</ul> | |
<br> | |
<ul class="modules"> | |
<li><span data-uid="module-01" draggable="true">module-01</span></li> | |
<li><span data-uid="module-02" draggable="true">module-02</span></li> | |
<li><span data-uid="module-03" draggable="true">module-03</span></li> | |
<li><span data-uid="module-04" draggable="true">module-04</span></li> | |
<li><span data-uid="module-05" draggable="true">module-05</span></li> | |
<li><span data-uid="module-06" draggable="true">module-06</span></li> | |
<li><span data-uid="module-07" draggable="true">module-07</span></li> | |
<li><span data-uid="module-08" draggable="true">module-08</span></li> | |
</ul> | |
</section> | |
<section> | |
<div data-box="container"> | |
<div class="boxes-container" id="boxes_container"></div> | |
</div> | |
</section> | |
<!-- libs --> | |
<script src="https://cdn.xgqfrms.xyz/Sortable/sortable.min.js"></script> | |
<script src="https://cdn.xgqfrms.xyz/sweetalert/sweetalert.min.js"></script> | |
<!-- <script src="./libs/Sortable.min.js"></script> | |
<script src="./libs/sweetalert.min.js"></script> --> | |
<!-- js --> | |
<!-- <script src="./group.js"></script> --> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
/** | |
* | |
* @author xgqfrms | |
* @license MIT | |
* @copyright xgqfrms | |
* | |
* @description group | |
* @augments | |
* @example | |
* | |
*/ | |
// no need jQuery | |
let $ = { | |
qs: function qs(uid = ``) { | |
return document.querySelector(uid); | |
}, | |
qsa: function qsa(uid = ``) { | |
return document.querySelectorAll(uid); | |
}, | |
}; | |
const checkModuleExist = (uid = ``) => { | |
let result = false; | |
const exsitModules = [...$.qsa(`[data-exist*="module-"]`)]; | |
exsitModules.filter( | |
(exsitModule, i) => { | |
let id = exsitModule.dataset.exist.substr(7); | |
if (uid === id) { | |
console.log(`module exist`, uid); | |
result = true; | |
return result; | |
} else { | |
console.log(`module not exist`, uid); | |
} | |
} | |
); | |
return result; | |
}; | |
const showContainer = (that = ``, uid = ``) => { | |
// showContainer(uid); | |
let template = ` | |
<div data-id="modules-container-box" class="modules-container-box"> | |
<div data-id="modules-container-title" class="modules-container-title">容器 ${uid} title</div> | |
<ul data-id="modules-container" class="modules-container" draggable="false"></ul> | |
</div> | |
`; | |
if (that) { | |
that.insertAdjacentHTML(`beforeend`, template); | |
setTimeout(() => { | |
// sortContainers(); | |
sortModules(); | |
ModuleLoader.init(); | |
}, 0); | |
} | |
// const box = $.qs(`[data-test="container"]`); | |
// if (box) { | |
// box.insertAdjacentHTML(`beforeend`, template); | |
// } | |
}; | |
const showModule = (that = ``, uid = ``) => { | |
// showModule(uid); | |
// const box = $.qs(`[data-test="container"]`); | |
let template = ` | |
<li draggable="false" class="module"> | |
<p class="drag-handle-box" draggable="false"> | |
<span class="drag-handle">☰</span> | |
<span class="drag-handle-title" draggable="false">模块 ${uid} title</span> | |
</p> | |
<div data-modules="safety-content" draggable="false" data-exist="module-${uid}"> | |
模块 ${uid} content<br/> | |
</div> | |
</li> | |
`; | |
let exist = checkModuleExist(uid); | |
if (that && !exist) { | |
that.insertAdjacentHTML(`beforeend`, template); | |
setTimeout(() => { | |
// sortContainers(); | |
// sortModules(); | |
}, 0); | |
} else { | |
swal({ | |
title: "此模块已存在!", | |
text: ` | |
此模块已存在, 不能再次拖放!\n | |
1 秒后自动关闭. | |
`, | |
icon: "warning", | |
className: "warning-alert-style", | |
timer: 2000, | |
button: { | |
text: "关闭", | |
value: true, | |
visible: true, | |
closeModal: true | |
} | |
}); | |
} | |
}; | |
const ContainerLoader = ( | |
(debug = false) => { | |
return { | |
dragstart: function(e) { | |
let uid = e.target.dataset.uid; | |
// let uid = e.target.dataset.uid.substr(10); | |
e.dataTransfer.effectAllowed = `move`; | |
e.target.style.opacity = "1"; | |
e.dataTransfer.setData("text/plain", `${uid}`); | |
console.log(`%c container drag uid!`, `color: #0f0`, uid); | |
}, | |
dragend: function(e) { | |
e.preventDefault(); | |
// e.stopPropagation(); | |
return true; | |
}, | |
dragenter: function(e) { | |
e.preventDefault(); | |
// this.classList.add(`over-border`); | |
return true; | |
}, | |
dragover: function(e) { | |
e.preventDefault(); | |
// return false; | |
// this.classList.add(`over-border`); | |
return true; | |
}, | |
dragleave: function(e) { | |
e.preventDefault(); | |
// this.classList.remove(`over-border`); | |
return true; | |
}, | |
drop: function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
// this.classList.remove(`over-border`); | |
let that = this; | |
let uid = e.dataTransfer.getData("text/plain"); | |
let regex = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/igm; | |
if (regex.test(uid)) { | |
console.warn(`uid is invalid!`, uid.length); | |
} else { | |
if (typeof(uid) === "string" && uid.length) { | |
try { | |
// Container | |
if (uid.includes(`container-`)) { | |
console.log(`%c container drop uid!`, `color: #f0f`, uid); | |
showContainer(that, uid.replace(`container-`, ``)); | |
} else { | |
// console.warn(`BUG: 模块只能托放到容器中!`, uid.length); | |
swal({ | |
title: "模块只能托放到容器中!", | |
text: ` | |
请先拖放容器到布局,再拖放模块!\n | |
1 秒后自动关闭. | |
`, | |
icon: "warning", | |
className: "warning-alert-style", | |
timer: 2000, | |
button: { | |
text: "关闭", | |
value: true, | |
visible: true, | |
closeModal: true | |
} | |
}); | |
} | |
} catch (error) { | |
console.log(`%c Sorry, showContainer some errors occurred!`, `color: #f0f`, error); | |
} | |
} else { | |
// invalid value | |
} | |
} | |
}, | |
init: function() { | |
let containers = $.qsa(`[data-uid*="container"]`); | |
for (let i = 0; i < containers.length; i++) { | |
containers[i].addEventListener(`dragstart`, this.dragstart, false); | |
} | |
// H5 DnD & capture | |
// let container_box = $.qs(`[data-test="container"]`); | |
// let container_box = $.qs(`.boxes-container`); | |
let container_box = $.qs(`#boxes_container`); | |
// container_box.addEventListener(`dragenter`, this.dragenter, false); | |
container_box.addEventListener(`dragover`, this.dragover, false);// must set dragover !!! | |
// container_box.addEventListener(`dragleave`, this.dragleave, false); | |
container_box.addEventListener(`drop`, this.drop, false); | |
} | |
}; | |
} | |
)(); | |
const ModuleLoader = ( | |
(debug = false) => { | |
return { | |
dragstart: function(e) { | |
let uid = e.target.dataset.uid; | |
// let uid = e.target.dataset.uid.substr(7); | |
e.dataTransfer.effectAllowed = `move`; | |
e.target.style.opacity = "1"; | |
e.dataTransfer.setData("text/plain", `${uid}`); | |
console.log(`%c module drag uid!`, `color: #0f0`, uid); | |
}, | |
dragend: function(e) { | |
e.preventDefault(); | |
// e.stopPropagation(); | |
return true; | |
}, | |
dragenter: function(e) { | |
e.preventDefault(); | |
// this.classList.add(`over-border`); | |
return true; | |
}, | |
dragover: function(e) { | |
e.preventDefault(); | |
// return false; | |
// this.classList.add(`over-border`); | |
return true; | |
}, | |
dragleave: function(e) { | |
e.preventDefault(); | |
// this.classList.remove(`over-border`); | |
return true; | |
}, | |
drop: function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
// this.classList.remove(`over-border`); | |
let that = this; | |
// console.warn(`that = `, this); | |
let uid = e.dataTransfer.getData("text/plain"); | |
// let uid = e.dataTransfer.getData("text/plain").split(",")[0]; | |
let regex = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/igm; | |
if (regex.test(uid) || uid.includes(`☰`)) { | |
console.warn(`uid is invalid!`, uid.length); | |
} else { | |
if (typeof(uid) === "string" && uid.length) { | |
try { | |
// Module | |
if (uid.includes(`module-`)) { | |
console.log(`%c module drop uid!`, `color: #f0f`, uid); | |
showModule(that, uid.replace(`module-`, ``)); | |
} else { | |
swal({ | |
// title: "容器不可以托放(模块/容器)中!", | |
title: "容器只能托放到布局中!", | |
text: ` | |
请先拖放容器到布局,再拖放模块!\n | |
1 秒后自动关闭. | |
`, | |
icon: "warning", | |
className: "warning-alert-style", | |
timer: 2000, | |
button: { | |
text: "关闭", | |
value: true, | |
visible: true, | |
closeModal: true | |
} | |
}); | |
} | |
} catch (error) { | |
console.log(`%c Sorry, showModule some errors occurred!`, `color: #f0f`, error); | |
} | |
} else { | |
// invalid value | |
} | |
} | |
}, | |
init: function() { | |
let modules = $.qsa(`[data-uid*="module"]`); | |
for (let i = 0; i < modules.length; i++) { | |
modules[i].addEventListener(`dragstart`, this.dragstart, false); | |
} | |
// H5 DnD & capture | |
let modules_container = $.qsa(`[data-id="modules-container"]`); | |
modules_container.forEach( | |
(container, i) => { | |
// container.addEventListener(`dragenter`, this.dragenter, false); | |
container.addEventListener(`dragover`, this.dragover, false); | |
// container.addEventListener(`dragleave`, this.dragleave, false); | |
container.addEventListener(`drop`, this.drop, false); | |
} | |
); | |
} | |
}; | |
} | |
)(); | |
const sortContainers = () => { | |
try { | |
// typeof(Sortable) === "function" | |
if (Sortable) { | |
// containers | |
Sortable.create($.qs("#boxes_container"), { | |
draggable: ".modules-container-box", | |
handle: ".modules-container-title", | |
animation: 150 | |
}); | |
} else { | |
throw new Error(`Sortable.js is not imported!`); | |
} | |
} catch (error) { | |
console.error(`sortContainers Error: \n`, error); | |
} | |
}; | |
const sortModules = () => { | |
try { | |
// typeof(Sortable) === "function" | |
if (Sortable) { | |
// modules | |
let uids = [...$.qsa(`[data-id="modules-container"]`)]; | |
[].forEach.call(uids, (uid) => { | |
Sortable.create(uid, { | |
group: "containers", // groupe & drag handle | |
draggable: '.module', | |
handle: ".drag-handle", | |
animation: 150 | |
}); | |
}); | |
} else { | |
throw new Error(`Sortable.js is not imported!`); | |
} | |
} catch (error) { | |
console.error(`sortModules Error: \n`, error); | |
} | |
}; | |
const initSort = () => { | |
try { | |
// typeof(Sortable) === "function" | |
if (Sortable) { | |
// containers | |
Sortable.create($.qs("#boxes_container"), { | |
draggable: ".modules-container-box", | |
handle: ".modules-container-title", | |
animation: 150 | |
}); | |
// modules | |
let uids = [...$.qsa(`[data-id="modules-container"]`)]; | |
[].forEach.call(uids, (uid) => { | |
Sortable.create(uid, { | |
group: "containers", // groupe & drag handle | |
draggable: '.module', | |
handle: ".drag-handle", | |
animation: 150 | |
}); | |
}); | |
} else { | |
throw new Error(`Sortable.js is not imported!`); | |
} | |
} catch (error) { | |
console.error(`Sortable Error: \n`, error); | |
} | |
}; | |
document.addEventListener(`DOMContentLoaded`, () => { | |
ContainerLoader.init(); | |
ModuleLoader.init(); | |
}); | |
window.onload = () => { | |
// delay | |
initSort(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@charset "UTf-8"; | |
/* group.css */ | |
html, | |
body { | |
margin: 0; | |
padding: 0; | |
position: relative; | |
color: #464637; | |
min-height: 100%; | |
font-size: 20px; | |
font-family: 'Roboto', sans-serif; | |
font-weight: 300; | |
} | |
html { | |
background-image: linear-gradient(to bottom, #F4E2C9 20%, #F4D7C9 100%); | |
} | |
.layer { | |
/* float: left; */ | |
} | |
[data-test="container"] { | |
width: 80%; | |
margin-left: 20%; | |
min-height: 200px; | |
background: #000; | |
color: #fff; | |
} | |
.modules-boxes { | |
width: 20%; | |
color: #fff; | |
float: left; | |
} | |
.containers li span, | |
.modules li span { | |
cursor: move; | |
/* -webkit-user-drag: auto; */ | |
} | |
[data-uid*="container"], | |
[data-uid*="module"] { | |
user-select: none; | |
} | |
.drag-handle-box { | |
height: 23px; | |
line-height: 1.2rem; | |
background: #FF7373; | |
width: 200px; | |
} | |
.title { | |
color: #fff; | |
padding: 3px 10px; | |
display: inline-block; | |
position: relative; | |
background-color: #FF7373; | |
z-index: 1000; | |
margin: 10px; | |
} | |
[data-id="modules-container-box"] { | |
box-sizing: border-box; | |
float: left; | |
width: 100%; | |
} | |
[data-id="modules-container-title"] { | |
background: #FF7373; | |
margin: 5px; | |
padding: 5px; | |
margin-bottom: 0; | |
padding-bottom: 0; | |
/* fallback */ | |
cursor: move; | |
cursor: grabbing; | |
user-select: none; | |
width: 200px; | |
} | |
ul { | |
list-style: none; | |
margin: 5px; | |
padding: 5px; | |
margin-top: 0; | |
display: block; | |
background: #ccc; | |
height: 100%; | |
min-height: 100px; | |
/* min-height: 300px; */ | |
} | |
.boxes-container { | |
width: 80%; | |
height: 100%; | |
/* min-height: 400px; | |
max-height: 500px; */ | |
/* max-height: 600px; | |
min-height: 600px; */ | |
max-height: 100vh; | |
min-height: 100vh; | |
margin: 0 10px 0 20%; | |
background: #2196f3; | |
overflow: auto; | |
} | |
.boxes-container li { | |
background-color: #fff; | |
padding: 10px 40px; | |
/* display: inline-block; */ | |
display: block; | |
border: 1px solid #5F9EDF; | |
margin: 5px; | |
} | |
.boxes-container li:first-letter { | |
text-transform: uppercase; | |
} | |
.drag-handle { | |
margin-right: 10px; | |
font: bold 20px Sans-Serif; | |
color: #5F9EDF; | |
display: inline-block; | |
/* css fallback */ | |
cursor: move; | |
cursor: grabbing; | |
user-select: none; | |
} | |
.drag-handle-title { | |
user-select: none; | |
} | |
[data-modules="safety-content"] { | |
color: #fff; | |
background: #000; | |
/* user-select: none; */ | |
} | |
/* sweetalert */ | |
.warning-alert-style { | |
/* color: #0f0 !important; */ | |
/* background: #fd1f1fd0; */ | |
background: #ffffff; | |
box-sizing: border-box; | |
/* border: 1px solid red; */ | |
} | |
.swal-title { | |
color: #000; | |
} | |
.swal-text { | |
color: #fd1f1fd0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment