Skip to content

Commit

Permalink
feat: Panel popping up & withdrawing
Browse files Browse the repository at this point in the history
  • Loading branch information
NriotHrreion committed Sep 1, 2024
1 parent f7f2050 commit ea54428
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 14 deletions.
6 changes: 3 additions & 3 deletions src/render/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export class Render extends Disposable {
}

private _init() {
this._ctx.fillStyle = "#dddddd";
this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
this._ctx.fill();
// this._ctx.fillStyle = "#dddddd";
// this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
// this._ctx.fill();
}

/** @todo */
Expand Down
2 changes: 1 addition & 1 deletion src/style/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
body {
margin: 0;
padding: 0;
overflow: hidden;

main#root {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
overflow: hidden;

canvas.motive-canvas {
position: absolute;
Expand Down
15 changes: 15 additions & 0 deletions src/ui/button/buttonGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, ComponentLike, IComponent } from "@/ui/ui";
import { Emitter, Event, Listener } from "@/common/event";
import { Switcher, SwitcherOptions, type SwitcherEvent } from "@/ui/switcher/switcher";
import { Button, type ButtonOptions } from "./button";

import "./button.less";
Expand All @@ -18,6 +19,7 @@ const defaultOptions: ButtonGroupOptions = {

interface IButtonGroup extends IComponent {
addButton(options: ButtonOptions): Button
addSwitcher(options: SwitcherOptions): Switcher

onDidChange: Event<Button>
}
Expand Down Expand Up @@ -50,6 +52,19 @@ export class ButtonGroup extends Component<HTMLDivElement, ButtonGroupOptions> i
return button;
}

public addSwitcher(options: SwitcherOptions, onDidChange?: Listener<SwitcherEvent>): Switcher {
const switcher = new Switcher(this, options);
switcher.element.classList.add("grouped");
if(this._options.variant) switcher.variant = this._options.variant;
if(this._options.disabled) switcher.disabled = true;

if(onDidChange) switcher.onDidChange(onDidChange);

this._register(switcher);
this._onDidChange.fire(switcher);
return switcher;
}

public get onDidChange() {
return this._onDidChange.event;
}
Expand Down
8 changes: 8 additions & 0 deletions src/ui/panel/panel.less
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// Panel Properties
--mt-panel-border-radius: 12px;
--mt-panel-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--mt-panel-transition-fn: ease;
--mt-panel-transition-duration: .3s;
}

.panel {
Expand All @@ -21,6 +23,12 @@
border-radius: var(--mt-panel-border-radius);
overflow: hidden;
box-shadow: var(--mt-panel-shadow);
transition: transform var(--mt-panel-transition-fn) var(--mt-panel-transition-duration);
transform: translateY(100%);

&.panel-popped-up {
transform: translateY(0);
}

&-toolbar {
display: flex;
Expand Down
82 changes: 73 additions & 9 deletions src/ui/panel/panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Circle, RotateCw, Settings, Spline } from "lucide";
import { Box, Circle, Pin, RotateCw, Settings, Spline, X } from "lucide";

import { Component, ComponentLike, createElement, IComponent } from "@/ui/ui";
import { Button } from "@/ui/button/button";
Expand All @@ -23,10 +23,16 @@ interface IPanel extends IComponent {
}

export class Panel extends Component<HTMLDivElement, PanelOptions> implements IPanel {
private static readonly WITHDRAW_TIME = 1500; // ms
private _renderer: Render | null = null;
private _controller: AbortController | null = new AbortController();

private _isPoppedUp: boolean = false;
private _isPinned: boolean = false;
private _withdrawTimer: NodeJS.Timeout | null = null;

private _refreshButton: Button;

private _closeButton: Button;
private _switchers: Switcher[] = [];

public constructor(target: ComponentLike, _options?: PanelOptions) {
Expand All @@ -40,22 +46,73 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
if(this._options.width) this._element.style.width = `${this._options.width}px`;
if(this._options.height) this._element.style.height = `${this._options.height}px`;

// UI

const toolbar = createElement("div", this);
toolbar.classList.add("panel-toolbar");

const toolbarLeftGroup = new ButtonGroup(toolbar);
toolbarLeftGroup.addButton({ icon: Settings });
toolbarLeftGroup.addButton({ icon: Box });

toolbarLeftGroup.addButton({ icon: Settings }, () => {});
toolbarLeftGroup.addButton({ icon: Box }, () => {});
this._refreshButton = toolbarLeftGroup.addButton({ icon: RotateCw });

const toolbarRightGroup = new ButtonGroup(toolbar);
this._refreshButton = toolbarRightGroup.addButton({ icon: RotateCw });
toolbarRightGroup.addSwitcher({ icon: Pin }, ({ isActive }) => {
isActive ? this._pin() : this._unpin();
});
this._closeButton = toolbarRightGroup.addButton({ icon: X }, () => this._withdraw(false));

const switcherContainer = createElement("div", this);
switcherContainer.classList.add("panel-switcher-container");

this._switchers.push(new Switcher(switcherContainer, { id: "ball", text: "小球", icon: Circle, defaultValue: true }));
this._switchers.push(new Switcher(switcherContainer, { id: "board", text: "木板", icon: Box }));
this._switchers.push(new Switcher(switcherContainer, { id: "rope", text: "绳子", icon: Spline }));
this._switchers.push(new Switcher(switcherContainer, { id: "objects.ball", text: "小球", icon: Circle, defaultValue: true }));
this._switchers.push(new Switcher(switcherContainer, { id: "objects.board", text: "木板", icon: Box }));
this._switchers.push(new Switcher(switcherContainer, { id: "objects.rope", text: "绳子", icon: Spline }));

// Listeners

this._element.addEventListener("mouseenter", () => this._popUp(), { signal: this._controller.signal });
this._element.addEventListener("mouseleave", () => this._withdraw(), { signal: this._controller.signal });
}

private _popUp(): void {
if(this._withdrawTimer) {
clearTimeout(this._withdrawTimer);
this._withdrawTimer = null;
}
if(this._isPoppedUp) return;

this._isPoppedUp = true;
this._element.classList.add("panel-popped-up");
}

private _withdraw(shouldTimerSet: boolean = true): void {
if(!this._isPoppedUp || this._isPinned) return;

if(shouldTimerSet) {
this._withdrawTimer = setTimeout(() => {
this._isPoppedUp = false;
this._element.classList.remove("panel-popped-up");
}, Panel.WITHDRAW_TIME);
} else {
this._isPoppedUp = false;
this._element.classList.remove("panel-popped-up");
}
}

private _pin(): void {
if(this._isPinned) return;

this._isPinned = true;
this._popUp();
this._closeButton.disabled = true;
}

private _unpin(): void {
if(!this._isPinned) return;

this._isPinned = false;
this._closeButton.disabled = false;
}

public linkRenderer(renderer: Render) {
Expand All @@ -80,4 +137,11 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
);
}
}

public override dispose(): void {
this._controller.abort();
this._controller = null;

super.dispose();
}
}
3 changes: 2 additions & 1 deletion src/ui/switcher/switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface SwitcherOptions extends ButtonOptions {
defaultValue?: boolean
}

interface SwitcherEvent {
export interface SwitcherEvent {
id?: string
isActive: boolean
}
Expand All @@ -27,6 +27,7 @@ interface ISwitcher extends IButton {


export class Switcher extends Button implements ISwitcher {
// events
private _onDidChange = new Emitter<SwitcherEvent>();

public isActive: boolean = false;
Expand Down

0 comments on commit ea54428

Please sign in to comment.