Vue - prevent item dragging in a grid - javascript

I created a grid of items using this library and it works flawlessly. In my items, i have a very simple bootstrap grid with some forms, inputs and other content, the problem is that a lot of times while using the content inside of the bootstrap grid, i move the entire item.
I was wondering if there is a way to disable dragging only inside of the so that when i use what's inside of the container, the item is not moved, but if i want to move the item i can drag it by grabbing it outside the container.
Here is my code:
<template>
<grid-layout :layout.sync="layout"
:col-num="1"
:row-height="30"
:is-draggable="draggable"
:is-resizable="resizable"
:vertical-compact="true"
:use-css-transforms="true"
>
<grid-item v-for="item in layout"
:static="item.static"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
<div class="container">
<div class="row">
<div class="col-sm-7">Here is some content...</div>
<div class="col-sm-5">Here is some other content.. </div>
</div>
</div>
</grid-item>
</grid-layout>
</template>
<script>
import { GridLayout, GridItem } from "vue-grid-layout"
export default {
components: {
GridLayout,
GridItem
},
data() {
return {
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", static: false},
],
draggable: true,
resizable: true,
index: 0
}
},
methods: {
itemTitle(item) {
let result = item.i;
if (item.static) {
result += " - Static";
}
return result;
}
}
}
</script>
<style scoped>
.vue-grid-layout {
background: #eee;
}
.vue-grid-item:not(.vue-grid-placeholder) {
background: #ccc;
border: 1px solid black;
}
.vue-grid-item .resizing {
opacity: 0.9;
}
.vue-grid-item .static {
background: #cce;
}
.vue-grid-item .text {
font-size: 24px;
text-align: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 100%;
width: 100%;
}
.vue-grid-item .no-drag {
height: 100%;
width: 100%;
}
.vue-grid-item .minMax {
font-size: 12px;
}
.vue-grid-item .add {
cursor: pointer;
}
.vue-draggable-handle {
position: absolute;
width: 20px;
height: 20px;
top: 0;
left: 0;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
background-position: bottom right;
padding: 0 8px 8px 0;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: pointer;
}
</style>
So basically i should not be able to drag or move the item from inside the container. Any kind of advice is appreciated. Thanks in advance!

Since you asked you probably already solved your problem, but the easy way to avoid moving the entire item is to declare an attribute of grid-item where dragging is not allowed.
<grid-item class=""
drag-allow-from=".vue-draggable-handle"
drag-ignore-from=".no-drag"
:autoSize="true" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i">
<div class="vue-draggable-handle"></div>
<div class="no-drag"></div>
</grid-item>
Thoses two attributes are in fact css class reference.
.vue-grid-item .no-drag {height: 100%;width: 100%;}
.vue-draggable-handle { position: initial; width: 5px;height: 5px;top: 0;right: 0;padding: 0 8px 8px 0;background-origin: content-box;box-sizing: border-box;border-radius: 10px;cursor: pointer;color: #005B60 !important;}

You can either set static to true which would not allow you to drag/resize the grid or add an element draggable in your layout and set it to false where you will be able to only resize the grid.
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", static: true},
] // If you want to lock the grid (no resize/no dragging)
or
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", draggable: false},
] // you will be able to resize but drag
<grid-item
:key="item.i"
v-for="item in layout"
:is-draggable="item.draggable"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>

Related

Show Guides and do snapping while dragging jquery draggable divs

If there are multiple jquery draggable divs I want to see guides and snap to guides, edges and corners of others divs.
Here's the code:
$(".draggable").draggable();
$(".draggable").resizable();
body {
font-family: courier new, courier;
font-size: 12px;
}
.draggable {
border: 1px solid #ccc;
width: 100px;
height: 80px;
cursor: move;
position: relative;
}
.guide {
display: none;
position: absolute;
left: 0;
top: 0;
}
#guide-h {
border-top: 1px dashed #55f;
width: 100%;
}
#guide-v {
border-left: 1px dashed #55f;
height: 100%;
}
#image{
height: 150px;
width: 200px;
}
img{
width: 100%;
height: 100%;
}
#image_h {
position: absolute;
color: white;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css"/>
<div class="draggable">drag me!</div>
<div class="draggable">you can drag me too, if you like</div>
<div class="draggable">hep hep</div>
<div class="draggable" id="image">
<img src="https://cdn.pixabay.com/photo/2021/02/06/16/29/jay-5988657__340.jpg">
<div id="image_h">
Hello
</div>
</div>
<div id="guide-h" class="guide"></div>
<div id="guide-v" class="guide"></div>
The blue line in following image is what i want to be shown while divs are being dragged
see image here. Divs should snap to the blue guiding lines when divs are aligned.
don't change the relative and absolute position of classes as I've used them to overlay one div on another.
I tried searching online but solutions are too old, awakward and work with jquery 2.x
Please help!
To address the first part of your question, you can manage the guides like so.
$(function() {
function moveGuides(top, left) {
$("#guide-h").css("top", top + "px");
$("#guide-v").css("left", left + "px");
}
function getMyCorners(el) {
var p = $(el).position();
return {
top: p.top,
left: p.left,
bottom: p.top + $(el).height(),
right: p.left + $(el).width()
};
}
function startGuides(targetEl) {
var c = getMyCorners(targetEl);
moveGuides(c.top, c.right);
$(".guide").show();
}
function stopGuides() {
$(".guide").hide();
}
$(".draggable").draggable({
start: function(e, ui) {
startGuides(this);
},
drag: function(e, ui) {
var c = getMyCorners(this);
moveGuides(c.top, c.right);
},
stop: stopGuides
});
$(".draggable").resizable({
start: function(e, ui) {
startGuides(this);
},
resize: function(e, ui) {
var c = getMyCorners(this);
moveGuides(c.top, c.right);
},
stop: stopGuides
});
});
body {
font-family: courier new, courier;
font-size: 12px;
}
.draggable {
border: 1px solid #ccc;
width: 100px;
height: 80px;
cursor: move;
position: relative;
}
.guide {
display: none;
position: absolute;
left: 0;
top: 0;
}
#guide-h {
border-top: 1px dashed #55f;
width: 100%;
}
#guide-v {
border-left: 1px dashed #55f;
height: 100%;
}
#image {
height: 150px;
width: 200px;
}
img {
width: 100%;
height: 100%;
}
#image_h {
position: absolute;
color: white;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
<div class="draggable">drag me!</div>
<div class="draggable">you can drag me too, if you like</div>
<div class="draggable">hep hep</div>
<div class="draggable" id="image">
<img src="https://cdn.pixabay.com/photo/2021/02/06/16/29/jay-5988657__340.jpg">
<div id="image_h">
Hello
</div>
</div>
<div id="guide-h" class="guide"></div>
<div id="guide-v" class="guide"></div>
With helper functions, you can reveal the guides, and move them based on specific events.
The next, and much more involved portion, would be to create collision detection for the various elements. This will require checking against each of the elements involved against all the other elements. For example:
if($("#guide-h").position().top == $(".draggable").position().top){
// Stop event
}
I am guessing that you are looking to create alignment. So when a Guide collides with an edge, it should prevent the User from dragging or resizing the element further in that direction. Also, do you want it to Snap and what tolerance should that snap be?
Since you did not provide those details, I am not really able to address your second question in full.

How to put scroll bar in vertical position and also how to move images with up and down arrows?

I am having difficulty to put the scroll bar in a vertical position instead of horizontal. Also, I want to slide images with up
and down arrow key of the keyboard. Please help me I have an
assignment due. I'll appreciate your help.
For more information please check my code into jsfiddle https://jsfiddle.net/mgj7hb0k/
The code below is from HTML file
<div class="slider-wrap">
<div class="slider" id="slider">
<div class="holder">
<div class="slide" id="slide-0"><span class="temp">74°</span></div>
<div class="slide" id="slide-1"><span class="temp">64°</span></div>
<div class="slide" id="slide-2"><span class="temp">82°</span></div>
</div>
</div>
<nav class="slider-nav">
Slide 0
Slide 1
Slide 2
</nav>
</div>
CSS file. I have added some styles into separate css file.The "slider" (visual container) and the slides need to have explicity the same size. We'll use pixels here but you could make it work with anything.
#import url(https://fonts.googleapis.com/css?family=Josefin+Slab:100);
.slider-wrap {
width: 300px;
height: 500px;
margin: 20px auto;
}
.slider {
overflow-x: scroll;
}
.holder {
width: 300%;
}
.slide {
float: left;
width: 300px;
height: 500px;
position: relative;
background-position: -100px 0;
}
.temp {
position: absolute;
color: white;
font-size: 100px;
bottom: 15px;
left: 15px;
font-family: 'Josefin Slab', serif;
font-weight: 100;
}
#slide-0 {
background-image: url(http://farm8.staticflickr.com/7347/8731666710_34d07e709e_z.jpg);
}
#slide-1 {
background-image: url(http://farm8.staticflickr.com/7384/8730654121_05bca33388_z.jpg);
}
#slide-2 {
background-image: url(http://farm8.staticflickr.com/7382/8732044638_9337082fc6_z.jpg);
}
.slide:before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40%;
background: linear-gradient(transparent, black);
}
.slider-nav {
text-align: center;
margin: 10px 0 0 0;
}
.slider-nav a {
width: 10px;
height: 10px;
display: inline-block;
background: #ddd;
overflow: hidden;
text-indent: -9999px;
border-radius: 50%;
}
.slider-nav a.active {
background: #999;
}
We're going to use jQuery here because we love life. Our goal is the adjust the background-position of the slides as we scroll. We can set background-position in percentages in CSS, but that alone doesn't do the cool hide/reveal more effect we're looking for. Based the amount scrolled (which we can measure in JavaScript), we'll adjust the background-position. Alone, that would look something like this:
Js file
var slider = {
// Not sure if keeping element collections like this
// together is useful or not.
el: {
slider: $("#slider"),
allSlides: $(".slide"),
sliderNav: $(".slider-nav"),
allNavButtons: $(".slider-nav > a")
},
timing: 800,
slideWidth: 300, // could measure this
// In this simple example, might just move the
// binding here to the init function
init: function() {
this.bindUIEvents();
},
bindUIEvents: function() {
// You can either manually scroll...
this.el.slider.on("scroll", function(event) {
slider.moveSlidePosition(event);
});
// ... or click a thing
this.el.sliderNav.on("click", "a", function(event) {
slider.handleNavClick(event, this);
});
// What would be cool is if it had touch
// events where you could swipe but it
// also kinda snapped into place.
},
moveSlidePosition: function(event) {
// Magic Numbers =(
this.el.allSlides.css({
"background-position": $(event.target).scrollLeft()/6-100+ "px 0"
});
},
handleNavClick: function(event, el) {
event.preventDefault();
var position = $(el).attr("href").split("-").pop();
this.el.slider.animate({
scrollLeft: position * this.slideWidth
}, this.timing);
this.changeActiveNav(el);
},
changeActiveNav: function(el) {
this.el.allNavButtons.removeClass("active");
$(el).addClass("active");
}
};
slider.init();

Display text in input of type number only on focus

Here is what I try to acomplish: I need an input field containing a value with a unit, that would look like this:
On focussing the input, I want it to move the unit to the right side, looking like this:
I can think of two ways to do so:
1. Replace input field with a Div that looks exactly like the input when focus is lost, and set the value of the input as its content:
$('#fakeInput').bind('click', changeToRealInput);
$('#realInput').bind('blur', changeToFakeInput);
$('#realInput').trigger('blur');
$('#unitAddon').html($('#realInput').attr('unit'));
function changeToFakeInput() {
// hide actual input and show a div with its contents instead
$('#fakeInput').show();
$('#realInputContainer').hide();
$('#fakeInput').html($('#realInput').val() + $('#realInput').attr('unit'));
}
function changeToRealInput() {
// hide fake-div and set the actual input active
$('#fakeInput').hide();
$('#realInputContainer').show();
$('#realInput').focus();
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
div#container {
display: flex;
background: #8aaac7;
padding: 10px;
width: 200px;
}
div#unitAddon,
input#realInput,
div#fakeInput {
font-family: sans-serif;
font-size: 26px;
padding: 5px;
width: 100%;
background-color: #FFFFFF;
border: none;
outline: none;
}
div#realInputContainer,
div#fakeInput {
border: 2px solid #dadada;
}
div#realInputContainer {
display: flex;
}
div#unitAddon {
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="container">
<div id="fakeInput"></div>
<div id="realInputContainer">
<input type="number" unit="kg" id="realInput" value="3.3">
<div id="unitAddon"></div>
</div>
</div>
(also see this jsFiddle)
Problem here is (as you can see in the screenshot above) that, depending on your local settings, chrome automatically converts the decimal point into a comma (in the input, but not in the fake-div)
Another way I thought of is: When the focus is lost, set the size of the input field to match its content and, by doing so, pull the addon displaying the unit just behind the number.
Problem here is to get the size of the content of an input (cross-browser):
$('#realInput').bind('focus', changeToRealInput);
$('#realInput').bind('blur', changeToFakeInput);
$('#realInput').trigger('blur');
$('#unitAddon').html($('#realInput').attr('unit'));
function changeToFakeInput() {
// here is the question: what width should it be?
$('#realInput').css({'width' : '40%'});
}
function changeToRealInput() {
$('#unitAddon').css({'width' : 'auto'});
$('#realInput').css({'width' : '100%'});
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
div#container {
display: flex;
background: #8aaac7;
padding: 10px;
width: 300px;
}
div#unitAddon,
input#realInput{
font-family: sans-serif;
font-size: 26px;
padding: 5px;
width: 100%;
border: none;
outline: none;
}
div#realInputContainer {
border: 2px solid #dadada;
display: flex;
background-color: #FFFFFF;
}
div#realInputContainer.setAddonAway > div#unitAddon {
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="container">
<div id="realInputContainer" class="setAddonClose">
<input type="number" unit="kg" id="realInput" value="3.3">
<div id="unitAddon"></div>
</div>
</div>
also see this jsFiddle
I could accomlish this with an input[type=text], but I dont want to loose the benefits of type[number] (min/max/step validation, on-screen keyboard, etc.)
Is there any way of getting around the flaws of my two ideas? Or is thre a more elegant way to do so?
The idea is to: (1) make the input box to cover the entire container; (2) create a helper element, and set it the same length as the input value via JS, and make it invisible as a place holder; (3) apply some style for moving around the unit box.
codepen
$(document).ready(function() {
$(".value").text($(".number").val());
$(".unit").text($(".number").attr("unit"));
$(".number").on("change keypress input", function() {
$(".value").text($(".number").val());
});
});
* {
box-sizing: border-box;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.container {
position: relative;
display: flex;
border: 4px solid teal;
width: 200px;
}
.container > * {
font-family: sans-serif;
font-size: 20px;
}
.number {
position: absolute;
left: 0;
top: 0;
width: 100%;
padding: 0;
border: 0;
outline: 0;
background: transparent;
}
.value {
visibility: hidden;
max-width: 100%;
overflow: hidden;
}
.unit {
position: relative;
flex: 1;
pointer-events: none;
background: white;
}
.number:focus ~ .value {
flex: 1;
}
.number:focus ~ .unit {
flex: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<input class="number" type="number" value="1.23" unit="kg">
<span class="value"></span>
<span class="unit"></span>
</div>

Having issues typing into my input box or clicking on things[React]

I am a bit confused as why my input box is frozen and a click is not working on my label. I looked around for an answer and read one that said i needed to set the value and a callback. Did that and still no go. Here is my code.
import React, { Component } from 'react'
class ItemsNew extends Component{
constructor(props){
super(props)
this.state = {
name: 'a',
price: '',
location: '',
used: '',
descriptipn: '',
imgUrl: '',
shippingCost: ''
}
console.log(this.state)
}
handleOnChange = (event) => {
debugger
this.setState({
name: event.target.value
})
}
handleOnClick = (event) => {
debugger
}
render(){
return (
<div>
<label onClick={this.handleOnClick}>name</label>
<input id='name' type='text' onChange={this.handleOnChange} value={this.state.name}/><br/>
</div>
)
}
}
export default ItemsNew;
My console.log hits and prints out this.state. However none of my event handler get hit with the debugger. The input box does get printed out with an 'a' when on the first load of the page.
Looking through the React dev tools this is my DOM.
![image of DOM]: https://ibb.co/mQEaN6
Also the tools say that the props are Read Only.
After building my app back up from scratch I found the problem is in the CSS. Which I find strange. The rendered out div was nested in a main div that had the z-index style of -1 so that my header content was on top of it. I changed the z-index around for the header and gave the header a position of relative and now everything works.
Previous css code:
.main{
width: 600px;
height: 650px;
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
border: 7px;
border-style: solid;
border-color: grey;
box-shadow: 10px 13px 15px 0 rgba(0,0,0,0.3);
transform: translateY(-12%);
overflow-y: auto;
**z-index: -1;**
background: lightblue;
}
.app-title{
text-align: center;
color: teal;
margin-top: 38px;
}
New CSS code:
.main{
width: 600px;
height: 650px;
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
border: 7px;
border-style: solid;
border-color: grey;
box-shadow: 10px 13px 15px 0 rgba(0,0,0,0.3);
transform: translateY(-12%);
overflow-y: auto;
background: lightblue;
}
.app-title{
text-align: center;
color: teal;
margin-top: 38px;
**position: relative;**
**z-index: 1;**
}

Having a DIV 'stick' for x amount of scrolls

I need a 'Page section' to stick in place for (x) amount of scrolling and then move onto the next section. I've tried putting them into the child theme but nothing... Can someone tell me a good way to do wthis that's not Javascript heavy?
CSS
.isSticky {
top: 0;
position: fixed;
}
HTML
<div>
<section id="top"></section>
<section id="test2"></section>
<section id="bottom"></section>
</div>
JS
$(document).ready(function () {
var el = $('#test2');
var elTop = el.position().top;
$(window).scroll(function () {
var windowTop = $(window).scrollTop();
if (windowTop >= elTop) {
el.addClass('isSticky');
} else {
el.removeClass('isSticky');
}
This answer might not be 100% pragmatic, due to current lack of support, but soon you will be able to use the position: sticky property of CSS, currently supported in Firefox and prefixed in Safari/iOS (Caniuse).
The feature was previously enabled in Chrome, but then subsequently removed in the interest of re-doing it more efficiently.
html, body {
margin: 0;
}
body * {
margin: 20px;
padding: 20px;
}
.header {
margin: 0;
padding: 20px;
background: #000;
}
.header span {
display: block;
color: #fff;
margin: 0 auto;
text-align: center;
padding: 0;
}
.placeholder {
border: 1px solid black;
margin: 0 auto;
text-align: center;
height: 300px;
}
.slider {
background: #006264;
color: white;
font-weight: bold;
margin: 0 auto;
position: sticky;
top: 0px;
}
<div class="header"><span>This is a header</span></div>
<div class="placeholder">This div holds place</div>
<div class="slider">This should slide up and then stick.</div>
<div class="placeholder">This div holds place</div>
<div class="placeholder">This div holds place</div>
<div class="placeholder">This div holds place</div>
<div class="placeholder">This div holds place</div>

Categories