3

I am defining a popup dialog box component which will allow a user to input some data to place on the page. The end result is for it to appear when a button on the screen is clicked, and also able to be dragged around the page.

I don't have much of this component yet, here is the code for it:

//edit-global-names-dialog-box.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'edit-global-names-dialog-box',
    templateUrl: './edit-global-names-dialog-box.component.html',
    styleUrls: ['./edit-global-names-dialog-box.component.css']
})
export class EditGlobalNamesDialogBoxComponent implements OnInit{

    constructor() {}

    ngOnInit() {
    }
}

//edit-global-names-dialog-box.component.html

<div id="dialog-box-container">
    <div id="header">

    </div>
    <div id="content">

    </div>
    <div id="footer">

    </div>
</div>

//edit-global-names-dialog-box.component.css

#dialog-box-container {
    height: 12%;
    width: 25%;
    z-index: 2 !important;
    position: absolute;
    background-color: lightgrey;
    right: 50%;
    bottom: 50%;
    transform: translate(45%,-50.1%);
    -moz-box-shadow: 8px 8px 8px #d9d9d9;
    -webkit-box-shadow: 8px 8px 8px #d9d9d9;
    box-shadow: 8px 8px 8px #d9d9d9; 
}

#header {
    border: 0.5px solid dimgrey;
    border-bottom: none;
    height: 20%;
}

#content {
    border: 0.5px solid dimgrey;
    border-bottom: none;
    height: 50%;
}

#footer {
    border: 0.5px solid dimgrey;
    height: 26%;
}

You can see that it's just a template at the moment, a div that appears onto of all other page content, has a shadow and is centered in the page.

Want I want now is to implement a feature whereby when the user clicks and drags on the header div of the dialog box, the entire dialog box is moved around the page.

I have taken a look at a few questions posted previously: Using JS to move a div around the page Make Div Draggable using CSS These suggest pure javascript, or JQuery ways of doing things which I am not sure would mesh well with angular.

How can I enable my component to be dragged around the page?

Community
  • 1
  • 1
JavascriptLoser
  • 1,853
  • 5
  • 34
  • 61
  • 1
    One solution is to use Javascript to grab the onmousemove event, then setting your popup div to the mouse's position. It's both complicated and easy at the same time, if you want me to show you an example, might take a few minutes for me to give it to you though. – Simon Hyll Feb 20 '17 at 00:38
  • @SimonHyll if you could post an example as an answer, that would be great. – JavascriptLoser Feb 20 '17 at 00:41

3 Answers3

1

Alright here's the promised example of a draggable popup using Javascript. Note that I'm also using JQuery for this to make it a little easier, but all the functions used are essentially just wrappers for "regular" Javascript functions, so this can quite easily be converted to "regular" javascript, I just couldn't be bothered. :)

If you create your content within the div with id "myPopup" and just change "myPopup" id to whatever you want you can pretty much copy paste this assuming you have jquery available. Ofc there are ways to improve it but this should get you started.

function displayPopup() {
$("#myPopup").toggleClass("popupVisible");
}

$(document).ready(function() {

var isDragging = false;
$("#myPopup")
.mousedown(function() {
    isDragging = false;
$("#myPopup").addClass("clicked")
})
.mousemove(function() {
    isDragging = true;
if($("#myPopup").hasClass("clicked")) {
$("#myPopup").css("left", event.pageX - 20);
$("#myPopup").css("top", event.pageY - 20);
}
 })
.mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    if (!wasDragging) {

    }
$("#myPopup").removeClass("clicked")
});


});
<html>  
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<style>
#myPopup {
position: absolute;
width: 5cm;
height: 5cm;
background: #00ff00;
top: calc(50% - 2.5cm);
left: calc(50% - 2.5cm);
display: none;
}
.popupVisible {
display: block !important;
}
</style>
</head>
<body>
<a href="#" onclick="displayPopup()">Here's the popup!</a>

<div id="myPopup">
</div>

    </body>
</html>
Simon Hyll
  • 3,265
  • 3
  • 24
  • 44
  • Can I use these functions within the angular class? I'd like the class to be an expert with respect to moving itself – JavascriptLoser Feb 20 '17 at 01:14
  • I honestly don't know, I haven't used angular before, sorry. But you should be able to take the theory from the above code and convert it fairly easily into angular code by just googling what the functions would be in angular (e.g. google "how do i grab mouse move event in angular"). Otherwise I'd suggest not using angular, there's little reason with it, if you lack power I recommend creating a Cordova + Crosswalk project, it gives you near infinite power and you use javascript which is easier to get help with. – Simon Hyll Feb 20 '17 at 01:22
  • Unfortunately I'm too far into my product to consider anything other than angular at this point in time. I'll try to translate your code and edit my OP with results. – JavascriptLoser Feb 20 '17 at 02:00
  • So, I can't get this to work on typescript. I get the error `Property 'pageX' does not exist on type 'Event'.` – JavascriptLoser Feb 20 '17 at 03:44
  • In typescript it's event.clientX and event.clientY – Simon Hyll Feb 20 '17 at 21:38
  • I still get the same issue, `Property 'clientX' does not exist on type 'Event'` – JavascriptLoser Feb 21 '17 at 00:02
  • Then I don't know what the problem is, sorry. :/ Check this issue, might help: http://stackoverflow.com/questions/36993248/rxjs-typescript-throws-property-clientx-does-not-exist-on-type – Simon Hyll Feb 21 '17 at 01:22
1

So the other answer is a great solution for javascript/Jquery, but I've managed to do this in a more angular-y/typescript-y way, with no JQuery. Here it is:

//edit-global-names-dialog-box.component.html

<div id="dialog-box-container">
    <div id="header"
        (mousedown)="mousedown($event)" 
        (mousemove)="mousemove($event)" 
        (mouseup)="mouseup($event)"
    >
        <div id="title-div">
            <h5 id="title">Edit Global Name</h5>
        </div>
    </div>
    <div id="content">
        <div id="label-area">

        </div>
            <input type="text" id="text-box">
    </div>
    <div id="footer">
        <div id="ok-button-div">
            <button type="button" id="ok-button">OK</button>
        </div>
        <div id="cancel-button-div">
            <button type="button" id="cancel-button">Cancel</button>
        </div>
    </div>
</div>

CSS isn't that important but I'll link it in a fiddle with the HTML if you want to see it, I don't want it taking up space in my answer: FIDDLE

Here's the angular component:

//edit-global-names-dialog-box.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'edit-global-names-dialog-box',
    templateUrl: './edit-global-names-dialog-box.component.html',
    styleUrls: ['./edit-global-names-dialog-box.component.css']
})
export class EditGlobalNamesDialogBoxComponent implements OnInit{

    private mousePosition : PIXI.Point;
    private dragOffset;
    private isDown;
    private dialogBoxDiv;

    constructor() {
        this.mousePosition = new PIXI.Point();
        this.isDown = false;
        this.dragOffset = [0, 0];
    }

    ngOnInit() {
        this.dialogBoxDiv = document.getElementById('dialog-box-container');
    }

    mousedown($event){
        this.isDown = true;
        this.dragOffset = [ 
            this.dialogBoxDiv.offsetLeft - $event.clientX,
            this.dialogBoxDiv.offsetTop - $event.clientY
        ]
    }

    mouseup($event){
        this.isDown = false;
    }

    mousemove($event){
        $event.preventDefault();

        if (this.isDown){
             var mousePosition = {
                 x : $event.clientX,
                 y : $event.clientY
             };

             this.dialogBoxDiv.style.left = (mousePosition.x + this.dragOffset[0]) + 'px';
             this.dialogBoxDiv.style.top  = (mousePosition.y + this.dragOffset[1]) + 'px';
        }
    }
}

It's a little jittery, but it works mostly well. If anyone has any improvements that can be made, please let me know.

JavascriptLoser
  • 1,853
  • 5
  • 34
  • 61
  • What is PIXI? Can we do it without it? – Brackets Dec 21 '20 at 12:44
  • I know this is an old post, but moving the mouse events to the container instead of the header solves the jittery issue. – Tim Southard Oct 05 '21 at 11:38
  • PIXI comes from PixiJS which is an open source library for 2D and interactive animations in a web browser: https://pixijs.download/dev/docs/PIXI.Point.html – Timtim Nov 05 '21 at 20:20
0

Inspired by the top answer, but I used:

@ViewChild('container') container: ElementRef;

as a reference to the container of the content that would be tracking the mouse, and:

@HostListener('document:mousemove', ['$event'])
handleMouseMove($event: MouseEvent) {
    this.container.nativeElement.style.top = ($event.pageY + 20) + "px";
    this.container.nativeElement.style.left = ($event.clientX - 50) + "px";
}
Sean
  • 14,359
  • 13
  • 74
  • 124