Added new and improved drag-and-drop. Works between stores and on touch-devices.

master
Eirik Th S 2021-07-16 19:58:34 +02:00
parent fe7ae67f1e
commit 1479893234
8 changed files with 2838 additions and 24 deletions

View File

@ -155,7 +155,7 @@ body.dollars span.price::after {
.drag-over {
border-bottom: dashed 3px red;
border-bottom-width: 1px;
border-bottom-width: 1px !important;
}
#totalPriceWrapper {

33
www/js/hammer.jquery.js Normal file
View File

@ -0,0 +1,33 @@
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery', 'hammerjs'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'), require('hammerjs'));
} else {
factory(jQuery, Hammer);
}
}(function($, Hammer) {
function hammerify(el, options) {
var $el = $(el);
if(!$el.data("hammer")) {
$el.data("hammer", new Hammer($el[0], options));
}
}
$.fn.hammer = function(options) {
return this.each(function() {
hammerify(this, options);
});
};
// extend the emit method to also trigger jQuery events
Hammer.Manager.prototype.emit = (function(originalEmit) {
return function(type, data) {
originalEmit.call(this, type, data);
$(this.element).trigger({
type: type,
gesture: data
});
};
})(Hammer.Manager.prototype.emit);
}));

2643
www/js/hammer.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -311,7 +311,6 @@ function initStore($storeName){
$matchingStores = $storeCheckRes->fetch_row()[0];
if($matchingStores == 1){
// $sql = "UPDATE plan_store SET null WHERE `user_id` = '$user_id' AND `name` = '$storeName';";
$sql = "SELECT plan_store_id FROM plan_store WHERE `space_id` = '$spaceID' AND `name` = '$storeName';";
if( ($res = $db->query($sql)) !== false){
@ -343,6 +342,7 @@ function renameStore($storeID, $newName){
if($db->query($renameStoreSql) !== false){
return true;
}
return false;
}
function deleteStore($storeID, $storeName, $itemsLength){
@ -385,9 +385,9 @@ function addItem($storeID, $name, $price){
}
function checkItem($storeID, $itemID, $checked){
global $db, $spaceID;
global $db;
$verifyUserOwnershipSQL = "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$findRowSql = "SELECT `plan_item_id` FROM plan_store_item WHERE `plan_store_id` = ($verifyUserOwnershipSQL) AND `plan_item_id` = '$itemID'";
$checkItemSql = "UPDATE plan_store_item SET `checked` = ".($checked?1:0)." WHERE `plan_item_id` = ($findRowSql);";
if($db->query($checkItemSql)){
@ -397,9 +397,9 @@ function checkItem($storeID, $itemID, $checked){
}
function remItem($storeID, $itemID, $price){
global $db, $spaceID;
global $db;
$verifyUserOwnershipSQL = "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$findRowSql = "SELECT `plan_item_id` FROM plan_store_item WHERE `plan_store_id` = ($verifyUserOwnershipSQL) AND `plan_item_id` = '$itemID'";
$removeItemSql = "DELETE FROM plan_store_item WHERE `plan_item_id` = ($findRowSql) AND `price` = '$price';";
if($db->query($removeItemSql) && mysqli_affected_rows($db) > 0){
@ -409,9 +409,23 @@ function remItem($storeID, $itemID, $price){
}
function moveItem($storeID, $itemID, $afterID){
global $db, $spaceID;
global $db;
$verifyUserOwnershipSQL = "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$sameStoreCheckSQL = "SELECT (SELECT plan_store_id FROM plan_store_item WHERE `plan_item_id` = $itemID AND `plan_store_id` = ($verifyUserOwnershipSQL)) moved, (SELECT plan_store_id FROM plan_store_item WHERE plan_item_id = $afterID) after";
$differentStoreAddSQL = "";
if($sameStoreCheck = $db->query($sameStoreCheckSQL)){
$stores = $sameStoreCheck->fetch_array();
if($stores[0] != $stores[1]){
// DIFFERENT STORES
$storeID = $stores[1];
$differentStoreAddSQL = ", plan_store_id = $storeID";
}
}
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$getStoreItemsSQL = "SELECT plan_item_id FROM plan_store_item WHERE `plan_store_id` = ($verifyUserOwnershipSQL) ORDER BY if(pos is \N,1,0), pos;";
if($getStoreItems = $db->query($getStoreItemsSQL)){
@ -420,19 +434,19 @@ function moveItem($storeID, $itemID, $afterID){
$newQuery = "";
if($afterID == 0){
$newQuery .= "UPDATE plan_store_item SET pos = $position WHERE plan_item_id = $itemID;";
$newQuery .= "UPDATE plan_store_item SET pos = $position $differentStoreAddSQL WHERE plan_item_id = $itemID;";
$position++;
}
while($row = $getStoreItems->fetch_assoc()){
if($row['plan_item_id'] != $itemID){
$newQuery .= "UPDATE plan_store_item SET pos = $position WHERE plan_item_id = $row[plan_item_id];";
$newQuery .= "UPDATE plan_store_item SET pos = $position $differentStoreAddSQL WHERE plan_item_id = $row[plan_item_id];";
$position++;
}
if($row['plan_item_id'] == $afterID){
$newQuery .= "UPDATE plan_store_item SET pos = $position WHERE plan_item_id = $itemID;";
$newQuery .= "UPDATE plan_store_item SET pos = $position $differentStoreAddSQL WHERE plan_item_id = $itemID;";
$position++;
}
}
@ -445,9 +459,9 @@ function moveItem($storeID, $itemID, $afterID){
}
function setItemAmount($storeID, $itemID, $newAmount = 1){
global $db, $spaceID;
global $db;
$verifyUserOwnershipSQL = "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$updateAmountSQL = "UPDATE plan_store_item SET amount = $newAmount WHERE plan_item_id = $itemID AND plan_store_id = ($verifyUserOwnershipSQL);";
if($db->query($updateAmountSQL)){
@ -458,9 +472,9 @@ function setItemAmount($storeID, $itemID, $newAmount = 1){
}
function setState($storeID, $newState){
global $db, $spaceID;
global $db;
$verifyUserOwnershipSQL = "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
$verifyUserOwnershipSQL = getVerifyStoreOwnerSQL($storeID);
$setStateSQL = "UPDATE plan_store SET state = '$newState' WHERE plan_store_id = ($verifyUserOwnershipSQL);";
if($db->query($setStateSQL)){
@ -468,4 +482,10 @@ function setState($storeID, $newState){
}
return false;
}
function getVerifyStoreOwnerSQL($storeID): string {
global $spaceID;
return "SELECT `plan_store_id` FROM plan_store WHERE `space_id` = '$spaceID' AND `plan_store_id` = '$storeID'";
}

97
www/plan/draggingClass.js Normal file
View File

@ -0,0 +1,97 @@
class Draggable {
constructor(draggableClass, holderClass, hoverClassName, parentIdentifier, doneMovingFunc) {
this.parentIdentifier = parentIdentifier+" " || '';
this.moveableClass = draggableClass || "moveable";
this.holderClass = holderClass || "holder";
this.hoverClass = hoverClassName || "elementHover";
this.activeDragging = false;
this.draggingElem = null;
this.doneMoving = doneMovingFunc || function(draggedElement, afterPosElement){ console.log("No custom done-moving-function.", draggedElement, afterPosElement); };
this.update();
}
update(){
let hammerOptions = {};
let elemHolderHtml = "<div class='elemHolder' style='position: absolute;'></div>";
$(this.parentIdentifier+"."+this.moveableClass+":not(:eq(0))").off().hammer(hammerOptions).bind('panstart panend panmove', ev => {
if(ev.type === "panmove") {
this.moveDraggingElem(ev);
}
else if(ev.type === "panstart") {
// console.log("start", ev);
this.activeDragging = true;
this.draggingElem = $(ev.currentTarget);
if($(".elemHolder").length < 1){
$("body").append(elemHolderHtml);
}
this.draggingElem.clone().appendTo(".elemHolder");
this.draggingElem.css('opacity', '30%');
$(".elemHolder").css('height', '').css('height', ($(".elemHolder").height()+10)+"px");
}
else if(ev.type === "panend"){
// console.log("end", ev);
this.activeDragging = false;
this.draggingElem.css('opacity', '');
$(document).off('mousemove');
$(".elemHolder").html('');
let moveAfterElem = $("."+this.hoverClass);
if(moveAfterElem.length === 1){
if(!this.draggingElem.hasClass(this.hoverClass)){
this.doneMoving(this.draggingElem, moveAfterElem);
this.draggingElem.detach().insertAfter("."+this.hoverClass);
}
}
this.draggingElem = null;
$('.'+this.hoverClass).removeClass(this.hoverClass);
}
});
}
moveDraggingElem(event){
// console.log(event);
let elem = $('.elemHolder');
let x = event.gesture.center.x;
let y = event.gesture.center.y;
elem.css({ left: ((window.scrollX + x) - elem.width() / 4), top: ((window.scrollY + y)+4) });
$('.'+this.hoverClass).removeClass(this.hoverClass);
// let elemAtPos = $(document.elementFromPoint(x, y));
let elemAtPos = this.findClosestPos(x, y);
if(elemAtPos !== null){
elemAtPos.addClass(this.hoverClass);
}
}
findClosestPos(posX, posY){
let elemAtPos = $(document.elementFromPoint(posX, posY));
// let posClass = "posBelow";
let closest = elemAtPos.closest("."+this.moveableClass);
if(closest.length === 1){
return closest.first();
}
if(elemAtPos.hasClass(this.holderClass)){
if(posY < (elemAtPos.offset().top + elemAtPos.height()/2)){
return elemAtPos.find("."+this.moveableClass).first();
}
return elemAtPos.find("."+this.moveableClass).last();
}
return null;
}
}

View File

@ -34,6 +34,7 @@
</div>
<script src='plan.js'></script>
<script src='draggingClass.js'></script>
</div>
<?php include $rPath.'webdata/footer.html'; ?>
</div>

View File

@ -10,7 +10,7 @@ class Store {
this.storeID = storeID || null;
let html = "";
html += "<div class='card store' style='width: 25rem; max-width: 90vw;'>";
html += "<div class='card store' style='width: 25rem; max-width: 90vw;' id='store"+storeNum+"'>";
html += " <div class='iconWrapper' style='position: absolute; top: 1px; left: 1px;'>";
html += " <nav aria-label='State changer'><ul class='pagination'>";
html += " <li class='page-item planningState "+(this.state === 'planning'?"active":"")+"'><span class='page-link'><img src='../icon/list-check.svg' class='ariaButton' alt='Planning state' title='Planning state' data-toggle='tooltip' tabindex=0 role='button' /></span></li>";
@ -25,8 +25,9 @@ class Store {
html += " </div>";
html += " <div class='card-header'>"+this.title+"</div>";
html += " <div class='card-body'>";
html += " <div class='draggable' style='width: 100%; height: 10px; border-width: 1px;'></div> <ul class='list-group list-group-flush storeItems'>";
html += " <ul class='list-group list-group-flush storeItems dragHolder'>";
html += " <li class='list-group-item emptyList'>No items added</li>";
html += " <li class='list-group-item draggable' style='height: 10px; width: 100%;'></li>";
html += " </ul>";
html += " <span class='addItemFormWrapper'>";
@ -93,6 +94,22 @@ class Store {
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
// INIT DRAGGING
this.draggingClass = new Draggable("draggable", 'dragHolder', 'drag-over', "#store"+storeNum,(moved, after)=> {
// console.log(moved, after);
let movedID = moved.attr('data-itemid');
let afterID = after.attr('data-itemid');
if(typeof movedID !== "undefined" && typeof afterID !== "undefined"){
this.setItemPosition(movedID, afterID);
if(moved.parent().parent().parent().attr('id') !== after.parent().parent().parent().attr('id')){
console.info("Moved item to different store, so refreshing all");
$("#refreshAll").trigger('click');
}
}
});
}
setState(state, animTime){
@ -215,7 +232,7 @@ class Store {
this.itemsObj[itemID] = { text: text, price: price, itemID: itemID, amount: amount, checked: checked };
let html = "\n";
html += "<li class='list-group-item draggable"+(checked?' checkedItem':'')+"' id='item_"+itemID+"' data-itemid='"+itemID+"' draggable='true'>";
html += "<li class='list-group-item draggable"+(checked?' checkedItem':'')+"' id='item_"+itemID+"' data-itemid='"+itemID+"' style='height: 100%; min-width: 200px;'>"; // draggable='true'
html += " <span style='float: left; display: none; margin-right: 5px;' class='checkItems'><input type='checkbox'"+(checked?" checked":"")+"></span>";
html += " <span style='float: left;'>"+text+"</span>";
@ -390,7 +407,8 @@ class Store {
});
// DRAGGABLE
let draggingObj = null, dragPos = null;
this.draggingClass.update();
/*let draggingObj = null, dragPos = null;
this.selector.find('.draggable').unbind().on('dragstart dragover dragenter dragleave dragend', ev => {
// this.selector.find('.draggable').hammer().bind('swipe', ev => {
if(ev.type === 'dragover' || ev.type === 'dragenter'){
@ -432,6 +450,7 @@ class Store {
$('.drag-over').removeClass('drag-over');
}
});
*/
// BIND remove-buttons
$(this.selector).find(".remItem").each((key, val) => {
@ -509,9 +528,10 @@ $("#addStore").click(ev => {
stores.push(new Store());
});
$("#refreshAll").click(ev => {
$("#refreshAll").on('click', ev => {
$("#stores").css('height', $("#stores").height());
$(".tooltip").remove();
getStores(spaceID);
getStores(spaceID).done(json => { $("#stores").css('height', ''); });
});
$("body").on('click', function(ev){
@ -582,7 +602,7 @@ function getStores(spaceID){
options = { plan: '', space: spaceID }
}
$.getJSON('do.php', options)
return $.getJSON('do.php', options)
.done(json => {
if(handleJsonErrors(json)){
return;

View File

@ -54,9 +54,9 @@ function getHtmlHeaders($prepend = ""){
<script src='".$prepend."js/jquery-3.5.1.min.js'></script>
<script src='".$prepend."js/popper.min.js'></script>
<script src='".$prepend."js/bootstrap.min.js'></script>
<!--<script src='//cdn.jsdelivr.net/npm/marked/marked.min.js'></script>
<!--<script src='//cdn.jsdelivr.net/npm/marked/marked.min.js'></script>-->
<script src='".$prepend."js/hammer.js'></script>
<script src='".$prepend."js/hammer.jquery.js'></script>-->
<script src='".$prepend."js/hammer.jquery.js'></script>
\n";
}