Fix FieldInput and several other Svelte bugs

This commit is contained in:
Keavon Chambers 2023-03-08 01:35:22 -08:00
parent 2e3495aa0e
commit 7ce9d6db05
9 changed files with 56 additions and 25 deletions

View file

@ -0,0 +1,7 @@
Copyright (c) 2021-2023 Keavon Chambers
The design assets in this directory (including SVG code for icons and logos) are NOT licensed under the Apache 2.0 license terms applied to other Graphite source code files. This directory and its entire contents are excluded from the Apache 2.0 source code license, and copyrights are held by the author for the creative works contained as files herein.
Parties interested in using Graphite source code in a capacity that deploys the Graphite Editor reference frontend are advised to substitute all assets and "Graphite" branding or otherwise arrange written permission from the rightsholder. The recommended use case for adopting Graphite open source code is to develop one's own unique frontend user interface implementation that integrates Graphite's backend technology.
The author and rightsholder, Keavon Chambers, may be reached through the email address listed at https://graphite.rs/contact/ or https://keavon.com.

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path class="bright" d="M6,1C4.3,1,3,2.3,3,4v4h5V1H6z M7,6v1H4c0,0,0-0.6,0.2-1c0.3-0.6,0.8-1.1,1.3-1.4c1.1-0.9,0.8-1.9,0.1-1.7C5,3,4.8,3.6,4.9,4.1H4C3.9,2.9,4.7,2,5.8,2c1.1,0,1.5,1.2,1,2.3C6.4,5.1,5.5,5.5,5.4,6H7z" />
<path class="dim" d="M10,1H9v1h1c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V9H3v1c0,2.76,2.24,5,5,5s5-2.24,5-5V4C13,2.35,11.65,1,10,1z" />
</svg>

After

Width:  |  Height:  |  Size: 432 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path class="bright" d="M10,1H8v7h5V4C13,2.3,11.7,1,10,1z M12,7H9.1c0,0,0-0.6,0.2-1c0.2-0.6,0.8-1.1,1.3-1.5c0.9-0.8,0.4-1.8-0.4-1.6C9.6,3.1,9.9,4.1,9.9,4.1H9.1c-0.3-1,0.1-2.2,1.3-2.1c1,0.1,1.6,1.1,1.4,2.1C11.6,5,10.6,5.4,10.5,6H12V7z" />
<path class="dim" d="M6,1h1v1H6C4.9,2,4,2.9,4,4v6c0,2.21,1.79,4,4,4s4-1.79,4-4V9h1v1c0,2.76-2.24,5-5,5s-5-2.24-5-5V4C3,2.35,4.35,1,6,1z" />
</svg>

After

Width:  |  Height:  |  Size: 448 B

View file

@ -320,7 +320,10 @@
{/if}
<NumberInput
value={strength}
on:value={({ detail }) => setColorRGB(channel, detail)}
on:value={({ detail }) => {
strength = detail;
setColorRGB(channel, detail);
}}
min={0}
max={255}
minWidth={56}
@ -341,7 +344,10 @@
{/if}
<NumberInput
value={strength}
on:value={({ detail }) => setColorHSV(channel, detail)}
on:value={({ detail }) => {
strength = detail;
setColorHSV(channel, detail);
}}
min={0}
max={channel === "h" ? 360 : 100}
unit={channel === "h" ? "°" : "%"}
@ -358,7 +364,10 @@
<NumberInput
label="Alpha"
value={!isNone ? alpha * 100 : undefined}
on:value={({ detail }) => setColorAlphaPercent(detail)}
on:value={({ detail }) => {
if (detail !== undefined) alpha = detail / 100;
setColorAlphaPercent(detail);
}}
min={0}
max={100}
rangeMin={0}

View file

@ -59,7 +59,7 @@
return sparse;
}
function buildNodeCategories(nodeTypes: FrontendNodeType[], searchTerm: string) {
function buildNodeCategories(nodeTypes: FrontendNodeType[], searchTerm: string): [string, FrontendNodeType[]][] {
const categories = new Map();
nodeTypes.forEach((node) => {
if (searchTerm.length > 0 && !node.name.toLowerCase().includes(searchTerm.toLowerCase()) && !node.category.toLowerCase().includes(searchTerm.toLowerCase())) {
@ -488,10 +488,10 @@
{#if nodeListLocation}
<LayoutCol class="node-list" data-node-list styles={{ "margin-left": `${nodeListX}px`, "margin-top": `${nodeListY}px` }}>
<TextInput placeholder="Search Nodes..." value={searchTerm} on:value={({ detail }) => (searchTerm = detail)} bind:this={nodeSearchInput} />
{#each nodeCategories as nodeCategory (nodeCategory[0])}
{#each nodeCategories as nodeCategory}
<LayoutCol>
<TextLabel>{nodeCategory[0]}</TextLabel>
{#each nodeCategory[1] as nodeType (String(nodeType))}
{#each nodeCategory[1] as nodeType}
<TextButton label={nodeType.name} action={() => createNode(nodeType.name)} />
{/each}
</LayoutCol>

View file

@ -121,7 +121,7 @@
{#if numberInput}
<NumberInput
{...exclude(numberInput)}
on:value={({ detail }) => debouncer(() => updateLayout(index, detail))}
on:value={({ detail }) => debouncer((value) => updateLayout(index, value)).updateValue(detail)}
incrementCallbackIncrease={() => updateLayout(index, "Increment")}
incrementCallbackDecrease={() => updateLayout(index, "Decrement")}
sharpRightCorners={nextIsSuffix}

View file

@ -31,8 +31,8 @@
let inputOrTextarea: HTMLInputElement | HTMLTextAreaElement;
let id = `${Math.random()}`.substring(2);
let macKeyboardLayout = platformIsMac();
let inputValue = value;
$: inputValue = value;
$: dispatch("value", inputValue);
// Select (highlight) all the text. For technical reasons, it is necessary to pass the current text.

View file

@ -52,7 +52,7 @@
export let incrementCallbackDecrease: (() => void) | undefined = undefined;
let self: FieldInput;
let text = displayText(value);
let text = displayText(value, displayDecimalPlaces, unit);
let editing = false;
// Stays in sync with a binding to the actual input range slider element.
let rangeSliderValue = value !== undefined ? value : 0;
@ -84,10 +84,10 @@
if (typeof min === "number") sanitized = Math.max(sanitized, min);
if (typeof max === "number") sanitized = Math.min(sanitized, max);
text = displayText(sanitized);
text = displayText(sanitized, displayDecimalPlaces, unit);
}
function sliderInput() {
function onSliderInput() {
// Keep only 4 digits after the decimal point
const ROUNDING_EXPONENT = 4;
const ROUNDING_MAGNITUDE = 10 ** ROUNDING_EXPONENT;
@ -113,10 +113,10 @@
// If we're in a dragging state, we want to use the new slider value
rangeSliderValueAsRendered = roundedValue;
updateValue(roundedValue);
updateValue(roundedValue, min, max, displayDecimalPlaces, unit);
}
function sliderPointerDown() {
function onSliderPointerDown() {
// We want to render the fake slider thumb at the old position, which is still the number held by `value`
rangeSliderValueAsRendered = value || 0;
@ -124,7 +124,7 @@
// invocation will transition the state machine to `mousedown`. That's why we don't do it here.
}
function sliderPointerUp() {
function onSliderPointerUp() {
// User clicked but didn't drag, so we focus the text input element
if (rangeSliderClickDragState === "mousedown") {
const inputElement = self.element().querySelector("[data-input-element]") as HTMLInputElement | undefined;
@ -160,7 +160,7 @@
const parsed = parseFloat(text);
const newValue = Number.isNaN(parsed) ? undefined : parsed;
updateValue(newValue);
updateValue(newValue, min, max, displayDecimalPlaces, unit);
editing = false;
@ -168,7 +168,7 @@
}
function onCancelTextChange() {
updateValue(undefined);
updateValue(undefined, min, max, displayDecimalPlaces, unit);
editing = false;
@ -181,11 +181,11 @@
const actions: Record<NumberInputIncrementBehavior, () => void> = {
Add: () => {
const directionAddend = direction === "Increase" ? step : -step;
updateValue(value !== undefined ? value + directionAddend : undefined);
updateValue(value !== undefined ? value + directionAddend : undefined, min, max, displayDecimalPlaces, unit);
},
Multiply: () => {
const directionMultiplier = direction === "Increase" ? step : 1 / step;
updateValue(value !== undefined ? value * directionMultiplier : undefined);
updateValue(value !== undefined ? value * directionMultiplier : undefined, min, max, displayDecimalPlaces, unit);
},
Callback: () => {
if (direction === "Increase") incrementCallbackIncrease?.();
@ -197,20 +197,20 @@
action();
}
function updateValue(newValue: number | undefined) {
function updateValue(newValue: number | undefined, min: number | undefined, max: number | undefined, displayDecimalPlaces: number, unit: string) {
// Check if the new value is valid, otherwise we use the old value (rounded if it's an integer)
const nowValid = value !== undefined && isInteger ? Math.round(value) : value;
let cleaned = newValue !== undefined ? newValue : nowValid;
if (typeof min === "number" && !Number.isNaN(min) && cleaned !== undefined) cleaned = Math.max(cleaned, min);
if (typeof max === "number" && !Number.isNaN(max) && cleaned !== undefined) cleaned = Math.min(cleaned, max);
// Required as the call to update:value can, not change the value
text = displayText(value);
text = displayText(cleaned, displayDecimalPlaces, unit);
if (newValue !== undefined) dispatch("value", cleaned);
}
function displayText(value: number | undefined): string {
function displayText(value: number | undefined, displayDecimalPlaces: number, unit: string): string {
if (value === undefined) return "-";
// Find the amount of digits on the left side of the decimal
@ -261,9 +261,9 @@
max={rangeMax}
step={sliderStepValue}
{disabled}
on:input={sliderInput}
on:pointerdown={sliderPointerDown}
on:pointerup={sliderPointerUp}
on:input={onSliderInput}
on:pointerdown={onSliderPointerDown}
on:pointerup={onSliderPointerUp}
tabindex="-1"
/>
{/if}

View file

@ -0,0 +1,7 @@
Copyright (c) 2021-2023 Keavon Chambers
The design assets in this directory (including SVG code for icons and logos) are NOT licensed under the Apache 2.0 license terms applied to other Graphite source code files. This directory and its entire contents are excluded from the Apache 2.0 source code license, and copyrights are held by the author for the creative works contained as files herein.
Parties interested in using Graphite source code in a capacity that deploys the Graphite Editor reference frontend are advised to substitute all assets and "Graphite" branding or otherwise arrange written permission from the rightsholder. The recommended use case for adopting Graphite open source code is to develop one's own unique frontend user interface implementation that integrates Graphite's backend technology.
The author and rightsholder, Keavon Chambers, may be reached through the email address listed at https://graphite.rs/contact/ or https://keavon.com.