mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-02-01 12:01:14 -05:00
feat: drag plugin modal
This commit is contained in:
parent
385c5bfa09
commit
7d147b06e3
3 changed files with 119 additions and 0 deletions
79
libs/plugins-runtime/src/lib/drag-handler.spec.ts
Normal file
79
libs/plugins-runtime/src/lib/drag-handler.spec.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { expect, describe, vi } from 'vitest';
|
||||||
|
import { dragHandler } from './drag-handler.js';
|
||||||
|
|
||||||
|
describe('dragHandler', () => {
|
||||||
|
let element: HTMLElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
element = document.createElement('div');
|
||||||
|
document.body.appendChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.removeChild(element);
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should attach mousedown event listener to the element', () => {
|
||||||
|
const addEventListenerMock = vi.spyOn(element, 'addEventListener');
|
||||||
|
|
||||||
|
dragHandler(element);
|
||||||
|
|
||||||
|
expect(addEventListenerMock).toHaveBeenCalledWith(
|
||||||
|
'mousedown',
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update element transform on mousemove', () => {
|
||||||
|
const mouseDownEvent = new MouseEvent('mousedown', {
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
dragHandler(element);
|
||||||
|
|
||||||
|
element.dispatchEvent(mouseDownEvent);
|
||||||
|
|
||||||
|
const mouseMoveEvent = new MouseEvent('mousemove', {
|
||||||
|
clientX: 150,
|
||||||
|
clientY: 150,
|
||||||
|
});
|
||||||
|
document.dispatchEvent(mouseMoveEvent);
|
||||||
|
|
||||||
|
expect(element.style.transform).toBe('translate(50px, 50px)');
|
||||||
|
|
||||||
|
const mouseMoveEvent2 = new MouseEvent('mousemove', {
|
||||||
|
clientX: 200,
|
||||||
|
clientY: 200,
|
||||||
|
});
|
||||||
|
document.dispatchEvent(mouseMoveEvent2);
|
||||||
|
|
||||||
|
expect(element.style.transform).toBe('translate(100px, 100px)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove event listeners on mouseup', () => {
|
||||||
|
const removeEventListenerMock = vi.spyOn(document, 'removeEventListener');
|
||||||
|
|
||||||
|
const mouseDownEvent = new MouseEvent('mousedown', {
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
dragHandler(element);
|
||||||
|
|
||||||
|
element.dispatchEvent(mouseDownEvent);
|
||||||
|
|
||||||
|
const mouseUpEvent = new MouseEvent('mouseup');
|
||||||
|
document.dispatchEvent(mouseUpEvent);
|
||||||
|
|
||||||
|
expect(removeEventListenerMock).toHaveBeenCalledWith(
|
||||||
|
'mousemove',
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
expect(removeEventListenerMock).toHaveBeenCalledWith(
|
||||||
|
'mouseup',
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
32
libs/plugins-runtime/src/lib/drag-handler.ts
Normal file
32
libs/plugins-runtime/src/lib/drag-handler.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
export const dragHandler = (el: HTMLElement) => {
|
||||||
|
let currentTranslate = { x: 0, y: 0 };
|
||||||
|
let initialTranslate = { x: 0, y: 0 };
|
||||||
|
let initialClientPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
const handleMouseMove = (moveEvent: MouseEvent) => {
|
||||||
|
const { clientX: moveX, clientY: moveY } = moveEvent;
|
||||||
|
const deltaX = moveX - initialClientPosition.x + initialTranslate.x;
|
||||||
|
const deltaY = moveY - initialClientPosition.y + initialTranslate.y;
|
||||||
|
|
||||||
|
currentTranslate = { x: deltaX, y: deltaY };
|
||||||
|
|
||||||
|
el.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
initialClientPosition = { x: e.clientX, y: e.clientY };
|
||||||
|
initialTranslate = { x: currentTranslate.x, y: currentTranslate.y };
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
el.addEventListener('mousedown', handleMouseDown);
|
||||||
|
|
||||||
|
return handleMouseUp;
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ const closeSvg = `
|
||||||
<svg width="16" height="16"xmlns="http://www.w3.org/2000/svg" fill="none"><g class="fills"><rect rx="0" ry="0" width="16" height="16" class="frame-background"/></g><g class="frame-children"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" class="fills"/><g class="strokes"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" style="fill: none; stroke-width: 1; stroke: rgb(143, 157, 163); stroke-opacity: 1; stroke-linecap: round;" class="stroke-shape"/></g></g></svg>`;
|
<svg width="16" height="16"xmlns="http://www.w3.org/2000/svg" fill="none"><g class="fills"><rect rx="0" ry="0" width="16" height="16" class="frame-background"/></g><g class="frame-children"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" class="fills"/><g class="strokes"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" style="fill: none; stroke-width: 1; stroke: rgb(143, 157, 163); stroke-opacity: 1; stroke-linecap: round;" class="stroke-shape"/></g></g></svg>`;
|
||||||
|
|
||||||
import type { PenpotTheme } from '@penpot/plugin-types';
|
import type { PenpotTheme } from '@penpot/plugin-types';
|
||||||
|
import { dragHandler } from './drag-handler.js';
|
||||||
|
|
||||||
export class PluginModalElement extends HTMLElement {
|
export class PluginModalElement extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -10,6 +11,7 @@ export class PluginModalElement extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper: HTMLElement | null = null;
|
#wrapper: HTMLElement | null = null;
|
||||||
|
#dragEvents: ReturnType<typeof dragHandler> | null = null;
|
||||||
|
|
||||||
setTheme(theme: PenpotTheme) {
|
setTheme(theme: PenpotTheme) {
|
||||||
if (this.#wrapper) {
|
if (this.#wrapper) {
|
||||||
|
@ -17,6 +19,10 @@ export class PluginModalElement extends HTMLElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.#dragEvents?.();
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
const title = this.getAttribute('title');
|
const title = this.getAttribute('title');
|
||||||
const iframeSrc = this.getAttribute('iframe-src');
|
const iframeSrc = this.getAttribute('iframe-src');
|
||||||
|
@ -33,6 +39,7 @@ export class PluginModalElement extends HTMLElement {
|
||||||
|
|
||||||
this.#wrapper = document.createElement('div');
|
this.#wrapper = document.createElement('div');
|
||||||
this.#wrapper.classList.add('wrapper');
|
this.#wrapper.classList.add('wrapper');
|
||||||
|
this.#dragEvents = dragHandler(this.#wrapper);
|
||||||
|
|
||||||
const header = document.createElement('div');
|
const header = document.createElement('div');
|
||||||
header.classList.add('header');
|
header.classList.add('header');
|
||||||
|
@ -149,6 +156,7 @@ export class PluginModalElement extends HTMLElement {
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-inline-end: var(--spacing-4);
|
margin-inline-end: var(--spacing-4);
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
|
|
Loading…
Add table
Reference in a new issue