diff --git a/site/src/esl-popup/esl-d-popup-game.less b/site/src/esl-popup/esl-d-popup-game.less new file mode 100644 index 000000000..f1a465de7 --- /dev/null +++ b/site/src/esl-popup/esl-d-popup-game.less @@ -0,0 +1,35 @@ +esl-d-popup-game { + position: relative; + display: inline-block; + + border: 1px solid #000; + + min-height: 300px; + height: 95%; + width: 100%; + + &::before, &::after { + position: absolute; + font-size: calc(min(100vh, 300px) / 3); + display: inline-block; + opacity: 0.3; + } + + &::before { + content: '🌒'; + } + + &::after { + content: '🪐'; + bottom: 0; + right: 0; + } +} + +.game-popup-trigger { + position: absolute; + padding: 2px 6px; + border-radius: 50%; + background-color: #bce0ff45; + font-size: 2rem; +} diff --git a/site/src/esl-popup/esl-d-popup-game.ts b/site/src/esl-popup/esl-d-popup-game.ts index 1e31242bb..2f3b225c2 100644 --- a/site/src/esl-popup/esl-d-popup-game.ts +++ b/site/src/esl-popup/esl-d-popup-game.ts @@ -1,12 +1,17 @@ import {ESLBaseElement} from '@exadel/esl/modules/esl-base-element/core'; -import {attr, boolAttr, decorate, listen, memoize} from '@exadel/esl/modules/esl-utils/decorators'; -import {rafDecorator} from '@exadel/esl/modules//esl-utils/async'; +import {ESLPopup} from '@exadel/esl/modules/esl-popup/core'; +import {attr, boolAttr, listen, memoize} from '@exadel/esl/modules/esl-utils/decorators'; +import {ESLResizeObserverTarget} from '@exadel/esl/modules/esl-utils/dom'; import {ESLTraversingQuery} from '@exadel/esl/modules/esl-traversing-query/core'; +import type {Point} from '@exadel/esl/modules/esl-utils/dom'; + /** Fun component like a game for checking popup positioning on edge cases */ export class ESLDemoPopupGame extends ESLBaseElement { static override is = 'esl-d-popup-game'; + protected currentCoords: Point; + /** Trigger selector */ @attr() public trigger: string; /** Marker shows that element is in dragging mode */ @@ -17,62 +22,65 @@ export class ESLDemoPopupGame extends ESLBaseElement { if (this.trigger) return ESLTraversingQuery.first(this.trigger, this) as HTMLElement | undefined; } - protected updateTriggerPosition(dX: number, dY: number): void { - if (!this.$trigger) return; - - const limitX = this.clientWidth - this.$trigger.clientWidth; - const limitY = this.clientHeight - this.$trigger.clientHeight; + protected override connectedCallback(): void { + this.setTriggerDefaultPosition(); + super.connectedCallback(); + } - let x = parseInt(this.$trigger.style.left, 10) || 0; - let y = parseInt(this.$trigger.style.top, 10) || 0; - x += dX; - y += dY; - x = Math.max(0, Math.min(x, limitX)); - y = Math.max(0, Math.min(y, limitY)); + protected setTriggerDefaultPosition(): void { + const center = this.calculateCenter(); + this.updateTriggerPosition(center.x, center.y); + } - this.$trigger.style.top = `${y}px`; - this.$trigger.style.left = `${x}px`; + protected calculateCenter(): Point { + return {x: this.clientWidth / 2, y: this.clientHeight / 2}; } - @listen({event: 'mousedown'}) + @listen({event: 'mousedown', target: ($this: any) => $this.$trigger}) protected _onDragStart(event: MouseEvent): void { this.dragging = true; this.$$on(this._onDragging); this.$$on(this._onDragStop); event.preventDefault(); + this.currentCoords = {x: event.clientX, y: event.clientY}; } - @listen({auto: false, event: 'mouseleave mouseup'}) - protected _onDragStop(): void { - this.dragging = false; - this.$$off(this._onDragging); - this.$$off(this._onDragStop); - } - - @listen({auto: false, event: 'mousemove'}) - @decorate(rafDecorator) + @listen({auto: false, event: 'mousemove', target: window}) protected _onDragging(event: MouseEvent): void { if (!this.dragging || !this.$trigger) return; - - const {movementX, movementY} = event; - this.updateTriggerPosition(movementX, movementY); + const {clientX, clientY} = event; + const dX = clientX - this.currentCoords.x; + const dY = clientY - this.currentCoords.y; + this.currentCoords = {x: clientX, y: clientY}; + this.updateTriggerPosition(dX, dY); event.preventDefault(); } - @listen({event: 'wheel'}) - @decorate(rafDecorator) - protected _onWheel(event: WheelEvent): void { + protected updateTriggerPosition(dX: number, dY: number): void { if (!this.$trigger) return; + this.$$fire(ESLPopup.prototype.REFRESH_EVENT); + const limitX = this.clientWidth - this.$trigger.clientWidth; + const limitY = this.clientHeight - this.$trigger.clientHeight; - let {deltaX, deltaY} = event; - if (event.shiftKey) [deltaY, deltaX] = [deltaX, deltaY]; - const {clientWidth, clientHeight} = this; - this.style.setProperty('--_width', `${clientWidth + deltaX / 10}px`); - this.style.setProperty('--_height', `${clientHeight + deltaY / 10}px`); + let x = (parseInt(this.$trigger.style.left, 10) || 0) + dX; + let y = (parseInt(this.$trigger.style.top, 10) || 0) + dY; + x = Math.max(0, Math.min(x, limitX)); + y = Math.max(0, Math.min(y, limitY)); - this.updateTriggerPosition(0, 0); + this.$trigger.style.left = `${x}px`; + this.$trigger.style.top = `${y}px`; + } - event.preventDefault(); + @listen({auto: false, event: 'mouseup', target: window}) + protected _onDragStop(): void { + this.dragging = false; + this.$$off(this._onDragging); + this.$$off(this._onDragStop); + } + + @listen({event: 'resize', target: ESLResizeObserverTarget.for}) + protected _onResize(): void { + this.updateTriggerPosition(0, 0); } } diff --git a/site/src/localdev.less b/site/src/localdev.less index 3c2e47907..0064e0897 100644 --- a/site/src/localdev.less +++ b/site/src/localdev.less @@ -39,6 +39,7 @@ @import './collection-grid/collection-grid.less'; @import './esl-share/esl-share.less'; @import './esl-events-demo/esl-events-demo.less'; +@import './esl-popup/esl-d-popup-game.less'; @import './back-link/back-link'; diff --git a/site/views/draft/popup-game.njk b/site/views/draft/popup-game.njk index 9cd39342e..c5c2e9f2d 100644 --- a/site/views/draft/popup-game.njk +++ b/site/views/draft/popup-game.njk @@ -12,162 +12,103 @@ aside: - esl-trigger --- -

This cool game will let you play with a popup.

Drag a trigger element and watch how the popup and its arrow move. Use the mouse wheel to resize the container. Use shift to switch the resize direction.

The green area on the edges of the container is determined by the offsetContainer value and means the safe zone in which popup can't come. The red zone on the edges of popup demonstrates the limits of arrow placement.

-

Horizontal, direction: LTR

-
-
- - - 🚀 - - -
-
- - - - -
-

Horizontal, direction: RTL

-
-
-
- - - 🚀 - - -
-
- - - - -
-

Vertical

-
-
-
- - - 🚀 - - -
+
+ + + + + + + + + + + + + + + + + + + + + + + +
- - - -