Skip to content

Commit

Permalink
Merge pull request #97 from bridgedxyz/feature/exporter
Browse files Browse the repository at this point in the history
Feature/exporter
  • Loading branch information
softmarshmallow authored Apr 26, 2021
2 parents 577c638 + e61cf2b commit 765a343
Show file tree
Hide file tree
Showing 25 changed files with 551 additions and 452 deletions.
5 changes: 3 additions & 2 deletions app/lib/main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { ToolboxScreen } from "../screens/tool-box";
import { MetaEditorScreen } from "../screens/tool-box/meta-editor";
import { ExporterScreen } from "../screens/tool-box/exporter";
import { DataMapperScreen } from "../screens/tool-box/data-mapper/data-mapper-screen";
import { TargetPlatform } from "../utils/plugin-init/init-target-platform";
// endregion screens import

interface TabPanelProps {
Expand Down Expand Up @@ -116,7 +117,7 @@ function worspaceModeToName(workspaceMode: WorkspaceMode): string {
return "N/A";
}

export default function App() {
export default function App(props: { platform: TargetPlatform }) {
// region init firebase
try {
firebase.analytics();
Expand Down Expand Up @@ -361,7 +362,7 @@ export default function App() {
const workspaceModeSelectLayout = makeWorkspaceModeSelect();

return (
<PluginApp>
<PluginApp platform={props.platform}>
<BrowserRouter>
{workspaceModeSelectLayout}
{screenLayout}
Expand Down
23 changes: 13 additions & 10 deletions app/lib/repositories/metadata/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ASSISTANT_PLUGIN_NAMESPACE } from "../../constants";
import { MetaDataMockDataProvider } from "../../mock";
import { _platform } from "@bridged.xyz/design-sdk";
import {
TargetPlatform,
TARGET_PLATFORM,
} from "../../utils/plugin-init/init-target-platform";
import { PluginSdk } from "../../utils/plugin-provider/plugin-app-sdk";

export class MetaDataRepositoryFactory {
Expand All @@ -21,43 +24,43 @@ export abstract class MetaDataRepository<T = any> {
abstract key: string;

async fetch(): Promise<T> {
switch (_platform.TARGET_PLATFORM) {
case _platform.TargetPlatform.figma:
switch (TARGET_PLATFORM) {
case TargetPlatform.figma:
return await PluginSdk.fetchMetadata({
id: this.id,
namespace: ASSISTANT_PLUGIN_NAMESPACE,
key: this.key,
});

case _platform.TargetPlatform.webdev:
case TargetPlatform.webdev:
return MetaDataMockDataProvider.componentData() as any;
}
}

async update(data: T) {
switch (_platform.TARGET_PLATFORM) {
switch (TARGET_PLATFORM) {
// TODO -- figma api call does not work here.
case _platform.TargetPlatform.figma:
case TargetPlatform.figma:
return await PluginSdk.updateMetadata({
id: this.id,
namespace: ASSISTANT_PLUGIN_NAMESPACE,
key: this.key,
value: data,
});
return data;
case _platform.TargetPlatform.webdev:
case TargetPlatform.webdev:
console.log("data updated (mocked)");
return data;
}
}

clear() {
switch (_platform.TARGET_PLATFORM) {
case _platform.TargetPlatform.figma:
switch (TARGET_PLATFORM) {
case TargetPlatform.figma:
const data = figma
.getNodeById(this.id)
.setSharedPluginData(ASSISTANT_PLUGIN_NAMESPACE, this.key, undefined);
case _platform.TargetPlatform.webdev:
case TargetPlatform.webdev:
console.log("data cleared (mocked)");
}
}
Expand Down
4 changes: 2 additions & 2 deletions app/lib/screens/g11n-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export class GlobalizationScreen extends React.Component<any, State> {
}

onMessage = (ev: MessageEvent) => {
const msg = ev.data.pluginMessage;
const msg = ev?.data?.pluginMessage;

switch (msg.type) {
switch (msg?.type) {
case EK_COMPUTE_STARTED:
this.setState(() => {
return {
Expand Down
13 changes: 13 additions & 0 deletions app/lib/screens/tool-box/exporter/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# UI Exporter

- reflect json exporter - expors ui as a whole json tree

## Export mode

- reflect vanilla
- reflect standard
- figma raw (only for figma platform)

## Export range

- current page
- whole docs
- selecetd single node
- selected nodes (this will download multiple files per selection)
10 changes: 10 additions & 0 deletions app/lib/screens/tool-box/exporter/export-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function downloadFile(content) {
const blob = new Blob([content], { type: "text/plain" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `export-node.json`;
a.click();
a.remove();
window.URL.revokeObjectURL(url);
}
12 changes: 12 additions & 0 deletions app/lib/screens/tool-box/exporter/exporter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import { FigmaExporter } from "./figma-exporter";
import { VanillaExporter } from "./vanilla-exporter";

export function ExporterScreen() {
return (
<>
<FigmaExporter />
</>
);
// <VanillaExporter />;
}
22 changes: 22 additions & 0 deletions app/lib/screens/tool-box/exporter/figma-api-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* file id of bridged design - https://www.figma.com/file/Y0Gh77AqBoHH7dG1GtK3xF/bridged
*/
export const FIGMA_DEMO_DEFAULT_FILE_ID = "Y0Gh77AqBoHH7dG1GtK3xF";

/**
* extracts file id from share link
*
* e.g. in - "https://www.figma.com/file/Y0Gh77AqBoHH7dG1GtK3xF/bridged?node-id=775%3A112"
*
* out - "Y0Gh77AqBoHH7dG1GtK3xF"
* @param url
* @returns
*/
export function parseFileIdFromUrl_Figma(url: string) {
// this logic is dangerous, but clean and simple. works for now. (think the url format won't change)
if (url.includes("https://www.figma.com/file/")) {
return url.split("/")[4];
} else {
throw `figma file url must contain "https://www.figma.com/file/". the givven was ${url}, which we cannot extract file id from it.`;
}
}
144 changes: 144 additions & 0 deletions app/lib/screens/tool-box/exporter/figma-exporter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useState } from "react";
import * as FigmaApi from "figma-js";
import { Button, LinearProgress, TextField, Tooltip } from "@material-ui/core";
import { useSingleSelection } from "../../../utils/plugin-hooks";
import { parseFileIdFromUrl_Figma } from "./figma-api-utils";
import { PluginSdk } from "../../../utils/plugin-provider/plugin-app-sdk";
import { downloadFile } from "./export-utils";

const _maybetoken = process.env.FIGMA_PERSONAL_ACCESS_TOKEN;
function makeClient(token?: string): FigmaApi.ClientInterface {
if (!token) {
const maybePersonalAccessToken = process.env.FIGMA_PERSONAL_ACCESS_TOKEN;
if (maybePersonalAccessToken) {
} else {
throw "no personal access token provided";
}
token = maybePersonalAccessToken;
}

return FigmaApi.Client({
personalAccessToken: token,
});
}

export async function fetchFile(
id: string,
opt?: {
token?: string;
}
) {
const client = makeClient(opt?.token);
const _fileRes = await client.file(id);
const file = _fileRes.data;
return file;
}

async function fetchNode(
fileId: string,
nodeIds: string[],
opt?: {
token?: string;
}
) {
const client = makeClient(opt?.token);
const _nodesRes = await client.fileNodes(fileId, {
ids: nodeIds,
});
return _nodesRes.data.nodes;
}

export function FigmaExporter() {
const selection = useSingleSelection();
const [personalToken, setPersonalToken] = useState(_maybetoken);
const [computing, setComputing] = useState<boolean>(false);
const [fileId, setFileId] = useState(undefined);
const handleExportCurrentSelection = () => {
setComputing(true);
fetchNode(fileId, [selection.id], {
token: personalToken,
}).then((d) => {
downloadFile(JSON.stringify(d, null, 4));
setComputing(false);
});
};

const handleExportCurrentFile = () => {
setComputing(true);
fetchFile(fileId, {
token: personalToken,
}).then((d) => {
downloadFile(JSON.stringify(d, null, 4));
setComputing(false);
});
};

const handleFileUrlUpdate = (e) => {
const input = e.target.value;
try {
const id = parseFileIdFromUrl_Figma(input);
setFileId(id);
} catch (_) {
PluginSdk.notify("url is invalid");
}
};

return (
<>
<h4>Figma exporter</h4>
<p>
requirements: this feature is for bridged contributors, enabling them to
export figma data for design to code development. You'll need to provide
your own "FIGMA_PERSONAL_ACCESS_TOKEN" in figma/.env currently, figma
plugin sdk does not allow us to retrieve file id, so you'll have to
explicitly provide us the current figma file's id which is from file
share link. copy-pase the whole url, we will automatically extract file
id from it.
</p>
{computing ? (
<>
<LinearProgress />
</>
) : (
<>
<TextField
label="personal access token"
fullWidth
type="password"
defaultValue={personalToken}
onChange={(e) => {
setPersonalToken(e.target.value);
}}
/>
<TextField
required
disabled={fileId !== undefined}
label="figma file share url"
fullWidth
onKeyPress={(ev) => {
if (ev.key === "Enter") {
handleFileUrlUpdate(ev);
ev.preventDefault();
}
}}
/>

{fileId && (
<>
<Button
disabled={selection == undefined}
onClick={handleExportCurrentSelection}
>
export current selection
</Button>
<Button onClick={handleExportCurrentFile}>
export current file
</Button>
<Button disabled>export current page</Button>
</>
)}
</>
)}
</>
);
}
Loading

0 comments on commit 765a343

Please sign in to comment.