This commit is contained in:
Daniel Ledda
2023-05-07 21:14:48 +02:00
parent 90a39ee2fe
commit 4268cec832
17 changed files with 800 additions and 2292 deletions

View File

@@ -216,7 +216,6 @@
}
.toolbox {
flex: 1;
width: 2em;
}
}

View File

@@ -1,48 +1,61 @@
<template>
<div class="toolbox">
<div class="main-row">
<icon
class="toolbox-button"
icon-name="lh"
:class="{ active: selectedTool === 'track-unit-type' }"
@click="selectedTool = 'track-unit-type'" />
<icon
class="toolbox-button"
icon-name="lf"
:class="{ active: selectedTool === 'sticking' }"
@click="selectedTool = 'sticking'" />
<icon
class="toolbox-button"
:class="{ active: selectedTool === 'eraser' }"
@click="selectedTool = 'eraser'" />
<dropdown>
<div class="toolbox-button paint-button-cont"
:class="{ active: selectedTool === 'track-unit-type' }"
@click="selectedTool = 'track-unit-type'">
<div
class="paint-button"
:class="getClasses({ on: true, type: TrackUnitTypeList[activeTrackUnitType]!, stickingType: null })" />
</div>
<template #content>
<div class="details">
<div v-for="(type, i) in TrackUnitTypeList"
:key="type"
class="toolbox-button"
:class="{ active: i === activeTrackUnitType }"
@click="activeTrackUnitType = i; selectedTool = 'track-unit-type'">
<div :class="getClasses({ on: true, type, stickingType: null })" />
</div>
</div>
</template>
</dropdown>
<dropdown>
<icon
class="toolbox-button"
:icon-name="PaintableTrackUnitStickingTypeList[activeStickingType]!"
:class="{ active: selectedTool === 'sticking' }"
@click="selectedTool = 'sticking'" />
<template #content>
<div class="details">
<div v-for="(stickingType, i) in PaintableTrackUnitStickingTypeList"
:key="stickingType"
class="toolbox-button"
:class="{ active: i === activeStickingType }"
@click="activeStickingType = i; selectedTool = 'sticking'">
<icon :icon-name="StickingTypeIconMap[stickingType]" />
</div>
</div>
</template>
</dropdown>
<dropdown>
<icon
class="toolbox-button"
icon-name="eraser"
:class="{ active: selectedTool === 'eraser' }"
@click="selectedTool = 'eraser'" />
</dropdown>
</div>
<div v-if="selectedTool === 'track-unit-type'" class="details">
<div v-for="(type, i) in TrackUnitTypeList"
:key="type"
class="toolbox-button"
:class="{ active: i === activeTrackUnitType }"
@click="activeTrackUnitType = i">
<div :class="getClasses({ on: true, type, stickingType: 'none' })" />
</div>
</div>
<div v-else-if="selectedTool === 'sticking'" class="details">
<div v-for="(stickingType, i) in TrackUnitStickingTypeList.slice(1)"
:key="stickingType"
class="toolbox-button"
:class="{ active: i + 1 === activeStickingType }"
@click="activeStickingType = i + 1">
<icon :icon-name="StickingTypeIconMap[stickingType]" />
</div>
</div>
<div v-else-if="selectedTool === 'eraser'" class="details hidden" />
</div>
</template>
<script setup lang="ts">
import { useAppStateStore } from "@/AppState";
import { TrackUnitStickingTypeList, TrackUnitTypeList } from "@/Track";
import { PaintableTrackUnitStickingTypeList, TrackUnitTypeList } from "@/Track";
import { StickingTypeIconMap } from "@/ui/TrackUnit/trackUnit";
import Icon from "@/ui/Widgets/Icon/Icon.vue";
import Dropdown from '@/ui/Widgets/Dropdown/Dropdown.vue';
import { getClasses } from "@/ui/TrackUnit/trackUnit";
const {
@@ -53,41 +66,60 @@
</script>
<style scoped lang="scss">
.details {
margin: auto;
padding: 0.5em;
background-color: var(--color-ui-neutral-dark-active);
}
.toolbox {
.main-row {
margin: auto;
display: flex;
background-color: var(--color-ui-neutral-dark);
justify-content: center;
width: 100%;
flex-direction: column;
}
.details {
margin: auto;
height: 4em;
width: min-content;
border-radius: 0 0 1em 1em;
padding: 0.5em;
background-color: var(--color-ui-neutral-dark-active);
display: flex;
justify-content: center;
&.hidden {
visibility: hidden;
}
}
.track-unit {
margin: 0;
padding: 0;
}
.toolbox-button {
padding: 0.5em;
cursor: pointer;
color: black;
background-color: var(--color-ui-neutral-dark);
&.paint-button-cont {
padding: 0.25em;
background-color: var(--color-ui-bg-dark);
&:hover {
background-color: var(--color-ui-bg-dark);
.paint-button {
background-color: var(--color-ui-neutral-dark-hover);
}
}
&.active {
background-color: var(--color-ui-bg-dark);
.paint-button {
background-color: var(--color-ui-neutral-dark-active);
}
}
.paint-button {
height: 1.5em;
width: 1.5em;
background-color: black;
&.active {
background-color: var(--color-ui-neutral-dark);
}
}
}
&:hover {
background-color: var(--color-ui-neutral-dark-hover);
@@ -97,18 +129,19 @@
background-color: var(--color-ui-neutral-dark-active);
}
}
}
.details {
.toolbox-button {
background-color: var(--color-ui-neutral-dark-active);
.details {
.toolbox-button {
display: inline-block;
background-color: var(--color-ui-neutral-dark-active);
&.active {
background-color: var(--color-ui-neutral-dark);
}
&.active {
background-color: var(--color-ui-neutral-dark);
}
&:hover {
background-color: var(--color-ui-neutral-dark-hover);
}
&:hover {
background-color: var(--color-ui-neutral-dark-hover);
}
}
}

View File

@@ -8,7 +8,7 @@
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
@contextmenu.prevent.stop="() => false">
<icon :icon-name="iconName" />
<icon v-if="iconName" :icon-name="iconName" />
</div>
</template>
@@ -35,7 +35,6 @@
const {
selectingUnits,
lastTrackUnit,
deselectingUnits,
unitMouseStart,
} = useAppStateStore();
@@ -47,12 +46,19 @@
const classes = computed(() => getClasses({
on: props.on,
stickingType: TrackUnitStickingTypeList[props.stickingType] ?? 'none',
stickingType: TrackUnitStickingTypeList[props.stickingType] ?? null,
type: TrackUnitTypeList[props.type] ?? 'Normal',
highlightable: true,
}));
const iconName = computed(() => StickingTypeIconMap[TrackUnitStickingTypeList[props.stickingType] ?? 'none']);
const iconName = computed(() => {
const type = TrackUnitStickingTypeList[props.stickingType];
if (type) {
StickingTypeIconMap[type];
} else {
return null;
}
});
function handleMouseDown(ev: MouseEvent): void {
blockNextMouseUp = false;
@@ -118,7 +124,7 @@
}
</script>
<style lang="scss">
<style scoped lang="scss">
.track-unit {
width: 2em;
height: 2em;

View File

@@ -4,26 +4,25 @@ import type { IconName } from "@/ui/Widgets/Icon/icons";
export const TypeClasses = [ "Ghost", "Accent" ] as const;
export const StickingTypeIconMap = {
none: null,
lf: 'lf',
lh: 'lh',
rf: 'rf',
rh: 'rh',
} as const satisfies Readonly<Record<TrackUnitStickingType, IconName | null>>;
} as const satisfies Record<TrackUnitStickingType, IconName | null>;
export const TrackUnitTypeClassMap = {
"Normal": [],
"GhostNote": ["Ghost"],
"Accent": ["Accent"],
"GhostNoteAccent": ["Ghost", "Accent"],
} as const satisfies Readonly<Record<TrackUnitType, Readonly<string[]>>>;
} as const satisfies Record<TrackUnitType, Readonly<string[]>>;
export function getClasses(options: { on: boolean, stickingType: TrackUnitStickingType, type: TrackUnitType, highlightable?: boolean }) {
export function getClasses(options: { on: boolean, stickingType: TrackUnitStickingType | null, type: TrackUnitType, highlightable?: boolean }) {
const classes = ["track-unit"];
if (options.on) {
classes.push("on");
}
if (StickingTypeIconMap[options.stickingType]) {
if (options.stickingType && StickingTypeIconMap[options.stickingType]) {
classes.push("icon-visible");
}
if (options.type) {

View File

@@ -0,0 +1,81 @@
<template>
<div
class="trigger"
ref="trigger"
@mouseenter="onMouseEnter"
@touchstart="onMouseEnter"
@mouseleave="onMouseLeave">
<slot />
</div>
<Teleport to="#dropdowns">
<div
v-if="visible"
ref="dropdown"
class="dropdown"
:class="{ visible }"
@onmouseleave="visible = false">
<slot name="content" />
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const trigger = ref<HTMLDivElement>();
const dropdown = ref<HTMLDivElement>();
const visible = ref(false);
const top = ref('0px');
const left = ref('0px');
function onMouseEnter(e: MouseEvent | TouchEvent) {
visible.value = true;
if (trigger.value) {
const rect = trigger.value.getBoundingClientRect();
top.value = rect.top + 'px';
left.value = rect.width + rect.left + 'px';
}
window.addEventListener('touchstart', onWindowClick);
window.addEventListener('click', onWindowClick);
}
function outside(e: MouseEvent, el: HTMLElement) {
const rect = el.getBoundingClientRect();
return e.clientX < rect.x
|| e.clientX > rect.x + rect.width
|| e.clientY < rect.y
|| e.clientY > rect.y + rect.height;
}
function onWindowClick(e: MouseEvent | TouchEvent) {
if (visible.value && e instanceof MouseEvent && dropdown.value && !outside(e, dropdown.value)) {
visible.value = false;
window.removeEventListener('touchstart', onWindowClick);
window.removeEventListener('click', onWindowClick);
}
}
function onMouseLeave(e: MouseEvent) {
if (trigger.value && outside(e, trigger.value)) {
visible.value = false;
}
}
</script>
<style scoped lang="scss">
.dropdown {
position: absolute;
top: v-bind(top);
left: v-bind(left);
visibility: hidden;
&.visible {
visibility: visible;
}
&:hover {
visibility: visible;
}
}
</style>

View File

@@ -26,7 +26,6 @@
height: 2em;
-webkit-mask-size: 2em;
mask-size: 2em;
display: inline-block;
background-color: var(--icon-bg);
}
</style>

View File

@@ -7,6 +7,7 @@ import Download from "assets/svgs/download.svg";
import RightHand from "assets/svgs/RH.png";
import LeftFoot from "assets/svgs/LF.png";
import RightFoot from "assets/svgs/RF.png";
import Eraser from "assets/svgs/eraser-fill.svg";
export const IconUrlMap = {
arrowClockwise: ArrowClockwise,
@@ -18,6 +19,7 @@ export const IconUrlMap = {
rh: RightHand,
lf: LeftFoot,
rf: RightFoot,
eraser: Eraser,
} as const;
export type IconName = keyof typeof IconUrlMap;

View File

@@ -27,6 +27,20 @@ html, body {
height: 100%;
}
#dropdowns {
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 100;
position: absolute;
pointer-events: none;
}
#dropdowns * {
pointer-events: all;
}
* {
box-sizing: border-box;
}