Skip to content

Commit

Permalink
perf: compute tags statically (#4687)
Browse files Browse the repository at this point in the history
Got rid of computing tags from dom in canvas. Now tags are computed from
possible preset styles and tag prop if available.
  • Loading branch information
TrySound authored Dec 31, 2024
1 parent cee3931 commit 5408a11
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 86 deletions.
39 changes: 36 additions & 3 deletions apps/builder/app/builder/features/style-panel/shared/model.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo, useRef } from "react";
import type { HtmlTags } from "html-tags";
import { computed, type ReadableAtom } from "nanostores";
import { useStore } from "@nanostores/react";
import { properties } from "@webstudio-is/css-data";
Expand All @@ -22,9 +23,9 @@ import { rootComponent, WsComponentMeta } from "@webstudio-is/react-sdk";
import {
$breakpoints,
$instances,
$propsIndex,
$registeredComponentMetas,
$selectedBreakpoint,
$selectedInstanceIntanceToTag,
$selectedInstanceStates,
$selectedOrLastStyleSourceSelector,
$styles,
Expand Down Expand Up @@ -57,6 +58,38 @@ const $presetStyles = computed($registeredComponentMetas, (metas) => {
return presetStyles;
});

export const $instanceTags = computed(
[$registeredComponentMetas, $selectedInstancePath, $propsIndex],
(metas, instancePath, propsIndex) => {
const instanceTags = new Map<Instance["id"], HtmlTags>();
if (instancePath === undefined) {
return instanceTags;
}
for (const { instance } of instancePath) {
const meta = metas.get(instance.component);
if (meta === undefined) {
continue;
}
const tags = Object.keys(meta.presetStyle ?? {}) as HtmlTags[];
if (tags.length > 0) {
// take first tag from preset
let currentTag = tags[0];
// when more than one tag is defined in preset look for specific one in props
if (tags.length > 1) {
const props = propsIndex.propsByInstanceId.get(instance.id);
// @todo rewrite adhoc solution when ws:tag is supported
const tagProp = props?.find((prop) => prop.name === "tag");
if (tagProp) {
currentTag = tagProp.value as HtmlTags;
}
}
instanceTags.set(instance.id, currentTag);
}
}
return instanceTags;
}
);

const $instanceComponents = computed($selectedInstancePath, (instancePath) => {
const instanceComponents = new Map<Instance["id"], Instance["component"]>([
[ROOT_INSTANCE_ID, rootComponent],
Expand Down Expand Up @@ -204,7 +237,7 @@ const $model = computed(
$styles,
$styleSourceSelections,
$presetStyles,
$selectedInstanceIntanceToTag,
$instanceTags,
$instanceComponents,
$matchingBreakpoints,
$selectedInstanceStates,
Expand All @@ -222,7 +255,7 @@ const $model = computed(
styles,
styleSourceSelections,
presetStyles,
instanceTags: instanceTags ?? new Map(),
instanceTags,
instanceComponents,
matchingBreakpoints,
matchingStates,
Expand Down
11 changes: 5 additions & 6 deletions apps/builder/app/builder/features/style-panel/style-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ import { useStore } from "@nanostores/react";
import { computed } from "nanostores";
import { StyleSourcesSection } from "./style-source-section";
import { $selectedInstanceRenderState } from "~/shared/nano-states";
import { $selectedInstanceIntanceToTag } from "~/shared/nano-states";
import { sections } from "./sections";
import { toValue } from "@webstudio-is/css-engine";
import { useParentComputedStyleDecl } from "./shared/model";
import { $instanceTags, useParentComputedStyleDecl } from "./shared/model";
import { $selectedInstance } from "~/shared/awareness";

const $selectedInstanceTag = computed(
[$selectedInstance, $selectedInstanceIntanceToTag],
(selectedInstance, instanceToTag) => {
if (selectedInstance === undefined || instanceToTag === undefined) {
[$selectedInstance, $instanceTags],
(selectedInstance, instanceTags) => {
if (selectedInstance === undefined) {
return;
}
return instanceToTag.get(selectedInstance.id);
return instanceTags.get(selectedInstance.id);
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import {
LinkIcon,
PaintBrushIcon,
} from "@webstudio-is/icons";
import {
$selectedInstanceIntanceToTag,
$selectedInstanceSelector,
} from "~/shared/nano-states";
import { $selectedInstanceSelector } from "~/shared/nano-states";
import { type TextToolbarState, $textToolbar } from "~/shared/nano-states";
import { $scale } from "~/builder/shared/nano-states";
import { emitCommand } from "~/builder/shared/commands";
import { $instanceTags } from "../../style-panel/shared/model";

const getRectForRelativeRect = (
parent: DOMRect,
Expand All @@ -39,16 +37,13 @@ const getRectForRelativeRect = (
};

const $isWithinLink = computed(
[$selectedInstanceSelector, $selectedInstanceIntanceToTag],
(selectedInstanceSelector, selectedInstanceIntanceToTag) => {
if (
selectedInstanceSelector === undefined ||
selectedInstanceIntanceToTag === undefined
) {
[$selectedInstanceSelector, $instanceTags],
(selectedInstanceSelector, instanceTags) => {
if (selectedInstanceSelector === undefined) {
return false;
}
for (const instanceId of selectedInstanceSelector) {
const tag = selectedInstanceIntanceToTag.get(instanceId);
const tag = instanceTags.get(instanceId);
if (tag === "a") {
return true;
}
Expand Down
53 changes: 2 additions & 51 deletions apps/builder/app/canvas/instance-selected.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ROOT_INSTANCE_ID, type Instance } from "@webstudio-is/sdk";
import { idAttribute, selectorIdAttribute } from "@webstudio-is/react-sdk";
import type { Instance } from "@webstudio-is/sdk";
import { selectorIdAttribute } from "@webstudio-is/react-sdk";
import { subscribeWindowResize } from "~/shared/dom-hooks";
import {
$isResizingCanvas,
$selectedInstanceBrowserStyle,
$selectedInstanceIntanceToTag,
$selectedInstanceUnitSizes,
$selectedInstanceRenderState,
$stylesIndex,
Expand All @@ -15,7 +14,6 @@ import {
$selectedInstanceStates,
$styleSourceSelections,
} from "~/shared/nano-states";
import htmlTags, { type HtmlTags } from "html-tags";
import {
getAllElementsBoundingBox,
getVisibleElementsByInstanceSelector,
Expand All @@ -35,9 +33,6 @@ import type { InstanceSelector } from "~/shared/tree-utils";
import { shallowEqual } from "shallow-equal";
import warnOnce from "warn-once";

const isHtmlTag = (tag: string): tag is HtmlTags =>
htmlTags.includes(tag as HtmlTags);

const setOutline = (instanceId: Instance["id"], elements: HTMLElement[]) => {
$selectedInstanceOutline.set({
instanceId,
Expand Down Expand Up @@ -83,36 +78,6 @@ const calculateUnitSizes = (element: HTMLElement): UnitSizes => {
};
};

export const getElementAndAncestorInstanceTags = (
instanceSelector: Readonly<InstanceSelector>
) => {
const elements = getAllElementsByInstanceSelector(instanceSelector);

if (elements.length === 0) {
return;
}

const [element] = elements;

const instanceToTag = new Map<Instance["id"], HtmlTags>([
[ROOT_INSTANCE_ID, "html"],
]);
for (
let ancestorOrSelf: HTMLElement | null = element;
ancestorOrSelf !== null;
ancestorOrSelf = ancestorOrSelf.parentElement
) {
const tagName = ancestorOrSelf.tagName.toLowerCase();
const instanceId = ancestorOrSelf.getAttribute(idAttribute);

if (isHtmlTag(tagName) && instanceId !== null) {
instanceToTag.set(instanceId, tagName);
}
}

return instanceToTag;
};

const subscribeSelectedInstance = (
selectedInstanceSelector: Readonly<InstanceSelector>,
debounceEffect: (callback: () => void) => void
Expand Down Expand Up @@ -188,20 +153,6 @@ const subscribeSelectedInstance = (
// trigger style recomputing every time instance styles are changed
$selectedInstanceBrowserStyle.set(getBrowserStyle(element));

// Map self and ancestor instance ids to tag names
const instanceToTag = getElementAndAncestorInstanceTags(
selectedInstanceSelector
);

if (
!shallowEqual(
[...($selectedInstanceIntanceToTag.get()?.entries() ?? [])].flat(),
[...(instanceToTag?.entries() ?? [])].flat()
)
) {
$selectedInstanceIntanceToTag.set(instanceToTag);
}

const unitSizes = calculateUnitSizes(element);
$selectedInstanceUnitSizes.set(unitSizes);

Expand Down
2 changes: 0 additions & 2 deletions apps/builder/app/shared/canvas-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { monitorForExternal } from "@atlaskit/pragmatic-drag-and-drop/external/a
import { createRecursiveProxy } from "@trpc/server/shared";
import invariant from "tiny-invariant";
import { $canvasIframeState } from "./nano-states";
import { getElementAndAncestorInstanceTags } from "~/canvas/instance-selected";
import { detectSupportedFontWeights } from "~/canvas/shared/font-weight-support";

const apiWindowNamespace = "__webstudio__$__canvasApi";
Expand All @@ -15,7 +14,6 @@ const _canvasApi = {
resetInert,
preventUnhandled,
monitorForExternal,
getElementAndAncestorInstanceTags,
detectSupportedFontWeights,
};

Expand Down
8 changes: 0 additions & 8 deletions apps/builder/app/shared/nano-states/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import type { MarketplaceProduct } from "@webstudio-is/project-build";
import type { TokenPermissions } from "@webstudio-is/authorization-token";
import type { DragStartPayload } from "~/canvas/shared/use-drag-drop";
import { type InstanceSelector } from "../tree-utils";
import type { HtmlTags } from "html-tags";
import { $selectedInstanceSelector } from "./instances";
import type { UnitSizes } from "~/builder/features/style-panel/shared/css-value-input/convert-units";
import type { Simplify } from "type-fest";
Expand Down Expand Up @@ -159,13 +158,6 @@ export const $selectedInstanceUnitSizes = atom<UnitSizes>({
px: 1,
});

/**
* instanceId => tagName store for selected instance and its ancestors
*/
export const $selectedInstanceIntanceToTag = atom<
undefined | Map<Instance["id"], HtmlTags>
>();

/**
* pending means: previous selected instance unmounted,
* and we don't know yet whether a new one will mount
Expand Down
5 changes: 0 additions & 5 deletions apps/builder/app/shared/sync/sync-stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
$selectedInstanceSelector,
$selectedInstanceBrowserStyle,
$selectedInstanceUnitSizes,
$selectedInstanceIntanceToTag,
$selectedInstanceRenderState,
$hoveredInstanceSelector,
$authTokenPermissions,
Expand Down Expand Up @@ -102,10 +101,6 @@ export const createObjectPool = () => {
"selectedInstanceBrowserStyle",
$selectedInstanceBrowserStyle
),
new NanostoresSyncObject(
"selectedInstanceIntanceToTag",
$selectedInstanceIntanceToTag
),
new NanostoresSyncObject(
"selectedInstanceUnitSizes",
$selectedInstanceUnitSizes
Expand Down

0 comments on commit 5408a11

Please sign in to comment.