From dc9dde504480d6e39e6cdad28efd484ed3667023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Tue, 9 Apr 2024 12:48:03 +0200 Subject: [PATCH 1/3] feat: add ignoring options to settings --- src/main/main.ts | 81 ++-------------- src/main/utils/nodeTools.ts | 41 ++++++--- src/main/utils/settingsTools.ts | 92 +++++++++++++++++++ src/types.ts | 2 + .../ActionsBottom/ActionsBottom.css | 1 + src/{tools => ui/hooks}/useWindowSize.ts | 0 src/ui/views/CopyView/CopyView.tsx | 2 +- src/ui/views/Index/Index.tsx | 2 +- src/ui/views/PageSetup/PageSetup.tsx | 2 +- src/ui/views/Settings/ProjectSettings.tsx | 40 +++++++- src/ui/views/Settings/Settings.tsx | 6 ++ 11 files changed, 176 insertions(+), 93 deletions(-) create mode 100644 src/main/utils/settingsTools.ts rename src/{tools => ui/hooks}/useWindowSize.ts (100%) diff --git a/src/main/main.ts b/src/main/main.ts index 2a4d237..37689d8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,95 +1,30 @@ import { showUI } from "@create-figma-plugin/utilities"; import { on, emit } from "@/utilities"; -import { TOLGEE_PLUGIN_CONFIG_NAME } from "../constants"; import { ConfigChangeHandler, - CurrentDocumentSettings, - CurrentPageSettings, DocumentChangeHandler, - GlobalSettings, ResetHandler, ResizeHandler, SelectionChangeHandler, SetLanguageHandler, SetupHandle, SyncCompleteHandler, - TolgeeConfig, } from "../types"; -import { DEFAULT_SIZE } from "../tools/useWindowSize"; import { getScreenshotsEndpoint } from "./endpoints/getScreenshots"; import { getSelectedNodesEndpoint } from "./endpoints/getSelectedNodes"; import { getConnectedNodesEndpoint } from "./endpoints/getConnectedNodes"; -import { setPageData } from "./utils/pages"; import { copyPageEndpoint } from "./endpoints/copyPage"; import { updateNodesEndpoint } from "./endpoints/updateNodes"; import { setNodesDataEndpoint } from "./endpoints/setNodesData"; - -const getGlobalSettings = async () => { - const pluginData = await figma.clientStorage.getAsync( - TOLGEE_PLUGIN_CONFIG_NAME - ); - return pluginData ? (JSON.parse(pluginData) as Partial) : {}; -}; - -const setGlobalSettings = async (data: Partial) => { - await figma.clientStorage.setAsync( - TOLGEE_PLUGIN_CONFIG_NAME, - JSON.stringify(data) - ); -}; - -const deleteGlobalSettings = async () => { - await figma.clientStorage.deleteAsync(TOLGEE_PLUGIN_CONFIG_NAME); -}; - -const getDocumentData = () => { - const pluginData = figma.root.getPluginData(TOLGEE_PLUGIN_CONFIG_NAME); - return pluginData - ? (JSON.parse(pluginData) as Partial) - : {}; -}; - -const setDocumentData = (data: Partial) => { - figma.root.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, JSON.stringify(data)); -}; - -const deleteDocumentData = () => { - figma.root.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, ""); -}; - -const getPageData = (page = figma.currentPage) => { - const pluginData = page.getPluginData(TOLGEE_PLUGIN_CONFIG_NAME); - return pluginData - ? (JSON.parse(pluginData) as Partial) - : {}; -}; - -const deletePageData = (page = figma.currentPage) => { - page.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, ""); -}; - -const getPluginData = async () => { - return { - ...(await getGlobalSettings()), - ...getDocumentData(), - ...getPageData(), - }; -}; - -const setPluginData = async (data: Partial) => { - const { apiKey, apiUrl, language, namespace, namespacesDisabled } = data; - await setGlobalSettings({ apiKey, apiUrl }); - setDocumentData({ - apiKey, - apiUrl, - namespace, - namespacesDisabled, - documentInfo: true, - }); - setPageData({ language, pageInfo: true }); - emit("CONFIG_CHANGE", await getPluginData()); -}; +import { + deleteDocumentData, + deleteGlobalSettings, + deletePageData, + getPluginData, + setPluginData, +} from "./utils/settingsTools"; +import { DEFAULT_SIZE } from "@/ui/hooks/useWindowSize"; const getAllPages = () => { const document = figma.root; diff --git a/src/main/utils/nodeTools.ts b/src/main/utils/nodeTools.ts index dfa60d4..93e90e7 100644 --- a/src/main/utils/nodeTools.ts +++ b/src/main/utils/nodeTools.ts @@ -1,5 +1,6 @@ import { TOLGEE_NODE_INFO } from "@/constants"; -import { NodeInfo } from "@/types"; +import { CurrentDocumentSettings, NodeInfo } from "@/types"; +import { getDocumentData } from "./settingsTools"; export const getNodeInfo = (node: TextNode): NodeInfo => { const pluginData = JSON.parse( @@ -15,27 +16,37 @@ export const getNodeInfo = (node: TextNode): NodeInfo => { }; }; -export function* findTextNodesGenerator( - nodes: readonly SceneNode[] -): Generator { - const toExplore = [...nodes].reverse(); - let node: SceneNode | undefined; - while ((node = toExplore.pop())) { +function shouldIncludeNode( + node: TextNode, + settings: Partial +) { + if (settings.ignoreNumbers && /^\d+$/.test(node.characters)) { + return false; + } + if (settings.ignorePrefix && node.name.startsWith(settings.ignorePrefix)) { + return false; + } + return true; +} + +export const findTextNodes = (nodes: readonly SceneNode[]): TextNode[] => { + const documentSettings = getDocumentData(); + const result: TextNode[] = []; + for (const node of nodes) { if (node.type === "TEXT") { - yield node; + if (shouldIncludeNode(node, documentSettings)) { + result.push(node); + } } // @ts-ignore if (node.children) { // @ts-ignore - [...(node.children as SceneNode[])].reverse().forEach((value) => { - toExplore.push(value); - }); + findTextNodes(node.children as SceneNode[]).forEach((n) => + result.push(n) + ); } } -} - -export const findTextNodes = (nodes: readonly SceneNode[]): TextNode[] => { - return [...findTextNodesGenerator(nodes)]; + return result; }; export const findTextNodesInfo = (nodes: readonly SceneNode[]): NodeInfo[] => { diff --git a/src/main/utils/settingsTools.ts b/src/main/utils/settingsTools.ts new file mode 100644 index 0000000..75f1996 --- /dev/null +++ b/src/main/utils/settingsTools.ts @@ -0,0 +1,92 @@ +import { TOLGEE_PLUGIN_CONFIG_NAME } from "@/constants"; +import { + ConfigChangeHandler, + CurrentDocumentSettings, + CurrentPageSettings, + GlobalSettings, + TolgeeConfig, +} from "@/types"; +import { emit } from "@create-figma-plugin/utilities"; +import { setPageData } from "./pages"; + +const getGlobalSettings = async () => { + const pluginData = await figma.clientStorage.getAsync( + TOLGEE_PLUGIN_CONFIG_NAME + ); + return pluginData ? (JSON.parse(pluginData) as Partial) : {}; +}; + +const setGlobalSettings = async (data: Partial) => { + await figma.clientStorage.setAsync( + TOLGEE_PLUGIN_CONFIG_NAME, + JSON.stringify(data) + ); +}; + +export const deleteGlobalSettings = async () => { + await figma.clientStorage.deleteAsync(TOLGEE_PLUGIN_CONFIG_NAME); +}; + +export const getDocumentData = (): Partial => { + const pluginData = figma.root.getPluginData(TOLGEE_PLUGIN_CONFIG_NAME); + const result = pluginData + ? (JSON.parse(pluginData) as Partial) + : {}; + + return { + ignorePrefix: "_", + ignoreNumbers: true, + ...result, + }; +}; + +const setDocumentData = (data: Partial) => { + figma.root.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, JSON.stringify(data)); +}; + +export const deleteDocumentData = () => { + figma.root.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, ""); +}; + +const getPageData = (page = figma.currentPage) => { + const pluginData = page.getPluginData(TOLGEE_PLUGIN_CONFIG_NAME); + return pluginData + ? (JSON.parse(pluginData) as Partial) + : {}; +}; + +export const deletePageData = (page = figma.currentPage) => { + page.setPluginData(TOLGEE_PLUGIN_CONFIG_NAME, ""); +}; + +export const getPluginData = async () => { + return { + ...(await getGlobalSettings()), + ...getDocumentData(), + ...getPageData(), + }; +}; + +export const setPluginData = async (data: Partial) => { + const { + apiKey, + apiUrl, + language, + namespace, + namespacesDisabled, + ignorePrefix, + ignoreNumbers, + } = data; + await setGlobalSettings({ apiKey, apiUrl, ignorePrefix, ignoreNumbers }); + setDocumentData({ + apiKey, + apiUrl, + namespace, + namespacesDisabled, + ignorePrefix, + ignoreNumbers, + documentInfo: true, + }); + setPageData({ language, pageInfo: true }); + emit("CONFIG_CHANGE", await getPluginData()); +}; diff --git a/src/types.ts b/src/types.ts index bccbbc1..c6325a6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -87,6 +87,8 @@ export type PartialNodeInfo = Partial & { export type GlobalSettings = { apiUrl: string; apiKey: string; + ignorePrefix: string; + ignoreNumbers: boolean; }; export type CurrentDocumentSettings = GlobalSettings & { diff --git a/src/ui/components/ActionsBottom/ActionsBottom.css b/src/ui/components/ActionsBottom/ActionsBottom.css index 8ba2095..c8d1798 100644 --- a/src/ui/components/ActionsBottom/ActionsBottom.css +++ b/src/ui/components/ActionsBottom/ActionsBottom.css @@ -7,6 +7,7 @@ flex-direction: column; align-items: flex-end; background-color: var(--figma-color-bg); + z-index: 1000; } .actions { diff --git a/src/tools/useWindowSize.ts b/src/ui/hooks/useWindowSize.ts similarity index 100% rename from src/tools/useWindowSize.ts rename to src/ui/hooks/useWindowSize.ts diff --git a/src/ui/views/CopyView/CopyView.tsx b/src/ui/views/CopyView/CopyView.tsx index a03e9d0..c9a6ad4 100644 --- a/src/ui/views/CopyView/CopyView.tsx +++ b/src/ui/views/CopyView/CopyView.tsx @@ -14,7 +14,7 @@ import { COMPACT_SIZE, DEFAULT_SIZE, useWindowSize, -} from "@/tools/useWindowSize"; +} from "@/ui/hooks/useWindowSize"; import { NodeList } from "../../components/NodeList/NodeList"; import { TopBar } from "../../components/TopBar/TopBar"; diff --git a/src/ui/views/Index/Index.tsx b/src/ui/views/Index/Index.tsx index 0b4ec37..536d3ea 100644 --- a/src/ui/views/Index/Index.tsx +++ b/src/ui/views/Index/Index.tsx @@ -21,7 +21,7 @@ import { COMPACT_SIZE, DEFAULT_SIZE, useWindowSize, -} from "@/tools/useWindowSize"; +} from "@/ui/hooks/useWindowSize"; import { NodeList } from "../../components/NodeList/NodeList"; import { TopBar } from "../../components/TopBar/TopBar"; diff --git a/src/ui/views/PageSetup/PageSetup.tsx b/src/ui/views/PageSetup/PageSetup.tsx index f82424d..eb49393 100644 --- a/src/ui/views/PageSetup/PageSetup.tsx +++ b/src/ui/views/PageSetup/PageSetup.tsx @@ -3,7 +3,7 @@ import { ActionsBottom } from "@/ui/components/ActionsBottom/ActionsBottom"; import { FullPageLoading } from "@/ui/components/FullPageLoading/FullPageLoading"; import { TopBar } from "@/ui/components/TopBar/TopBar"; import { useGlobalActions, useGlobalState } from "@/ui/state/GlobalState"; -import { COMPACT_SIZE, useWindowSize } from "@/tools/useWindowSize"; +import { COMPACT_SIZE, useWindowSize } from "@/ui/hooks/useWindowSize"; import { CurrentPageSettings } from "@/types"; import { VerticalSpace, diff --git a/src/ui/views/Settings/ProjectSettings.tsx b/src/ui/views/Settings/ProjectSettings.tsx index 25ee360..acb09aa 100644 --- a/src/ui/views/Settings/ProjectSettings.tsx +++ b/src/ui/views/Settings/ProjectSettings.tsx @@ -2,7 +2,13 @@ import { useApiQuery } from "@/ui/client/useQueryApi"; import { FullPageLoading } from "@/ui/components/FullPageLoading/FullPageLoading"; import { NamespaceSelect } from "@/ui/components/NamespaceSelect/NamespaceSelect"; import { TolgeeConfig } from "@/types"; -import { VerticalSpace, Text, Muted, Checkbox } from "@create-figma-plugin/ui"; +import { + VerticalSpace, + Text, + Muted, + Checkbox, + Textbox, +} from "@create-figma-plugin/ui"; import { Fragment, FunctionComponent, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import styles from "./ProjectSettings.css"; @@ -75,6 +81,8 @@ export const ProjectSettings: FunctionComponent = ({ namespace: initialData?.namespace ?? namespaces?.[0] ?? "", namespacesDisabled: initialData?.namespacesDisabled ?? namespacesNotPresent, + ignoreNumbers: initialData?.ignoreNumbers ?? true, + ignorePrefix: initialData?.ignorePrefix ?? "_", }); } }, [languages, namespaces]); @@ -129,7 +137,6 @@ export const ProjectSettings: FunctionComponent = ({ })) } /> - @@ -142,6 +149,35 @@ export const ProjectSettings: FunctionComponent = ({ Hide namespace selectors + + + Ignore text nodes prefixed with: + + + { + setSettings((settings) => ({ + ...settings, + ignorePrefix, + })); + }} + value={settings?.ignorePrefix ?? "_"} + variant="border" + /> + + + + setSettings((settings) => ({ + ...settings!, + ignoreNumbers: Boolean(e.currentTarget.checked), + })) + } + > + Ignore nodes with numbers + ); }; diff --git a/src/ui/views/Settings/Settings.tsx b/src/ui/views/Settings/Settings.tsx index af4b3af..9c38154 100644 --- a/src/ui/views/Settings/Settings.tsx +++ b/src/ui/views/Settings/Settings.tsx @@ -19,6 +19,8 @@ import { ActionsBottom } from "@/ui/components/ActionsBottom/ActionsBottom"; import { TopBar } from "../../components/TopBar/TopBar"; import styles from "./Settings.css"; import { ProjectSettings } from "./ProjectSettings"; +import { useWindowSize } from "@/ui/hooks/useWindowSize"; +import { useQueryClient } from "react-query"; const DEFAULT_TOLGEE_URL = "https://app.tolgee.io"; @@ -27,6 +29,7 @@ type Props = { }; export const Settings: FunctionComponent = ({ noNavigation }) => { + const queryClient = useQueryClient(); const config = useGlobalState((c) => c.config) || {}; const [tolgeeConfig, setTolgeeConfig] = useState({ apiUrl: DEFAULT_TOLGEE_URL, @@ -44,6 +47,8 @@ export const Settings: FunctionComponent = ({ noNavigation }) => { }, }); + useWindowSize({ width: 500, height: 500 }); + const [validated, setValidated] = useState(false); const { setRoute, setConfig, resetConfig } = useGlobalActions(); @@ -97,6 +102,7 @@ export const Settings: FunctionComponent = ({ noNavigation }) => { } catch (e: any) { setError(e.message || e); } + queryClient.clear(); }; const handleGoBack = useCallback(() => { From e29630ae701330edcb81336858919391fd3f26b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Tue, 9 Apr 2024 15:13:41 +0200 Subject: [PATCH 2/3] feat: find node in the document --- src/main/endpoints/highlightNode.ts | 15 ++++++ src/main/main.ts | 2 + src/main/utils/nodeTools.ts | 3 ++ src/ui/hooks/useHighlightNodeMutation.ts | 12 +++++ src/ui/icons/SvgIcons.tsx | 11 ++++ src/ui/views/Index/Index.css | 15 ++++++ src/ui/views/Index/Index.css.d.ts | 2 + src/ui/views/Index/Index.tsx | 65 +++++++++++++++--------- 8 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 src/main/endpoints/highlightNode.ts create mode 100644 src/ui/hooks/useHighlightNodeMutation.ts diff --git a/src/main/endpoints/highlightNode.ts b/src/main/endpoints/highlightNode.ts new file mode 100644 index 0000000..7be8261 --- /dev/null +++ b/src/main/endpoints/highlightNode.ts @@ -0,0 +1,15 @@ +import { createEndpoint } from "../utils/createEndpoint"; + +export type HighlightNodeProps = { + key: string; +}; + +export const highlightNodeEndpoint = createEndpoint( + "HIGHLIGHT_NODE", + async ({ key }: HighlightNodeProps) => { + const node = figma.getNodeById(key); + if (node) { + figma.viewport.scrollAndZoomIntoView([node]); + } + } +); diff --git a/src/main/main.ts b/src/main/main.ts index 37689d8..13a20b6 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -25,6 +25,7 @@ import { setPluginData, } from "./utils/settingsTools"; import { DEFAULT_SIZE } from "@/ui/hooks/useWindowSize"; +import { highlightNodeEndpoint } from "./endpoints/highlightNode"; const getAllPages = () => { const document = figma.root; @@ -91,6 +92,7 @@ export default async function () { copyPageEndpoint.register(); updateNodesEndpoint.register(); setNodesDataEndpoint.register(); + highlightNodeEndpoint.register(); const config = await getPluginData(); diff --git a/src/main/utils/nodeTools.ts b/src/main/utils/nodeTools.ts index 93e90e7..625b051 100644 --- a/src/main/utils/nodeTools.ts +++ b/src/main/utils/nodeTools.ts @@ -26,6 +26,9 @@ function shouldIncludeNode( if (settings.ignorePrefix && node.name.startsWith(settings.ignorePrefix)) { return false; } + if (node.characters.trim().length === 0) { + return false; + } return true; } diff --git a/src/ui/hooks/useHighlightNodeMutation.ts b/src/ui/hooks/useHighlightNodeMutation.ts new file mode 100644 index 0000000..c54f971 --- /dev/null +++ b/src/ui/hooks/useHighlightNodeMutation.ts @@ -0,0 +1,12 @@ +import { + HighlightNodeProps, + highlightNodeEndpoint, +} from "@/main/endpoints/highlightNode"; +import { useMutation } from "react-query"; + +export const useHighlightNodeMutation = () => { + return useMutation( + [highlightNodeEndpoint.name], + (data: HighlightNodeProps) => highlightNodeEndpoint.call(data) + ); +}; diff --git a/src/ui/icons/SvgIcons.tsx b/src/ui/icons/SvgIcons.tsx index 6bdcaf7..fd715ce 100644 --- a/src/ui/icons/SvgIcons.tsx +++ b/src/ui/icons/SvgIcons.tsx @@ -50,3 +50,14 @@ export const AddCircle = (props: h.JSX.SVGAttributes) => { ); }; + +export const MyLocation = (props: h.JSX.SVGAttributes) => { + return ( + + + + ); +}; diff --git a/src/ui/views/Index/Index.css b/src/ui/views/Index/Index.css index 85cb1cd..9862621 100644 --- a/src/ui/views/Index/Index.css +++ b/src/ui/views/Index/Index.css @@ -24,6 +24,16 @@ height: 20px; } +.highlightButton { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + width: 20px; + height: 20px; + color: var(--figma-color-text-secondary); +} + .disabled { color: var(--figma-color-text-disabled); } @@ -46,3 +56,8 @@ display: grid; grid-template-rows: auto 1fr; } + +.actionsContainer { + display: flex; + gap: 4px; +} diff --git a/src/ui/views/Index/Index.css.d.ts b/src/ui/views/Index/Index.css.d.ts index 5c1ca0f..bb7b636 100644 --- a/src/ui/views/Index/Index.css.d.ts +++ b/src/ui/views/Index/Index.css.d.ts @@ -1,7 +1,9 @@ declare const styles: { + readonly "actionsContainer": string; readonly "connectButton": string; readonly "container": string; readonly "disabled": string; + readonly "highlightButton": string; readonly "keyInput": string; readonly "languageContainer": string; readonly "nsSelect": string; diff --git a/src/ui/views/Index/Index.tsx b/src/ui/views/Index/Index.tsx index 536d3ea..88d56a0 100644 --- a/src/ui/views/Index/Index.tsx +++ b/src/ui/views/Index/Index.tsx @@ -11,7 +11,7 @@ import { } from "@create-figma-plugin/ui"; import { NodeInfo } from "@/types"; -import { Settings, InsertLink } from "@/ui/icons/SvgIcons"; +import { Settings, InsertLink, MyLocation } from "@/ui/icons/SvgIcons"; import { useApiQuery } from "@/ui/client/useQueryApi"; import { getConflictingNodes } from "@/tools/getConflictingNodes"; import { FullPageLoading } from "@/ui/components/FullPageLoading/FullPageLoading"; @@ -29,6 +29,7 @@ import styles from "./Index.css"; import { KeyInput } from "./KeyInput"; import { useSelectedNodes } from "@/ui/hooks/useSelectedNodes"; import { useSetNodesDataMutation } from "@/ui/hooks/useSetNodesDataMutation"; +import { useHighlightNodeMutation } from "@/ui/hooks/useHighlightNodeMutation"; export const Index = () => { const selectionLoadable = useSelectedNodes(); @@ -50,6 +51,8 @@ export const Index = () => { method: "get", }); + const highlightMutation = useHighlightNodeMutation(); + const setNodesDataMutation = useSetNodesDataMutation(); const languages = languagesLoadable.data?._embedded?.languages; @@ -99,6 +102,10 @@ export const Index = () => { } }; + const handleHighlight = (node: NodeInfo) => { + highlightMutation.mutate({ key: node.id }); + }; + const handlePull = () => { setRoute("pull", { lang: language, @@ -244,28 +251,40 @@ export const Index = () => { } actionCallback={(node) => { return ( -
handleConnect(node)} - className={styles.connectButton} - > - {node.connected ? ( - - ) : ( - - )} +
+
handleHighlight(node)} + className={styles.highlightButton} + > + +
+ +
handleConnect(node)} + className={styles.connectButton} + > + {node.connected ? ( + + ) : ( + + )} +
); }} From bfd193b662b68a6c7a2afc4bdf0491e6f361bc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Tue, 9 Apr 2024 16:39:56 +0200 Subject: [PATCH 3/3] feat: add locate node button where possible --- src/main/endpoints/highlightNode.ts | 7 +++-- src/tools/getPushChanges.ts | 2 +- .../LocateNodeButton/LocateNodeButton.css | 9 ++++++ .../LocateNodeButton.css.d.ts | 5 ++++ .../LocateNodeButton/LocateNodeButton.tsx | 29 ++++++++++++++++++ src/ui/components/NodeList/NodeList.tsx | 30 ++++++++++--------- src/ui/components/NodeList/NodeRow.css | 5 ++++ src/ui/components/NodeList/NodeRow.css.d.ts | 1 + src/ui/components/NodeList/NodeRow.tsx | 11 ++++++- src/ui/views/CopyView/CopyView.tsx | 4 ++- src/ui/views/Index/Index.css | 10 ------- src/ui/views/Index/Index.css.d.ts | 1 - src/ui/views/Index/Index.tsx | 24 ++++----------- src/ui/views/Pull/Pull.tsx | 9 +++++- src/ui/views/Push/Changes.tsx | 4 +-- 15 files changed, 98 insertions(+), 53 deletions(-) create mode 100644 src/ui/components/LocateNodeButton/LocateNodeButton.css create mode 100644 src/ui/components/LocateNodeButton/LocateNodeButton.css.d.ts create mode 100644 src/ui/components/LocateNodeButton/LocateNodeButton.tsx diff --git a/src/main/endpoints/highlightNode.ts b/src/main/endpoints/highlightNode.ts index 7be8261..111af21 100644 --- a/src/main/endpoints/highlightNode.ts +++ b/src/main/endpoints/highlightNode.ts @@ -1,13 +1,14 @@ import { createEndpoint } from "../utils/createEndpoint"; export type HighlightNodeProps = { - key: string; + id: string; }; export const highlightNodeEndpoint = createEndpoint( "HIGHLIGHT_NODE", - async ({ key }: HighlightNodeProps) => { - const node = figma.getNodeById(key); + async ({ id }: HighlightNodeProps) => { + const node = figma.getNodeById(id); + if (node) { figma.viewport.scrollAndZoomIntoView([node]); } diff --git a/src/tools/getPushChanges.ts b/src/tools/getPushChanges.ts index 9dfdf8d..32f4e02 100644 --- a/src/tools/getPushChanges.ts +++ b/src/tools/getPushChanges.ts @@ -52,7 +52,7 @@ export const getPushChanges = ( compareNs(t.keyNamespace, node.ns) && t.translations[language]?.text ); - const change = { + const change: KeyChangeValue = { key: node.key, ns: node.ns, oldValue: key?.translations[language]?.text, diff --git a/src/ui/components/LocateNodeButton/LocateNodeButton.css b/src/ui/components/LocateNodeButton/LocateNodeButton.css new file mode 100644 index 0000000..7de368e --- /dev/null +++ b/src/ui/components/LocateNodeButton/LocateNodeButton.css @@ -0,0 +1,9 @@ +.highlightButton { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + width: 20px; + height: 20px; + color: var(--figma-color-text-secondary); +} diff --git a/src/ui/components/LocateNodeButton/LocateNodeButton.css.d.ts b/src/ui/components/LocateNodeButton/LocateNodeButton.css.d.ts new file mode 100644 index 0000000..cc19981 --- /dev/null +++ b/src/ui/components/LocateNodeButton/LocateNodeButton.css.d.ts @@ -0,0 +1,5 @@ +declare const styles: { + readonly "highlightButton": string; +}; +export = styles; + diff --git a/src/ui/components/LocateNodeButton/LocateNodeButton.tsx b/src/ui/components/LocateNodeButton/LocateNodeButton.tsx new file mode 100644 index 0000000..d1a7f00 --- /dev/null +++ b/src/ui/components/LocateNodeButton/LocateNodeButton.tsx @@ -0,0 +1,29 @@ +import { useHighlightNodeMutation } from "@/ui/hooks/useHighlightNodeMutation"; +import { h } from "preact"; + +import styles from "./LocateNodeButton.css"; +import { MyLocation } from "@/ui/icons/SvgIcons"; + +type Props = { + nodeId: string; +}; + +export const LocateNodeButton = ({ nodeId }: Props) => { + const highlightMutation = useHighlightNodeMutation(); + + const handleHighlight = () => { + highlightMutation.mutate({ id: nodeId }); + }; + + return ( +
+ +
+ ); +}; diff --git a/src/ui/components/NodeList/NodeList.tsx b/src/ui/components/NodeList/NodeList.tsx index 18cf0d5..0dd4cfc 100644 --- a/src/ui/components/NodeList/NodeList.tsx +++ b/src/ui/components/NodeList/NodeList.tsx @@ -1,38 +1,40 @@ import { ComponentChildren, h } from "preact"; -import { PartialNodeInfo } from "@/types"; import styles from "./NodeList.css"; import { NodeRow } from "./NodeRow"; import { useRef } from "preact/hooks"; -type Props = { - nodes: T[]; - actionCallback?: (node: T) => ComponentChildren; - keyComponent?: (node: T) => ComponentChildren; - nsComponent?: (node: T) => ComponentChildren; +type Props = { + items: T[]; + actionCallback?: (item: T) => ComponentChildren; + keyComponent?: (item: T) => ComponentChildren; + nsComponent?: (item: T) => ComponentChildren; compact?: boolean; + onClick?: (item: T) => void; onBottomReached?: () => void; }; -export function NodeList({ - nodes, +export function NodeList({ + items, actionCallback, keyComponent, nsComponent, compact, + onClick, }: Props) { const containerRef = useRef(null); return (
- {nodes?.map((node) => ( + {items?.map((item) => ( onClick?.(item) : undefined} /> ))}
diff --git a/src/ui/components/NodeList/NodeRow.css b/src/ui/components/NodeList/NodeRow.css index 012fe42..d37be1e 100644 --- a/src/ui/components/NodeList/NodeRow.css +++ b/src/ui/components/NodeList/NodeRow.css @@ -19,6 +19,7 @@ grid-column: 1 / span 2; min-height: 16px; font-weight: bold; + cursor: unset; } .action { @@ -27,6 +28,10 @@ align-self: center; } +.key { + cursor: unset; +} + .disabled { color: var(--figma-color-text-disabled); } diff --git a/src/ui/components/NodeList/NodeRow.css.d.ts b/src/ui/components/NodeList/NodeRow.css.d.ts index 5bd5377..188fd69 100644 --- a/src/ui/components/NodeList/NodeRow.css.d.ts +++ b/src/ui/components/NodeList/NodeRow.css.d.ts @@ -2,6 +2,7 @@ declare const styles: { readonly "action": string; readonly "container": string; readonly "disabled": string; + readonly "key": string; readonly "text": string; }; export = styles; diff --git a/src/ui/components/NodeList/NodeRow.tsx b/src/ui/components/NodeList/NodeRow.tsx index 7746506..406b16e 100644 --- a/src/ui/components/NodeList/NodeRow.tsx +++ b/src/ui/components/NodeList/NodeRow.tsx @@ -9,6 +9,7 @@ type Props = { keyComponent?: ComponentChildren; nsComponent?: ComponentChildren; compact?: boolean; + onClick?: () => void; }; export const NodeRow = ({ @@ -17,6 +18,7 @@ export const NodeRow = ({ keyComponent, nsComponent, compact, + onClick, }: Props) => { const showText = node.characters || !compact || action; @@ -26,7 +28,10 @@ export const NodeRow = ({ className={styles.container} style={{ gridTemplateColumns: action ? "1fr 1fr auto" : "1fr 1fr", + cursor: onClick ? "pointer" : "default", }} + role={onClick ? "button" : undefined} + onClick={onClick} > {showText && (
{action}
-
+
{keyComponent ? keyComponent : node.key}
{ const selectionLoadable = useSelectedNodes(); @@ -102,13 +103,14 @@ export const CopyView = () => { ) : ( { if (!node.connected) { return Not connected; } return node.key || ""; }} + actionCallback={(item) => } /> )} diff --git a/src/ui/views/Index/Index.css b/src/ui/views/Index/Index.css index 9862621..df45614 100644 --- a/src/ui/views/Index/Index.css +++ b/src/ui/views/Index/Index.css @@ -24,16 +24,6 @@ height: 20px; } -.highlightButton { - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - width: 20px; - height: 20px; - color: var(--figma-color-text-secondary); -} - .disabled { color: var(--figma-color-text-disabled); } diff --git a/src/ui/views/Index/Index.css.d.ts b/src/ui/views/Index/Index.css.d.ts index bb7b636..3024008 100644 --- a/src/ui/views/Index/Index.css.d.ts +++ b/src/ui/views/Index/Index.css.d.ts @@ -3,7 +3,6 @@ declare const styles: { readonly "connectButton": string; readonly "container": string; readonly "disabled": string; - readonly "highlightButton": string; readonly "keyInput": string; readonly "languageContainer": string; readonly "nsSelect": string; diff --git a/src/ui/views/Index/Index.tsx b/src/ui/views/Index/Index.tsx index 88d56a0..a53ff29 100644 --- a/src/ui/views/Index/Index.tsx +++ b/src/ui/views/Index/Index.tsx @@ -11,7 +11,7 @@ import { } from "@create-figma-plugin/ui"; import { NodeInfo } from "@/types"; -import { Settings, InsertLink, MyLocation } from "@/ui/icons/SvgIcons"; +import { Settings, InsertLink } from "@/ui/icons/SvgIcons"; import { useApiQuery } from "@/ui/client/useQueryApi"; import { getConflictingNodes } from "@/tools/getConflictingNodes"; import { FullPageLoading } from "@/ui/components/FullPageLoading/FullPageLoading"; @@ -22,14 +22,14 @@ import { DEFAULT_SIZE, useWindowSize, } from "@/ui/hooks/useWindowSize"; +import { LocateNodeButton } from "@/ui/components/LocateNodeButton/LocateNodeButton"; +import { useSelectedNodes } from "@/ui/hooks/useSelectedNodes"; import { NodeList } from "../../components/NodeList/NodeList"; import { TopBar } from "../../components/TopBar/TopBar"; import styles from "./Index.css"; import { KeyInput } from "./KeyInput"; -import { useSelectedNodes } from "@/ui/hooks/useSelectedNodes"; import { useSetNodesDataMutation } from "@/ui/hooks/useSetNodesDataMutation"; -import { useHighlightNodeMutation } from "@/ui/hooks/useHighlightNodeMutation"; export const Index = () => { const selectionLoadable = useSelectedNodes(); @@ -51,8 +51,6 @@ export const Index = () => { method: "get", }); - const highlightMutation = useHighlightNodeMutation(); - const setNodesDataMutation = useSetNodesDataMutation(); const languages = languagesLoadable.data?._embedded?.languages; @@ -102,10 +100,6 @@ export const Index = () => { } }; - const handleHighlight = (node: NodeInfo) => { - highlightMutation.mutate({ key: node.id }); - }; - const handlePull = () => { setRoute("pull", { lang: language, @@ -228,7 +222,7 @@ export const Index = () => { ) : ( !node.connected && ( { actionCallback={(node) => { return (
-
handleHighlight(node)} - className={styles.highlightButton} - > - -
+
; @@ -75,6 +76,8 @@ export const Pull: FunctionalComponent = ({ lang }) => { translationsLoadable.refetch(); }; + const highlightNode = useHighlightNodeMutation(); + const isLoading = translationsLoadable.isLoading || selectedNodes.isLoading; return ( @@ -115,7 +118,11 @@ export const Pull: FunctionalComponent = ({ lang }) => {
Missing keys:
- + highlightNode.mutate({ id: item.id })} + compact + />
)} diff --git a/src/ui/views/Push/Changes.tsx b/src/ui/views/Push/Changes.tsx index ddc6b2f..65e601f 100644 --- a/src/ui/views/Push/Changes.tsx +++ b/src/ui/views/Push/Changes.tsx @@ -25,7 +25,7 @@ export const Changes = ({ changes }: Props) => { data-cy="changes_new_keys" > ({ + items={changes.newKeys.map((k) => ({ id: k.key, key: k.key, ns: k.ns, @@ -47,7 +47,7 @@ export const Changes = ({ changes }: Props) => { data-cy="changes_changed_keys" > ({ + items={changes.changedKeys.map((k) => ({ id: k.key, key: k.key, ns: k.ns,