Svelte: Fix calling DOM API functions on the actual elements

This commit is contained in:
Keavon Chambers 2023-01-13 17:17:38 -08:00
parent 0019340096
commit fb10e5194e
15 changed files with 162 additions and 96 deletions

View File

@ -321,7 +321,6 @@ impl NodeGraphMessageHandler {
disabled: network.disabled.contains(id), disabled: network.disabled.contains(id),
}) })
} }
log::debug!("Frontend Nodes:\n{:#?}\n\nLinks:\n{:#?}", nodes, links);
responses.push_back(FrontendMessage::UpdateNodeGraph { nodes, links }.into()); responses.push_back(FrontendMessage::UpdateNodeGraph { nodes, links }.into());
} }
@ -423,7 +422,6 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
input_node, input_node,
input_node_connector_index, input_node_connector_index,
} => { } => {
log::debug!("Connect primary output from node {output_node} to input of index {input_node_connector_index} on node {input_node}.");
let node_id = input_node; let node_id = input_node;
let Some(network) = self.get_active_network(document) else { let Some(network) = self.get_active_network(document) else {

View File

@ -11,7 +11,7 @@
const dialog = getContext<DialogState>("dialog"); const dialog = getContext<DialogState>("dialog");
let dialogModal: FloatingMenu; let self: FloatingMenu;
export function dismiss() { export function dismiss() {
dialog.dismissDialog(); dialog.dismissDialog();
@ -19,12 +19,12 @@
onMount(() => { onMount(() => {
// Focus the first button in the popup // Focus the first button in the popup
const emphasizedOrFirstButton = (dialogModal.div().querySelector("[data-emphasized]") || dialogModal.div().querySelector("[data-text-button]") || undefined) as HTMLButtonElement | undefined; const emphasizedOrFirstButton = (self.div().querySelector("[data-emphasized]") || self.div().querySelector("[data-text-button]") || undefined) as HTMLButtonElement | undefined;
emphasizedOrFirstButton?.focus(); emphasizedOrFirstButton?.focus();
}); });
</script> </script>
<FloatingMenu open={true} class="dialog-modal" type="Dialog" direction="Center" bind:this={dialogModal} data-dialog-modal> <FloatingMenu open={true} class="dialog-modal" type="Dialog" direction="Center" bind:this={self} data-dialog-modal>
<LayoutRow> <LayoutRow>
<LayoutCol class="icon-column"> <LayoutCol class="icon-column">
<!-- `$dialog.icon` class exists to provide special sizing in CSS to specific icons --> <!-- `$dialog.icon` class exists to provide special sizing in CSS to specific icons -->

View File

@ -11,7 +11,7 @@
import TextLabel from "@/components/widgets/labels/TextLabel.svelte"; import TextLabel from "@/components/widgets/labels/TextLabel.svelte";
import UserInputLabel from "@/components/widgets/labels/UserInputLabel.svelte"; import UserInputLabel from "@/components/widgets/labels/UserInputLabel.svelte";
let floatingMenu: FloatingMenu; let self: FloatingMenu;
let scroller: LayoutCol; let scroller: LayoutCol;
// emits: ["update:open", "update:activeEntry", "naturalWidth"], // emits: ["update:open", "update:activeEntry", "naturalWidth"],
@ -35,8 +35,8 @@
// Called only when `open` is changed from outside this component (with v-model) // Called only when `open` is changed from outside this component (with v-model)
$: watchOpen(open); $: watchOpen(open);
$: dispatch("open", isOpen); $: dispatch("open", isOpen);
$: watchEntries(entries, floatingMenu); $: watchEntries(entries, self);
$: watchDrawIcon(drawIcon, floatingMenu); $: watchDrawIcon(drawIcon, self);
$: virtualScrollingTotalHeight = entries.length === 0 ? 0 : entries[0].length * virtualScrollingEntryHeight; $: virtualScrollingTotalHeight = entries.length === 0 ? 0 : entries[0].length * virtualScrollingEntryHeight;
$: virtualScrollingStartIndex = Math.floor(virtualScrollingEntriesStart / virtualScrollingEntryHeight) || 0; $: virtualScrollingStartIndex = Math.floor(virtualScrollingEntriesStart / virtualScrollingEntryHeight) || 0;
$: virtualScrollingEndIndex = entries.length === 0 ? 0 : Math.min(entries[0].length, virtualScrollingStartIndex + 1 + 400 / virtualScrollingEntryHeight); $: virtualScrollingEndIndex = entries.length === 0 ? 0 : Math.min(entries[0].length, virtualScrollingStartIndex + 1 + 400 / virtualScrollingEntryHeight);
@ -48,12 +48,12 @@
// TODO: Svelte: fix infinite loop and reenable // TODO: Svelte: fix infinite loop and reenable
function watchEntries(_: MenuListEntry[][], floatingMenu: FloatingMenu) { function watchEntries(_: MenuListEntry[][], floatingMenu: FloatingMenu) {
// floatingMenu?.measureAndEmitNaturalWidth(); // floatingMenu?.div().measureAndEmitNaturalWidth();
} }
// TODO: Svelte: fix infinite loop and reenable // TODO: Svelte: fix infinite loop and reenable
function watchDrawIcon(_: boolean, floatingMenu: FloatingMenu) { function watchDrawIcon(_: boolean, floatingMenu: FloatingMenu) {
// floatingMenu?.measureAndEmitNaturalWidth(); // floatingMenu?.div().measureAndEmitNaturalWidth();
} }
function onScroll(e: Event) { function onScroll(e: Event) {
@ -198,7 +198,7 @@
{direction} {direction}
{minWidth} {minWidth}
scrollableY={scrollableY && virtualScrollingEntryHeight === 0} scrollableY={scrollableY && virtualScrollingEntryHeight === 0}
bind:this={floatingMenu} bind:this={self}
> >
<!-- If we put the scrollableY on the layoutcol for non-font dropdowns then for some reason it always creates a tiny scrollbar. <!-- If we put the scrollableY on the layoutcol for non-font dropdowns then for some reason it always creates a tiny scrollbar.
However when we are using the virtual scrolling then we need the layoutcol to be scrolling so we can bind the events without using $refs. --> However when we are using the virtual scrolling then we need the layoutcol to be scrolling so we can bind the events without using $refs. -->

View File

@ -132,10 +132,10 @@
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping) // Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
// We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever // We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever
const tailOffset = type === "Popover" ? 10 : 0; const tailOffset = type === "Popover" ? 10 : 0;
if (direction === "Bottom") floatingMenuContent.style.top = `${tailOffset + floatingMenuBounds.top}px`; if (direction === "Bottom") floatingMenuContent.div().style.top = `${tailOffset + floatingMenuBounds.top}px`;
if (direction === "Top") floatingMenuContent.style.bottom = `${tailOffset + floatingMenuBounds.bottom}px`; if (direction === "Top") floatingMenuContent.div().style.bottom = `${tailOffset + floatingMenuBounds.bottom}px`;
if (direction === "Right") floatingMenuContent.style.left = `${tailOffset + floatingMenuBounds.left}px`; if (direction === "Right") floatingMenuContent.div().style.left = `${tailOffset + floatingMenuBounds.left}px`;
if (direction === "Left") floatingMenuContent.style.right = `${tailOffset + floatingMenuBounds.right}px`; if (direction === "Left") floatingMenuContent.div().style.right = `${tailOffset + floatingMenuBounds.right}px`;
// Required to correctly position tail when scrolled (it has a `position: fixed` to prevent clipping) // Required to correctly position tail when scrolled (it has a `position: fixed` to prevent clipping)
// We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever // We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever
@ -154,11 +154,11 @@
// We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever // We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever
if (floatingMenuContentBounds.left - windowEdgeMargin <= workspaceBounds.left) { if (floatingMenuContentBounds.left - windowEdgeMargin <= workspaceBounds.left) {
floatingMenuContent.style.left = `${windowEdgeMargin}px`; floatingMenuContent.div().style.left = `${windowEdgeMargin}px`;
if (workspaceBounds.left + floatingMenuContainerBounds.left === 12) zeroedBorderHorizontal = "Left"; if (workspaceBounds.left + floatingMenuContainerBounds.left === 12) zeroedBorderHorizontal = "Left";
} }
if (floatingMenuContentBounds.right + windowEdgeMargin >= workspaceBounds.right) { if (floatingMenuContentBounds.right + windowEdgeMargin >= workspaceBounds.right) {
floatingMenuContent.style.right = `${windowEdgeMargin}px`; floatingMenuContent.div().style.right = `${windowEdgeMargin}px`;
if (workspaceBounds.right - floatingMenuContainerBounds.right === 12) zeroedBorderHorizontal = "Right"; if (workspaceBounds.right - floatingMenuContainerBounds.right === 12) zeroedBorderHorizontal = "Right";
} }
} }
@ -167,11 +167,11 @@
// We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever // We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever
if (floatingMenuContentBounds.top - windowEdgeMargin <= workspaceBounds.top) { if (floatingMenuContentBounds.top - windowEdgeMargin <= workspaceBounds.top) {
floatingMenuContent.style.top = `${windowEdgeMargin}px`; floatingMenuContent.div().style.top = `${windowEdgeMargin}px`;
if (workspaceBounds.top + floatingMenuContainerBounds.top === 12) zeroedBorderVertical = "Top"; if (workspaceBounds.top + floatingMenuContainerBounds.top === 12) zeroedBorderVertical = "Top";
} }
if (floatingMenuContentBounds.bottom + windowEdgeMargin >= workspaceBounds.bottom) { if (floatingMenuContentBounds.bottom + windowEdgeMargin >= workspaceBounds.bottom) {
floatingMenuContent.style.bottom = `${windowEdgeMargin}px`; floatingMenuContent.div().style.bottom = `${windowEdgeMargin}px`;
if (workspaceBounds.bottom - floatingMenuContainerBounds.bottom === 12) zeroedBorderVertical = "Bottom"; if (workspaceBounds.bottom - floatingMenuContainerBounds.bottom === 12) zeroedBorderVertical = "Bottom";
} }
} }
@ -181,16 +181,16 @@
// We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever // We use `.style` on a ref (instead of a `:style` Vue binding) because the binding causes the `updated()` hook to call the function we're in recursively forever
switch (`${zeroedBorderVertical}${zeroedBorderHorizontal}`) { switch (`${zeroedBorderVertical}${zeroedBorderHorizontal}`) {
case "TopLeft": case "TopLeft":
floatingMenuContent.style.borderTopLeftRadius = "0"; floatingMenuContent.div().style.borderTopLeftRadius = "0";
break; break;
case "TopRight": case "TopRight":
floatingMenuContent.style.borderTopRightRadius = "0"; floatingMenuContent.div().style.borderTopRightRadius = "0";
break; break;
case "BottomLeft": case "BottomLeft":
floatingMenuContent.style.borderBottomLeftRadius = "0"; floatingMenuContent.div().style.borderBottomLeftRadius = "0";
break; break;
case "BottomRight": case "BottomRight":
floatingMenuContent.style.borderBottomRightRadius = "0"; floatingMenuContent.div().style.borderBottomRightRadius = "0";
break; break;
default: default:
break; break;
@ -219,7 +219,7 @@
// Measure the width of the floating menu content element, if it's currently visible // Measure the width of the floating menu content element, if it's currently visible
// The result will be `undefined` if the menu is invisible, perhaps because an ancestor component is hidden with a falsy `v-if` condition // The result will be `undefined` if the menu is invisible, perhaps because an ancestor component is hidden with a falsy `v-if` condition
const naturalWidth: number | undefined = floatingMenuContent?.clientWidth; const naturalWidth: number | undefined = floatingMenuContent?.div().clientWidth;
// Turn off measuring mode for the component, which triggers another call to the `updated()` Vue event, so we can turn off the protection after that has happened // Turn off measuring mode for the component, which triggers another call to the `updated()` Vue event, so we can turn off the protection after that has happened
measuringOngoing = false; measuringOngoing = false;

View File

@ -9,7 +9,7 @@
export let scrollableX: boolean = false; export let scrollableX: boolean = false;
export let scrollableY: boolean = false; export let scrollableY: boolean = false;
let divElement: HTMLDivElement; let self: HTMLDivElement;
$: extraClasses = Object.entries(classes) $: extraClasses = Object.entries(classes)
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : [])) .flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
@ -19,7 +19,7 @@
.join(" "); .join(" ");
export function div(): HTMLDivElement { export function div(): HTMLDivElement {
return divElement; return self;
} }
</script> </script>
@ -29,21 +29,52 @@
class:scrollable-y={scrollableY} class:scrollable-y={scrollableY}
style={`${styleName} ${extraStyles}`.trim() || undefined} style={`${styleName} ${extraStyles}`.trim() || undefined}
title={tooltip} title={tooltip}
bind:this={divElement} bind:this={self}
on:focus
on:blur
on:fullscreenchange
on:fullscreenerror
on:scroll
on:cut
on:copy
on:paste
on:keydown
on:keypress
on:keyup
on:auxclick
on:click on:click
on:contextmenu
on:dblclick on:dblclick
on:mousedown
on:mouseenter
on:mouseleave
on:mousemove
on:mouseover
on:mouseout
on:mouseup
on:select
on:wheel
on:drag
on:dragend
on:dragenter
on:dragstart
on:dragleave
on:dragover
on:drop
on:touchcancel
on:touchend
on:touchmove
on:touchstart
on:pointerover
on:pointerenter
on:pointerdown on:pointerdown
on:pointermove on:pointermove
on:pointerup on:pointerup
on:dragleave on:pointercancel
on:dragover on:pointerout
on:dragstart on:pointerleave
on:dragend on:gotpointercapture
on:drop on:lostpointercapture
on:wheel
on:scroll
on:focus
on:blur
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View File

@ -9,7 +9,7 @@
export let scrollableX: boolean = false; export let scrollableX: boolean = false;
export let scrollableY: boolean = false; export let scrollableY: boolean = false;
let divElement: HTMLDivElement; let self: HTMLDivElement;
$: extraClasses = Object.entries(classes) $: extraClasses = Object.entries(classes)
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : [])) .flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
@ -19,7 +19,7 @@
.join(" "); .join(" ");
export function div(): HTMLDivElement { export function div(): HTMLDivElement {
return divElement; return self;
} }
</script> </script>
@ -29,21 +29,52 @@
class:scrollable-y={scrollableY} class:scrollable-y={scrollableY}
style={`${styleName} ${extraStyles}`.trim() || undefined} style={`${styleName} ${extraStyles}`.trim() || undefined}
title={tooltip} title={tooltip}
bind:this={divElement} bind:this={self}
on:focus
on:blur
on:fullscreenchange
on:fullscreenerror
on:scroll
on:cut
on:copy
on:paste
on:keydown
on:keypress
on:keyup
on:auxclick
on:click on:click
on:contextmenu
on:dblclick on:dblclick
on:mousedown
on:mouseenter
on:mouseleave
on:mousemove
on:mouseover
on:mouseout
on:mouseup
on:select
on:wheel
on:drag
on:dragend
on:dragenter
on:dragstart
on:dragleave
on:dragover
on:drop
on:touchcancel
on:touchend
on:touchmove
on:touchstart
on:pointerover
on:pointerenter
on:pointerdown on:pointerdown
on:pointermove on:pointermove
on:pointerup on:pointerup
on:dragleave on:pointercancel
on:dragover on:pointerout
on:dragstart on:pointerleave
on:dragend on:gotpointercapture
on:drop on:lostpointercapture
on:wheel
on:scroll
on:focus
on:blur
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View File

@ -28,10 +28,9 @@
import { type Editor } from "@/wasm-communication/editor"; import { type Editor } from "@/wasm-communication/editor";
import { type DocumentState } from "@/state-providers/document"; import { type DocumentState } from "@/state-providers/document";
let self: LayoutCol;
let rulerHorizontal: CanvasRuler; let rulerHorizontal: CanvasRuler;
let rulerVertical: CanvasRuler; let rulerVertical: CanvasRuler;
let canvasDiv: HTMLDivElement; let canvasContainer: HTMLDivElement;
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
const document = getContext<DocumentState>("document"); const document = getContext<DocumentState>("document");
@ -116,7 +115,7 @@
function canvasPointerDown(e: PointerEvent) { function canvasPointerDown(e: PointerEvent) {
const onEditbox = e.target instanceof HTMLDivElement && e.target.contentEditable; const onEditbox = e.target instanceof HTMLDivElement && e.target.contentEditable;
if (!onEditbox) canvasDiv?.setPointerCapture(e.pointerId); if (!onEditbox) canvasContainer?.setPointerCapture(e.pointerId);
} }
// Update rendered SVGs // Update rendered SVGs
@ -127,7 +126,7 @@
await tick(); await tick();
if (textInput) { if (textInput) {
const foreignObject = canvasDiv.getElementsByTagName("foreignObject")[0] as SVGForeignObjectElement; const foreignObject = canvasContainer.getElementsByTagName("foreignObject")[0] as SVGForeignObjectElement;
if (foreignObject.children.length > 0) return; if (foreignObject.children.length > 0) return;
const addedInput = foreignObject.appendChild(textInput); const addedInput = foreignObject.appendChild(textInput);
@ -285,8 +284,8 @@
// Resize elements to render the new viewport size // Resize elements to render the new viewport size
export function viewportResize() { export function viewportResize() {
// Resize the canvas // Resize the canvas
canvasSvgWidth = Math.ceil(parseFloat(getComputedStyle(canvasDiv).width)); canvasSvgWidth = Math.ceil(parseFloat(getComputedStyle(canvasContainer).width));
canvasSvgHeight = Math.ceil(parseFloat(getComputedStyle(canvasDiv).height)); canvasSvgHeight = Math.ceil(parseFloat(getComputedStyle(canvasContainer).height));
// Resize the rulers // Resize the rulers
rulerHorizontal?.resize(); rulerHorizontal?.resize();
@ -382,7 +381,7 @@
}); });
</script> </script>
<LayoutCol class="document" bind:this={self}> <LayoutCol class="document">
<LayoutRow class="options-bar" scrollableX={true}> <LayoutRow class="options-bar" scrollableX={true}>
<WidgetLayout layout={$document.documentModeLayout} /> <WidgetLayout layout={$document.documentModeLayout} />
<WidgetLayout layout={$document.toolOptionsLayout} /> <WidgetLayout layout={$document.toolOptionsLayout} />
@ -422,7 +421,7 @@
y={cursorTop} y={cursorTop}
/> />
{/if} {/if}
<div class="canvas" on:pointerdown={(e) => canvasPointerDown(e)} on:dragover={(e) => e.preventDefault()} on:drop={(e) => pasteFile(e)} bind:this={canvasDiv} data-canvas> <div class="canvas" on:pointerdown={(e) => canvasPointerDown(e)} on:dragover={(e) => e.preventDefault()} on:drop={(e) => pasteFile(e)} bind:this={canvasContainer} data-canvas>
<svg class="artboards" style:width={canvasWidthCSS} style:height={canvasHeightCSS}> <svg class="artboards" style:width={canvasWidthCSS} style:height={canvasHeightCSS}>
{@html artboardSvg} {@html artboardSvg}
</svg> </svg>

View File

@ -109,7 +109,7 @@
await tick(); await tick();
const textInput: HTMLInputElement | undefined = list?.querySelector("[data-text-input]:not([disabled])") || undefined; const textInput = (list?.div().querySelector("[data-text-input]:not([disabled])") || undefined) as HTMLInputElement | undefined;
textInput?.select(); textInput?.select();
} }
@ -169,7 +169,7 @@
function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData { function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData {
const treeChildren = tree.div().children; const treeChildren = tree.div().children;
const treeOffset = tree.getBoundingClientRect().top; const treeOffset = tree.div().getBoundingClientRect().top;
// Closest distance to the middle of the row along the Y axis // Closest distance to the middle of the row along the Y axis
let closest = Infinity; let closest = Infinity;

View File

@ -180,7 +180,7 @@
let zoomFactor = 1 + Math.abs(scrollY) * WHEEL_RATE; let zoomFactor = 1 + Math.abs(scrollY) * WHEEL_RATE;
if (scrollY > 0) zoomFactor = 1 / zoomFactor; if (scrollY > 0) zoomFactor = 1 / zoomFactor;
const { x, y, width, height } = graph.getBoundingClientRect(); const { x, y, width, height } = graph.div().getBoundingClientRect();
transform.scale *= zoomFactor; transform.scale *= zoomFactor;
@ -222,7 +222,7 @@
// Handle the add node popup on right click // Handle the add node popup on right click
if (e.button === 2) { if (e.button === 2) {
const graphBounds = graph.getBoundingClientRect(); const graphBounds = graph.div().getBoundingClientRect();
nodeListLocation = { nodeListLocation = {
x: Math.round(((e.clientX - graphBounds.x) / transform.scale - transform.x) / GRID_SIZE), x: Math.round(((e.clientX - graphBounds.x) / transform.scale - transform.x) / GRID_SIZE),
y: Math.round(((e.clientY - graphBounds.y) / transform.scale - transform.y) / GRID_SIZE), y: Math.round(((e.clientY - graphBounds.y) / transform.scale - transform.y) / GRID_SIZE),

View File

@ -28,7 +28,7 @@
export let sharpRightCorners = false; export let sharpRightCorners = false;
export let placeholder: string | undefined = undefined; export let placeholder: string | undefined = undefined;
let input: HTMLInputElement | HTMLTextAreaElement; let inputOrTextarea: HTMLInputElement | HTMLTextAreaElement;
let id = `${Math.random()}`.substring(2); let id = `${Math.random()}`.substring(2);
let macKeyboardLayout = platformIsMac(); let macKeyboardLayout = platformIsMac();
let inputValue = value; let inputValue = value;
@ -40,20 +40,28 @@
export function selectAllText(currentText: string) { export function selectAllText(currentText: string) {
// Setting the value directly is required to make `input.select()` work // Setting the value directly is required to make `input.select()` work
// TODO: Svelte: Test if the above message is still true // TODO: Svelte: Test if the above message is still true
input.value = currentText; inputOrTextarea.value = currentText;
input.select(); inputOrTextarea.select();
}
export function focus() {
inputOrTextarea.focus();
} }
export function unFocus() { export function unFocus() {
input.blur(); inputOrTextarea.blur();
} }
export function getInputElementValue(): string { export function getValue(): string {
return input.value; return inputOrTextarea.value;
} }
export function setInputElementValue(value: string) { export function setInputElementValue(value: string) {
input.value = value; inputOrTextarea.value = value;
}
export function element(): HTMLInputElement | HTMLTextAreaElement {
return inputOrTextarea;
} }
</script> </script>
@ -68,7 +76,7 @@
{disabled} {disabled}
{placeholder} {placeholder}
bind:value={inputValue} bind:value={inputValue}
bind:this={input} bind:this={inputOrTextarea}
on:focus={() => dispatch("textFocused")} on:focus={() => dispatch("textFocused")}
on:blur={() => dispatch("textChanged")} on:blur={() => dispatch("textChanged")}
on:change={() => dispatch("textChanged")} on:change={() => dispatch("textChanged")}
@ -85,7 +93,7 @@
{spellcheck} {spellcheck}
{disabled} {disabled}
bind:value={inputValue} bind:value={inputValue}
bind:this={input} bind:this={inputOrTextarea}
on:focus={() => dispatch("textFocused")} on:focus={() => dispatch("textFocused")}
on:blur={() => dispatch("textChanged")} on:blur={() => dispatch("textChanged")}
on:change={() => dispatch("textChanged")} on:change={() => dispatch("textChanged")}

View File

@ -51,7 +51,7 @@
export let incrementCallbackIncrease: (() => void) | undefined = undefined; export let incrementCallbackIncrease: (() => void) | undefined = undefined;
export let incrementCallbackDecrease: (() => void) | undefined = undefined; export let incrementCallbackDecrease: (() => void) | undefined = undefined;
let fieldInput: FieldInput; let self: FieldInput;
let text = displayText(value); let text = displayText(value);
let editing = false; let editing = false;
// Stays in sync with a binding to the actual input range slider element. // Stays in sync with a binding to the actual input range slider element.
@ -127,7 +127,7 @@
function sliderPointerUp() { function sliderPointerUp() {
// User clicked but didn't drag, so we focus the text input element // User clicked but didn't drag, so we focus the text input element
if (rangeSliderClickDragState === "mousedown") { if (rangeSliderClickDragState === "mousedown") {
const inputElement = fieldInput.querySelector("[data-input-element]") as HTMLInputElement | undefined; const inputElement = self.element().querySelector("[data-input-element]") as HTMLInputElement | undefined;
if (!inputElement) return; if (!inputElement) return;
// Set the slider position back to the original position to undo the user moving it // Set the slider position back to the original position to undo the user moving it
@ -148,7 +148,7 @@
editing = true; editing = true;
fieldInput.selectAllText(text); self.selectAllText(text);
} }
// Called only when `value` is changed from the <input> element via user input and committed, either with the // Called only when `value` is changed from the <input> element via user input and committed, either with the
@ -164,7 +164,7 @@
editing = false; editing = false;
fieldInput.unFocus(); self.unFocus();
} }
function onCancelTextChange() { function onCancelTextChange() {
@ -172,7 +172,7 @@
editing = false; editing = false;
fieldInput.unFocus(); self.unFocus();
} }
function onIncrement(direction: "Decrease" | "Increase") { function onIncrement(direction: "Decrease" | "Increase") {
@ -245,7 +245,7 @@
{sharpRightCorners} {sharpRightCorners}
spellcheck={false} spellcheck={false}
styles={{ "min-width": minWidth > 0 ? `${minWidth}px` : undefined, "--progress-factor": (rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin) }} styles={{ "min-width": minWidth > 0 ? `${minWidth}px` : undefined, "--progress-factor": (rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin) }}
bind:this={fieldInput} bind:this={self}
> >
{#if value !== undefined && mode === "Increment" && incrementBehavior !== "None"} {#if value !== undefined && mode === "Increment" && incrementBehavior !== "None"}
<button class="arrow left" on:click={() => onIncrement("Decrease")} tabindex="-1" /> <button class="arrow left" on:click={() => onIncrement("Decrease")} tabindex="-1" />

View File

@ -1,24 +1,17 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte";
import { type IconName } from "@/utility-functions/icons"; import { type IconName } from "@/utility-functions/icons";
import LayoutRow from "@/components/layout/LayoutRow.svelte"; import LayoutRow from "@/components/layout/LayoutRow.svelte";
import CheckboxInput from "@/components/widgets/inputs/CheckboxInput.svelte"; import CheckboxInput from "@/components/widgets/inputs/CheckboxInput.svelte";
// emits: ["update:checked"],
const dispatch = createEventDispatcher<{ checked: boolean }>();
export let checked: boolean; export let checked: boolean;
export let disabled = false; export let disabled = false;
export let icon: IconName = "Checkmark"; export let icon: IconName = "Checkmark";
export let tooltip: string | undefined = undefined; export let tooltip: string | undefined = undefined;
let checkboxInput: CheckboxInput;
</script> </script>
<LayoutRow class="optional-input" classes={{ disabled }}> <LayoutRow class="optional-input" classes={{ disabled }}>
<CheckboxInput {checked} on:checked {disabled} {icon} {tooltip} bind:this={checkboxInput} /> <CheckboxInput {checked} on:checked {disabled} {icon} {tooltip} />
</LayoutRow> </LayoutRow>
<style lang="scss" global> <style lang="scss" global>

View File

@ -11,7 +11,7 @@
export let tooltip: string | undefined = undefined; export let tooltip: string | undefined = undefined;
export let disabled = false; export let disabled = false;
let fieldInput: FieldInput; let self: FieldInput;
let editing = false; let editing = false;
let inputValue = value; let inputValue = value;
@ -30,16 +30,20 @@
onCancelTextChange(); onCancelTextChange();
// TODO: Find a less hacky way to do this // TODO: Find a less hacky way to do this
dispatch("commitText", fieldInput.getInputElementValue()); dispatch("commitText", self.getValue());
// Required if value is not changed by the parent component upon update:value event // Required if value is not changed by the parent component upon update:value event
fieldInput.setInputElementValue(value); self.setInputElementValue(value);
} }
function onCancelTextChange() { function onCancelTextChange() {
editing = false; editing = false;
fieldInput.unFocus(); self.unFocus();
}
export function focus() {
self.focus();
} }
</script> </script>
@ -59,7 +63,7 @@
{disabled} {disabled}
{tooltip} {tooltip}
value={inputValue} value={inputValue}
bind:this={fieldInput} bind:this={self}
/> />
<style lang="scss" global> <style lang="scss" global>

View File

@ -19,7 +19,7 @@
export let minWidth = 0; export let minWidth = 0;
export let sharpRightCorners = false; export let sharpRightCorners = false;
let fieldInput: FieldInput; let self: FieldInput;
let editing = false; let editing = false;
let text = value; let text = value;
@ -28,7 +28,7 @@
function onTextFocused() { function onTextFocused() {
editing = true; editing = true;
fieldInput.selectAllText(text); self.selectAllText(text);
} }
// Called only when `value` is changed from the <input> element via user input and committed, either with the // Called only when `value` is changed from the <input> element via user input and committed, either with the
@ -40,16 +40,20 @@
onCancelTextChange(); onCancelTextChange();
// TODO: Find a less hacky way to do this // TODO: Find a less hacky way to do this
dispatch("commitText", fieldInput.getInputElementValue()); dispatch("commitText", self.getValue());
// Required if value is not changed by the parent component upon update:value event // Required if value is not changed by the parent component upon update:value event
fieldInput.setInputElementValue(value); self.setInputElementValue(value);
} }
function onCancelTextChange() { function onCancelTextChange() {
editing = false; editing = false;
fieldInput.unFocus(); self.unFocus();
}
export function focus() {
self.focus();
} }
</script> </script>
@ -68,7 +72,7 @@
{tooltip} {tooltip}
{placeholder} {placeholder}
{sharpRightCorners} {sharpRightCorners}
bind:this={fieldInput} bind:this={self}
/> />
<style lang="scss" global> <style lang="scss" global>

View File

@ -115,8 +115,6 @@ fn handle_message(message: String) -> String {
let serialized = ron::to_string(&send_frontend_message_to_js(response.clone())).unwrap(); let serialized = ron::to_string(&send_frontend_message_to_js(response.clone())).unwrap();
if let Err(error) = ron::from_str::<FrontendMessage>(&serialized) { if let Err(error) = ron::from_str::<FrontendMessage>(&serialized) {
log::error!("Error deserializing message: {}", error); log::error!("Error deserializing message: {}", error);
log::debug!("{:#?}", response);
log::debug!("{}", serialized);
} }
} }