diff --git a/editor/core/states/editor-state.ts b/editor/core/states/editor-state.ts
index e0441504..7d80175e 100644
--- a/editor/core/states/editor-state.ts
+++ b/editor/core/states/editor-state.ts
@@ -1,4 +1,5 @@
import type { ReflectSceneNode } from "@design-sdk/figma-node";
+import { ComponentNode } from "@design-sdk/figma-types";
import { DesignInput } from "@designto/config/input";
export interface EditorState {
@@ -15,7 +16,7 @@ export interface EditorSnapshot {
design: FigmaReflectRepository;
}
-interface FigmaReflectRepository {
+export interface FigmaReflectRepository {
/**
* fileid; filekey
*/
@@ -23,5 +24,7 @@ interface FigmaReflectRepository {
// TODO:
pages: { id: string; name: string; children: ReflectSceneNode[] }[];
+ components: { [key: string]: ComponentNode };
+ // styles: { [key: string]: {} };
input: DesignInput;
}
diff --git a/editor/core/states/workspace-initial-state.ts b/editor/core/states/workspace-initial-state.ts
index a54976be..b65a3304 100644
--- a/editor/core/states/workspace-initial-state.ts
+++ b/editor/core/states/workspace-initial-state.ts
@@ -12,8 +12,9 @@ export function createInitialWorkspaceState(
return {
history: createInitialHistoryState(editor),
preferences: {
- debug_mode: false,
- enable_preview_feature_components_support: false,
+ // TODO: temporarily always true for components dev
+ debug_mode: true,
+ enable_preview_feature_components_support: true,
preview_runner_framework_config: vanilla_presets.vanilla_default,
framework_config: react_presets.react_default,
},
@@ -24,8 +25,8 @@ export function createPendingWorkspaceState(): WorkspaceState {
return {
history: createPendingHistoryState(),
preferences: {
- debug_mode: false,
- enable_preview_feature_components_support: false,
+ debug_mode: null,
+ enable_preview_feature_components_support: null,
preview_runner_framework_config: null,
framework_config: null,
},
diff --git a/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx b/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx
index 7c1e53d6..989924ee 100644
--- a/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx
+++ b/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx
@@ -40,9 +40,7 @@ export function WorkspaceBottomPanelDockLayout(props: {
}
const DockRootWrap = styled.div`
- border: solid #d2d2d2;
align-self: stretch;
- border-width: 1px;
display: flex;
flex-direction: row;
`;
diff --git a/editor/pages/figma/inspect-component.tsx b/editor/pages/figma/inspect-component.tsx
index fe5ea97e..b32be44f 100644
--- a/editor/pages/figma/inspect-component.tsx
+++ b/editor/pages/figma/inspect-component.tsx
@@ -87,26 +87,22 @@ export default function InspectComponent() {
-
*/}
- {/*
-
-
-
*/}
{/*
diff --git a/editor/pages/files/[key]/[id].tsx b/editor/pages/files/[key]/[id].tsx
index e83d17c2..39bc740b 100644
--- a/editor/pages/files/[key]/[id].tsx
+++ b/editor/pages/files/[key]/[id].tsx
@@ -56,15 +56,8 @@ export default function Page() {
if (file) {
let val: EditorSnapshot;
- const pages = file.document.children.map((page) => ({
- id: page.id,
- name: page.name,
- children: page["children"]?.map((child) => {
- const _mapped = mapper.mapFigmaRemoteToFigma(child);
- return convert.intoReflectNode(_mapped);
- }),
- type: "design",
- }));
+ const components = warmup.componentsFrom(file);
+ const pages = warmup.pagesFrom(file);
if (prevstate) {
val = {
@@ -102,6 +95,7 @@ export default function Page() {
input: null,
key: filekey,
pages: pages,
+ components: components,
},
selectedPage: warmup.selectedPage(prevstate, pages, null),
};
diff --git a/editor/pages/files/[key]/index.tsx b/editor/pages/files/[key]/index.tsx
index eae514c3..de184686 100644
--- a/editor/pages/files/[key]/index.tsx
+++ b/editor/pages/files/[key]/index.tsx
@@ -5,8 +5,7 @@ import { Editor, EditorDefaultProviders } from "scaffolds/editor";
import { EditorSnapshot, StateProvider } from "core/states";
import { WorkspaceAction } from "core/actions";
import { useDesignFile } from "hooks";
-import { convert } from "@design-sdk/figma-node-conversion";
-import { mapper } from "@design-sdk/figma-remote";
+
import { warmup } from "scaffolds/editor";
export default function FileEntryEditor() {
@@ -31,15 +30,11 @@ export default function FileEntryEditor() {
if (file) {
let val: EditorSnapshot;
- const pages = file.document.children.map((page) => ({
- id: page.id,
- name: page.name,
- children: page["children"]?.map((child) => {
- const _mapped = mapper.mapFigmaRemoteToFigma(child);
- return convert.intoReflectNode(_mapped);
- }),
- type: "design",
- }));
+ // TODO: seed this as well
+ // ->> file.styles;
+
+ const components = warmup.componentsFrom(file);
+ const pages = warmup.pagesFrom(file);
if (prevstate) {
val = {
@@ -60,6 +55,8 @@ export default function FileEntryEditor() {
selectedLayersOnPreview: [],
design: {
input: null,
+ components: components,
+ // styles: null,
key: filekey,
pages: pages,
},
@@ -75,7 +72,6 @@ export default function FileEntryEditor() {
}, [filekey, file?.document?.children]);
const safe_value = warmup.safestate(initialState);
-
return (
diff --git a/editor/pages/to-code/index.tsx b/editor/pages/to-code/index.tsx
index 0adaa184..2770eaae 100644
--- a/editor/pages/to-code/index.tsx
+++ b/editor/pages/to-code/index.tsx
@@ -69,15 +69,8 @@ export default function Page() {
if (file) {
let val: EditorSnapshot;
- const pages = file.document.children.map((page) => ({
- id: page.id,
- name: page.name,
- children: page["children"]?.map((child) => {
- const _mapped = mapper.mapFigmaRemoteToFigma(child);
- return convert.intoReflectNode(_mapped);
- }),
- type: "design",
- }));
+ const components = warmup.componentsFrom(file);
+ const pages = warmup.pagesFrom(file);
if (prevstate) {
val = {
@@ -115,6 +108,7 @@ export default function Page() {
input: null,
key: _input.file,
pages: pages,
+ components: components,
},
selectedPage: warmup.selectedPage(prevstate, pages, null),
};
diff --git a/editor/scaffolds/editor/editor.tsx b/editor/scaffolds/editor/editor.tsx
index cc010dde..89f3e054 100644
--- a/editor/scaffolds/editor/editor.tsx
+++ b/editor/scaffolds/editor/editor.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import styled from "@emotion/styled";
import { useRouter } from "next/router";
import { DefaultEditorWorkspaceLayout } from "layouts/default-editor-workspace-layout";
@@ -19,7 +19,6 @@ import {
ImageRepository,
MainImageRepository,
} from "@design-sdk/core/assets-repository";
-import { personal } from "@design-sdk/figma-auth-store";
import { useFigmaAccessToken } from "hooks";
import { get_framework_config } from "query/to-code-options-from-query";
import { CodeOptionsControl } from "components/codeui-code-options-control";
@@ -31,8 +30,8 @@ import {
} from "utils/design-query";
import { vanilla_presets } from "@grida/builder-config-preset";
import { EditorSkeleton } from "./skeleton";
-import { MonacoEmptyMock } from "components/code-editor/monaco-mock-empty";
import { colors } from "theme";
+import Link from "next/link";
export function Editor() {
const router = useRouter();
@@ -61,7 +60,17 @@ export function Editor() {
find_node_by_id_under_inpage_nodes(targetId, thisPageNodes) || null;
const root = thisPageNodes
- ? container_of_target && DesignInput.fromDesign(container_of_target)
+ ? container_of_target &&
+ (container_of_target.origin === "COMPONENT"
+ ? DesignInput.forMasterComponent({
+ master: container_of_target,
+ all: state.design.pages,
+ components: state.design.components,
+ })
+ : DesignInput.fromDesignWithComponents({
+ design: container_of_target,
+ components: state.design.components,
+ }))
: state.design?.input;
const targetted =
@@ -129,12 +138,14 @@ export function Editor() {
}).then(on_result);
// build final code with asset fetch
- designToCode({
- input: root,
- framework: framework_config,
- asset_config: { asset_repository: MainImageRepository.instance },
- build_config: build_config,
- }).then(on_result);
+ if (!MainImageRepository.instance.empty) {
+ designToCode({
+ input: root,
+ framework: framework_config,
+ asset_config: { asset_repository: MainImageRepository.instance },
+ build_config: build_config,
+ }).then(on_result);
+ }
}
}, [targetted?.id, framework_config?.framework]);
@@ -168,12 +179,14 @@ export function Editor() {
},
}).then(on_preview_result);
- designToCode({
- input: root,
- build_config: build_config,
- framework: vanilla_presets.vanilla_default,
- asset_config: { asset_repository: MainImageRepository.instance },
- }).then(on_preview_result);
+ if (!MainImageRepository.instance.empty) {
+ designToCode({
+ input: root,
+ build_config: build_config,
+ framework: vanilla_presets.vanilla_default,
+ asset_config: { asset_repository: MainImageRepository.instance },
+ }).then(on_preview_result);
+ }
}
},
[targetted?.id]
@@ -246,43 +259,14 @@ export function Editor() {
{wstate.preferences.debug_mode && (
-
-
-
-
-
- {(root.entry.origin === "INSTANCE" ||
- root.entry.origin === "COMPONENT") && (
-
- )}
-
-
-
-
-
-
-
-
-
+
+
)}
@@ -292,6 +276,57 @@ export function Editor() {
);
}
+const Debugger = ({
+ id,
+ file,
+ type,
+ entry,
+ widget,
+}: {
+ type: string;
+ id: string;
+ file: string;
+ entry: any;
+ widget: any;
+}) => {
+ const router = useRouter();
+
+ return (
+
+
+
+
+ {(type === "INSTANCE" || type === "COMPONENT") && (
+
+ inspect component
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+};
+
const CodeEditorContainer = styled.div`
display: flex;
flex-direction: column;
diff --git a/editor/scaffolds/editor/warmup.ts b/editor/scaffolds/editor/warmup.ts
index 2b0994a8..906f57bd 100644
--- a/editor/scaffolds/editor/warmup.ts
+++ b/editor/scaffolds/editor/warmup.ts
@@ -1,6 +1,7 @@
import {
createPendingWorkspaceState,
EditorSnapshot,
+ FigmaReflectRepository,
WorkspaceState,
} from "core/states";
import { createInitialWorkspaceState } from "core/states";
@@ -9,6 +10,10 @@ import { PendingState } from "core/utility-types";
import { DesignInput } from "@designto/config/input";
import { TargetNodeConfig } from "query/target-node";
import { WorkspaceAction } from "core/actions";
+import { FileResponse } from "@design-sdk/figma-remote-types";
+import { convert } from "@design-sdk/figma-node-conversion";
+import { mapper } from "@design-sdk/figma-remote";
+import { find, visit } from "tree-visit";
const pending_workspace_state = createPendingWorkspaceState();
//
@@ -38,6 +43,61 @@ export function initialReducer(
}
}
+export function pagesFrom(file: FileResponse): FigmaReflectRepository["pages"] {
+ return file.document.children.map((page) => ({
+ id: page.id,
+ name: page.name,
+ children: page["children"]?.map((child) => {
+ const _mapped = mapper.mapFigmaRemoteToFigma(child);
+ return convert.intoReflectNode(_mapped);
+ }),
+ type: "design",
+ }));
+}
+
+/**
+ * only fetch in-file components. components from shared-library (external file) won't be loaded.
+ * @param file
+ * @returns
+ */
+export function componentsFrom(
+ file: FileResponse
+): FigmaReflectRepository["components"] {
+ const tomap = (a, v) => ({ ...a, [v.id]: v });
+
+ // only fetch in-file components. components from shared-library (external file) won't be loaded.
+ const components_in_file = [];
+ visit<{ id: string; type: string }>(file.document, {
+ getChildren: (node) => {
+ if ("children" in node) return node["children"];
+ return [];
+ },
+ onEnter: (node) => {
+ if (node["type"] == "COMPONENT") {
+ components_in_file.push(node);
+ }
+ },
+ });
+
+ // return components_in_file.reduce(tomap, {});
+
+ return Object.keys(file.components)
+ .map((k) => {
+ const id = k;
+ const meta = file.components[k];
+ const master = components_in_file.find((c) => c.id === id);
+ if (!master) return;
+ return {
+ key: meta.key, // only available with api response. the hash key of current version of component for another api call. (not used)
+ id: master.id,
+ name: master.name,
+ ...master,
+ };
+ })
+ .filter((c) => c)
+ .reduce(tomap, {});
+}
+
export function initializeDesign(design: TargetNodeConfig): EditorSnapshot {
return {
selectedNodes: [design.node],
@@ -45,6 +105,8 @@ export function initializeDesign(design: TargetNodeConfig): EditorSnapshot {
selectedPage: null,
design: {
pages: [],
+ components: null,
+ // styles: null,
key: design.file,
input: DesignInput.fromApiResponse({
...design,
diff --git a/externals/coli b/externals/coli
index 83d3cb54..7beb540c 160000
--- a/externals/coli
+++ b/externals/coli
@@ -1 +1 @@
-Subproject commit 83d3cb546b753859cb23ef2a79346ac438aae563
+Subproject commit 7beb540c885c7b40bf163004a182d60fbdf001ce
diff --git a/externals/design-sdk b/externals/design-sdk
index 34a45ef2..54d7aae0 160000
--- a/externals/design-sdk
+++ b/externals/design-sdk
@@ -1 +1 @@
-Subproject commit 34a45ef238a9fbc635cfdf5f66baab77c7a0cab9
+Subproject commit 54d7aae0b6b1b41ca7de23f063f09de2c32e3fc0
diff --git a/packages/builder-config/input/design-input.ts b/packages/builder-config/input/design-input.ts
index f2e971b2..e1966a43 100644
--- a/packages/builder-config/input/design-input.ts
+++ b/packages/builder-config/input/design-input.ts
@@ -1,6 +1,7 @@
import type { ReflectSceneNode } from "@design-sdk/core";
import { mapGrandchildren } from "@design-sdk/core/utils";
import { NodeRepository } from "@design-sdk/figma";
+import type { ComponentNode } from "@design-sdk/figma";
import { RawNodeResponse } from "@design-sdk/figma-remote";
export interface IDesignInput {
@@ -35,20 +36,50 @@ export class DesignInput implements IDesignInput {
}
static fromDesign(design: ReflectSceneNode): DesignInput {
- const _allnodes = mapGrandchildren(design, 0, {
- includeThis: true,
- ignoreGroup: false,
+ const repository = new NodeRepository({
+ // components not supported for `fromdesign`
+ components: null,
+ nodes: this._flat_all(design),
});
+ return new DesignInput({ entry: design, repository: repository });
+ }
+
+ static fromDesignWithComponents({
+ design,
+ components,
+ }: {
+ design: ReflectSceneNode;
+ components: { [key: string]: ComponentNode } | ComponentNode[];
+ }) {
const repository = new NodeRepository({
- // TODO: components not supported for `fromdesign`
- components: [],
- nodes: [...(_allnodes as any)],
+ components: Object.values(components),
+ nodes: this._flat_all(design),
});
return new DesignInput({ entry: design, repository: repository });
}
+ static forMasterComponent({
+ all,
+ master,
+ components,
+ }: {
+ /**
+ * usually pages. Document#pages
+ */
+ all: { id: string; name: string; children: ReflectSceneNode[] }[];
+ master: ReflectSceneNode;
+ components: { [key: string]: ComponentNode } | ComponentNode[];
+ }) {
+ const repository = new NodeRepository({
+ components: Object.values(components),
+ nodes: all.map((p) => p.children.map(this._flat_all).flat()).flat(),
+ });
+
+ return new DesignInput({ entry: master, repository: repository });
+ }
+
static fromApiResponse({
raw,
entry,
@@ -56,15 +87,17 @@ export class DesignInput implements IDesignInput {
raw: RawNodeResponse;
entry: ReflectSceneNode;
}): DesignInput {
- const _allnodes = mapGrandchildren(entry, 0, {
- includeThis: true,
- ignoreGroup: false,
- });
-
const repository = new NodeRepository({
components: [...(Object.values(raw.components) as any)],
- nodes: [...(_allnodes as any)],
+ nodes: this._flat_all(entry),
});
return new DesignInput({ entry: entry, repository: repository });
}
+
+ private static _flat_all(entry) {
+ return mapGrandchildren(entry, 0, {
+ includeThis: true,
+ ignoreGroup: false,
+ });
+ }
}
diff --git a/packages/builder-web-core/widget-instanciation/index.ts b/packages/builder-web-core/widget-instanciation/index.ts
index 8a005e53..f615e97e 100644
--- a/packages/builder-web-core/widget-instanciation/index.ts
+++ b/packages/builder-web-core/widget-instanciation/index.ts
@@ -1,20 +1,53 @@
import { WidgetKey } from "../widget-key";
import { JSXElementConfig, JsxWidget } from "../widget-core";
-import { JSX } from "coli";
+import { JSX, JSXAttribute, Types } from "coli";
export class InstanciationElement extends JsxWidget {
readonly identifier: string;
- // TODO: support arguments
+ readonly arguments: { [key: string]: any };
- constructor({ key, identifier }: { key: WidgetKey; identifier: string }) {
+ constructor({
+ key,
+ identifier,
+ arguments: _arguments,
+ }: {
+ key: WidgetKey;
+ identifier: string;
+ arguments: { [key: string]: any };
+ }) {
super({ key });
this.identifier = identifier;
+ this.arguments = _arguments ?? {};
}
jsxConfig(): JSXElementConfig {
+ const _attrs = this.makeJsxAttributes();
return {
type: "tag-and-attr",
tag: JSX.identifier(this.identifier),
+ attributes: _attrs,
};
}
+
+ private makeJsxAttributes() {
+ const jsxValue = (value: any) => {
+ switch (typeof value) {
+ case "undefined":
+ return JSX.exp(undefined);
+ case "string":
+ return JSX.text(value, "template-literal");
+ case "number":
+ return JSX.number(value);
+ default:
+ console.error(`Unsupported type of value: ${typeof value}`);
+ }
+ };
+
+ return Object.keys(this.arguments)
+ .map((key, index) => {
+ const rec = this.arguments[key];
+ return new JSXAttribute(rec.key, jsxValue(rec.value));
+ })
+ .filter((a) => a);
+ }
}
diff --git a/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts b/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts
index 082cfdac..1d1a2811 100644
--- a/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts
+++ b/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts
@@ -31,9 +31,9 @@ export function finalizeReactReusable_StyledComponents__Experimental({
};
const token = hanlde(tree);
- console.log("token", token);
+ console.log("from-reusable-widget-tree::token", { token, tree });
const webwi = buildWebWidgetFromTokens(token);
- console.log("webwi", webwi);
+ console.log("from-reusable-widget-tree::web-widget", webwi);
const builder = new ReactStyledComponentsBuilder({
entry: webwi,
config: {
diff --git a/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts b/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts
index aab80cac..deac26bd 100644
--- a/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts
+++ b/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts
@@ -106,9 +106,9 @@ export class ReactStyledComponentsBuilder {
partDeclarations() {
return Array.from(this.styledConfigWidgetMap.keys())
.map((k) => {
- return (this.styledConfigWidgetMap.get(
- k
- ) as StyledComponentJSXElementConfig).styledComponent;
+ return (
+ this.styledConfigWidgetMap.get(k) as StyledComponentJSXElementConfig
+ ).styledComponent;
})
.filter((s) => s);
}
@@ -170,23 +170,21 @@ export class ReactStyledComponentWidgetModuleExportable {
});
file.imports(...this.imports);
- console.log("exporting", exporting);
+ // console.log("exporting", exporting);
switch (exporting.type) {
case "export-default-anonymous-functional-component": {
// exporting.declaration_syntax_choice;
// exporting.export_declaration_syntax_choice;
// exporting.exporting_position;
- const export_default_anaonymous_functional_component = new FunctionDeclaration(
- undefined,
- {
+ const export_default_anaonymous_functional_component =
+ new FunctionDeclaration(undefined, {
body: this.body,
modifiers: {
default: SyntaxKind.DefaultKeyword,
export: SyntaxKind.ExportKeyword,
},
- }
- );
+ });
file.declare(export_default_anaonymous_functional_component);
file.declare(...this.declarations);
break;
@@ -219,9 +217,10 @@ export class ReactStyledComponentWidgetModuleExportable {
);
break;
case "with-declaration":
- const _exported_named_function_declaration = add_export_keyword_modifier_to_declaration(
- named_function_declaration
- );
+ const _exported_named_function_declaration =
+ add_export_keyword_modifier_to_declaration(
+ named_function_declaration
+ );
file.declare(_exported_named_function_declaration);
file.declare(...this.declarations);
break;
diff --git a/packages/designto-code/universal/design-to-code.ts b/packages/designto-code/universal/design-to-code.ts
index c6ab27c8..93d1ef93 100644
--- a/packages/designto-code/universal/design-to-code.ts
+++ b/packages/designto-code/universal/design-to-code.ts
@@ -37,13 +37,16 @@ export async function designToCode({
asset_config: AssetsConfig;
}): Promise {
if (process.env.NODE_ENV === "development") {
- console.info(
- "dev: starting designtocode with user input",
- input,
- framework,
- build_config,
- asset_config
- );
+ if (framework.framework == "vanilla") {
+ } else {
+ console.info(
+ "dev: starting designtocode with user input",
+ input,
+ framework,
+ build_config,
+ asset_config
+ );
+ }
}
// post token processing
@@ -72,7 +75,7 @@ export async function designToCode({
console.log("reusable_widget_tree", reusable_widget_tree);
// TODO: WIP
} catch (_) {
- console.error(_);
+ console.error("error while building reusable widget tree.", _);
}
}
@@ -143,6 +146,12 @@ export async function designToReact({
!input.reusable_widget_tree
) {
const reactwidget = toreact.buildReactWidget(input.widget);
+ if (process.env.NODE_ENV === "development") {
+ console.info("dev::", "final web token composed", {
+ input: input.widget,
+ reactwidget,
+ });
+ }
const res = toreact.buildReactApp(reactwidget, react_config);
// ------------------------------------------------------------------------
diff --git a/packages/designto-react/app/index.ts b/packages/designto-react/app/index.ts
index 0104fcd6..214d0ed6 100644
--- a/packages/designto-react/app/index.ts
+++ b/packages/designto-react/app/index.ts
@@ -33,7 +33,7 @@ export function buildReactApp(
export function buildReactWidget(widget: Widget) {
assert(
- widget,
+ widget instanceof Widget,
"A valid reflect widget manifest should be passed as an input. none was passed."
);
diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts
index aecc5e19..c9a14a00 100644
--- a/packages/designto-token/main.ts
+++ b/packages/designto-token/main.ts
@@ -163,10 +163,11 @@ function handleNode(
}
// - button -
- const _detect_if_button = detectIf.button(node);
- if (_detect_if_button.result) {
- return tokenizeButton.fromManifest(node, _detect_if_button.data);
- }
+ // TODO: temporarily disabled - remove comment after button widget is ready
+ // const _detect_if_button = detectIf.button(node);
+ // if (_detect_if_button.result) {
+ // return tokenizeButton.fromManifest(node, _detect_if_button.data);
+ // }
// -------------------------------------------------------------------------
// --------------------------- Detected tokens -----------------------------
diff --git a/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts b/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts
index 40c7bd61..cb9e8671 100644
--- a/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts
+++ b/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts
@@ -1,18 +1,24 @@
import { Composer } from ".";
import * as reusable from "@code-features/component/tokens";
import * as web from "@web-builder/core";
+import { nameit, NameCases } from "coli";
export function compose_instanciation(
widget: reusable.InstanceWidget,
- child_composer: Composer
+ child_composer: Composer // not used
) {
const masterkey = widget.meta.master.key;
+
+ const identifier = nameit(widget.meta.master.key.originName, {
+ case: NameCases.pascal,
+ }).name;
+
return new web.InstanciationElement({
key: {
- name: "foo",
- id: masterkey.id,
+ name: "ExampleUsageOf_" + identifier, // FIXME: should not use identifier as name
+ id: widget.key.id,
},
- // TODO: fix this
- identifier: "foo",
+ identifier: identifier,
+ arguments: widget.meta.arguments,
});
}
diff --git a/packages/designto-web/tokens-to-web-widget/index.ts b/packages/designto-web/tokens-to-web-widget/index.ts
index b7a10482..54d17d52 100644
--- a/packages/designto-web/tokens-to-web-widget/index.ts
+++ b/packages/designto-web/tokens-to-web-widget/index.ts
@@ -23,10 +23,6 @@ export function buildWebWidgetFromTokens(widget: core.Widget): JsxWidget {
is_root: true,
});
- if (process.env.NODE_ENV === "development") {
- console.info("dev::", "final web token composed", composed);
- }
-
return composed;
}
@@ -232,6 +228,10 @@ function compose(
// end of logic gate
// -------------------------------------
else {
+ if (thisWebWidget)
+ throw new Error(
+ "internal error. this final exception gate should not be entered since there is already a composed widget."
+ );
// todo - handle case more specific
thisWebWidget = new web.ErrorWidget({
key: _key,
@@ -239,6 +239,7 @@ function compose(
widget.key.originName
}" type of "${widget._type}" - ${JSON.stringify(widget.key)}`,
});
+ console.warn("not handled", widget);
}
// -------------------------------------
// -------------------------------------
diff --git a/packages/support-components/define.ts b/packages/support-components/define.ts
index b0cf4d9e..d7656589 100644
--- a/packages/support-components/define.ts
+++ b/packages/support-components/define.ts
@@ -1,14 +1,20 @@
-import { Figma, ReflectSceneNode } from "@design-sdk/figma";
+import { ComponentNode, Figma, ReflectSceneNode } from "@design-sdk/figma";
import {
compare_instance_with_master,
- InstanceDiff,
+ InstanceDiff_1on1,
+ MultichildDiff,
NodeDiff,
} from "@design-sdk/diff";
import { ComponentsUsageRepository } from "./components-usage-repository";
-import { MasterComponentMetaToken } from "./tokens/token-master-component";
+import {
+ MasterComponentMetaToken,
+ Property,
+} from "./tokens/token-master-component";
import { InstanceMetaToken } from "./tokens/token-instance";
import { keyFromNode } from "@designto/token/key";
-
+import { NameCases, nameit, ScopedVariableNamer } from "coli";
+import { ReservedKeywordPlatformPresets } from "@coli.codes/naming/reserved";
+import { visit } from "tree-visit";
type IDMappable =
| {
[key: string]: T;
@@ -23,6 +29,25 @@ function findIn(map: IDMappable, id: string) {
}
}
+function findDeepUnderComponent(component: ComponentNode, id: string) {
+ let found = null;
+ visit<{ id; children }>(component, {
+ getChildren: (node) => {
+ if ("children" in node) {
+ return node.children;
+ }
+ return [];
+ },
+ onEnter: (node) => {
+ if (node.id === id) {
+ found = node;
+ return "stop";
+ }
+ },
+ });
+ return found;
+}
+
// based on default strategy
// WIP
@@ -35,75 +60,155 @@ interface Input {
references?: Figma.InstanceNode[];
}
-interface Definition {
+/**
+ * A single property definition
+ */
+interface PropertyDefinition {
type: string;
+ /**
+ * id of the master
+ */
master: string;
+ /**
+ * id of the instance
+ */
use: string;
+ /**
+ * default value from master
+ */
default_value: string;
+ /**
+ * overrided value from instance
+ */
overrided_value: string;
}
+/**
+ * defines properties as array of PropertyDefinition from whole diff data between master/instance
+ * @param diff
+ * @returns
+ */
+function define_props(diff: NodeDiff): PropertyDefinition[] {
+ if (!diff.diff) return;
+ const masterId = diff.ids[0];
+ const instanceId = diff.ids[1];
+ switch (diff.type) {
+ case "instance-to-master":
+ return define_props__instance(diff);
+ case "text-node":
+ return [
+ diff.characters.diff
+ ? {
+ type: "text.data",
+ default_value: diff.characters.values[0],
+ overrided_value: diff.characters.values[1],
+ master: masterId,
+ use: instanceId,
+ }
+ : null,
+ diff.fills.diff
+ ? {
+ type: "text.fill",
+ default_value: JSON.stringify(diff.fills.values[0]),
+ overrided_value: JSON.stringify(diff.fills.values[1]),
+ master: masterId,
+ use: instanceId,
+ }
+ : null,
+ // TODO: add text styles diff support
+ ].filter((d) => d);
+ case "multi-child":
+ return define_props_multichild(diff);
+ default:
+ throw "not handled yet - " + diff["type"];
+ }
+}
+
+const define_props_multichild = (diff: MultichildDiff) => {
+ return diff.values
+ .map((d) => {
+ return define_props(d);
+ })
+ .flat()
+ .filter(Boolean);
+};
+
+const define_props__instance = (diff: InstanceDiff_1on1) => {
+ return diff.values
+ .map((d) => {
+ return define_props(d);
+ })
+ .flat()
+ .filter(Boolean);
+};
+
export function make_instance_component_meta({ entry, components }: Input) {
+ const propertyNamer = new ScopedVariableNamer(
+ "property",
+ ReservedKeywordPlatformPresets.universal
+ );
+
const property_meta = overrided_property_meta({ entry, components });
- const define = (diff: NodeDiff): Definition[] | Definition[][] => {
- if (diff.diff) {
- const master = diff.ids[0];
- const use = diff.ids[1];
- switch (diff.type) {
- case "instance-to-master":
- return define_instance(diff);
- case "text-node":
- return [
- diff.characters.diff
- ? {
- type: "text.data",
- default_value: diff.characters.values[0],
- overrided_value: diff.characters.values[1],
- master: master,
- use: use,
- }
- : null,
- ];
- break;
+ const masterId = property_meta.ids[0];
+ const master = findIn(components, masterId);
+
+ const properties = define_props__instance(property_meta).flat();
+
+ const __name_cache = {};
+ /**
+ *
+ * @param propertyOriginId - the origin node of the property will be targetted. e.g. in `master(group(text))`, the master's text's id will be used.
+ * @returns
+ */
+ const get_property_key = (type: string, propertyOriginId: string) => {
+ const uid = type + "-" + propertyOriginId;
+ const originNodeName = findDeepUnderComponent(
+ master,
+ propertyOriginId
+ )?.name;
+
+ if (originNodeName) {
+ const { name, register } = propertyNamer.nameit(originNodeName, {
+ case: NameCases.camel,
+ register: false,
+ });
+ if (__name_cache[uid]) {
+ return __name_cache[uid];
+ } else {
+ __name_cache[uid] = name;
+ register();
+ return name;
}
}
+ throw new Error("origin layer does not contain a valid name");
};
- const define_instance = (diff: InstanceDiff) => {
- const definitions = diff.values.map((d) => {
- return define(d);
- });
- return definitions.filter((d) => d) as any;
- };
-
- const properties = define_instance(property_meta);
-
- const master = new MasterComponentMetaToken({
- key: keyFromNode(findIn(components, property_meta.ids[0])),
+ const masterMeta = new MasterComponentMetaToken({
+ key: keyFromNode(findIn(components, masterId)),
properties: properties.map((p) => {
- return {
- key: p.type,
+ return >{
+ key: get_property_key(p.type, p.master),
type: p.type,
defaultValue: p.default_value,
link: {
type: "design-link",
linksto: {
type: "path-property-link",
- path: "",
- property: p.type,
+ path: p.type,
+ property: [{ type: "name", value: p.type }],
}, // TODO:
},
};
}),
- child: findIn(components, property_meta.ids[0]),
+ child: findIn(components, masterId),
});
- const entryInstance = new InstanceMetaToken({
- master: master,
+ const entryInstanceMeta = new InstanceMetaToken({
+ master: masterMeta,
key: keyFromNode(entry),
arguments: properties.reduce(function (result, item, index, array) {
result[item.type] = {
- key: item.type,
+ key: get_property_key(item.type, item.master),
value: item.overrided_value,
};
return result;
@@ -111,8 +216,8 @@ export function make_instance_component_meta({ entry, components }: Input) {
});
return new ComponentsUsageRepository({
- components: [master],
- usage: { [entry.id]: entryInstance },
+ components: [masterMeta],
+ usage: { [entry.id]: entryInstanceMeta },
});
}
@@ -120,17 +225,24 @@ function overrided_property_meta({ entry, components }: Input) {
if (
// TODO: needs cleanup
"origin" in entry
- ? ((entry as any) as ReflectSceneNode).origin !== "INSTANCE"
+ ? (entry as any as ReflectSceneNode).origin !== "INSTANCE"
: entry.type !== "INSTANCE"
) {
throw new Error("not a instance");
}
const _master = findIn(components, entry.mainComponentId);
+ if (!_master)
+ throw new Error(
+ "cannot find master with `mainComponentId` - id " +
+ entry.mainComponentId +
+ `\nIn map provided - length of ${components.length}`
+ );
+
const diff = compare_instance_with_master({
instance: entry,
master: _master,
components: Array.from(Object.values(components)),
});
- // TODO: make meta based on diff
+ // TODO: make meta based on diff `{ diff, ... }`
return diff;
}
diff --git a/packages/support-components/main.ts b/packages/support-components/main.ts
index 14db3876..db965e77 100644
--- a/packages/support-components/main.ts
+++ b/packages/support-components/main.ts
@@ -37,15 +37,16 @@ export function reusable({
components: repository.components,
});
- const components = component_use_repository.components.map(
- composeComponentMeta
- );
+ const components =
+ component_use_repository.components.map(composeComponentMeta);
- return {
+ const _ = {
// asumming root is always a multi child widget
tree: composeInstanciationTree(entry, repository, component_use_repository),
components: components,
};
+ // console.log("reusable", _);
+ return _;
}
function composeInstanciationTree(
@@ -56,15 +57,10 @@ function composeInstanciationTree(
widget = unwrappedChild(widget); // unwrap child to reach original input.
const { key, _type: _widget_type } = widget;
const node = repository.get(key.id);
- if (!node) {
- console.warn(
- "node not found",
- key,
- repository,
- "this is a know issue when trying to find a masking group. this will be fixed in the future."
- );
- return;
- }
+
+ // prettier-ignore
+ if (!node) { console.warn("node not found", key, repository, "this is a know issue when trying to find a masking group. this will be fixed in the future."); return; }
+
if (node.origin === "INSTANCE") {
const instanceMeta = componentsUsageRepository.getUsageOf(node.id);
const instance = new InstanceWidget({
@@ -77,25 +73,25 @@ function composeInstanciationTree(
widget instanceof MultiChildRenderObjectWidget &&
widget.children.length > 0
) {
- return {
- ...widget,
- children: widget.children.map((c) => {
- return composeInstanciationTree(
- c,
- repository,
- componentsUsageRepository
- );
- }),
- };
- } else if (widget instanceof SingleChildRenderObjectWidget) {
- return {
- ...widget,
- child: composeInstanciationTree(
- widget.child,
+ // @ts-ignore
+ widget.children = widget.children.map((c) => {
+ return composeInstanciationTree(
+ c,
repository,
componentsUsageRepository
- ),
- };
+ );
+ });
+
+ return widget;
+ } else if (widget instanceof SingleChildRenderObjectWidget) {
+ // @ts-ignore
+ widget.child = composeInstanciationTree(
+ widget.child,
+ repository,
+ componentsUsageRepository
+ );
+
+ return widget;
} else {
return widget;
}
@@ -106,9 +102,8 @@ function composeComponentMeta(
component: MasterComponentMetaToken
): MasterComponentWidget {
const componentNode = component.body as ComponentNode;
- const componentTokenizedBody = tokenizeComponent.fromComponentNode(
- componentNode
- );
+ const componentTokenizedBody =
+ tokenizeComponent.fromComponentNode(componentNode);
return new MasterComponentWidget({
key: component.key,
diff --git a/packages/support-components/tokens/token-master-component.ts b/packages/support-components/tokens/token-master-component.ts
index 950ea86b..7889cd74 100644
--- a/packages/support-components/tokens/token-master-component.ts
+++ b/packages/support-components/tokens/token-master-component.ts
@@ -46,25 +46,27 @@ export class MasterComponentMetaToken {
}
}
-interface Property {
+export interface Property {
key: string;
type: any;
defaultValue: any;
link: PropertyLink;
}
-type PropertyLink = InstanciationPropertyLink | DesignPropertyLink;
+export type PropertyLink =
+ | InstanciationPropertyLink
+ | DesignPropertyLink;
/**
* Property link to a instanciation of (another) component.
*/
-interface InstanciationPropertyLink {
+export interface InstanciationPropertyLink {
type: "instanciation-link";
master: MasterComponentMetaToken;
linksto: Link;
}
-interface DesignPropertyLink {
+export interface DesignPropertyLink {
type: "design-link";
/**
* path to a property as indexpath.