1import {html, LitElement} from 'lit';
2import {customElement, property} from 'lit/decorators.js';
3
4import {DeviceDragZone} from './device-dragzone.js';
5import {simulationState} from './device-observer.js';
6
7@customElement('ns-device-dropzone')
8export class DeviceDropZone extends LitElement {
9  @property({type: String, attribute: 'serial'}) serial: string = '';
10
11  @property({type: String, attribute: 'type'}) type: string = '';
12
13  constructor() {
14    super();
15    this.addEventListener('drop', this.handleDrop);
16    this.addEventListener('drag', this.handleDragOver);
17    this.addEventListener('dragenter', DeviceDropZone.handleDragEnter);
18    this.addEventListener('dragleave', DeviceDropZone.handleDragLeave);
19    this.addEventListener('dragover', this.handleDragOver);
20  }
21
22  static handleDragEnter(ev: DragEvent) {
23    ev.preventDefault();
24  }
25
26  static handleDragLeave(ev: DragEvent) {
27    ev.preventDefault();
28  }
29
30  slottedDropZone() {
31    // Returns the #dropzone div inside the slotted children, where devices are
32    // stored. note: needs better checking when not the first element.
33    const slot = this.shadowRoot?.querySelector('slot');
34    return slot?.assignedElements({flatten: true})[0];
35  }
36
37  handleDrop(ev: DragEvent) {
38    ev.preventDefault();
39    const dropzone = this.slottedDropZone();
40    if (dropzone) {
41      const draggedElement = DeviceDragZone.dragged as HTMLElement;
42      if (ev.dataTransfer?.effectAllowed === 'move') {
43        draggedElement.parentNode?.removeChild(draggedElement);
44        draggedElement.style.opacity = '';
45        dropzone.appendChild(draggedElement);
46      } else {
47        // copy
48        dropzone.appendChild(draggedElement.cloneNode(true));
49      }
50      const dropped = dropzone.lastChild as HTMLElement;
51      if (dropped) {
52        const rect = (dropzone as HTMLElement).getBoundingClientRect();
53        dropped.setAttribute('action', 'move');
54        dropped.style.position = 'absolute';
55        dropped.style.left = `${ev.clientX - rect.left}px`;
56        dropped.style.top = `${ev.clientY - rect.top}px`;
57        dropped.style.opacity = `1.0`;
58        // Patch the position of a dropped element
59        let id = dropped.getElementsByTagName('ns-cube-sprite')
60                     .item(0)
61                     ?.getAttribute('id');
62        if (id === undefined) {
63          id = dropped.getElementsByTagName('ns-pyramid-sprite')
64                   .item(0)
65                   ?.getAttribute('id');
66        }
67        if (id === undefined || id === null) {
68          id = '';
69        }
70        simulationState.handleDrop(
71            id, (ev.clientX - rect.left) / 100, (ev.clientY - rect.top) / 100);
72      }
73    }
74  }
75
76  handleDragOver(ev: DragEvent) {
77    ev.preventDefault();
78    this.slottedDropZone();
79  }
80
81  render() {
82    return html`<slot></slot>`;
83  }
84}
85