Files
ComfyBox/src/lib/widgets/NumberWidget.svelte
Haze Booth 63d51e9119 Introduce vibrateIfPossible into the utils package. This will check if the browser
has support for vibrating. This fixes a number of bugs for iOS safari & friends.
2023-08-24 12:02:22 -04:00

174 lines
5.3 KiB
Svelte

<script lang="ts">
import type { ComfyNumberNode } from "$lib/nodes/widgets";
import { type WidgetLayout } from "$lib/stores/layoutStates";
import { Range } from "$lib/components/gradio/form";
import { get, type Writable } from "svelte/store";
import { debounce, vibrateIfPossible } from "$lib/utils";
import interfaceState from "$lib/stores/interfaceState";
import { isDisabled } from "./utils"
export let widget: WidgetLayout | null = null;
export let isMobile: boolean = false;
let node: ComfyNumberNode | null = null;
let nodeValue: Writable<number> | null = null;
let propsChanged: Writable<number> | null = null;
let option: number | null = null;
let isDragging: boolean = false;
$: widget && setNodeValue(widget);
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyNumberNode
nodeValue = node.value;
propsChanged = node.propsChanged;
setOption($nodeValue); // don't react on option
}
isDragging = false;
};
// I don't know why but this is necessary to watch for changes to node
// properties from ComfyWidgetNode.
$: if (nodeValue !== null && (!$propsChanged || $propsChanged)) {
setOption($nodeValue)
setNodeValue(widget)
node.properties = node.properties
}
function setOption(value: any) {
option = value;
}
function onRelease(e: Event) {
if (nodeValue && option != null) {
$nodeValue = option
}
}
function setBackgroundSize(input: HTMLInputElement) {
input.style.setProperty("--background-size", `${getBackgroundSize(input)}%`);
}
function getBackgroundSize(input: HTMLInputElement) {
const min = +input.min || 0;
const max = +input.max || 100;
const value = +input.value;
return (value - min) / (max - min) * 100;
}
function updateSliderForMobile() {
const target = elem.querySelector<HTMLInputElement>("input[type=range]");
setBackgroundSize(target);
}
let elem: HTMLDivElement = null;
$: if (elem) {
updateSliderForMobile()
}
$: if (elem && node !== null && option !== null && (!$propsChanged || $propsChanged)) {
const slider = elem.querySelector("input[type='range']") as any
//const range_selectors = "[id$='_clone']:is(input[type='range'])";
let spacing = ((slider.step / ( slider.max - slider.min )) * 100.0);
let tsp = 'max(3px, calc('+spacing+'% - 2px))';
let fsp = 'max(4px, calc('+spacing+'% + 0px))';
const style = elem.style;
style.setProperty('--ae-slider-bg-overlay', 'repeating-linear-gradient( 90deg, transparent, transparent '+tsp+', var(--ae-input-border-color) '+tsp+', var(--ae-input-border-color) '+fsp+' )');
}
function onPointerDown(e: PointerEvent) {
if (!isMobile)
return;
interfaceState.showIndicator(e.clientX, e.clientY, option);
}
let canVibrate = true;
let lastDisplayValue = null;
function onPointerMove(e: PointerEvent) {
if (!isMobile)
return;
interfaceState.showIndicator(e.clientX, e.clientY, option);
if (canVibrate && lastDisplayValue != option) {
lastDisplayValue = option;
canVibrate = false;
setTimeout(() => { canVibrate = true }, 30)
vibrateIfPossible(10)
}
}
</script>
<div class="wrapper gradio-slider" class:mobile={isMobile} bind:this={elem}>
{#if node !== null && option !== null}
<Range
bind:value={option}
disabled={isDisabled(widget)}
minimum={node.properties.min}
maximum={node.properties.max}
step={node.properties.step}
label={widget.attrs.title}
show_label={true}
on:release={onRelease}
on:change={updateSliderForMobile}
on:pointerdown={onPointerDown}
on:pointermove={onPointerMove}
/>
{/if}
</div>
<style lang="scss">
.wrapper {
padding: 2px;
width: 100%;
:global(input[type=number]) {
text-overflow: ellipsis;
&:disabled {
@include disable-input;
}
}
&.mobile {
// Prevent swiping on the slider track from accidentally changing the value
:global(input[type="range"]) {
pointer-events: none;
-webkit-appearance: none;
appearance: none;
cursor: default;
height: 0.6rem;
padding: initial;
border: initial;
margin: 0.8rem 0;
width: 100%;
background: linear-gradient(to right, var(--color-blue-600), var(--color-blue-600)), #D7D7D7;
background-size: var(--background-size, 0%) 100%;
background-repeat: no-repeat;
border-radius: 1rem;
border: 1px solid var(--neutral-400);
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
pointer-events: all;
width: 1.75rem;
height: 1.75rem;
border-radius: 50%;
background: var(--color-blue-600);
cursor: pointer;
border: 2px solid var(--neutral-100);
box-shadow: 0px 0px 0px 1px var(--neutral-400);
}
:global(input[type=number]) {
font-size: 16px;
height: var(--size-6);
}
}
}
}
</style>