Rework spinner buttons (#2230)
* Align spinner buttons on right The initial rationale for splitting them up was to be more touch friendly, but we won't be able to use them on mobile anyway due to the conflicts with double taps zooming in. On desktop, having them apart requires more mouse movement when overshooting, so it's better to have them in one place. Text is now left-aligned again, which matches our other inputs like learning steps. The left/right buttons have been changed to up/down, which matches our Qt spinners, and avoids RTL concerns. This commit also removes the border on hover/select - it caused the left-aligned content to flicker, and didn't look correct. Perhaps we could add it back in a better way in the future. * Hide spinner buttons on mobile devices Tapping on them conflicts with the page zoom gesture. * Remove min-height on spinner buttons * Only show spinner on hover Since they're only useful with a mouse, and only useful when they're under the cursor, hiding them when focused keeps things less cluttered.
This commit is contained in:
parent
627313666e
commit
3d8c7f5ea1
@ -3,16 +3,16 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { isDesktop } from "@tslib/platform";
|
||||||
|
|
||||||
import IconConstrain from "./IconConstrain.svelte";
|
import IconConstrain from "./IconConstrain.svelte";
|
||||||
import { chevronLeft, chevronRight } from "./icons";
|
import { chevronDown, chevronUp } from "./icons";
|
||||||
|
|
||||||
export let value: number;
|
export let value: number;
|
||||||
export let step = 1;
|
export let step = 1;
|
||||||
export let min = 1;
|
export let min = 1;
|
||||||
export let max = 9999;
|
export let max = 9999;
|
||||||
|
|
||||||
const rtl: boolean = window.getComputedStyle(document.body).direction == "rtl";
|
|
||||||
|
|
||||||
let input: HTMLInputElement;
|
let input: HTMLInputElement;
|
||||||
let focused = false;
|
let focused = false;
|
||||||
|
|
||||||
@ -67,31 +67,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="spin-box" on:wheel={handleWheel}>
|
<div class="spin-box" on:wheel={handleWheel}>
|
||||||
<button
|
|
||||||
class="decrement"
|
|
||||||
disabled={value == min}
|
|
||||||
tabindex="-1"
|
|
||||||
on:click={() => {
|
|
||||||
input.focus();
|
|
||||||
if (value > min) {
|
|
||||||
change(-step);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:mousedown={() =>
|
|
||||||
longPress(() => {
|
|
||||||
if (value > min) {
|
|
||||||
change(-step);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
on:mouseup={() => {
|
|
||||||
clearTimeout(pressTimer);
|
|
||||||
pressed = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconConstrain>
|
|
||||||
{@html rtl ? chevronRight : chevronLeft}
|
|
||||||
</IconConstrain>
|
|
||||||
</button>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
pattern="[0-9]*"
|
pattern="[0-9]*"
|
||||||
@ -105,31 +80,58 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||||||
on:focusin={() => (focused = true)}
|
on:focusin={() => (focused = true)}
|
||||||
on:focusout={() => (focused = false)}
|
on:focusout={() => (focused = false)}
|
||||||
/>
|
/>
|
||||||
<button
|
{#if isDesktop()}
|
||||||
class="increment"
|
<button
|
||||||
disabled={value == max}
|
class="decrement"
|
||||||
tabindex="-1"
|
disabled={value == min}
|
||||||
on:click={() => {
|
tabindex="-1"
|
||||||
input.focus();
|
on:click={() => {
|
||||||
if (value < max) {
|
input.focus();
|
||||||
change(step);
|
if (value > min) {
|
||||||
}
|
change(-step);
|
||||||
}}
|
}
|
||||||
on:mousedown={() =>
|
}}
|
||||||
longPress(() => {
|
on:mousedown={() =>
|
||||||
|
longPress(() => {
|
||||||
|
if (value > min) {
|
||||||
|
change(-step);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
on:mouseup={() => {
|
||||||
|
clearTimeout(pressTimer);
|
||||||
|
pressed = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconConstrain>
|
||||||
|
{@html chevronDown}
|
||||||
|
</IconConstrain>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="increment"
|
||||||
|
disabled={value == max}
|
||||||
|
tabindex="-1"
|
||||||
|
on:click={() => {
|
||||||
|
input.focus();
|
||||||
if (value < max) {
|
if (value < max) {
|
||||||
change(step);
|
change(step);
|
||||||
}
|
}
|
||||||
})}
|
}}
|
||||||
on:mouseup={() => {
|
on:mousedown={() =>
|
||||||
clearTimeout(pressTimer);
|
longPress(() => {
|
||||||
pressed = false;
|
if (value < max) {
|
||||||
}}
|
change(step);
|
||||||
>
|
}
|
||||||
<IconConstrain>
|
})}
|
||||||
{@html rtl ? chevronLeft : chevronRight}
|
on:mouseup={() => {
|
||||||
</IconConstrain>
|
clearTimeout(pressTimer);
|
||||||
</button>
|
pressed = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconConstrain>
|
||||||
|
{@html chevronUp}
|
||||||
|
</IconConstrain>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -149,22 +151,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
text-align: center;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
&::-webkit-inner-spin-button {
|
&::-webkit-inner-spin-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover {
|
||||||
&:focus-within {
|
|
||||||
button {
|
button {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
input {
|
|
||||||
border-left: 1px solid var(--border-subtle);
|
|
||||||
border-right: 1px solid var(--border-subtle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
@ -172,12 +170,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||||||
@include button.base($border: false);
|
@include button.base($border: false);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&.decrement {
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
&.increment {
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -8,5 +8,6 @@ export { default as vsplitIcon } from "@mdi/svg/svg/arrow-split-vertical.svg";
|
|||||||
export { default as chevronDown } from "@mdi/svg/svg/chevron-down.svg";
|
export { default as chevronDown } from "@mdi/svg/svg/chevron-down.svg";
|
||||||
export { default as chevronLeft } from "@mdi/svg/svg/chevron-left.svg";
|
export { default as chevronLeft } from "@mdi/svg/svg/chevron-left.svg";
|
||||||
export { default as chevronRight } from "@mdi/svg/svg/chevron-right.svg";
|
export { default as chevronRight } from "@mdi/svg/svg/chevron-right.svg";
|
||||||
|
export { default as chevronUp } from "@mdi/svg/svg/chevron-up.svg";
|
||||||
export { default as horizontalHandle } from "@mdi/svg/svg/drag-horizontal.svg";
|
export { default as horizontalHandle } from "@mdi/svg/svg/drag-horizontal.svg";
|
||||||
export { default as verticalHandle } from "@mdi/svg/svg/drag-vertical.svg";
|
export { default as verticalHandle } from "@mdi/svg/svg/drag-vertical.svg";
|
||||||
|
@ -9,3 +9,7 @@ export function isApplePlatform(): boolean {
|
|||||||
|| platform.startsWith("iP")
|
|| platform.startsWith("iP")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDesktop(): boolean {
|
||||||
|
return !(/iphone|ipad|ipod|android/i.test(window.navigator.userAgent));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user