Move scrollable behavior into LayoutRow/LayoutCol
This commit is contained in:
parent
3bf5023ef8
commit
45d75bd13f
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<MainWindow />
|
||||
|
||||
<div class="unsupported-modal-backdrop" v-if="showUnsupportedModal">
|
||||
<div class="unsupported-modal">
|
||||
<h2>Your browser currently doesn't support Graphite</h2>
|
||||
|
|
@ -91,72 +92,74 @@ img {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.scrollable,
|
||||
.scrollable-x,
|
||||
.scrollable-y {
|
||||
// Standard
|
||||
scrollbar-width: thin;
|
||||
scrollbar-width: 6px;
|
||||
scrollbar-gutter: 6px;
|
||||
scrollbar-color: var(--color-5-dullgray) transparent;
|
||||
.layout-row,
|
||||
.layout-col {
|
||||
.scrollable-x,
|
||||
.scrollable-y {
|
||||
// Standard
|
||||
scrollbar-width: thin;
|
||||
scrollbar-width: 6px;
|
||||
scrollbar-gutter: 6px;
|
||||
scrollbar-color: var(--color-5-dullgray) transparent;
|
||||
|
||||
&:not(:hover) {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
&:not(:hover) {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
&::-webkit-scrollbar {
|
||||
width: calc(2px + 6px + 2px);
|
||||
height: calc(2px + 6px + 2px);
|
||||
}
|
||||
// WebKit
|
||||
&::-webkit-scrollbar {
|
||||
width: calc(2px + 6px + 2px);
|
||||
height: calc(2px + 6px + 2px);
|
||||
}
|
||||
|
||||
&:not(:hover)::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
&:not(:hover)::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 0 1px var(--color-5-dullgray);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
&::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 0 1px var(--color-5-dullgray);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--color-6-lowergray);
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--color-6-lowergray);
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-clip: padding-box;
|
||||
background-color: var(--color-5-dullgray);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
margin: 2px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-6-lowergray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-clip: padding-box;
|
||||
background-color: var(--color-5-dullgray);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
margin: 2px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-6-lowergray);
|
||||
}
|
||||
.scrollable-x.scrollable-y {
|
||||
// Standard
|
||||
overflow: auto;
|
||||
// WebKit
|
||||
overflow: overlay;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
// Standard
|
||||
overflow: auto;
|
||||
// WebKit
|
||||
overflow: overlay;
|
||||
}
|
||||
.scrollable-x:not(.scrollable-y) {
|
||||
// Standard
|
||||
overflow-x: auto;
|
||||
// WebKit
|
||||
overflow-x: overlay;
|
||||
}
|
||||
|
||||
.scrollable-x {
|
||||
// Standard
|
||||
overflow-x: auto;
|
||||
// WebKit
|
||||
overflow-x: overlay;
|
||||
}
|
||||
|
||||
.scrollable-y {
|
||||
// Standard
|
||||
overflow-y: auto;
|
||||
// WebKit
|
||||
overflow-y: overlay;
|
||||
.scrollable-y:not(.scrollable-x) {
|
||||
// Standard
|
||||
overflow-y: auto;
|
||||
// WebKit
|
||||
overflow-y: overlay;
|
||||
}
|
||||
}
|
||||
|
||||
// For placeholder messages (remove eventually)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="['layout-col']">
|
||||
<div class="layout-col" :class="{ 'scrollable-x': scrollableX, 'scrollable-y': scrollableY }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -19,7 +19,12 @@
|
|||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
export default defineComponent({
|
||||
props: {
|
||||
scrollableX: { type: Boolean as PropType<boolean>, default: false },
|
||||
scrollableY: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="['layout-row']">
|
||||
<div class="layout-row" :class="{ 'scrollable-x': scrollableX, 'scrollable-y': scrollableY }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -19,7 +19,12 @@
|
|||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
export default defineComponent({
|
||||
props: {
|
||||
scrollableX: { type: Boolean as PropType<boolean>, default: false },
|
||||
scrollableY: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<LayoutCol :class="'document'">
|
||||
<LayoutRow :class="'options-bar scrollable-x'">
|
||||
<LayoutCol class="document">
|
||||
<LayoutRow class="options-bar" :scrollableX="true">
|
||||
<div class="left side">
|
||||
<DropdownInput :menuEntries="documentModeEntries" v-model:selectedIndex="documentModeSelectionIndex" :drawIcon="true" />
|
||||
|
||||
|
|
@ -66,9 +66,9 @@
|
|||
/>
|
||||
</div>
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'shelf-and-viewport'">
|
||||
<LayoutCol :class="'shelf'">
|
||||
<div class="tools scrollable-y">
|
||||
<LayoutRow class="shelf-and-viewport">
|
||||
<LayoutCol class="shelf">
|
||||
<LayoutCol class="tools" :scrollableY="true">
|
||||
<ShelfItemInput icon="LayoutSelectTool" title="Select Tool (V)" :active="activeTool === 'Select'" :action="() => selectTool('Select')" />
|
||||
<ShelfItemInput icon="LayoutCropTool" title="Crop Tool" :active="activeTool === 'Crop'" :action="() => (dialog.comingSoon(289), false) && selectTool('Crop')" />
|
||||
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => selectTool('Navigate')" />
|
||||
|
|
@ -104,50 +104,52 @@
|
|||
<ShelfItemInput icon="VectorRectangleTool" title="Rectangle Tool (M)" :active="activeTool === 'Rectangle'" :action="() => selectTool('Rectangle')" />
|
||||
<ShelfItemInput icon="VectorEllipseTool" title="Ellipse Tool (E)" :active="activeTool === 'Ellipse'" :action="() => selectTool('Ellipse')" />
|
||||
<ShelfItemInput icon="VectorShapeTool" title="Shape Tool (Y)" :active="activeTool === 'Shape'" :action="() => selectTool('Shape')" />
|
||||
</div>
|
||||
</LayoutCol>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="working-colors">
|
||||
|
||||
<LayoutCol class="working-colors">
|
||||
<SwatchPairInput />
|
||||
<div class="swap-and-reset">
|
||||
<IconButton :action="swapWorkingColors" :icon="'Swap'" title="Swap (Shift+X)" :size="16" />
|
||||
<IconButton :action="resetWorkingColors" :icon="'ResetColors'" title="Reset (Ctrl+Shift+X)" :size="16" />
|
||||
</div>
|
||||
</div>
|
||||
</LayoutCol>
|
||||
</LayoutCol>
|
||||
<LayoutCol :class="'viewport'">
|
||||
<LayoutRow :class="'bar-area'">
|
||||
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Horizontal'" :class="'top-ruler'" />
|
||||
<LayoutCol class="viewport">
|
||||
<LayoutRow class="bar-area">
|
||||
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Horizontal'" class="top-ruler" />
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'canvas-area'">
|
||||
<LayoutCol :class="'bar-area'">
|
||||
<LayoutRow class="canvas-area">
|
||||
<LayoutCol class="bar-area">
|
||||
<CanvasRuler :origin="rulerOrigin.y" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Vertical'" />
|
||||
</LayoutCol>
|
||||
<LayoutCol :class="'canvas-area'">
|
||||
<LayoutCol class="canvas-area">
|
||||
<div class="canvas" ref="canvas" :style="{ cursor: canvasCursor }" @pointerdown="(e: PointerEvent) => canvasPointerDown(e)">
|
||||
<svg class="artboards" v-html="artboardSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
|
||||
<svg class="artwork" v-html="artworkSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
|
||||
<svg class="overlays" v-html="overlaysSvg" :style="{ width: canvasSvgWidth, height: canvasSvgHeight }"></svg>
|
||||
</div>
|
||||
</LayoutCol>
|
||||
<LayoutCol :class="'bar-area'">
|
||||
<LayoutCol class="bar-area">
|
||||
<PersistentScrollbar
|
||||
:direction="'Vertical'"
|
||||
:handlePosition="scrollbarPos.y"
|
||||
@update:handlePosition="(newValue: number) => translateCanvasY(newValue)"
|
||||
v-model:handleLength="scrollbarSize.y"
|
||||
@pressTrack="(delta: number) => pageY(delta)"
|
||||
:class="'right-scrollbar'"
|
||||
class="right-scrollbar"
|
||||
/>
|
||||
</LayoutCol>
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'bar-area'">
|
||||
<LayoutRow class="bar-area">
|
||||
<PersistentScrollbar
|
||||
:direction="'Horizontal'"
|
||||
:handlePosition="scrollbarPos.x"
|
||||
@update:handlePosition="(newValue: number) => translateCanvasX(newValue)"
|
||||
v-model:handleLength="scrollbarSize.x"
|
||||
@pressTrack="(delta: number) => pageX(delta)"
|
||||
:class="'bottom-scrollbar'"
|
||||
class="bottom-scrollbar"
|
||||
/>
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
|
|
@ -191,9 +193,13 @@
|
|||
min-height: 8px;
|
||||
}
|
||||
|
||||
.working-colors .swap-and-reset {
|
||||
.working-colors {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
|
||||
.swap-and-reset {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<LayoutCol :class="'layer-tree-panel'">
|
||||
<LayoutRow :class="'options-bar'">
|
||||
<LayoutCol class="layer-tree-panel">
|
||||
<LayoutRow class="options-bar">
|
||||
<DropdownInput
|
||||
v-model:selectedIndex="blendModeSelectedIndex"
|
||||
@update:selectedIndex="(newSelectedIndex: number) => setLayerBlendMode(newSelectedIndex)"
|
||||
|
|
@ -28,8 +28,8 @@
|
|||
<p>The contents of this popover menu are coming soon</p>
|
||||
</PopoverButton>
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'layer-tree scrollable-y'">
|
||||
<LayoutCol :class="'list'" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="updateInsertLine($event)" @dragend="drop()">
|
||||
<LayoutRow class="layer-tree" :scrollableY="true">
|
||||
<LayoutCol class="list" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="updateInsertLine($event)" @dragend="drop()">
|
||||
<div class="layer-row" v-for="({ entry: layer }, index) in layers" :key="String(layer.path.slice(-1))">
|
||||
<div class="visibility">
|
||||
<IconButton
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
<div class="dialog-modal">
|
||||
<FloatingMenu :type="'Dialog'" :direction="'Center'">
|
||||
<LayoutRow>
|
||||
<LayoutCol :class="'icon-column'">
|
||||
<LayoutCol class="icon-column">
|
||||
<!-- `dialog.state.icon` class exists to provide special sizing in CSS to specific icons -->
|
||||
<IconLabel :icon="dialog.state.icon" :class="dialog.state.icon.toLowerCase()" />
|
||||
</LayoutCol>
|
||||
<LayoutCol :class="'main-column'">
|
||||
<TextLabel :bold="true" :class="'heading'">{{ dialog.state.heading }}</TextLabel>
|
||||
<TextLabel :class="'details'">{{ dialog.state.details }}</TextLabel>
|
||||
<LayoutRow :class="'buttons-row'" v-if="dialog.state.buttons.length > 0">
|
||||
<LayoutCol class="main-column">
|
||||
<TextLabel :bold="true" class="heading">{{ dialog.state.heading }}</TextLabel>
|
||||
<TextLabel class="details">{{ dialog.state.details }}</TextLabel>
|
||||
<LayoutRow class="buttons-row" v-if="dialog.state.buttons.length > 0">
|
||||
<TextButton v-for="(button, index) in dialog.state.buttons" :key="index" :title="button.tooltip" :action="() => button.callback && button.callback()" v-bind="button.props" />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<div class="floating-menu" :class="[direction.toLowerCase(), type.toLowerCase()]" v-if="open || type === 'Dialog'" ref="floatingMenu">
|
||||
<div class="tail" v-if="type === 'Popover'"></div>
|
||||
<div class="floating-menu-container" ref="floatingMenuContainer">
|
||||
<div class="floating-menu-content" :class="{ 'scrollable-y': scrollable }" ref="floatingMenuContent" :style="floatingMenuContentStyle">
|
||||
<LayoutCol class="floating-menu-content" :scrollableY="scrollableY" ref="floatingMenuContent" :style="floatingMenuContentStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</LayoutCol>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -179,6 +179,8 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, PropType, StyleValue } from "vue";
|
||||
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
|
||||
export type MenuDirection = "Top" | "Bottom" | "Left" | "Right" | "TopLeft" | "TopRight" | "BottomLeft" | "BottomRight" | "Center";
|
||||
export type MenuType = "Popover" | "Dropdown" | "Dialog";
|
||||
|
||||
|
|
@ -190,14 +192,13 @@ export default defineComponent({
|
|||
type: { type: String as PropType<MenuType>, required: true },
|
||||
windowEdgeMargin: { type: Number as PropType<number>, default: 6 },
|
||||
minWidth: { type: Number as PropType<number>, default: 0 },
|
||||
scrollable: { type: Boolean as PropType<boolean>, default: false },
|
||||
scrollableY: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
data() {
|
||||
const containerResizeObserver = new ResizeObserver((entries) => {
|
||||
const content = entries[0].target.querySelector(".floating-menu-content") as HTMLElement;
|
||||
content.style.minWidth = `${entries[0].contentRect.width}px`;
|
||||
});
|
||||
|
||||
return {
|
||||
open: false,
|
||||
pointerStillDown: false,
|
||||
|
|
@ -206,9 +207,11 @@ export default defineComponent({
|
|||
},
|
||||
updated() {
|
||||
const floatingMenuContainer = this.$refs.floatingMenuContainer as HTMLElement;
|
||||
const floatingMenuContent = this.$refs.floatingMenuContent as HTMLElement;
|
||||
const floatingMenuContentComponent = this.$refs.floatingMenuContent as typeof LayoutCol;
|
||||
const floatingMenuContent = floatingMenuContentComponent && (floatingMenuContentComponent.$el as HTMLElement);
|
||||
const workspace = document.querySelector(".workspace-row");
|
||||
if (!floatingMenuContainer || !floatingMenuContent || !workspace) return;
|
||||
|
||||
if (!floatingMenuContainer || !floatingMenuContentComponent || !floatingMenuContent || !workspace) return;
|
||||
|
||||
const workspaceBounds = workspace.getBoundingClientRect();
|
||||
const floatingMenuBounds = floatingMenuContent.getBoundingClientRect();
|
||||
|
|
@ -224,13 +227,11 @@ export default defineComponent({
|
|||
floatingMenuContent.style.left = `${this.windowEdgeMargin}px`;
|
||||
if (workspaceBounds.left + floatingMenuContainer.getBoundingClientRect().left === 12) zeroedBorderDirection2 = "Left";
|
||||
}
|
||||
|
||||
if (floatingMenuBounds.right + this.windowEdgeMargin >= workspaceBounds.right) {
|
||||
floatingMenuContent.style.right = `${this.windowEdgeMargin}px`;
|
||||
if (workspaceBounds.right - floatingMenuContainer.getBoundingClientRect().right === 12) zeroedBorderDirection2 = "Right";
|
||||
}
|
||||
}
|
||||
|
||||
if (this.direction === "Left" || this.direction === "Right") {
|
||||
zeroedBorderDirection2 = this.direction === "Left" ? "Right" : "Left";
|
||||
|
||||
|
|
@ -238,7 +239,6 @@ export default defineComponent({
|
|||
floatingMenuContent.style.top = `${this.windowEdgeMargin}px`;
|
||||
if (workspaceBounds.top + floatingMenuContainer.getBoundingClientRect().top === 12) zeroedBorderDirection1 = "Top";
|
||||
}
|
||||
|
||||
if (floatingMenuBounds.bottom + this.windowEdgeMargin >= workspaceBounds.bottom) {
|
||||
floatingMenuContent.style.bottom = `${this.windowEdgeMargin}px`;
|
||||
if (workspaceBounds.bottom - floatingMenuContainer.getBoundingClientRect().bottom === 12) zeroedBorderDirection1 = "Bottom";
|
||||
|
|
@ -277,23 +277,21 @@ export default defineComponent({
|
|||
},
|
||||
getWidth(callback: (width: number) => void) {
|
||||
this.$nextTick(() => {
|
||||
const floatingMenuContent = this.$refs.floatingMenuContent as HTMLElement;
|
||||
const floatingMenuContent = (this.$refs.floatingMenuContent as typeof LayoutCol).$el as HTMLElement;
|
||||
const width = floatingMenuContent.clientWidth;
|
||||
|
||||
callback(width);
|
||||
});
|
||||
},
|
||||
disableMinWidth(callback: (minWidth: string) => void) {
|
||||
this.$nextTick(() => {
|
||||
const floatingMenuContent = this.$refs.floatingMenuContent as HTMLElement;
|
||||
const floatingMenuContent = (this.$refs.floatingMenuContent as typeof LayoutCol).$el as HTMLElement;
|
||||
const initialMinWidth = floatingMenuContent.style.minWidth;
|
||||
floatingMenuContent.style.minWidth = "0";
|
||||
|
||||
callback(initialMinWidth);
|
||||
});
|
||||
},
|
||||
enableMinWidth(minWidth: string) {
|
||||
const floatingMenuContent = this.$refs.floatingMenuContent as HTMLElement;
|
||||
const floatingMenuContent = (this.$refs.floatingMenuContent as typeof LayoutCol).$el as HTMLElement;
|
||||
floatingMenuContent.style.minWidth = minWidth;
|
||||
},
|
||||
pointerMoveHandler(e: PointerEvent) {
|
||||
|
|
@ -303,22 +301,18 @@ export default defineComponent({
|
|||
// TODO: Simplify the following expression when optional chaining is supported by the build system
|
||||
const pointerOverOwnFloatingMenuSpawner =
|
||||
pointerOverFloatingMenuSpawner && pointerOverFloatingMenuSpawner.parentElement && pointerOverFloatingMenuSpawner.parentElement.contains(this.$refs.floatingMenu as HTMLElement);
|
||||
|
||||
// Swap this open floating menu with the one created by the floating menu spawner being hovered over
|
||||
if (pointerOverFloatingMenuSpawner && !pointerOverOwnFloatingMenuSpawner) {
|
||||
this.setClosed();
|
||||
pointerOverFloatingMenuSpawner.click();
|
||||
}
|
||||
|
||||
// Close the floating menu if the pointer has strayed far enough from its bounds
|
||||
if (this.isPointerEventOutsideFloatingMenu(e, POINTER_STRAY_DISTANCE) && !pointerOverOwnFloatingMenuSpawner && !pointerOverFloatingMenuKeepOpen) {
|
||||
// TODO: Extend this rectangle bounds check to all `data-hover-menu-keep-open` element bounds up the DOM tree since currently
|
||||
// submenus disappear with zero stray distance if the cursor is further than the stray distance from only the top-level menu
|
||||
this.setClosed();
|
||||
}
|
||||
|
||||
const eventIncludesLmb = Boolean(e.buttons & 1);
|
||||
|
||||
// Clean up any messes from lost pointerup events
|
||||
if (!this.open && !eventIncludesLmb) {
|
||||
this.pointerStillDown = false;
|
||||
|
|
@ -329,7 +323,6 @@ export default defineComponent({
|
|||
// Close the floating menu if the pointer clicked outside the floating menu (but within stray distance)
|
||||
if (this.isPointerEventOutsideFloatingMenu(e)) {
|
||||
this.setClosed();
|
||||
|
||||
// Track if the left pointer button is now down so its later click event can be canceled
|
||||
const eventIsForLmb = e.button === 0;
|
||||
if (eventIsForLmb) this.pointerStillDown = true;
|
||||
|
|
@ -337,12 +330,10 @@ export default defineComponent({
|
|||
},
|
||||
pointerUpHandler(e: PointerEvent) {
|
||||
const eventIsForLmb = e.button === 0;
|
||||
|
||||
if (this.pointerStillDown && eventIsForLmb) {
|
||||
// Clean up self
|
||||
this.pointerStillDown = false;
|
||||
window.removeEventListener("pointerup", this.pointerUpHandler);
|
||||
|
||||
// Prevent the click event from firing, which would normally occur right after this pointerup event
|
||||
window.addEventListener("click", this.clickHandlerCapture, true);
|
||||
}
|
||||
|
|
@ -350,7 +341,6 @@ export default defineComponent({
|
|||
clickHandlerCapture(e: MouseEvent) {
|
||||
// Stop the click event from reopening this floating menu if the click event targets the floating menu's button
|
||||
e.stopPropagation();
|
||||
|
||||
// Clean up self
|
||||
window.removeEventListener("click", this.clickHandlerCapture, true);
|
||||
},
|
||||
|
|
@ -374,13 +364,10 @@ export default defineComponent({
|
|||
if (newState && !oldState) {
|
||||
// Close floating menu if pointer strays far enough away
|
||||
window.addEventListener("pointermove", this.pointerMoveHandler);
|
||||
|
||||
// Close floating menu if pointer is outside (but within stray distance)
|
||||
window.addEventListener("pointerdown", this.pointerDownHandler);
|
||||
|
||||
// Cancel the subsequent click event to prevent the floating menu from reopening if the floating menu's button is the click event target
|
||||
window.addEventListener("pointerup", this.pointerUpHandler);
|
||||
|
||||
// Floating menu min-width resize observer
|
||||
this.$nextTick(() => {
|
||||
const floatingMenuContainer = this.$refs.floatingMenuContainer as HTMLElement;
|
||||
|
|
@ -390,12 +377,10 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Switching from open to closed
|
||||
if (!newState && oldState) {
|
||||
window.removeEventListener("pointermove", this.pointerMoveHandler);
|
||||
window.removeEventListener("pointerdown", this.pointerDownHandler);
|
||||
|
||||
this.containerResizeObserver.disconnect();
|
||||
}
|
||||
},
|
||||
|
|
@ -407,5 +392,6 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
},
|
||||
components: { LayoutCol },
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<FloatingMenu :class="'menu-list'" :direction="direction" :type="'Dropdown'" ref="floatingMenu" :windowEdgeMargin="0" :scrollable="scrollable" data-hover-menu-keep-open>
|
||||
<FloatingMenu class="menu-list" :direction="direction" :type="'Dropdown'" ref="floatingMenu" :windowEdgeMargin="0" :scrollableY="scrollableY" data-hover-menu-keep-open>
|
||||
<template v-for="(section, sectionIndex) in menuEntries" :key="sectionIndex">
|
||||
<Separator :type="'List'" :direction="'Vertical'" v-if="sectionIndex > 0" />
|
||||
<div
|
||||
|
|
@ -12,8 +12,8 @@
|
|||
@pointerleave="handleEntryPointerLeave(entry)"
|
||||
:data-hover-menu-spawner-extend="entry.children && []"
|
||||
>
|
||||
<CheckboxInput v-if="entry.checkbox" v-model:checked="entry.checked" :outlineStyle="true" :class="'entry-checkbox'" />
|
||||
<IconLabel v-else-if="entry.icon && drawIcon" :icon="entry.icon" :class="'entry-icon'" />
|
||||
<CheckboxInput v-if="entry.checkbox" v-model:checked="entry.checked" :outlineStyle="true" class="entry-checkbox" />
|
||||
<IconLabel v-else-if="entry.icon && drawIcon" :icon="entry.icon" class="entry-icon" />
|
||||
<div v-else-if="drawIcon" class="no-icon"></div>
|
||||
|
||||
<span class="entry-label">{{ entry.label }}</span>
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
v-if="entry.children"
|
||||
:direction="'TopRight'"
|
||||
:menuEntries="entry.children"
|
||||
v-bind="{ defaultAction, minWidth, drawIcon, scrollable }"
|
||||
v-bind="{ defaultAction, minWidth, drawIcon, scrollableY }"
|
||||
:ref="(ref: any) => setEntryRefs(entry, ref)"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -168,7 +168,7 @@ const MenuList = defineComponent({
|
|||
defaultAction: { type: Function as PropType<() => void>, required: false },
|
||||
minWidth: { type: Number as PropType<number>, default: 0 },
|
||||
drawIcon: { type: Boolean as PropType<boolean>, default: false },
|
||||
scrollable: { type: Boolean as PropType<boolean>, default: false },
|
||||
scrollableY: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
methods: {
|
||||
setEntryRefs(menuEntry: MenuListEntry, ref: typeof FloatingMenu) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="dropdown-input">
|
||||
<div class="dropdown-box" :class="{ disabled }" :style="{ minWidth: `${minWidth}px` }" @click="() => clickDropdownBox()" data-hover-menu-spawner>
|
||||
<IconLabel :class="'dropdown-icon'" :icon="activeEntry.icon" v-if="activeEntry.icon" />
|
||||
<IconLabel class="dropdown-icon" :icon="activeEntry.icon" v-if="activeEntry.icon" />
|
||||
<span>{{ activeEntry.label }}</span>
|
||||
<IconLabel :class="'dropdown-arrow'" :icon="'DropdownArrow'" />
|
||||
<IconLabel class="dropdown-arrow" :icon="'DropdownArrow'" />
|
||||
</div>
|
||||
<MenuList
|
||||
v-model:activeEntry="activeEntry"
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
:menuEntries="menuEntries"
|
||||
:direction="'Bottom'"
|
||||
:drawIcon="drawIcon"
|
||||
:scrollable="true"
|
||||
:scrollableY="true"
|
||||
ref="menuList"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<LayoutCol class="main-window">
|
||||
<LayoutRow :class="'title-bar-row'">
|
||||
<LayoutRow class="title-bar-row">
|
||||
<TitleBar :platform="platform" :maximized="maximized" />
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'workspace-row'">
|
||||
<LayoutRow class="workspace-row">
|
||||
<Workspace />
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'status-bar-row'">
|
||||
<LayoutRow class="status-bar-row">
|
||||
<StatusBar />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="panel">
|
||||
<div class="tab-bar" :class="{ 'min-widths': tabMinWidths }">
|
||||
<div class="tab-group scrollable-x">
|
||||
<LayoutCol class="panel">
|
||||
<LayoutRow class="tab-bar" :class="{ 'min-widths': tabMinWidths }">
|
||||
<LayoutRow class="tab-group" :scrollableX="true">
|
||||
<div
|
||||
class="tab"
|
||||
:class="{ active: tabIndex === tabActiveIndex }"
|
||||
|
|
@ -13,41 +13,35 @@
|
|||
<span>{{ tabLabel }}</span>
|
||||
<IconButton :action="(e) => (e && e.stopPropagation(), closeAction && closeAction(tabIndex))" :icon="'CloseX'" :size="16" v-if="tabCloseButtons" />
|
||||
</div>
|
||||
</div>
|
||||
</LayoutRow>
|
||||
<PopoverButton :icon="'VerticalEllipsis'">
|
||||
<h3>Panel Options</h3>
|
||||
<p>The contents of this popover menu are coming soon</p>
|
||||
</PopoverButton>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
</LayoutRow>
|
||||
<LayoutCol class="panel-body">
|
||||
<component :is="panelType" />
|
||||
</div>
|
||||
</div>
|
||||
</LayoutCol>
|
||||
</LayoutCol>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.panel {
|
||||
background: var(--color-1-nearblack);
|
||||
border-radius: 8px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-bar {
|
||||
height: 28px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: auto;
|
||||
|
||||
&.min-widths .tab-group .tab {
|
||||
min-width: 124px;
|
||||
min-width: 120px;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.tab-group {
|
||||
flex: 1 1 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
|
||||
// This always hangs out at the end of the last tab, providing 16px (15px plus the 1px reserved for the separator line) to the right of the tabs.
|
||||
|
|
@ -154,6 +148,8 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import Document from "@/components/panels/Document.vue";
|
||||
import LayerTree from "@/components/panels/LayerTree.vue";
|
||||
import Minimap from "@/components/panels/Minimap.vue";
|
||||
|
|
@ -161,7 +157,7 @@ import Properties from "@/components/panels/Properties.vue";
|
|||
import IconButton from "@/components/widgets/buttons/IconButton.vue";
|
||||
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
|
||||
|
||||
const components = {
|
||||
const panelComponents = {
|
||||
Document,
|
||||
Properties,
|
||||
LayerTree,
|
||||
|
|
@ -169,18 +165,23 @@ const components = {
|
|||
IconButton,
|
||||
PopoverButton,
|
||||
};
|
||||
type PanelTypes = keyof typeof panelComponents;
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["documents"],
|
||||
components,
|
||||
props: {
|
||||
tabMinWidths: { type: Boolean as PropType<boolean>, default: false },
|
||||
tabCloseButtons: { type: Boolean as PropType<boolean>, default: false },
|
||||
tabLabels: { type: Array as PropType<string[]>, required: true },
|
||||
tabActiveIndex: { type: Number as PropType<number>, required: true },
|
||||
panelType: { type: String as PropType<keyof typeof components>, required: true },
|
||||
panelType: { type: String as PropType<PanelTypes>, required: true },
|
||||
clickAction: { type: Function as PropType<(index: number) => void>, required: false },
|
||||
closeAction: { type: Function as PropType<(index: number) => void>, required: false },
|
||||
},
|
||||
components: {
|
||||
LayoutCol,
|
||||
LayoutRow,
|
||||
...panelComponents,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue