From 7fca44f6c0a104c0f0f54629472c1e75ebbddad1 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Sun, 27 Feb 2022 22:59:30 +0100 Subject: [PATCH] added root path alias, icons, improved framework semantics --- src/Beat.ts | 10 +-- src/BeatGroup.ts | 10 +-- src/main.ts | 6 +- src/types.d.ts | 12 +++ .../BeatGroup/Beat/BeatUnit/BeatUnitView.ts | 25 +++--- src/ui/BeatGroup/Beat/BeatView.ts | 13 ++-- src/ui/BeatGroup/BeatGroupView.ts | 10 +-- .../BeatGroupSettingsView.ts | 20 ++--- .../BeatLikeLoopSettingsView.ts | 19 +++-- src/ui/BeatSettings/BeatSettingsView.ts | 18 ++--- src/ui/Root/Root.css | 19 +---- src/ui/Root/RootView.ts | 22 +++--- src/ui/Root/drawing.svg | 73 ----------------- src/ui/Root/rotate.svg | 78 ------------------- src/ui/UINode.ts | 20 ++++- .../Widgets/ActionButton/ActionButtonView.ts | 41 ++++++---- src/ui/Widgets/BoolBox/BoolBoxView.ts | 4 +- src/ui/Widgets/Icon/Icon.css | 8 ++ src/ui/Widgets/Icon/IconView.ts | 34 ++++++++ src/ui/Widgets/Icon/svgs/arrow-clockwise.svg | 4 + src/ui/Widgets/Icon/svgs/list.svg | 3 + src/ui/Widgets/Icon/svgs/trash.svg | 3 + src/ui/Widgets/NumberInput/NumberInputView.ts | 12 ++- tsconfig.json | 6 +- webpack.config.js | 21 ++--- 25 files changed, 207 insertions(+), 284 deletions(-) create mode 100644 src/types.d.ts delete mode 100644 src/ui/Root/drawing.svg delete mode 100644 src/ui/Root/rotate.svg create mode 100644 src/ui/Widgets/Icon/Icon.css create mode 100644 src/ui/Widgets/Icon/IconView.ts create mode 100644 src/ui/Widgets/Icon/svgs/arrow-clockwise.svg create mode 100644 src/ui/Widgets/Icon/svgs/list.svg create mode 100644 src/ui/Widgets/Icon/svgs/trash.svg diff --git a/src/Beat.ts b/src/Beat.ts index 3ceea57..15cacca 100644 --- a/src/Beat.ts +++ b/src/Beat.ts @@ -1,8 +1,8 @@ -import BeatUnit from "./BeatUnit"; -import {IPublisher, Publisher} from "./Publisher"; -import ISubscriber from "./Subscriber"; -import BeatLike from "./BeatLike"; -import {isPosInt} from "./utils"; +import BeatUnit from "@/BeatUnit"; +import {IPublisher, Publisher} from "@/Publisher"; +import ISubscriber from "@/Subscriber"; +import BeatLike from "@/BeatLike"; +import {isPosInt} from "@/utils"; export type BeatInitOptions = { timeSig?: { diff --git a/src/BeatGroup.ts b/src/BeatGroup.ts index 5a10083..649f73c 100644 --- a/src/BeatGroup.ts +++ b/src/BeatGroup.ts @@ -1,8 +1,8 @@ -import Beat, {BeatEvents, BeatInitOptions} from "./Beat"; -import {IPublisher, Publisher} from "./Publisher"; -import ISubscriber from "./Subscriber"; -import BeatLike from "./BeatLike"; -import {greatestCommonDivisor, isPosInt} from "./utils"; +import Beat, {BeatEvents, BeatInitOptions} from "@/Beat"; +import {IPublisher, Publisher} from "@/Publisher"; +import ISubscriber from "@/Subscriber"; +import BeatLike from "@/BeatLike"; +import {greatestCommonDivisor, isPosInt} from "@/utils"; type BeatGroupInitOptions = { barCount: number; diff --git a/src/main.ts b/src/main.ts index 41b0e55..896a5eb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ -import BeatGroup from "./BeatGroup"; -import RootView from "./ui/Root/RootView"; -import "./ui/global.css"; +import BeatGroup from "@/BeatGroup"; +import RootView from "@/ui/Root/RootView"; +import "@/ui/global.css"; const defaultSettings = { barCount: 2, diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..bc39be9 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,12 @@ +declare module "*.gif" { + const value: string; + export = value; +} +declare module "*.png" { + const value: string; + export = value; +} +declare module "*.svg" { + const value: string; + export = value; +} \ No newline at end of file diff --git a/src/ui/BeatGroup/Beat/BeatUnit/BeatUnitView.ts b/src/ui/BeatGroup/Beat/BeatUnit/BeatUnitView.ts index 627aa16..3753e70 100644 --- a/src/ui/BeatGroup/Beat/BeatUnit/BeatUnitView.ts +++ b/src/ui/BeatGroup/Beat/BeatUnit/BeatUnitView.ts @@ -1,7 +1,7 @@ -import BeatUnit, {BeatUnitEvents, BeatUnitType} from "../../../../BeatUnit"; -import ISubscriber from "../../../../Subscriber"; -import UINode, {UINodeOptions} from "../../../UINode"; -import {IPublisher, ISubscription, Publisher} from "../../../../Publisher"; +import BeatUnit, {BeatUnitEvents, BeatUnitType} from "@/BeatUnit"; +import ISubscriber from "@/Subscriber"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import {IPublisher, ISubscription, Publisher} from "@/Publisher"; import "./BeatUnit.css"; export type BeatUnitUINodeOptions = UINodeOptions & { @@ -29,6 +29,12 @@ export default class BeatUnitView extends UINode implements ISubscriber { private setupBindings() { this.subscription?.unbind(); this.subscription = this.beatUnit.addSubscriber(this, "all"); + this.onMouseUp((ev: MouseEvent) => { + if (ev.button === 1) { + const currentType = this.beatUnit.getType(); + this.beatUnit.setType(currentType === BeatUnitType.GhostNote ? BeatUnitType.Normal : BeatUnitType.GhostNote); + } + }); } toggle(): void { @@ -59,22 +65,15 @@ export default class BeatUnitView extends UINode implements ISubscriber { } } - rebuild(): HTMLElement { + build(): HTMLElement { const classes = ["beat-unit"]; if (this.beatUnit.isOn()) { classes.push("beat-unit-on"); } - this.node = UINode.make("div", { + return UINode.make("div", { classes: classes, oncontextmenu: () => false, }); - this.onMouseUp((ev: MouseEvent) => { - if (ev.button === 1) { - const currentType = this.beatUnit.getType(); - this.beatUnit.setType(currentType === BeatUnitType.GhostNote ? BeatUnitType.Normal : BeatUnitType.GhostNote); - } - }); - return this.node; } onHover(cb: () => void): void { diff --git a/src/ui/BeatGroup/Beat/BeatView.ts b/src/ui/BeatGroup/Beat/BeatView.ts index 8ef384e..d4fabcd 100644 --- a/src/ui/BeatGroup/Beat/BeatView.ts +++ b/src/ui/BeatGroup/Beat/BeatView.ts @@ -1,7 +1,7 @@ -import UINode, {UINodeOptions} from "../../UINode"; -import Beat, {BeatEvents} from "../../../Beat"; -import {IPublisher} from "../../../Publisher"; -import ISubscriber from "../../../Subscriber"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import Beat, {BeatEvents} from "@/Beat"; +import {IPublisher} from "@/Publisher"; +import ISubscriber from "@/Subscriber"; import BeatUnitView from "./BeatUnit/BeatUnitView"; import "./Beat.css"; @@ -129,7 +129,7 @@ export default class BeatView extends UINode implements ISubscriber { this.respaceBeatUnits(); } - rebuild(): HTMLElement { + build(): HTMLElement { this.title = UINode.make("h3", { innerText: this.beat.getName(), classes: ["beat-title"], @@ -138,7 +138,7 @@ export default class BeatView extends UINode implements ISubscriber { if (!this.beatUnitViewBlock) { throw new Error("Beat unit block setup failed!"); } - this.node = UINode.make("div", { + return UINode.make("div", { classes: ["beat"], subs: [ UINode.make("div", { @@ -150,7 +150,6 @@ export default class BeatView extends UINode implements ISubscriber { }), ], }); - return this.node; } } diff --git a/src/ui/BeatGroup/BeatGroupView.ts b/src/ui/BeatGroup/BeatGroupView.ts index 7a14ca5..acd6ad9 100644 --- a/src/ui/BeatGroup/BeatGroupView.ts +++ b/src/ui/BeatGroup/BeatGroupView.ts @@ -1,9 +1,9 @@ -import UINode, {UINodeOptions} from "../UINode"; -import BeatGroup, {BeatGroupEvents} from "../../BeatGroup"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import BeatGroup, {BeatGroupEvents} from "@/BeatGroup"; import BeatView from "./Beat/BeatView"; import "./BeatGroup.css"; -import ISubscriber from "../../Subscriber"; -import {IPublisher} from "../../Publisher"; +import ISubscriber from "@/Subscriber"; +import {IPublisher} from "@/Publisher"; export type BeatGroupUINodeOptions = UINodeOptions & { title: string, @@ -28,7 +28,7 @@ export default class BeatGroupView extends UINode implements ISubscriber { } } - rebuild(): HTMLDivElement { + build(): HTMLDivElement { this.beatViews = []; for (let i = 0; i < this.beatGroup.getBeatCount(); i++) { this.beatViews.push(new BeatView({beat: this.beatGroup.getBeatByIndex(i)})); diff --git a/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts b/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts index de18505..7c55e29 100644 --- a/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts +++ b/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts @@ -1,13 +1,13 @@ import "./BeatGroupSettings.css"; -import UINode, {UINodeOptions} from "../UINode"; -import NumberInputView from "../Widgets/NumberInput/NumberInputView"; -import ISubscriber from "../../Subscriber"; -import BeatGroup, {BeatGroupEvents} from "../../BeatGroup"; -import {IPublisher} from "../../Publisher"; -import {BeatEvents} from "../../Beat"; -import BoolBoxView from "../Widgets/BoolBox/BoolBoxView"; -import BeatSettingsView from "../BeatSettings/BeatSettingsView"; -import ActionButtonView from "../Widgets/ActionButton/ActionButtonView"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import NumberInputView from "@/ui/Widgets/NumberInput/NumberInputView"; +import ISubscriber from "@/Subscriber"; +import BeatGroup, {BeatGroupEvents} from "@/BeatGroup"; +import {IPublisher} from "@/Publisher"; +import {BeatEvents} from "@/Beat"; +import BoolBoxView from "@/ui/Widgets/BoolBox/BoolBoxView"; +import BeatSettingsView from "@/ui/BeatSettings/BeatSettingsView"; +import ActionButtonView from "@/ui/Widgets/ActionButton/ActionButtonView"; export type BeatGroupSettingsUINodeOptions = UINodeOptions & { beatGroup: BeatGroup, @@ -71,7 +71,7 @@ export default class BeatGroupSettingsView extends UINode implements ISubscriber } } - rebuild(): HTMLElement { + build(): HTMLElement { this.barCountInput = new NumberInputView({ label: "Bars:", initialValue: this.beatGroup.getBarCount(), diff --git a/src/ui/BeatLikeLoopSettings/BeatLikeLoopSettingsView.ts b/src/ui/BeatLikeLoopSettings/BeatLikeLoopSettingsView.ts index d1f1d17..f44467b 100644 --- a/src/ui/BeatLikeLoopSettings/BeatLikeLoopSettingsView.ts +++ b/src/ui/BeatLikeLoopSettings/BeatLikeLoopSettingsView.ts @@ -1,11 +1,11 @@ import "./BeatLikeLoopSettings.css"; -import BeatLike from "../../BeatLike"; -import NumberInputView from "../Widgets/NumberInput/NumberInputView"; -import ISubscriber from "../../Subscriber"; -import UINode, {UINodeOptions} from "../UINode"; -import {BeatEvents} from "../../Beat"; -import {IPublisher} from "../../Publisher"; -import BoolBoxView from "../Widgets/BoolBox/BoolBoxView"; +import BeatLike from "@/BeatLike"; +import NumberInputView from "@/ui/Widgets/NumberInput/NumberInputView"; +import ISubscriber from "@/Subscriber"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import {BeatEvents} from "@/Beat"; +import {IPublisher} from "@/Publisher"; +import BoolBoxView from "@/ui/Widgets/BoolBox/BoolBoxView"; export type BeatLikeLoopSettingsViewUINodeOptions = UINodeOptions & { beatLike: BeatLike, @@ -52,7 +52,7 @@ export default class BeatLikeLoopSettingsView extends UINode implements ISubscri this.notify(null, BeatEvents.DisplayTypeChanged); } - rebuild(): HTMLElement { + build(): HTMLElement { this.loopLengthInput = new NumberInputView({ initialValue: this.beatLike.getLoopLength(), label: "Length:", @@ -76,7 +76,7 @@ export default class BeatLikeLoopSettingsView extends UINode implements ISubscri } else { this.loopLengthSection.classList.add("hide"); } - this.node = UINode.make("div", { + return UINode.make("div", { classes: ["loop-settings"], subs: [ UINode.make("p", {innerText: this.title}), @@ -94,6 +94,5 @@ export default class BeatLikeLoopSettingsView extends UINode implements ISubscri }), ] }); - return this.node; } } diff --git a/src/ui/BeatSettings/BeatSettingsView.ts b/src/ui/BeatSettings/BeatSettingsView.ts index f317886..429233a 100644 --- a/src/ui/BeatSettings/BeatSettingsView.ts +++ b/src/ui/BeatSettings/BeatSettingsView.ts @@ -1,11 +1,11 @@ import "./BeatSettings.css"; -import Beat, {BeatEvents} from "../../Beat"; -import UINode, {UINodeOptions} from "../UINode"; -import ISubscriber from "../../Subscriber"; -import {IPublisher, ISubscription} from "../../Publisher"; -import NumberInputView from "../Widgets/NumberInput/NumberInputView"; -import BoolBoxView from "../Widgets/BoolBox/BoolBoxView"; -import ActionButtonView from "../Widgets/ActionButton/ActionButtonView"; +import Beat, {BeatEvents} from "@/Beat"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import ISubscriber from "@/Subscriber"; +import {IPublisher, ISubscription} from "@/Publisher"; +import NumberInputView from "@/ui/Widgets/NumberInput/NumberInputView"; +import BoolBoxView from "@/ui/Widgets/BoolBox/BoolBoxView"; +import ActionButtonView from "@/ui/Widgets/ActionButton/ActionButtonView"; export type BeatSettingsViewUINodeOptions = UINodeOptions & { beat: Beat, @@ -54,7 +54,7 @@ export default class BeatSettingsView extends UINode implements ISubscriber { } } - rebuild(): HTMLElement { + build(): HTMLElement { this.nameInput = UINode.make("input", { value: this.beat.getName(), classes: ["beat-settings-name-field"], @@ -62,7 +62,7 @@ export default class BeatSettingsView extends UINode implements ISubscriber { oninput: (event: Event) => this.beat.setName((event.target as HTMLInputElement).value), }); this.deleteButton = new ActionButtonView({ - label: "Delete", + icon: "trash", type: "secondary", onClick: () => this.beat.delete(), }); diff --git a/src/ui/Root/Root.css b/src/ui/Root/Root.css index 84ba9d5..855cd22 100644 --- a/src/ui/Root/Root.css +++ b/src/ui/Root/Root.css @@ -64,28 +64,11 @@ left: 0; } -.root-hamburger { +.root-hamburger, .root-switch-mode { right: 0; width: 2em; height: 2em; cursor: pointer; - -webkit-mask-image: url(./drawing.svg); - mask-image: url(./drawing.svg); - -webkit-mask-size: 2em; - mask-size: 2em; - background-color: var(--color-ui-neutral-dark); -} - -.root-switch-mode { - right: 0; - width: 2em; - height: 2em; - cursor: pointer; - -webkit-mask-image: url(./rotate.svg); - mask-image: url(./rotate.svg); - -webkit-mask-size: 2em; - mask-size: 2em; - background-color: var(--color-ui-neutral-dark); } .root-beat-stage-container { diff --git a/src/ui/Root/RootView.ts b/src/ui/Root/RootView.ts index e67f233..54385c9 100644 --- a/src/ui/Root/RootView.ts +++ b/src/ui/Root/RootView.ts @@ -1,8 +1,9 @@ -import UINode, {UINodeOptions} from "../UINode"; -import BeatGroupView from "../BeatGroup/BeatGroupView"; -import BeatGroup from "../../BeatGroup"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import BeatGroupView from "@/ui/BeatGroup/BeatGroupView"; +import BeatGroup from "@/BeatGroup"; import "./Root.css"; -import BeatGroupSettingsView from "../BeatGroupSettings/BeatGroupSettingsView"; +import BeatGroupSettingsView from "@/ui/BeatGroupSettings/BeatGroupSettingsView"; +import IconView from "@/ui/Widgets/Icon/IconView"; export type RootUINodeOptions = UINodeOptions & { title: string, @@ -20,7 +21,7 @@ export default class RootView extends UINode { constructor(options: RootUINodeOptions) { super(options); this.mainBeatGroup = options.mainBeatGroup; - this.beatGroupView = new BeatGroupView({title: "THE BEAT", beatGroup: this.mainBeatGroup}); + this.beatGroupView = new BeatGroupView({title: options.title, beatGroup: this.mainBeatGroup}); this.title = options.title; } @@ -32,7 +33,7 @@ export default class RootView extends UINode { this.getNode().classList.toggle("vertical-mode"); } - rebuild(): HTMLElement { + build(): HTMLElement { this.beatGroupSettingsView = new BeatGroupSettingsView({beatGroup: this.mainBeatGroup}); const sidebarMain = UINode.make("div", { classes: ["root-settings"], @@ -41,15 +42,17 @@ export default class RootView extends UINode { this.beatGroupSettingsView.render(), ] }); - const sidebarToggle = UINode.make("div", { + const sidebarStrip = UINode.make("div", { classes: ["root-sidebar-toggle"], subs: [ UINode.make("div", { classes: ["root-hamburger"], + subs: [new IconView({iconName: "list"}).render()], onclick: () => this.toggleSidebar(), }), UINode.make("div", { classes: ["root-switch-mode"], + subs: [new IconView({iconName: "arrowClockwise"}).render()], onclick: () => this.toggleOrientation(), }) ] @@ -58,10 +61,10 @@ export default class RootView extends UINode { classes: ["root-sidebar"], subs: [ sidebarMain, - sidebarToggle, + sidebarStrip, ] }); - this.node = UINode.make("div", { + return UINode.make("div", { classes: ["root", "sidebar-visible"], subs: [ this.sidebar, @@ -78,6 +81,5 @@ export default class RootView extends UINode { }) ], }); - return this.node; } } \ No newline at end of file diff --git a/src/ui/Root/drawing.svg b/src/ui/Root/drawing.svg deleted file mode 100644 index 7401431..0000000 --- a/src/ui/Root/drawing.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/ui/Root/rotate.svg b/src/ui/Root/rotate.svg deleted file mode 100644 index 6b63d57..0000000 --- a/src/ui/Root/rotate.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/ui/UINode.ts b/src/ui/UINode.ts index 3d72619..d8a4993 100644 --- a/src/ui/UINode.ts +++ b/src/ui/UINode.ts @@ -17,7 +17,7 @@ export default abstract class UINode { render(): HTMLElement { if (!this.node) { - this.node = this.rebuild(); + this.node = this.build(); } return this.node; } @@ -37,12 +37,14 @@ export default abstract class UINode { } const parent = this.node.parentElement; if (parent) { - this.node = this.rebuild(); + this.node = this.build(); parent.replaceChild(this.node, oldNode); + } else { + this.render(); } } - abstract rebuild(): HTMLElement; + protected abstract build(): HTMLElement; static make< T extends keyof HTMLElementTagNameMap, @@ -64,4 +66,16 @@ export default abstract class UINode { } return element; } + + static q(text: string): Text { + return document.createTextNode(text); + } + + static frag(subs?: Node[]): DocumentFragment { + const frag = document.createDocumentFragment(); + if (subs) { + frag.append(...subs); + } + return frag; + } } \ No newline at end of file diff --git a/src/ui/Widgets/ActionButton/ActionButtonView.ts b/src/ui/Widgets/ActionButton/ActionButtonView.ts index b75c897..2c46678 100644 --- a/src/ui/Widgets/ActionButton/ActionButtonView.ts +++ b/src/ui/Widgets/ActionButton/ActionButtonView.ts @@ -1,39 +1,48 @@ import "./ActionButton.css"; -import UINode, {UINodeOptions} from "../../UINode"; +import UINode, {UINodeOptions} from "@/ui/UINode"; +import IconView, {IconName} from "@/ui/Widgets/Icon/IconView"; export type ActionButtonUINodeOptions = UINodeOptions & { - label: string, type?: "primary" | "secondary", onClick?: (isChecked: boolean) => void, -}; +} & ({ + icon: IconName, + label?: never, +} | { + label: string, + icon?: never, +}); export default class ActionButtonView extends UINode { - private label: string | null; + private label: string | null = null; + private icon: IconName | null = null; private buttonElement!: HTMLButtonElement; private onClick: (isChecked: boolean) => void; private type: "primary" | "secondary"; constructor(options: ActionButtonUINodeOptions) { super(options); - this.label = options.label ?? ""; + if (typeof options.icon !== "undefined") { + this.icon = options.icon; + } else if (typeof options.label !== "undefined") { + this.label = options.label; + } this.type = options.type ?? "primary"; this.onClick = options.onClick ?? (() => { /* dummy */ }); } - setLabel(newLabel: string | null): void { - if (newLabel !== null) { - this.buttonElement.innerText = newLabel; - } else { - this.buttonElement.innerText = ""; - } - } - - rebuild(): HTMLButtonElement { + protected build(): HTMLButtonElement { this.buttonElement = UINode.make("button", { classes: ["action-button", `action-button-${this.type}`], - innerText: this.label ?? "", onclick: this.onClick, + subs: [ + this.icon !== null ? new IconView({ + iconName: this.icon + }).render() : UINode.make("span", { + innerText: this.label ?? "" + }), + ], }); return this.buttonElement; } -} \ No newline at end of file +} diff --git a/src/ui/Widgets/BoolBox/BoolBoxView.ts b/src/ui/Widgets/BoolBox/BoolBoxView.ts index 4f584f4..7f47b9d 100644 --- a/src/ui/Widgets/BoolBox/BoolBoxView.ts +++ b/src/ui/Widgets/BoolBox/BoolBoxView.ts @@ -1,5 +1,5 @@ import "./BoolBox.css"; -import UINode, {UINodeOptions} from "../../UINode"; +import UINode, {UINodeOptions} from "@/ui/UINode"; export type BoolBoxUINodeOptions = UINodeOptions & { label?: string, @@ -35,7 +35,7 @@ export default class BoolBoxView extends UINode { this.checkboxElement.checked = isChecked; } - rebuild(): HTMLDivElement { + build(): HTMLDivElement { this.labelElement = UINode.make("label", { classes: ["bool-box-label"], innerText: this.label ?? "", diff --git a/src/ui/Widgets/Icon/Icon.css b/src/ui/Widgets/Icon/Icon.css new file mode 100644 index 0000000..20f410b --- /dev/null +++ b/src/ui/Widgets/Icon/Icon.css @@ -0,0 +1,8 @@ +.icon-view { + width: 2em; + height: 2em; + -webkit-mask-size: 2em; + mask-size: 2em; + display: inline-block; + background-color: black; +} \ No newline at end of file diff --git a/src/ui/Widgets/Icon/IconView.ts b/src/ui/Widgets/Icon/IconView.ts new file mode 100644 index 0000000..14c3c8f --- /dev/null +++ b/src/ui/Widgets/Icon/IconView.ts @@ -0,0 +1,34 @@ +import UINode, {UINodeOptions} from "@/ui/UINode"; +import "./Icon.css"; +import List from "./svgs/list.svg"; +import ArrowClockwise from "./svgs/arrow-clockwise.svg"; +import Trash from "./svgs/trash.svg"; + +const IconUrlMap = { + arrowClockwise: ArrowClockwise, + list: List, + trash: Trash, +} as const; + +export type IconName = keyof typeof IconUrlMap; + +export type IconViewOptions = UINodeOptions & { + iconName: IconName, +}; + +export default class IconView extends UINode { + private iconUrl: string; + + constructor(options: IconViewOptions) { + super(options); + this.iconUrl = IconUrlMap[options.iconName]; + } + + build(): HTMLSpanElement { + const icon = UINode.make("div", { + classes: ["icon-view"], + }); + icon.style.cssText = `-webkit-mask-image: url(${this.iconUrl}); mask-image: url(${this.iconUrl});`; + return icon; + } +} \ No newline at end of file diff --git a/src/ui/Widgets/Icon/svgs/arrow-clockwise.svg b/src/ui/Widgets/Icon/svgs/arrow-clockwise.svg new file mode 100644 index 0000000..b072eb0 --- /dev/null +++ b/src/ui/Widgets/Icon/svgs/arrow-clockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/ui/Widgets/Icon/svgs/list.svg b/src/ui/Widgets/Icon/svgs/list.svg new file mode 100644 index 0000000..e039056 --- /dev/null +++ b/src/ui/Widgets/Icon/svgs/list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/ui/Widgets/Icon/svgs/trash.svg b/src/ui/Widgets/Icon/svgs/trash.svg new file mode 100644 index 0000000..e0e81f1 --- /dev/null +++ b/src/ui/Widgets/Icon/svgs/trash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/ui/Widgets/NumberInput/NumberInputView.ts b/src/ui/Widgets/NumberInput/NumberInputView.ts index b555006..8edcb8d 100644 --- a/src/ui/Widgets/NumberInput/NumberInputView.ts +++ b/src/ui/Widgets/NumberInput/NumberInputView.ts @@ -1,4 +1,4 @@ -import UINode, { UINodeOptions } from "../../UINode"; +import UINode, { UINodeOptions } from "@/ui/UINode"; import "./NumberInput.css"; type NumberInputUINodeOptionsBase = UINodeOptions & { @@ -27,7 +27,6 @@ export type NumberInputUINodeOptions = NumberInputUINodeOptionsGetSet | NumberIn export default class NumberInputView extends UINode { private labelElement!: HTMLLabelElement; - private mainElement!: HTMLDivElement; private inputElement!: HTMLInputElement; private labelPosition: "top" | "left"; private value: number; @@ -63,12 +62,12 @@ export default class NumberInputView extends UINode { } disable(): void { - this.mainElement.classList.add("disabled"); + this.node?.classList.add("disabled"); this.inputElement.disabled = true; } enable(): void { - this.mainElement.classList.remove("disabled"); + this.node?.classList.remove("disabled"); this.inputElement.disabled = false; } @@ -77,7 +76,7 @@ export default class NumberInputView extends UINode { this.inputElement.valueAsNumber = value; } - rebuild(): HTMLDivElement { + build(): HTMLDivElement { this.labelElement = UINode.make("label", { classes: ["number-input-label", this.labelPosition], innerText: this.label ?? "", @@ -100,7 +99,7 @@ export default class NumberInputView extends UINode { } }, }); - this.mainElement = UINode.make("div", { + return UINode.make("div", { classes: ["number-input"], subs: [ this.labelElement, @@ -129,6 +128,5 @@ export default class NumberInputView extends UINode { }), ], }); - return this.mainElement; } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 87af555..de319fe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,11 @@ "allowJs": true, "strict": true, "moduleResolution": "Node", - "resolveJsonModule": true + "resolveJsonModule": true, + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } }, "include": ["./src/**/*"] } diff --git a/webpack.config.js b/webpack.config.js index b45100e..e6c67f5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -15,7 +15,7 @@ const webpackConfig = { test: /\.(ts|tsx)$/, loader: "ts-loader", include: [path.resolve(__dirname, "src")], - exclude: [/node_modules/] + exclude: [/node_modules/], }, { test: /.css$/, @@ -30,18 +30,21 @@ const webpackConfig = { } }] }, - // { - // test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i, - // use: [ - // { - // loader: "file-loader", - // }, - // ], - // } + { + test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i, + use: [ + { + loader: "file-loader", + }, + ], + } ] }, resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, extensions: [".tsx", ".ts", ".js"] },