Skip to content

Commit

Permalink
feat: figma plugin bugs (#42)
Browse files Browse the repository at this point in the history
* feat: improve highlighting and pulling

* fix: keys with namespaces bug

* feat: preserve scrolling state

* chore: fix e2e tests

* feat: remove unnecessary package

* fix: improve window size management

* fix: improve window size management

* fix: improve window size management
  • Loading branch information
stepan662 authored Jun 10, 2024
1 parent c398afb commit e598cf8
Show file tree
Hide file tree
Showing 23 changed files with 351 additions and 181 deletions.
6 changes: 5 additions & 1 deletion cypress/e2e/pull.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ describe("Pull", () => {
.contains("This action will replace translations in 1")
.should("be.visible");
cy.iframe().contains("Missing keys").should("be.visible");
cy.iframe().contains("nonexistant-key").should("be.visible");

cy.iframe()
.findDcy("dialog")
.contains("nonexistant-key")
.should("be.visible");

cy.iframe().findDcy("pull_submit_button").should("be.visible").click();

Expand Down
29 changes: 28 additions & 1 deletion src/main/endpoints/highlightNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,40 @@ export type HighlightNodeProps = {
id: string;
};

const highlitedNodes: Map<string, MinimalFillsMixin["fills"]> = new Map();

export const highlightNodeEndpoint = createEndpoint(
"HIGHLIGHT_NODE",
async ({ id }: HighlightNodeProps) => {
const node = figma.getNodeById(id);
const node = figma.getNodeById(id) as TextNode | null;

if (node) {
figma.viewport.scrollAndZoomIntoView([node]);
}

if (node && !highlitedNodes.has(id)) {
const paint: SolidPaint = {
type: "SOLID",
color: { r: 1, g: 0, b: 0 },
};
highlitedNodes.set(id, node.fills);
const original = node.fills;
node.fills = [paint];

setTimeout(() => {
node.fills = original;
highlitedNodes.delete(id);
}, 500);
}
}
);

export const cleanUp = () => {
highlitedNodes.forEach((value, id) => {
const node = figma.getNodeById(id) as TextNode | null;
if (node) {
node.fills = value;
highlitedNodes.delete(id);
}
});
};
30 changes: 19 additions & 11 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,39 @@ import {
getPluginData,
setPluginData,
} from "./utils/settingsTools";
import { DEFAULT_SIZE } from "@/ui/hooks/useWindowSize";
import { highlightNodeEndpoint } from "./endpoints/highlightNode";
import { cleanUp, highlightNodeEndpoint } from "./endpoints/highlightNode";
import { DEFAULT_SIZE } from "@/ui/state/sizes";

const getAllPages = () => {
const document = figma.root;

return document.children.filter((node) => node.type === "PAGE");
};

const isOnlyProperty = (e: NodeChangeEvent, property: string) => {
return e.nodeChanges.every((ch) => {
return (
ch.type === "PROPERTY_CHANGE" &&
ch.properties.every((p) => p === property)
);
});
};

export default async function () {
figma.on("selectionchange", () => {
emit<SelectionChangeHandler>("SELECTION_CHANGE");
});

figma.currentPage.on("nodechange", (e) => {
if (
!e.nodeChanges.every((ch) => {
return (
ch.type === "PROPERTY_CHANGE" &&
ch.properties.every((p) => p === "pluginData")
);
})
) {
emit<DocumentChangeHandler>("DOCUMENT_CHANGE");
if (isOnlyProperty(e, "pluginData") || isOnlyProperty(e, "fills")) {
return;
}

emit<DocumentChangeHandler>("DOCUMENT_CHANGE");
});

figma.on("close", () => {
cleanUp();
});

figma.on("currentpagechange", async () => {
Expand Down
9 changes: 9 additions & 0 deletions src/ui/components/Dialog/Dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.dialogBody {
z-index: 1300;
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background: var(--figma-color-bg);
}
5 changes: 5 additions & 0 deletions src/ui/components/Dialog/Dialog.css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare const styles: {
readonly "dialogBody": string;
};
export = styles;

29 changes: 29 additions & 0 deletions src/ui/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ComponentChildren, h } from "preact";
import { useEffect, useRef } from "preact/hooks";
import styles from "./Dialog.css";
import { useWindowSize } from "@/ui/hooks/useWindowSize";
import { DEFAULT_SIZE } from "@/ui/state/sizes";

type Props = {
onClose: () => void;
children: ComponentChildren;
};

export const Dialog = ({ children, onClose }: Props) => {
const ref = useRef<HTMLDivElement>(null);

useWindowSize(DEFAULT_SIZE);

useEffect(() => {
const handler = () => onClose();
ref.current?.focus();
ref.current?.addEventListener("keydown", handler);
return () => ref.current?.removeEventListener("keydown", handler);
}, []);

return (
<div ref={ref} tabIndex={0} data-cy="dialog">
<div className={styles.dialogBody}>{children}</div>
</div>
);
};
11 changes: 9 additions & 2 deletions src/ui/components/FullPageLoading/FullPageLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ import styles from "./FullPageLoading.css";

type Props = {
text?: string;
blocking?: boolean;
};

export const FullPageLoading: FunctionalComponent<Props> = ({ text }) => {
export const FullPageLoading: FunctionalComponent<Props> = ({
text,
blocking = true,
}) => {
return (
<div className={styles.container}>
<div
className={styles.container}
style={{ pointerEvents: blocking ? undefined : "none" }}
>
<div className={styles.content} data-cy="full_page_loading">
<LoadingIndicator />
<Muted>
Expand Down
6 changes: 2 additions & 4 deletions src/ui/components/NamespaceSelect/NamespaceSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,27 @@ import styles from "./NamespaceSelect.css";

type Props = {
namespaces: string[];
initialValue: string;
value: string;
onChange: (value: string) => void;
selectProps?: h.JSX.HTMLAttributes<HTMLSelectElement>;
};

const ADD_NEW_VALUE = Number.MAX_VALUE;

export const NamespaceSelect: FunctionComponent<Props> = ({
initialValue,
value,
namespaces,
selectProps,
onChange,
}) => {
const [modalOpen, setModalOpen] = useState(false);
const [nsName, setNsName] = useState<string>("");
const [value, _setValue] = useState(initialValue);

const handleModalClose = () => {
setModalOpen(false);
};

function setValue(value: string) {
_setValue(value);
onChange(value);
}

Expand Down
31 changes: 18 additions & 13 deletions src/ui/components/NodeList/NodeList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComponentChildren, h } from "preact";
import { ComponentChildren, Fragment, h } from "preact";

import styles from "./NodeList.css";
import { NodeRow } from "./NodeRow";
Expand All @@ -11,7 +11,7 @@ type Props<T extends { id: string }> = {
nsComponent?: (item: T) => ComponentChildren;
compact?: boolean;
onClick?: (item: T) => void;
onBottomReached?: () => void;
row?: (node: T) => ComponentChildren;
};

export function NodeList<T extends { id: string }>({
Expand All @@ -21,22 +21,27 @@ export function NodeList<T extends { id: string }>({
nsComponent,
compact,
onClick,
row,
}: Props<T>) {
const containerRef = useRef<HTMLDivElement>(null);

return (
<div className={styles.container} ref={containerRef}>
{items?.map((item) => (
<NodeRow
key={item.id}
node={item}
action={actionCallback?.(item)}
keyComponent={keyComponent?.(item)}
nsComponent={nsComponent?.(item)}
compact={compact}
onClick={onClick ? () => onClick?.(item) : undefined}
/>
))}
{items?.map((item) =>
row ? (
<Fragment key={item.id}>{row(item)}</Fragment>
) : (
<NodeRow
key={item.id}
node={item}
action={actionCallback?.(item)}
keyComponent={keyComponent?.(item)}
nsComponent={nsComponent?.(item)}
compact={compact}
onClick={onClick ? () => onClick?.(item) : undefined}
/>
)
)}
</div>
);
}
3 changes: 2 additions & 1 deletion src/ui/hooks/useSelectedNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { useQuery } from "react-query";
export const useSelectedNodes = () => {
const result = useQuery(
[getSelectedNodesEndpoint.name],
delayed(() => getSelectedNodesEndpoint.call())
delayed(() => getSelectedNodesEndpoint.call()),
{ keepPreviousData: true }
);

useEffect(() => {
Expand Down
36 changes: 27 additions & 9 deletions src/ui/hooks/useWindowSize.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import { emit } from "@/utilities";
import { useEffect } from "preact/hooks";
import { useEffect, useRef } from "preact/hooks";

import { ResizeHandler } from "@/types";
export const DEFAULT_SIZE = { width: 500, height: 400 };
export const COMPACT_SIZE = { width: 500, height: 160 };
import { useGlobalActions, useGlobalState } from "../state/GlobalState";

type WindowSize = {
width: number;
height: number;
};

export const useWindowSize = (size: WindowSize) => {
export const useWindowSize = (newSize: WindowSize) => {
const lastSize = useRef<WindowSize>();
const { setSizeStack } = useGlobalActions();
const sizeStack = useGlobalState((c) => c.sizeStack);
useEffect(() => {
emit<ResizeHandler>("RESIZE", size);
return () => emit<ResizeHandler>("RESIZE", DEFAULT_SIZE);
}, [size]);
const addedSize = { ...newSize };
if (sizeStack.includes(lastSize.current!)) {
setSizeStack((stack) =>
stack.map((i) => {
if (i === lastSize.current) {
return addedSize;
}
return i;
})
);
} else {
setSizeStack((stack) => [...stack, addedSize]);
}
lastSize.current = addedSize;
}, [newSize.width, newSize.height]);

useEffect(() => {
return () => {
setSizeStack((stack) => stack.filter((i) => i !== lastSize.current));
};
}, []);
};
15 changes: 14 additions & 1 deletion src/ui/state/GlobalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import {
ConfigChangeHandler,
NodeInfo,
ResetHandler,
ResizeHandler,
SetLanguageHandler,
SetupHandle,
TolgeeConfig,
WindowSize,
} from "@/types";
import { Route } from "../views/routes";
import type { Route } from "../views/routes";
import { createProvider } from "@/tools/createProvider";
import { DEFAULT_SIZE } from "./sizes";

type Props = {
initialConfig: Partial<TolgeeConfig> | null;
Expand All @@ -28,6 +31,13 @@ export const [GlobalState, useGlobalActions, useGlobalState] = createProvider(
const [globalError, setGlobalError] = useState<string | undefined>(
undefined
);
const [sizeStack, setSizeStack] = useState<WindowSize[]>([]);

useEffect(() => {
const size = sizeStack[sizeStack.length - 1] ?? DEFAULT_SIZE;
emit<ResizeHandler>("RESIZE", size);
return () => emit<ResizeHandler>("RESIZE", DEFAULT_SIZE);
}, [sizeStack]);

const [editedKeys, setEditedKeys] = useState<Record<string, string>>({});

Expand All @@ -44,6 +54,7 @@ export const [GlobalState, useGlobalActions, useGlobalState] = createProvider(
config,
globalError,
editedKeys,
sizeStack,
};

const actions = {
Expand All @@ -65,6 +76,7 @@ export const [GlobalState, useGlobalActions, useGlobalState] = createProvider(
resetConfig() {
emit<ResetHandler>("RESET");
},
setSizeStack,
setGlobalError,
};

Expand All @@ -73,3 +85,4 @@ export const [GlobalState, useGlobalActions, useGlobalState] = createProvider(
return [data, actions];
}
);
export { DEFAULT_SIZE };
2 changes: 2 additions & 0 deletions src/ui/state/sizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DEFAULT_SIZE = { width: 500, height: 400 };
export const COMPACT_SIZE = { width: 500, height: 160 };
9 changes: 0 additions & 9 deletions src/ui/views/CopyView/CopyView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import {
import { useGlobalState } from "@/ui/state/GlobalState";
import { useApiMutation } from "@/ui/client/useQueryApi";
import { FullPageLoading } from "@/ui/components/FullPageLoading/FullPageLoading";
import {
COMPACT_SIZE,
DEFAULT_SIZE,
useWindowSize,
} from "@/ui/hooks/useWindowSize";

import { NodeList } from "../../components/NodeList/NodeList";
import { TopBar } from "../../components/TopBar/TopBar";
Expand Down Expand Up @@ -63,10 +58,6 @@ export const CopyView = () => {
await updateNodesLoadalbe.mutateAsync({ nodes: changedNodes });
};

useWindowSize(
!selection || selection.length < 2 ? COMPACT_SIZE : DEFAULT_SIZE
);

if (
translationsLoadable.isLoading ||
connectedNodesLoadable.isLoading ||
Expand Down
Loading

0 comments on commit e598cf8

Please sign in to comment.