From 56122ff6eb6e017930b5fcf92044a98046eb22c0 Mon Sep 17 00:00:00 2001 From: Rustin Date: Tue, 6 Aug 2024 18:15:59 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20draggable=20sdk=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/sdk/src/lib/dom/index.ts | 78 +++++++++++++++++++++++-------- packages/sdk/src/lib/utils.ts | 4 ++ packages/sdk/src/lib/wallet.tsx | 20 +++++++- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/packages/sdk/src/lib/dom/index.ts b/packages/sdk/src/lib/dom/index.ts index 519679a6..03f6e7d4 100644 --- a/packages/sdk/src/lib/dom/index.ts +++ b/packages/sdk/src/lib/dom/index.ts @@ -1,21 +1,30 @@ import { CONTROLLER_CONTAINER_ID, IFRAME_CONTAINER_ID } from "../constants" import { HibitEnv, HibitIdPage } from "../types" -import { getHibitIdUrl } from "../utils" +import { clamp, getHibitIdUrl } from "../utils" import './index.css' export class HibitIdController { private container: HTMLDivElement private button: HTMLButtonElement private open = false + private dragging = false + private mouseDownStartAt = 0 + private onClick: () => void + private onMove: (x: number, y: number) => void + + constructor(onClick: () => void, onMove: (x: number, y: number) => void) { + this.onClick = onClick + this.onMove = onMove - constructor(onClick: () => void) { const existed = document.getElementById(CONTROLLER_CONTAINER_ID) existed?.remove() const container = document.createElement('div') container.id = CONTROLLER_CONTAINER_ID const button = document.createElement('button') button.classList.add('hidden') - button.onclick = this.getClickHandler(onClick) + button.onmousedown = this.handleMouseDown + window.addEventListener('mouseup', this.handleMouseUp) + window.addEventListener('mousemove', this.handleMouseMove) container.appendChild(button) document.body.appendChild(container) @@ -40,12 +49,35 @@ export class HibitIdController { public destroy = () => { this.container?.remove() + window.removeEventListener('mouseup', this.handleMouseUp) + window.removeEventListener('mousemove', this.handleMouseMove) + } + + private handleClick = () => { + this.onClick?.() + this.setOpen(!this.open) } - private getClickHandler = (onClick: () => void) => { - return () => { - onClick() - this.setOpen(!this.open) + private handleMouseDown = () => { + this.dragging = true + this.mouseDownStartAt = Date.now() + } + + private handleMouseUp = () => { + this.dragging = false + if (Date.now() - this.mouseDownStartAt < 200) { + this.handleClick() + } + } + + private handleMouseMove = (e: MouseEvent) => { + if (this.dragging) { + const rect = this.getBoundingRect() + const right = clamp(0, window.innerWidth - rect.right - e.movementX, window.innerWidth - rect.width) + const bottom = clamp(0, window.innerHeight - rect.bottom - e.movementY, window.innerHeight - rect.height) + this.container.style.right = `${right}px` + this.container.style.bottom = `${bottom}px` + this.onMove(e.clientX, e.clientY) } } } @@ -78,24 +110,32 @@ export class HibitIdIframe { return this._visible } + public updateStyle = (style: Record) => { + Object.keys(style).forEach((key) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this.container.style[key] = style[key] + }) + } + public show = (options: { fullscreen: boolean, style: Record }) => { if (options.fullscreen) { - this.container.style.top = '0' - this.container.style.left = '0' - this.container.style.width = '100%' - this.container.style.height = '100%' - this.container.style.bottom = 'unset' - this.container.style.right = 'unset' + this.updateStyle({ + top: '0', + left: '0', + width: '100%', + height: '100%', + bottom: 'unset', + right: 'unset', + }) } else { - this.container.style.top = 'unset' - this.container.style.left = 'unset' - Object.keys(options.style).forEach((key) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this.container.style[key] = options.style[key] + this.updateStyle({ + top: 'unset', + left: 'unset', + ...options.style }) } this._visible = true diff --git a/packages/sdk/src/lib/utils.ts b/packages/sdk/src/lib/utils.ts index b3cb600a..8c7a9062 100644 --- a/packages/sdk/src/lib/utils.ts +++ b/packages/sdk/src/lib/utils.ts @@ -19,3 +19,7 @@ export const getHibitIdUrl = (env: HibitEnv, initialPage: HibitIdPage) => { url = `${url}/${initialPage === 'login' ? 'login' : ''}` return url } + +export const clamp = (value: number, min: number, max: number) => { + return Math.min(Math.max(value, min), max) +} diff --git a/packages/sdk/src/lib/wallet.tsx b/packages/sdk/src/lib/wallet.tsx index c68b2d6d..e63fa922 100644 --- a/packages/sdk/src/lib/wallet.tsx +++ b/packages/sdk/src/lib/wallet.tsx @@ -3,6 +3,7 @@ import { RPC_SERVICE_NAME } from './constants'; import { HibitIdController, HibitIdIframe } from './dom'; import { AccountsChangedRequest, BridgePromise, ChainChangedRequest, ChainInfo, ConnectResponse, GetBalanceRequest, GetBalanceResponse, HibitEnv, HibitIdEventHandlerMap, HibitIdPage, LoginChangedRequest, SignMessageResponse, TransferRequest, TransferResponse, WalletAccount } from './types'; import { ClientExposeRPCMethod, HibitIdChainId, HibitIdExposeRPCMethod } from './enums'; +import { clamp } from './utils'; const LOGIN_SESSION_KEY = 'hibit-id-session' @@ -31,7 +32,7 @@ export class HibitIdWallet { } this.prepareIframe().then(() => { if (this._hasSession) { - this._controller = new HibitIdController(this.toggleIframe) + this._controller = new HibitIdController(this.toggleIframe, this.handleControllerMove) } }) } @@ -208,6 +209,21 @@ export class HibitIdWallet { this._rpc = rpc } + private handleControllerMove = (x: number, y: number) => { + if (!this._iframe) return + const controllerRect = this._controller?.getBoundingRect() + const maxRight = window.innerWidth - (controllerRect?.width ?? 0) + const maxBottom = window.innerHeight - (controllerRect?.height ?? 0) + const right = clamp(0, controllerRect ? (window.innerWidth - controllerRect.right) : 50, maxRight) + const bottom = clamp(0, controllerRect ? (window.innerHeight - controllerRect.top + 20) : 50, maxBottom) + this._iframe.updateStyle({ + // width: '332px', + // height: '502px', + right: `${right}px`, + bottom: `${bottom}px` + }) + } + private showIframe = (fullscreen?: boolean) => { if (!this._iframe) return if (fullscreen) { @@ -240,7 +256,7 @@ export class HibitIdWallet { this.showIframe(true) } else { if (!this._controller) { - this._controller = new HibitIdController(this.toggleIframe) + this._controller = new HibitIdController(this.toggleIframe, this.handleControllerMove) } if (this._hasSession) { return