added root path alias, icons, improved framework semantics

This commit is contained in:
Daniel Ledda
2022-02-27 22:59:30 +01:00
parent 352f6d6b9a
commit 7fca44f6c0
25 changed files with 207 additions and 284 deletions

View File

@@ -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?: {

View File

@@ -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;

View File

@@ -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,

12
src/types.d.ts vendored Normal file
View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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)}));

View File

@@ -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(),

View File

@@ -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;
}
}

View File

@@ -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(),
});

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg5"
inkscape:version="1.1 (ce6663b3b7, 2021-05-25)"
sodipodi:docname="drawing.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="6.0150393"
inkscape:cx="24.355618"
inkscape:cy="41.562488"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
width="64mm"
units="px">
<inkscape:grid
type="xygrid"
id="grid25"
empspacing="4" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#1a1a1a;fill-rule:evenodd;stroke-width:0.634;stop-color:#000000"
id="rect49"
width="12.7"
height="2.1166666"
x="2.1166666"
y="3.1750002"
ry="1.0583333" />
<rect
style="fill:#1a1a1a;fill-rule:evenodd;stroke-width:0.634;stop-color:#000000"
id="rect49-3"
width="12.7"
height="2.1166666"
x="2.1166666"
y="7.4083338"
ry="1.0583333" />
<rect
style="fill:#1a1a1a;fill-rule:evenodd;stroke-width:0.634;stop-color:#000000"
id="rect49-6"
width="12.7"
height="2.1166666"
x="2.1166668"
y="11.641666"
ry="1.0583333" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 16.933333 16.933334"
version="1.1"
id="svg5"
inkscape:version="1.1 (ce6663b3b7, 2021-05-25)"
sodipodi:docname="rotate.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
units="px"
width="64px"
inkscape:snap-bbox="true"
inkscape:zoom="6.0150393"
inkscape:cx="26.433743"
inkscape:cy="34.330615"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid113"
empspacing="4"
snapvisiblegridlinesonly="true"
visible="true"
enabled="true" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g1725"
transform="rotate(-39.669761,8.2863595,8.2940605)">
<path
id="path217"
style="fill:#000000;fill-rule:evenodd;stroke-width:0.55878;stop-color:#000000"
d="M 8.2923774,2.0552887 A 6.5293784,6.5293784 0 0 0 1.762999,8.5846671 6.5293784,6.5293784 0 0 0 8.2923774,15.114045 6.5293784,6.5293784 0 0 0 12.031194,13.937153 L 11.496948,13.171992 A 5.5966101,5.5966101 0 0 1 8.2923774,14.181277 5.5966101,5.5966101 0 0 1 2.6957673,8.5846671 5.5966101,5.5966101 0 0 1 8.2923774,2.988057 5.5966101,5.5966101 0 0 1 13.888988,8.5846671 h 0.932768 A 6.5293784,6.5293784 0 0 0 8.2923774,2.0552887 Z m 6.5293786,6.5293784 a 6.5293784,6.5293784 0 0 1 -0.05967,0.8649058 6.5293784,6.5293784 0 0 0 0.05967,-0.8649058 z m -0.06558,0.9136394 a 6.5293784,6.5293784 0 0 1 -0.173077,0.8284685 6.5293784,6.5293784 0 0 0 0.173073,-0.8284685 z m -0.18947,0.8863115 a 6.5293784,6.5293784 0 0 1 -0.286936,0.802964 6.5293784,6.5293784 0 0 0 0.286936,-0.802964 z m -0.300599,0.832113 a 6.5293784,6.5293784 0 0 1 -0.393967,0.755597 6.5293784,6.5293784 0 0 0 0.393967,-0.755597 z m -0.412641,0.787478 a 6.5293784,6.5293784 0 0 1 -0.493255,0.694567 6.5293784,6.5293784 0 0 0 0.493255,-0.694567 z m -0.529237,0.738746 a 6.5293784,6.5293784 0 0 1 -0.583435,0.619417 6.5293784,6.5293784 0 0 0 0.583435,-0.619417 z m -0.614862,0.647654 a 6.5293784,6.5293784 0 0 1 -0.667238,0.538801 6.5293784,6.5293784 0 0 0 0.667238,-0.538801 z" />
<path
sodipodi:type="star"
style="fill:#000000;fill-rule:evenodd;stroke-width:2.39622;stop-color:#000000"
id="path1641"
inkscape:flatsided="true"
sodipodi:sides="3"
sodipodi:cx="47"
sodipodi:cy="50"
sodipodi:r1="7"
sodipodi:r2="3.5"
sodipodi:arg1="1.5707963"
sodipodi:arg2="2.6179939"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 47,57 -6.062178,-10.5 12.124356,0 z"
transform="matrix(0.23319209,0,0,0.23319209,3.3953438,-2.6085529)"
inkscape:transform-center-y="0.40808613" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 ?? "",

View File

@@ -0,0 +1,8 @@
.icon-view {
width: 2em;
height: 2em;
-webkit-mask-size: 2em;
mask-size: 2em;
display: inline-block;
background-color: black;
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>

After

Width:  |  Height:  |  Size: 352 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5Zm-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5ZM4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 582 B

View File

@@ -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;
}
}

View File

@@ -7,7 +7,11 @@
"allowJs": true,
"strict": true,
"moduleResolution": "Node",
"resolveJsonModule": true
"resolveJsonModule": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["./src/**/*"]
}

View File

@@ -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"]
},