functioning resize

This commit is contained in:
Gerard Burns 2023-11-22 20:50:55 +00:00
parent 96046dab3d
commit f1c3d50930
2 changed files with 150 additions and 61 deletions

View File

@ -9,51 +9,42 @@
import NavItem from "components/common/NavItem.svelte" import NavItem from "components/common/NavItem.svelte"
import RoleIndicator from "./RoleIndicator.svelte" import RoleIndicator from "./RoleIndicator.svelte"
import DropdownMenu from "./DropdownMenu.svelte" import DropdownMenu from "./DropdownMenu.svelte"
import { onMount, tick } from "svelte"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { getVerticalResizeActions } from './resizable';
import { tick } from "svelte"
let search = false const [resizable, resizableHandle] = getVerticalResizeActions();
let searching = false
let resizing = false let resizing = false
let searchValue = "" let searchValue = ""
let searchInput let searchInput
let container let container
let screensContainer let screensContainer
let scrolling = false let scrolling = false
let previousHeight = null
let dragOffset
$: filteredScreens = getFilteredScreens($sortedScreens, searchValue) $: filteredScreens = getFilteredScreens($sortedScreens, searchValue)
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const openSearch = async () => { const openSearch = async () => {
search = true searching = true
await tick() await tick()
searchInput.focus() searchInput.focus()
screensContainer.scroll({ top: 0, behavior: "smooth" }) screensContainer.scroll({ top: 0, behavior: "smooth" })
previousHeight = $screensHeight
$screensHeight = "calc(100% + 1px)"
} }
const closeSearch = async () => { const closeSearch = async () => {
if (previousHeight) { searching = false
// Restore previous height and wait for animation
$screensHeight = previousHeight
previousHeight = null
await sleep(300)
}
search = false
searchValue = "" searchValue = ""
} }
const getFilteredScreens = (screens, search) => { const getFilteredScreens = (screens, searchValue) => {
return screens.filter(screen => { return screens.filter(screen => {
return !search || screen.routing.route.includes(search) return !searchValue || screen.routing.route.includes(searchValue)
}) })
} }
const handleAddButton = () => { const handleAddButton = () => {
if (search) { if (searching) {
closeSearch() closeSearch()
} else { } else {
$goto("../new") $goto("../new")
@ -70,67 +61,35 @@
scrolling = e.target.scrollTop !== 0 scrolling = e.target.scrollTop !== 0
} }
const startResizing = e => {
// Reset the height store to match the true height
$screensHeight = `${container.getBoundingClientRect().height}px`
// Store an offset to easily compute new height when moving the mouse
dragOffset = parseInt($screensHeight) - e.clientY
// Add event listeners
resizing = true
document.addEventListener("mousemove", resize)
document.addEventListener("mouseup", stopResizing)
}
const resize = e => {
// Prevent negative heights as this screws with layout
const newHeight = Math.max(0, e.clientY + dragOffset)
if (newHeight == null || isNaN(newHeight)) {
return
}
$screensHeight = `${newHeight}px`
}
const stopResizing = () => {
resizing = false
document.removeEventListener("mousemove", resize)
}
onMount(() => {
// Ensure we aren't stuck at 100% height from leaving while searching
if ($screensHeight == null || isNaN(parseInt($screensHeight))) {
$screensHeight = "210px"
}
})
</script> </script>
<svelte:window on:keydown={onKeyDown} /> <svelte:window on:keydown={onKeyDown} />
<div <div
class="screens" class="screens"
class:search class:searching
class:resizing class:resizing
style={`height:${$screensHeight};`} style={`height:${$screensHeight};`}
bind:this={container} bind:this={container}
use:resizable
> >
<div class="header" class:scrolling> <div class="header" class:scrolling>
<input <input
readonly={!search} readonly={!searching}
bind:value={searchValue} bind:value={searchValue}
bind:this={searchInput} bind:this={searchInput}
class="input" class="input"
placeholder="Search for screens" placeholder="Search for screens"
/> />
<div class="title" class:hide={search}> <div on:click={openSearch} class="title" class:hide={searching}>
<Body size="S">Screens</Body> <Body size="S">Screens</Body>
</div> </div>
<div on:click={openSearch} class="searchButton" class:hide={search}> <div on:click={openSearch} class="searchButton" class:hide={searching}>
<Icon size="S" name="Search" /> <Icon size="S" name="Search" />
</div> </div>
<div <div
on:click={handleAddButton} on:click={handleAddButton}
class="addButton" class="addButton"
class:closeButton={search} class:closeButton={searching}
> >
<Icon name="Add" /> <Icon name="Add" />
</div> </div>
@ -164,8 +123,10 @@
</div> </div>
<div <div
disabled={searching}
class="divider" class="divider"
on:mousedown={startResizing} class:disabled={searching}
use:resizableHandle
on:dblclick={() => screensHeight.set("210px")} on:dblclick={() => screensHeight.set("210px")}
/> />
</div> </div>
@ -177,10 +138,11 @@
min-height: 147px; min-height: 147px;
max-height: calc(100% - 147px); max-height: calc(100% - 147px);
position: relative; position: relative;
}
.screens.search {
transition: height 300ms ease-out; transition: height 300ms ease-out;
}
.screens.searching {
max-height: none; max-height: none;
height: 100% !important;
} }
.screens.resizing { .screens.resizing {
user-select: none; user-select: none;
@ -219,7 +181,7 @@
.input::placeholder { .input::placeholder {
color: var(--spectrum-global-color-gray-600); color: var(--spectrum-global-color-gray-600);
} }
.screens.search input { .screens.searching input {
display: block; display: block;
} }
@ -305,4 +267,10 @@
.divider:hover:after { .divider:hover:after {
background: var(--spectrum-global-color-gray-300); background: var(--spectrum-global-color-gray-300);
} }
.divider.disabled {
cursor: auto;
}
.divider.disabled:after {
background: var(--spectrum-global-color-gray-200);
}
</style> </style>

View File

@ -0,0 +1,121 @@
export const getHorizontalResizeActions = (initialValue, setValue = () => {}) => {
let element = null;
const elementAction = (node) => {
element = node;
if (initialValue != null) {
element.style.height = `${initialValue}px`
}
return {
destroy() {
element = null;
}
}
}
const dragHandleAction = (node) => {
let startWidth = null;
let startPosition = null;
const handleMouseMove = (e) => {
const change = e.pageX - startPosition;
element.style.width = `${startWidth + change}px`
}
const handleMouseUp = () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
element.style.removeProperty('transition'); // remove temporary transition override
setValue(element.clientHeight);
}
const handleMouseDown = (e) => {
if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") {
return;
}
element.style.transition = "width 0ms"; // temporarily override any width transitions
startWidth = element.clientWidth;
startPosition = e.pageX;
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
};
node.addEventListener("mousedown", handleMouseDown);
return {
destroy() {
node.removeEventListener("mousedown", handleMouseDown);
}
}
}
return [
elementAction,
dragHandleAction
]
};
export const getVerticalResizeActions = (initialValue, setValue = () => {}) => {
let element = null;
const elementAction = (node) => {
element = node;
if (initialValue != null) {
element.style.height = `${initialValue}px`
}
return {
destroy() {
element = null;
}
}
}
const dragHandleAction = (node) => {
let startHeight = null;
let startPosition = null;
const handleMouseMove = (e) => {
const change = e.pageY - startPosition;
element.style.height = `${startHeight + change}px`
}
const handleMouseUp = () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
element.style.removeProperty('transition'); // remove temporary transition override
setValue(element.clientHeight);
}
const handleMouseDown = (e) => {
if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") {
return;
}
element.style.transition = "height 0ms"; // temporarily override any height transitions
startHeight = element.clientHeight;
startPosition = e.pageY;
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
};
node.addEventListener("mousedown", handleMouseDown);
return {
destroy() {
node.removeEventListener("mousedown", handleMouseDown);
}
}
}
return [
elementAction,
dragHandleAction
]
};