Fix remaining known Svelte UI regressions

Closes #1040
This commit is contained in:
Keavon Chambers 2023-03-20 21:26:33 -07:00
parent 9c10d18308
commit bfbabbc4dc
17 changed files with 62 additions and 69 deletions

View file

@ -62,19 +62,17 @@
let draggingPickerTrack: HTMLDivElement | undefined = undefined;
let strayCloses = true;
$: rgbChannels = Object.entries(newColor.toRgb255() || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][];
$: hsvChannels = Object.entries(!isNone ? { h: hue * 360, s: saturation * 100, v: value * 100 } : { h: undefined, s: undefined, v: undefined }) as [keyof HSV, number | undefined][];
$: opaqueHueColor = new Color({ h: hue, s: 1, v: 1, a: 1 });
$: newColor = isNone ? new Color("none") : new Color({ h: hue, s: saturation, v: value, a: alpha });
$: initialColor = updateInitialColor(initialHue, initialSaturation, initialValue, initialAlpha, initialIsNone, open);
$: watchOpen(open);
$: watchColor(color);
// Taking `_open` is necessary to make Svelte order the reactive processing queue so this works as required, see:
// https://stackoverflow.com/questions/63934543/svelte-reactivity-not-triggering-when-variable-changed-in-a-function
function updateInitialColor(h: number, s: number, v: number, a: number, initialIsNone: boolean, _open: boolean) {
if (initialIsNone) return new Color("none");
$: initialColor = generateColor(initialHue, initialSaturation, initialValue, initialAlpha, initialIsNone);
$: newColor = generateColor(hue, saturation, value, alpha, isNone);
$: rgbChannels = Object.entries(newColor.toRgb255() || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][];
$: hsvChannels = Object.entries(!isNone ? { h: hue * 360, s: saturation * 100, v: value * 100 } : { h: undefined, s: undefined, v: undefined }) as [keyof HSV, number | undefined][];
$: opaqueHueColor = new Color({ h: hue, s: 1, v: 1, a: 1 });
function generateColor(h: number, s: number, v: number, a: number, none: boolean, ..._: any[]) {
if (none) return new Color("none");
return new Color({ h, s, v, a });
}
@ -96,7 +94,7 @@
// - ...reset the hue to 0° if the color's value is black, where all hues are equivalent
if (!(hsva.h === 0 && hue === 1) && hsva.s > 0 && hsva.v > 0) hue = hsva.h;
// Update the saturation, but only if it is necessary so we don't:
// - ...reset the saturation to the left is the color's value is black along the bottom edge, where all saturations are equivalent
// - ...reset the saturation to the left if the color's value is black along the bottom edge, where all saturations are equivalent
if (hsva.v !== 0) saturation = hsva.s;
// Update the value
value = hsva.v;

View file

@ -36,6 +36,7 @@
// Called only when `open` is changed from outside this component
$: watchOpen(open);
$: watchRemeasureWidth(entries, drawIcon);
$: virtualScrollingTotalHeight = entries.length === 0 ? 0 : entries[0].length * virtualScrollingEntryHeight;
$: virtualScrollingStartIndex = Math.floor(virtualScrollingEntriesStart / virtualScrollingEntryHeight) || 0;
$: virtualScrollingEndIndex = entries.length === 0 ? 0 : Math.min(entries[0].length, virtualScrollingStartIndex + 1 + 400 / virtualScrollingEntryHeight);
@ -62,7 +63,10 @@
dispatch("activeEntry", menuListEntry);
// Close the containing menu
if (menuListEntry.ref) menuListEntry.ref.open = false;
if (menuListEntry.ref) {
menuListEntry.ref.open = false;
entries = entries;
}
dispatch("open", false);
open = false;
}
@ -70,15 +74,19 @@
function onEntryPointerEnter(menuListEntry: MenuListEntry): void {
if (!menuListEntry.children?.length) return;
if (menuListEntry.ref) menuListEntry.ref.open = true;
else dispatch("open", true);
if (menuListEntry.ref) {
menuListEntry.ref.open = true;
entries = entries;
} else dispatch("open", true);
}
function onEntryPointerLeave(menuListEntry: MenuListEntry): void {
if (!menuListEntry.children?.length) return;
if (menuListEntry.ref) menuListEntry.ref.open = false;
else dispatch("open", false);
if (menuListEntry.ref) {
menuListEntry.ref.open = false;
entries = entries;
} else dispatch("open", false);
}
function isEntryOpen(menuListEntry: MenuListEntry): boolean {
@ -96,12 +104,15 @@
const flatEntries = entries.flat().filter((entry) => !entry.disabled);
const openChild = flatEntries.findIndex((entry) => entry.children?.length && entry.ref?.open);
const openSubmenu = (highlighted: MenuListEntry): void => {
if (highlighted.ref && highlighted.children?.length) {
highlighted.ref.open = true;
const openSubmenu = (highlightedEntry: MenuListEntry): void => {
if (highlightedEntry.ref && highlightedEntry.children?.length) {
highlightedEntry.ref.open = true;
// The reason we bother taking `highlightdEntry` as an argument is because, when this function is called, it can ensure `highlightedEntry` is not undefined.
// But here we still have to set `highlighted` to itself so Svelte knows to reactively update it after we set its `.ref.open` property.
highlighted = highlighted;
// Highlight first item
highlighted.ref.setHighlighted(highlighted.children[0][0]);
highlightedEntry.ref.setHighlighted(highlightedEntry.children[0][0]);
}
};

View file

@ -50,6 +50,8 @@
let floatingMenuBounds = new DOMRect();
let floatingMenuContentBounds = new DOMRect();
$: watchOpenChange(open);
$: minWidthStyleValue = measuringOngoing ? "0" : `${Math.max(minWidth, minWidthParentWidth)}px`;
$: displayTail = open && type === "Popover";
$: displayContainer = open || measuringOngoing;
@ -60,8 +62,6 @@
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
.join(" ");
$: watchOpenChange(open);
// Called only when `open` is changed from outside this component
async function watchOpenChange(isOpen: boolean) {
// Switching from closed to open

View file

@ -100,8 +100,9 @@
async function onEditLayerName(listing: LayerListingInfo) {
if (listing.editingName) return;
listing.editingName = true;
draggable = false;
listing.editingName = true;
layers = layers;
await tick();
@ -117,6 +118,7 @@
const name = (e.target as HTMLInputElement | undefined)?.value;
listing.editingName = false;
layers = layers;
if (name) editor.instance.setLayerName(listing.entry.path, name);
}
@ -124,6 +126,7 @@
draggable = true;
listing.editingName = false;
layers = layers;
await tick();
window.getSelection()?.removeAllRanges();
@ -211,7 +214,7 @@
});
}
markerHeight -= (treeOffset || 0);
markerHeight -= treeOffset || 0;
return {
select,

View file

@ -38,6 +38,8 @@
let searchTerm = "";
let nodeListLocation: { x: number; y: number } | undefined = undefined;
$: watchNodes($nodeGraph.nodes);
$: gridSpacing = calculateGridSpacing(transform.scale);
$: dotRadius = 1 + Math.floor(transform.scale - 0.5 + 0.001) / 2;
$: nodeGraphBarLayout = $nodeGraph.nodeGraphBarLayout;
@ -47,8 +49,6 @@
$: linkPathInProgress = createLinkPathInProgress(linkInProgressFromConnector, linkInProgressToConnector);
$: linkPaths = createLinkPaths(linkPathInProgress, nodeLinkPaths);
$: watchNodes($nodeGraph.nodes);
function calculateGridSpacing(scale: number): number {
const dense = scale * GRID_SIZE;
let sparse = dense;

View file

@ -9,7 +9,7 @@
</script>
<LayoutRow class="parameter-expose-button">
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="0" />
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="-1" />
</LayoutRow>
<style lang="scss" global>

View file

@ -29,11 +29,11 @@
let open = false;
let minWidth = 0;
$: selectedIndex, watchSelectedIndex();
$: watchSelectedIndex(selectedIndex);
$: watchActiveEntry(activeEntry);
// Called only when `selectedIndex` is changed from outside this component
function watchSelectedIndex() {
function watchSelectedIndex(_?: number) {
activeEntrySkipWatcher = true;
activeEntry = makeActiveEntry();
}

View file

@ -33,6 +33,7 @@
let macKeyboardLayout = platformIsMac();
$: inputValue = value;
$: dispatch("value", inputValue);
// Select (highlight) all the text. For technical reasons, it is necessary to pass the current text.

View file

@ -32,9 +32,9 @@
let activeEntry: MenuListEntry | undefined = undefined;
let minWidth = isStyle ? 0 : 300;
$: fontFamily, fontStyle, watchFont();
$: watchFont(fontFamily, fontStyle);
async function watchFont(): Promise<void> {
async function watchFont(..._: string[]): Promise<void> {
// We set this function's result to a local variable to avoid reading from `entries` which causes Svelte to trigger an update that results in an infinite loop
const newEntries = await getEntries();
entries = newEntries;

View file

@ -95,7 +95,10 @@
{#if entry.children && entry.children.length > 0}
<MenuList
on:open={({ detail }) => {
if (entry.ref) entry.ref.open = detail;
if (entry.ref) {
entry.ref.open = detail;
entries = entries;
}
}}
open={entry.ref?.open || false}
entries={entry.children || []}

View file

@ -64,9 +64,10 @@
// "dragging": the user is dragging the slider left/right.
let rangeSliderClickDragState: "default" | "mousedown" | "dragging" = "default";
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
$: watchValue(value);
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
// Called only when `value` is changed from outside this component
function watchValue(value: number | undefined) {
// Don't update if the slider is currently being dragged (we don't want the backend fighting with the user's drag)
@ -130,7 +131,7 @@
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;
const inputElement = self?.element();
if (!inputElement) return;
// Set the slider position back to the original position to undo the user moving it

View file

@ -38,29 +38,11 @@
<LayoutCol class="swatch-pair">
<LayoutRow class="primary swatch">
<button on:click={clickPrimarySwatch} style:--swatch-color={primary.toRgbaCSS()} data-floating-menu-spawner="no-hover-transfer" tabindex="0" />
<ColorPicker
open={primaryOpen}
on:open={({ detail }) => (primaryOpen = detail)}
color={primary}
on:color={({ detail }) => {
primary = detail;
primaryColorChanged(detail);
}}
direction="Right"
/>
<ColorPicker open={primaryOpen} on:open={({ detail }) => (primaryOpen = detail)} color={primary} on:color={({ detail }) => primaryColorChanged(detail)} direction="Right" />
</LayoutRow>
<LayoutRow class="secondary swatch">
<button on:click={clickSecondarySwatch} style:--swatch-color={secondary.toRgbaCSS()} data-floating-menu-spawner="no-hover-transfer" tabindex="0" />
<ColorPicker
open={secondaryOpen}
on:open={({ detail }) => (secondaryOpen = detail)}
color={secondary}
on:color={({ detail }) => {
secondary = detail;
secondaryColorChanged(detail);
}}
direction="Right"
/>
<ColorPicker open={secondaryOpen} on:open={({ detail }) => (secondaryOpen = detail)} color={secondary} on:color={({ detail }) => secondaryColorChanged(detail)} direction="Right" />
</LayoutRow>
</LayoutCol>

View file

@ -30,7 +30,7 @@
if (self) dispatch("commitText", self.getValue());
// Required if value is not changed by the parent component upon update:value event
self?.setInputElementValue(value);
self?.setInputElementValue(self.getValue());
}
function onCancelTextChange() {

View file

@ -40,7 +40,7 @@
if (self) dispatch("commitText", self.getValue());
// Required if value is not changed by the parent component upon update:value event
self?.setInputElementValue(value);
self?.setInputElementValue(self.getValue());
}
function onCancelTextChange() {

View file

@ -38,6 +38,7 @@
export let requiresLock = false;
$: keyboardLockInfoMessage = watchKeyboardLockInfoMessage(fullscreen.keyboardLockApiSupported);
$: displayKeyboardLockNotice = requiresLock && !$fullscreen.keyboardLocked;
function watchKeyboardLockInfoMessage(keyboardLockApiSupported: boolean): string {

View file

@ -8,20 +8,19 @@
const fullscreen = getContext<FullscreenState>("fullscreen");
$: windowFullscreen = $fullscreen.windowFullscreen;
$: requestFullscreenHotkeys = fullscreen.keyboardLockApiSupported && !$fullscreen.keyboardLocked;
async function handleClick() {
if (windowFullscreen) fullscreen.exitFullscreen();
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
else fullscreen.enterFullscreen();
}
</script>
<LayoutRow class="window-buttons-web" on:click={() => handleClick()} tooltip={(windowFullscreen ? "Exit" : "Enter") + " Fullscreen (F11)"}>
<LayoutRow class="window-buttons-web" on:click={() => handleClick()} tooltip={($fullscreen.windowFullscreen ? "Exit" : "Enter") + " Fullscreen (F11)"}>
{#if requestFullscreenHotkeys}
<TextLabel italic={true}>Go fullscreen to access all hotkeys</TextLabel>
{/if}
<IconLabel icon={windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
</LayoutRow>
<style lang="scss" global>

View file

@ -24,8 +24,8 @@
let panelSizes = PANEL_SIZES;
let documentPanel: Panel | undefined;
$: activeDocumentIndex = $portfolio.activeDocumentIndex;
$: nodeGraphVisible = $workspace.nodeGraphVisible;
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
$: documentTabLabels = $portfolio.documents.map((doc: FrontendDocumentDetails) => {
const name = doc.displayName;
@ -34,12 +34,6 @@
const tooltip = `Document ID ${doc.id}`;
return { name, tooltip };
});
$: {
scrollIntoView(activeDocumentIndex);
}
function scrollIntoView(activeDocumentIndex: number) {
documentPanel?.scrollTabIntoView(activeDocumentIndex);
}
const editor = getContext<Editor>("editor");
const workspace = getContext<WorkspaceState>("workspace");
@ -117,7 +111,7 @@
bind:this={documentPanel}
/>
</LayoutRow>
{#if nodeGraphVisible}
{#if $workspace.nodeGraphVisible}
<LayoutRow class="workspace-grid-resize-gutter" data-gutter-vertical on:pointerdown={resizePanel} />
<LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["graph"] }} data-subdivision-name="graph">
<Panel panelType="NodeGraph" tabLabels={[{ name: "Node Graph" }]} tabActiveIndex={0} />