mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 22:18:11 +08:00
Replace the Vue frontend with Svelte
This commit is contained in:
@@ -1,148 +1,141 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
|
||||
const RULER_THICKNESS = 16;
|
||||
const MAJOR_MARK_THICKNESS = 16;
|
||||
const MEDIUM_MARK_THICKNESS = 6;
|
||||
const MINOR_MARK_THICKNESS = 3;
|
||||
|
||||
export type RulerDirection = "Horizontal" | "Vertical";
|
||||
|
||||
// Modulo function that works for negative numbers, unlike the JS % operator
|
||||
const mod = (n: number, m: number): number => {
|
||||
const remainder = n % m;
|
||||
return Math.floor(remainder >= 0 ? remainder : remainder + m);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
direction: { type: String as PropType<RulerDirection>, default: "Vertical" },
|
||||
origin: { type: Number as PropType<number>, required: true },
|
||||
numberInterval: { type: Number as PropType<number>, required: true },
|
||||
majorMarkSpacing: { type: Number as PropType<number>, required: true },
|
||||
mediumDivisions: { type: Number as PropType<number>, default: 5 },
|
||||
minorDivisions: { type: Number as PropType<number>, default: 2 },
|
||||
},
|
||||
computed: {
|
||||
svgPath(): string {
|
||||
const isVertical = this.direction === "Vertical";
|
||||
const lineDirection = isVertical ? "H" : "V";
|
||||
|
||||
const offsetStart = mod(this.origin, this.majorMarkSpacing);
|
||||
const shiftedOffsetStart = offsetStart - this.majorMarkSpacing;
|
||||
|
||||
const divisions = this.majorMarkSpacing / this.mediumDivisions / this.minorDivisions;
|
||||
const majorMarksFrequency = this.mediumDivisions * this.minorDivisions;
|
||||
|
||||
let dPathAttribute = "";
|
||||
let i = 0;
|
||||
for (let location = shiftedOffsetStart; location < this.rulerLength; location += divisions) {
|
||||
let length;
|
||||
if (i % majorMarksFrequency === 0) length = MAJOR_MARK_THICKNESS;
|
||||
else if (i % this.minorDivisions === 0) length = MEDIUM_MARK_THICKNESS;
|
||||
else length = MINOR_MARK_THICKNESS;
|
||||
i += 1;
|
||||
|
||||
const destination = Math.round(location) + 0.5;
|
||||
const startPoint = isVertical ? `${RULER_THICKNESS - length},${destination}` : `${destination},${RULER_THICKNESS - length}`;
|
||||
dPathAttribute += `M${startPoint}${lineDirection}${RULER_THICKNESS} `;
|
||||
}
|
||||
|
||||
return dPathAttribute;
|
||||
},
|
||||
svgTexts(): { transform: string; text: number }[] {
|
||||
const isVertical = this.direction === "Vertical";
|
||||
|
||||
const offsetStart = mod(this.origin, this.majorMarkSpacing);
|
||||
const shiftedOffsetStart = offsetStart - this.majorMarkSpacing;
|
||||
|
||||
const svgTextCoordinates = [];
|
||||
|
||||
let text = (Math.ceil(-this.origin / this.majorMarkSpacing) - 1) * this.numberInterval;
|
||||
|
||||
for (let location = shiftedOffsetStart; location < this.rulerLength; location += this.majorMarkSpacing) {
|
||||
const destination = Math.round(location);
|
||||
const x = isVertical ? 9 : destination + 2;
|
||||
const y = isVertical ? destination + 1 : 9;
|
||||
|
||||
let transform = `translate(${x} ${y})`;
|
||||
if (isVertical) transform += " rotate(270)";
|
||||
|
||||
svgTextCoordinates.push({ transform, text });
|
||||
|
||||
text += this.numberInterval;
|
||||
}
|
||||
|
||||
return svgTextCoordinates;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resize() {
|
||||
const canvasRuler = this.$refs.canvasRuler as HTMLDivElement | undefined;
|
||||
if (!canvasRuler) return;
|
||||
|
||||
const isVertical = this.direction === "Vertical";
|
||||
|
||||
const newLength = isVertical ? canvasRuler.clientHeight : canvasRuler.clientWidth;
|
||||
const roundedUp = (Math.floor(newLength / this.majorMarkSpacing) + 1) * this.majorMarkSpacing;
|
||||
|
||||
if (roundedUp !== this.rulerLength) {
|
||||
this.rulerLength = roundedUp;
|
||||
const thickness = `${RULER_THICKNESS}px`;
|
||||
const length = `${roundedUp}px`;
|
||||
this.svgBounds = isVertical ? { width: thickness, height: length } : { width: length, height: thickness };
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rulerLength: 0,
|
||||
svgBounds: { width: "0px", height: "0px" },
|
||||
};
|
||||
},
|
||||
});
|
||||
<script lang="ts" context="module">
|
||||
export type RulerDirection = "Horizontal" | "Vertical";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="canvas-ruler" :class="direction.toLowerCase()" ref="canvasRuler">
|
||||
<svg :style="svgBounds">
|
||||
<path :d="svgPath" />
|
||||
<text v-for="(svgText, index) in svgTexts" :key="index" :transform="svgText.transform">{{ svgText.text }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
const RULER_THICKNESS = 16;
|
||||
const MAJOR_MARK_THICKNESS = 16;
|
||||
const MEDIUM_MARK_THICKNESS = 6;
|
||||
const MINOR_MARK_THICKNESS = 3;
|
||||
|
||||
<style lang="scss">
|
||||
.canvas-ruler {
|
||||
flex: 1 1 100%;
|
||||
background: var(--color-4-dimgray);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
export let direction: RulerDirection = "Vertical";
|
||||
export let origin: number;
|
||||
export let numberInterval: number;
|
||||
export let majorMarkSpacing: number;
|
||||
export let mediumDivisions: number = 5;
|
||||
export let minorDivisions: number = 2;
|
||||
|
||||
&.horizontal {
|
||||
height: 16px;
|
||||
let canvasRuler: HTMLDivElement;
|
||||
let rulerLength = 0;
|
||||
let svgBounds = { width: "0px", height: "0px" };
|
||||
|
||||
$: svgPath = computeSvgPath(direction, origin, majorMarkSpacing, mediumDivisions, minorDivisions, rulerLength);
|
||||
$: svgTexts = computeSvgTexts(direction, origin, majorMarkSpacing, numberInterval, rulerLength);
|
||||
|
||||
function computeSvgPath(direction: RulerDirection, origin: number, majorMarkSpacing: number, mediumDivisions: number, minorDivisions: number, rulerLength: number): string {
|
||||
const isVertical = direction === "Vertical";
|
||||
const lineDirection = isVertical ? "H" : "V";
|
||||
|
||||
const offsetStart = mod(origin, majorMarkSpacing);
|
||||
const shiftedOffsetStart = offsetStart - majorMarkSpacing;
|
||||
|
||||
const divisions = majorMarkSpacing / mediumDivisions / minorDivisions;
|
||||
const majorMarksFrequency = mediumDivisions * minorDivisions;
|
||||
|
||||
let dPathAttribute = "";
|
||||
let i = 0;
|
||||
for (let location = shiftedOffsetStart; location < rulerLength; location += divisions) {
|
||||
let length;
|
||||
if (i % majorMarksFrequency === 0) length = MAJOR_MARK_THICKNESS;
|
||||
else if (i % minorDivisions === 0) length = MEDIUM_MARK_THICKNESS;
|
||||
else length = MINOR_MARK_THICKNESS;
|
||||
i += 1;
|
||||
|
||||
const destination = Math.round(location) + 0.5;
|
||||
const startPoint = isVertical ? `${RULER_THICKNESS - length},${destination}` : `${destination},${RULER_THICKNESS - length}`;
|
||||
dPathAttribute += `M${startPoint}${lineDirection}${RULER_THICKNESS} `;
|
||||
}
|
||||
|
||||
return dPathAttribute;
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
width: 16px;
|
||||
function computeSvgTexts(direction: RulerDirection, origin: number, majorMarkSpacing: number, numberInterval: number, rulerLength: number): { transform: string; text: number }[] {
|
||||
const isVertical = direction === "Vertical";
|
||||
|
||||
svg text {
|
||||
text-anchor: end;
|
||||
const offsetStart = mod(origin, majorMarkSpacing);
|
||||
const shiftedOffsetStart = offsetStart - majorMarkSpacing;
|
||||
|
||||
const svgTextCoordinates = [];
|
||||
|
||||
let text = (Math.ceil(-origin / majorMarkSpacing) - 1) * numberInterval;
|
||||
|
||||
for (let location = shiftedOffsetStart; location < rulerLength; location += majorMarkSpacing) {
|
||||
const destination = Math.round(location);
|
||||
const x = isVertical ? 9 : destination + 2;
|
||||
const y = isVertical ? destination + 1 : 9;
|
||||
|
||||
let transform = `translate(${x} ${y})`;
|
||||
if (isVertical) transform += " rotate(270)";
|
||||
|
||||
svgTextCoordinates.push({ transform, text });
|
||||
|
||||
text += numberInterval;
|
||||
}
|
||||
|
||||
return svgTextCoordinates;
|
||||
}
|
||||
|
||||
export function resize() {
|
||||
const isVertical = direction === "Vertical";
|
||||
|
||||
const newLength = isVertical ? canvasRuler.clientHeight : canvasRuler.clientWidth;
|
||||
const roundedUp = (Math.floor(newLength / majorMarkSpacing) + 1) * majorMarkSpacing;
|
||||
|
||||
if (roundedUp !== rulerLength) {
|
||||
rulerLength = roundedUp;
|
||||
const thickness = `${RULER_THICKNESS}px`;
|
||||
const length = `${roundedUp}px`;
|
||||
svgBounds = isVertical ? { width: thickness, height: length } : { width: length, height: thickness };
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
// Modulo function that works for negative numbers, unlike the JS `%` operator
|
||||
function mod(n: number, m: number): number {
|
||||
const remainder = n % m;
|
||||
return Math.floor(remainder >= 0 ? remainder : remainder + m);
|
||||
}
|
||||
</script>
|
||||
|
||||
path {
|
||||
stroke-width: 1px;
|
||||
stroke: var(--color-7-middlegray);
|
||||
<div class={`canvas-ruler ${direction.toLowerCase()}`} bind:this={canvasRuler}>
|
||||
<svg style:width={svgBounds.width} style:height={svgBounds.height}>
|
||||
<path d={svgPath} />
|
||||
{#each svgTexts as svgText, index (index)}
|
||||
<text transform={svgText.transform}>{svgText.text}</text>
|
||||
{/each}
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<style lang="scss" global>
|
||||
.canvas-ruler {
|
||||
flex: 1 1 100%;
|
||||
background: var(--color-4-dimgray);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&.horizontal {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 12px;
|
||||
fill: var(--color-8-uppergray);
|
||||
&.vertical {
|
||||
width: 16px;
|
||||
|
||||
svg text {
|
||||
text-anchor: end;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
|
||||
path {
|
||||
stroke-width: 1px;
|
||||
stroke: var(--color-7-middlegray);
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 12px;
|
||||
fill: var(--color-8-uppergray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,219 +1,208 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
|
||||
export type ScrollbarDirection = "Horizontal" | "Vertical";
|
||||
|
||||
// Linear Interpolation
|
||||
const lerp = (x: number, y: number, a: number): number => x * (1 - a) + y * a;
|
||||
|
||||
// Convert the position of the handle (0-1) to the position on the track (0-1).
|
||||
// This includes the 1/2 handle length gap of the possible handle positionson each side so the end of the handle doesn't go off the track.
|
||||
const handleToTrack = (handleLen: number, handlePos: number): number => lerp(handleLen / 2, 1 - handleLen / 2, handlePos);
|
||||
|
||||
const pointerPosition = (direction: ScrollbarDirection, e: PointerEvent): number => (direction === "Vertical" ? e.clientY : e.clientX);
|
||||
|
||||
export default defineComponent({
|
||||
emits: {
|
||||
"update:handlePosition": null,
|
||||
pressTrack: (pointerOffset: number) => typeof pointerOffset === "number",
|
||||
},
|
||||
props: {
|
||||
direction: { type: String as PropType<ScrollbarDirection>, default: "Vertical" },
|
||||
handlePosition: { type: Number as PropType<number>, default: 0.5 },
|
||||
handleLength: { type: Number as PropType<number>, default: 0.5 },
|
||||
},
|
||||
computed: {
|
||||
thumbStart(): { left: string } | { top: string } {
|
||||
const start = handleToTrack(this.handleLength, this.handlePosition) - this.handleLength / 2;
|
||||
|
||||
return this.direction === "Vertical" ? { top: `${start * 100}%` } : { left: `${start * 100}%` };
|
||||
},
|
||||
thumbEnd(): { right: string } | { bottom: string } {
|
||||
const end = 1 - handleToTrack(this.handleLength, this.handlePosition) - this.handleLength / 2;
|
||||
|
||||
return this.direction === "Vertical" ? { bottom: `${end * 100}%` } : { right: `${end * 100}%` };
|
||||
},
|
||||
sides(): { left: string; right: string } | { top: string; bottom: string } {
|
||||
return this.direction === "Vertical" ? { left: "0%", right: "0%" } : { top: "0%", bottom: "0%" };
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
pointerPos: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("pointerup", this.pointerUp);
|
||||
window.addEventListener("pointermove", this.pointerMove);
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener("pointerup", this.pointerUp);
|
||||
window.removeEventListener("pointermove", this.pointerMove);
|
||||
},
|
||||
methods: {
|
||||
trackLength(): number | undefined {
|
||||
const track = this.$refs.scrollTrack as HTMLDivElement | undefined;
|
||||
if (track) return this.direction === "Vertical" ? track.clientHeight - this.handleLength : track.clientWidth;
|
||||
|
||||
return undefined;
|
||||
},
|
||||
trackOffset(): number | undefined {
|
||||
const track = this.$refs.scrollTrack as HTMLDivElement | undefined;
|
||||
if (track) return this.direction === "Vertical" ? track.getBoundingClientRect().top : track.getBoundingClientRect().left;
|
||||
|
||||
return undefined;
|
||||
},
|
||||
clampHandlePosition(newPos: number) {
|
||||
const clampedPosition = Math.min(Math.max(newPos, 0), 1);
|
||||
this.$emit("update:handlePosition", clampedPosition);
|
||||
},
|
||||
updateHandlePosition(e: PointerEvent) {
|
||||
const trackLength = this.trackLength();
|
||||
if (trackLength === undefined) return;
|
||||
|
||||
const position = pointerPosition(this.direction, e);
|
||||
|
||||
this.clampHandlePosition(this.handlePosition + (position - this.pointerPos) / (trackLength * (1 - this.handleLength)));
|
||||
this.pointerPos = position;
|
||||
},
|
||||
grabHandle(e: PointerEvent) {
|
||||
if (!this.dragging) {
|
||||
this.dragging = true;
|
||||
this.pointerPos = pointerPosition(this.direction, e);
|
||||
}
|
||||
},
|
||||
grabArea(e: PointerEvent) {
|
||||
if (!this.dragging) {
|
||||
const trackLength = this.trackLength();
|
||||
const trackOffset = this.trackOffset();
|
||||
if (trackLength === undefined || trackOffset === undefined) return;
|
||||
|
||||
const oldPointer = handleToTrack(this.handleLength, this.handlePosition) * trackLength + trackOffset;
|
||||
const pointerPos = pointerPosition(this.direction, e);
|
||||
this.$emit("pressTrack", pointerPos - oldPointer);
|
||||
}
|
||||
},
|
||||
pointerUp() {
|
||||
this.dragging = false;
|
||||
},
|
||||
pointerMove(e: PointerEvent) {
|
||||
if (this.dragging) this.updateHandlePosition(e);
|
||||
},
|
||||
changePosition(difference: number) {
|
||||
const trackLength = this.trackLength();
|
||||
if (trackLength === undefined) return;
|
||||
|
||||
this.clampHandlePosition(this.handlePosition + difference / trackLength);
|
||||
},
|
||||
},
|
||||
});
|
||||
<script lang="ts" context="module">
|
||||
export type ScrollbarDirection = "Horizontal" | "Vertical";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="persistent-scrollbar" :class="direction.toLowerCase()">
|
||||
<button class="arrow decrease" @pointerdown="() => changePosition(-50)" tabindex="-1"></button>
|
||||
<div class="scroll-track" ref="scrollTrack" @pointerdown="(e) => grabArea(e)">
|
||||
<div class="scroll-thumb" @pointerdown="(e) => grabHandle(e)" :class="{ dragging }" :style="[thumbStart, thumbEnd, sides]"></div>
|
||||
</div>
|
||||
<button class="arrow increase" @click="() => changePosition(50)" tabindex="-1"></button>
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount, onDestroy } from "svelte";
|
||||
|
||||
// Linear Interpolation
|
||||
const lerp = (x: number, y: number, a: number): number => x * (1 - a) + y * a;
|
||||
|
||||
// Convert the position of the handle (0-1) to the position on the track (0-1).
|
||||
// This includes the 1/2 handle length gap of the possible handle positionson each side so the end of the handle doesn't go off the track.
|
||||
const handleToTrack = (handleLen: number, handlePos: number): number => lerp(handleLen / 2, 1 - handleLen / 2, handlePos);
|
||||
|
||||
const pointerPosition = (direction: ScrollbarDirection, e: PointerEvent): number => (direction === "Vertical" ? e.clientY : e.clientX);
|
||||
|
||||
// emits: { "update:handlePosition": null, pressTrack: (pointerOffset: number) => typeof pointerOffset === "number" }
|
||||
const dispatch = createEventDispatcher<{ handlePosition: number; pressTrack: number }>();
|
||||
|
||||
export let direction: ScrollbarDirection = "Vertical";
|
||||
export let handlePosition: number = 0.5;
|
||||
export let handleLength: number = 0.5;
|
||||
|
||||
let scrollTrack: HTMLDivElement;
|
||||
let dragging = false;
|
||||
let pointerPos = 0;
|
||||
let thumbTop: string | undefined = undefined;
|
||||
let thumbBottom: string | undefined = undefined;
|
||||
let thumbLeft: string | undefined = undefined;
|
||||
let thumbRight: string | undefined = undefined;
|
||||
|
||||
$: start = handleToTrack(handleLength, handlePosition) - handleLength / 2;
|
||||
$: end = 1 - handleToTrack(handleLength, handlePosition) - handleLength / 2;
|
||||
$: [thumbTop, thumbBottom, thumbLeft, thumbRight] = direction === "Vertical" ? [`${start * 100}%`, `${end * 100}%`, "0%", "0%"] : ["0%", "0%", `${start * 100}%`, `${end * 100}%`];
|
||||
|
||||
function trackLength(): number | undefined {
|
||||
return direction === "Vertical" ? scrollTrack.clientHeight - handleLength : scrollTrack.clientWidth;
|
||||
}
|
||||
|
||||
function trackOffset(): number | undefined {
|
||||
return direction === "Vertical" ? scrollTrack.getBoundingClientRect().top : scrollTrack.getBoundingClientRect().left;
|
||||
}
|
||||
|
||||
function clampHandlePosition(newPos: number) {
|
||||
const clampedPosition = Math.min(Math.max(newPos, 0), 1);
|
||||
dispatch("handlePosition", clampedPosition);
|
||||
}
|
||||
|
||||
function updateHandlePosition(e: PointerEvent) {
|
||||
const length = trackLength();
|
||||
if (length === undefined) return;
|
||||
|
||||
const position = pointerPosition(direction, e);
|
||||
|
||||
clampHandlePosition(handlePosition + (position - pointerPos) / (length * (1 - handleLength)));
|
||||
pointerPos = position;
|
||||
}
|
||||
|
||||
function grabHandle(e: PointerEvent) {
|
||||
if (!dragging) {
|
||||
dragging = true;
|
||||
pointerPos = pointerPosition(direction, e);
|
||||
}
|
||||
}
|
||||
|
||||
function grabArea(e: PointerEvent) {
|
||||
if (!dragging) {
|
||||
const length = trackLength();
|
||||
const offset = trackOffset();
|
||||
if (length === undefined || offset === undefined) return;
|
||||
|
||||
const oldPointer = handleToTrack(handleLength, handlePosition) * length + offset;
|
||||
const pointerPos = pointerPosition(direction, e);
|
||||
dispatch("pressTrack", pointerPos - oldPointer);
|
||||
}
|
||||
}
|
||||
|
||||
function pointerUp() {
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
function pointerMove(e: PointerEvent) {
|
||||
if (dragging) updateHandlePosition(e);
|
||||
}
|
||||
|
||||
function changePosition(difference: number) {
|
||||
const length = trackLength();
|
||||
if (length === undefined) return;
|
||||
|
||||
clampHandlePosition(handlePosition + difference / length);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("pointerup", pointerUp);
|
||||
window.addEventListener("pointermove", pointerMove);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
window.removeEventListener("pointerup", pointerUp);
|
||||
window.removeEventListener("pointermove", pointerMove);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={`persistent-scrollbar ${direction.toLowerCase()}`}>
|
||||
<button class="arrow decrease" on:pointerdown={() => changePosition(-50)} tabindex="-1" />
|
||||
<div class="scroll-track" bind:this={scrollTrack} on:pointerdown={grabArea}>
|
||||
<div class="scroll-thumb" on:pointerdown={grabHandle} class:dragging style:top={thumbTop} style:bottom={thumbBottom} style:left={thumbLeft} style:right={thumbRight} />
|
||||
</div>
|
||||
</template>
|
||||
<button class="arrow increase" on:click={() => changePosition(50)} tabindex="-1" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.persistent-scrollbar {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
|
||||
.arrow {
|
||||
flex: 0 0 auto;
|
||||
background: none;
|
||||
border: none;
|
||||
border-style: solid;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.scroll-track {
|
||||
<style lang="scss" global>
|
||||
.persistent-scrollbar {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
position: relative;
|
||||
|
||||
.scroll-thumb {
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
background: var(--color-5-dullgray);
|
||||
.arrow {
|
||||
flex: 0 0 auto;
|
||||
background: none;
|
||||
border: none;
|
||||
border-style: solid;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.dragging {
|
||||
background: var(--color-6-lowergray);
|
||||
.scroll-track {
|
||||
flex: 1 1 100%;
|
||||
position: relative;
|
||||
|
||||
.scroll-thumb {
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
background: var(--color-5-dullgray);
|
||||
|
||||
&:hover,
|
||||
&.dragging {
|
||||
background: var(--color-6-lowergray);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-click-area {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-click-area {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
.arrow.decrease {
|
||||
margin: 4px 3px;
|
||||
border-width: 0 5px 8px 5px;
|
||||
border-color: transparent transparent var(--color-5-dullgray) transparent;
|
||||
|
||||
.arrow.decrease {
|
||||
margin: 4px 3px;
|
||||
border-width: 0 5px 8px 5px;
|
||||
border-color: transparent transparent var(--color-5-dullgray) transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent transparent var(--color-6-lowergray) transparent;
|
||||
&:hover {
|
||||
border-color: transparent transparent var(--color-6-lowergray) transparent;
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent transparent var(--color-c-brightgray) transparent;
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent transparent var(--color-c-brightgray) transparent;
|
||||
|
||||
.arrow.increase {
|
||||
margin: 4px 3px;
|
||||
border-width: 8px 5px 0 5px;
|
||||
border-color: var(--color-5-dullgray) transparent transparent transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-6-lowergray) transparent transparent transparent;
|
||||
}
|
||||
&:active {
|
||||
border-color: var(--color-c-brightgray) transparent transparent transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arrow.increase {
|
||||
margin: 4px 3px;
|
||||
border-width: 8px 5px 0 5px;
|
||||
border-color: var(--color-5-dullgray) transparent transparent transparent;
|
||||
&.horizontal {
|
||||
flex-direction: row;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-6-lowergray) transparent transparent transparent;
|
||||
.arrow.decrease {
|
||||
margin: 3px 4px;
|
||||
border-width: 5px 8px 5px 0;
|
||||
border-color: transparent var(--color-5-dullgray) transparent transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent var(--color-6-lowergray) transparent transparent;
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent var(--color-c-brightgray) transparent transparent;
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
border-color: var(--color-c-brightgray) transparent transparent transparent;
|
||||
|
||||
.arrow.increase {
|
||||
margin: 3px 4px;
|
||||
border-width: 5px 0 5px 8px;
|
||||
border-color: transparent transparent transparent var(--color-5-dullgray);
|
||||
|
||||
&:hover {
|
||||
border-color: transparent transparent transparent var(--color-6-lowergray);
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent transparent transparent var(--color-c-brightgray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
flex-direction: row;
|
||||
|
||||
.arrow.decrease {
|
||||
margin: 3px 4px;
|
||||
border-width: 5px 8px 5px 0;
|
||||
border-color: transparent var(--color-5-dullgray) transparent transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent var(--color-6-lowergray) transparent transparent;
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent var(--color-c-brightgray) transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow.increase {
|
||||
margin: 3px 4px;
|
||||
border-width: 5px 0 5px 8px;
|
||||
border-color: transparent transparent transparent var(--color-5-dullgray);
|
||||
|
||||
&:hover {
|
||||
border-color: transparent transparent transparent var(--color-6-lowergray);
|
||||
}
|
||||
&:active {
|
||||
border-color: transparent transparent transparent var(--color-c-brightgray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user