mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-21 06:02:34 -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>`;
|
||||
|
||||
import type { PenpotTheme } from '@penpot/plugin-types';
|
||||
import { dragHandler } from './drag-handler.js';
|
||||
|
||||
export class PluginModalElement extends HTMLElement {
|
||||
constructor() {
|
||||
|
@ -10,6 +11,7 @@ export class PluginModalElement extends HTMLElement {
|
|||
}
|
||||
|
||||
#wrapper: HTMLElement | null = null;
|
||||
#dragEvents: ReturnType<typeof dragHandler> | null = null;
|
||||
|
||||
setTheme(theme: PenpotTheme) {
|
||||
if (this.#wrapper) {
|
||||
|
@ -17,6 +19,10 @@ export class PluginModalElement extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.#dragEvents?.();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const title = this.getAttribute('title');
|
||||
const iframeSrc = this.getAttribute('iframe-src');
|
||||
|
@ -33,6 +39,7 @@ export class PluginModalElement extends HTMLElement {
|
|||
|
||||
this.#wrapper = document.createElement('div');
|
||||
this.#wrapper.classList.add('wrapper');
|
||||
this.#dragEvents = dragHandler(this.#wrapper);
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.classList.add('header');
|
||||
|
@ -149,6 +156,7 @@ export class PluginModalElement extends HTMLElement {
|
|||
font-weight: var(--font-weight-bold);
|
||||
margin: 0;
|
||||
margin-inline-end: var(--spacing-4);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
iframe {
|
||||
|
|
Loading…
Add table
Reference in a new issue