Better mobile slider handling

This commit is contained in:
space-nuko
2023-05-07 14:15:06 -05:00
parent 71c9617133
commit efb0010a0e
19 changed files with 327 additions and 212 deletions

View File

@@ -20,6 +20,7 @@
export let showHandles: boolean = false;
export let edit: boolean = false;
export let dragDisabled: boolean = false;
export let isMobile: boolean = false;
let attrsChanged: Writable<boolean> | null = null;
let children: IDragItem[] | null = null;
@@ -78,7 +79,7 @@
animate:flip={{duration:flipDurationMs}}
style={item?.attrs?.flexGrow ? `flex-grow: ${item.attrs.flexGrow}` : ""}
>
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
<div in:fade={{duration:200, easing: cubicIn}} class='drag-item-shadow'/>
{/if}
@@ -97,7 +98,7 @@
<Block elem_classes={["gradio-accordion"]}>
<Accordion label={container.attrs.title} open={container.attrs.openOnStartup}>
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
{/each}
</Accordion>
</Block>

View File

@@ -19,6 +19,7 @@
export let showHandles: boolean = false;
export let edit: boolean = false;
export let dragDisabled: boolean = false;
export let isMobile: boolean = false;
let attrsChanged: Writable<boolean> | null = null;
let children: IDragItem[] | null = null;
@@ -80,7 +81,7 @@
animate:flip={{duration:flipDurationMs}}
style={item?.attrs?.flexGrow ? `flex-grow: ${item.attrs.flexGrow}` : ""}
>
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
<div in:fade={{duration:200, easing: cubicIn}} class='drag-item-shadow'/>
{/if}

View File

@@ -19,6 +19,7 @@
export let zIndex: number = 0;
export let classes: string[] = [];
export let showHandles: boolean = false;
export let isMobile: boolean = false
let attrsChanged: Writable<boolean> | null = null;
$: if (container) {
@@ -34,11 +35,11 @@
{@const dragDisabled = zIndex === 0 || $layoutState.currentSelection.length > 2 || !$uiState.uiUnlocked}
{#key $attrsChanged}
{#if container.attrs.variant === "tabs"}
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} />
<TabsContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
{:else if container.attrs.variant === "accordion"}
<AccordionContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} />
<AccordionContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
{:else}
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} />
<BlockContainer {container} {zIndex} {classes} {showHandles} {edit} {dragDisabled} {isMobile} />
{/if}
{/key}
{/if}

View File

@@ -20,6 +20,7 @@
export let showHandles: boolean = false;
export let edit: boolean = false;
export let dragDisabled: boolean = false;
export let isMobile: boolean = false;
let attrsChanged: Writable<boolean> | null = null;
let children: IDragItem[] | null = null;
@@ -95,7 +96,7 @@
<label for={String(item.id)}>
<BlockTitle><strong>Tab {i+1}:</strong> {tabName}</BlockTitle>
</label>
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
<div in:fade={{duration:200, easing: cubicIn}} class='drag-item-shadow'/>
{/if}
@@ -115,7 +116,7 @@
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item, i(item.id)}
{@const tabName = getTabName(container, i)}
<TabItem name={tabName} on:select={() => console.log("tab " + i)}>
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
</TabItem>
{/each}
</Tabs>

View File

@@ -11,6 +11,7 @@
export let dragItem: IDragItem | null = null;
export let zIndex: number = 0;
export let classes: string[] = [];
export let isMobile: boolean = false;
let container: ContainerLayout | null = null;
let attrsChanged: Writable<boolean> | null = null;
let propsChanged: Writable<number> | null = null;
@@ -59,7 +60,7 @@
{#if container}
{#key $attrsChanged}
<Container {container} {classes} {zIndex} {showHandles} />
<Container {container} {classes} {zIndex} {showHandles} {isMobile} />
{/key}
{:else if widget && widget.node}
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
@@ -71,7 +72,7 @@
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}
class:hidden={widget.attrs.hidden}
>
<svelte:component this={widget.node.svelteComponentType} {widget} />
<svelte:component this={widget.node.svelteComponentType} {widget} {isMobile} />
</div>
{#if widget.attrs.hidden && edit}
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>

View File

@@ -0,0 +1,128 @@
<script context="module">
let _id = 0;
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { BlockTitle } from "@gradio/atoms";
export let value: number = 0;
export let minimum: number = 0;
export let maximum: number = 100;
export let step: number = 1;
export let disabled: boolean = false;
export let label: string;
export let info: string | undefined = undefined;
export let show_label: boolean;
const id = `range_id_${_id++}`;
const dispatch = createEventDispatcher<{ change: number; release: number }>();
let inputValue = value;
function handle_input(e: Event) {
const element = e.currentTarget as HTMLInputElement;
let newValue = parseFloat(element.value);
if (isNaN(newValue)) {
newValue = minimum;
}
inputValue = Math.min(Math.max(inputValue, minimum), maximum);
value = inputValue;
dispatch("release", value);
}
function handle_release(e: MouseEvent) {
dispatch("release", value);
}
$: {
inputValue = value;
dispatch("change", value);
}
const clamp = () => {
dispatch("release", value);
value = Math.min(Math.max(value, minimum), maximum);
};
</script>
<div class="wrap">
<div class="head">
<label for={id}>
<BlockTitle {show_label} {info}>{label}</BlockTitle>
</label>
<input
data-testid="number-input"
type="number"
bind:value={inputValue}
on:input={handle_input}
min={minimum}
max={maximum}
on:blur={clamp}
{step}
{disabled}
on:mouseup={handle_release}
/>
</div>
</div>
<input
type="range"
{id}
name="cowbell"
bind:value
min={minimum}
max={maximum}
{step}
{disabled}
on:mouseup={handle_release}
/>
<style lang="scss">
.wrap {
display: flex;
flex-direction: column;
width: 100%;
}
.head {
display: flex;
justify-content: space-between;
}
input[type="number"] {
display: block;
position: relative;
outline: none !important;
box-shadow: var(--input-shadow);
border: var(--input-border-width) solid var(--input-border-color);
border-radius: var(--input-radius);
background: var(--input-background-fill);
padding: var(--size-2) var(--size-2);
height: var(--size-6);
color: var(--body-text-color);
font-size: var(--input-text-size);
line-height: var(--line-sm);
text-align: center;
}
input:disabled {
-webkit-text-fill-color: var(--body-text-color);
-webkit-opacity: 1;
opacity: 1;
}
input[type="number"]:focus {
box-shadow: var(--input-shadow-focus);
border-color: var(--input-border-color-focus);
}
input::placeholder {
color: var(--input-placeholder-color);
}
input[type="range"] {
width: 100%;
accent-color: var(--slider-color);
}
input[disabled] {
cursor: not-allowed;
}
</style>

View File

@@ -0,0 +1 @@
export { default as Range } from "./Range.svelte"

View File

@@ -5,6 +5,7 @@
import { Button } from "@gradio/button";
import { get, type Writable, writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyButtonNode | null = null;
let nodeValue: Writable<boolean> | null = null;
let attrsChanged: Writable<boolean> | null = null;

View File

@@ -6,6 +6,7 @@
import { get, type Writable, writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyCheckboxNode | null = null;
let nodeValue: Writable<boolean> | null = null;
let attrsChanged: Writable<boolean> | null = null;

View File

@@ -5,6 +5,7 @@
import { type WidgetLayout } from "$lib/stores/layoutState";
import { get, type Writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyComboNode | null = null;
let nodeValue: Writable<string> | null = null;
let propsChanged: Writable<number> | null = null;

View File

@@ -11,6 +11,7 @@
import { clamp } from "$lib/utils";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyGalleryNode | null = null;
let nodeValue: Writable<GradioFileData[]> | null = null;
let propsChanged: Writable<number> | null = null;

View File

@@ -1,9 +1,10 @@
<script lang="ts">
import type { ComfySliderNode } from "$lib/nodes/index";
import { type WidgetLayout } from "$lib/stores/layoutState";
import { Range } from "@gradio/form";
import { Range } from "$lib/components/gradio/form";
import { get, type Writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfySliderNode | null = null;
let nodeValue: Writable<number> | null = null;
let propsChanged: Writable<number> | null = null;
@@ -39,7 +40,6 @@
}
}
let gradient: string = ""
let elem: HTMLDivElement = null;
$: if (elem && node !== null && option !== null && (!$propsChanged || $propsChanged)) {
@@ -53,7 +53,7 @@
}
</script>
<div class="wrapper gradio-slider" bind:this={elem}>
<div class="wrapper gradio-slider" class:mobile={isMobile} bind:this={elem}>
{#if node !== null && option !== null}
<Range
bind:value={option}
@@ -69,9 +69,18 @@
{/if}
</div>
<style>
<style lang="scss">
.wrapper {
padding: 2px;
width: 100%;
// Prevent swiping on the slider track from accidentally changing the value
&.mobile :global(input[type="range"]) {
pointer-events: none;
&::-webkit-slider-thumb {
pointer-events: all;
}
}
}
</style>

View File

@@ -4,6 +4,7 @@
import { type WidgetLayout } from "$lib/stores/layoutState";
import { get, type Writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyComboNode | null = null;
let nodeValue: Writable<string> | null = null;
let propsChanged: Writable<number> | null = null;