Skip to content

Commit

Permalink
Designer: Added feature to create interactive component states (#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
bheston authored Nov 1, 2023
1 parent 3ffc22c commit 24d13ec
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 66 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/adaptive-ui-figma-designer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@csstools/css-calc": "^1.1.1",
"@csstools/css-parser-algorithms": "^2.2.0",
"@csstools/css-tokenizer": "^2.1.1",
"@figma/plugin-typings": "^1.58.0",
"@figma/plugin-typings": "^1.80.0",
"concurrently": "^7.6.0",
"esbuild": "^0.17.10",
"rimraf": "^3.0.2",
Expand Down
39 changes: 35 additions & 4 deletions packages/adaptive-ui-figma-designer/src/core/model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { ValuesOf } from "@microsoft/fast-foundation";
import { StyleProperty } from "@adaptive-web/adaptive-ui";
import { PluginNode } from "./node.js";
import { SerializableNodeData } from "./serialization.js";

/**
* A key for passing the fill color from the tool to the plugin. Keeping it out of main design tokens to avoid a lot more special handling.
*/
export const TOOL_PARENT_FILL_COLOR = "tool-parent-fill-color";
export const AdditionalDataKeys = {
/**
* A key for passing the fill color from the tool to the plugin.
*
* @remarks
* Keeping it out of main design tokens to avoid a lot more special handling.
*/
toolParentFillColor: "tool-parent-fill-color",

/**
* The state of interactive state configuration. Applies to component sets.
*/
states: "states",

/**
* The interactive state of the node. Applies to all nodes.
*/
state: "state",
} as const;

export type AdditionalDataKeys = ValuesOf<typeof AdditionalDataKeys>;

/**
* A design token value.
Expand Down Expand Up @@ -269,3 +288,15 @@ export const pluginNodesToUINodes = (

return convertedNodes;
}

export interface CreateStatesMessage {
readonly type: 'CREATE_STATES';
id: string;
}

export interface NodeDataMessage {
readonly type: 'NODE_DATA';
nodes: SerializableNodeData[];
}

export type PluginMessage = CreateStatesMessage | NodeDataMessage;
36 changes: 32 additions & 4 deletions packages/adaptive-ui-figma-designer/src/core/node.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ColorRGBA64 } from "@microsoft/fast-colors";
import { ValuesOf } from "@microsoft/fast-foundation";
import { StyleProperty } from "@adaptive-web/adaptive-ui";
import {
AdditionalData,
AdditionalDataKeys,
AppliedDesignTokens,
AppliedStyleModules,
AppliedStyleValues,
Expand All @@ -10,11 +12,20 @@ import {
ReadonlyAppliedDesignTokens,
ReadonlyAppliedStyleModules,
ReadonlyDesignTokenValues,
TOOL_PARENT_FILL_COLOR,
} from "./model.js";

const DesignTokenCache: Map<string, ReadonlyDesignTokenValues> = new Map();

export const StatesState = {
notAvailable: "notAvailable",
available: "available",
configured: "configured",
} as const;

export type StatesState = ValuesOf<typeof StatesState>;

export type State = "Rest" | "Hover" | "Active" | "Focus" | "Disabled";

/**
* The abstract class the plugin Controller interacts with.
* Acts as a basic intermediary for node structure and data storage only.
Expand Down Expand Up @@ -147,6 +158,16 @@ export abstract class PluginNode {
*/
public abstract readonly fillColor: ColorRGBA64 | null;

/**
* The state of stateful component capabilities for this node.
*/
public abstract readonly states: StatesState;

/**
* The interactive state of the node.
*/
public abstract get state(): string | null;

/**
* Gets whether this type of node can have children or not.
*/
Expand Down Expand Up @@ -229,10 +250,17 @@ export abstract class PluginNode {
* Gets additional data associated with this node.
*/
public get additionalData(): AdditionalData {
if (!this._additionalData.has(TOOL_PARENT_FILL_COLOR) && this.parent?.fillColor) {
// console.log("PluginNode.get_additionalData - adding:", TOOL_PARENT_FILL_COLOR, this.debugInfo, this.parent?.fillColor.toStringHexARGB());
this._additionalData.set(TOOL_PARENT_FILL_COLOR, this.parent.fillColor.toStringHexARGB());
this._additionalData.set(AdditionalDataKeys.states, this.states);

if (this.state) {
this._additionalData.set(AdditionalDataKeys.state, this.state);
}

if (!this._additionalData.has(AdditionalDataKeys.toolParentFillColor) && this.parent?.fillColor) {
// console.log("PluginNode.get_additionalData - adding:", AdditionalDataKeys.toolParentFillColor, this.debugInfo, this.parent?.fillColor.toStringHexARGB());
this._additionalData.set(AdditionalDataKeys.toolParentFillColor, this.parent.fillColor.toStringHexARGB());
}

return this._additionalData;
}

Expand Down
23 changes: 17 additions & 6 deletions packages/adaptive-ui-figma-designer/src/figma/controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Controller, PluginUIState } from "../core/controller.js";
import type { PluginMessage } from "../core/model.js";
import { deserializeUINodes, SerializableUIState, serializeUINodes } from "../core/serialization.js";
import { FigmaPluginNode } from "./node.js";

Expand All @@ -12,20 +13,30 @@ export class FigmaController extends Controller {
}
}

public handleMessage(state: SerializableUIState): void {
const pluginNodes = deserializeUINodes(state.selectedNodes);
super.receiveStateFromUI({
selectedNodes: pluginNodes
})
public handleMessage(message: PluginMessage): void {
if (message.type === "NODE_DATA") {
const pluginNodes = deserializeUINodes(message.nodes);
super.receiveStateFromUI({
selectedNodes: pluginNodes
});

FigmaPluginNode.clearCache();
FigmaPluginNode.clearCache();
} else if (message.type === "CREATE_STATES") {
const node = this.getNode(message.id);
// Create the interactive state components
node?.createStates();
// Resend the nodes to the plugin UI
FigmaPluginNode.clearCache();
this.setSelectedNodes([message.id]);
}
}

public sendStateToUI(state: PluginUIState): void {
const message: SerializableUIState = {
selectedNodes: serializeUINodes(state.selectedNodes),
};

// Goes to ../ui/index.ts window.onmessage
figma.ui.postMessage(message);
}
}
5 changes: 3 additions & 2 deletions packages/adaptive-ui-figma-designer/src/figma/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SerializableUIState } from "../core/serialization.js";
import { PluginMessage } from "../core/model.js";
import { FigmaController } from "./controller.js";

const controller = new FigmaController();
Expand Down Expand Up @@ -62,7 +62,8 @@ function debounceSelection() {

figma.on("selectionchange", debounceSelection);

figma.ui.onmessage = (message: SerializableUIState): void => {
// Comes from ../ui/index.ts parent.postMessage
figma.ui.onmessage = (message: PluginMessage): void => {
notifyProcessing(() => {
controller.handleMessage(message);
});
Expand Down
Loading

0 comments on commit 24d13ec

Please sign in to comment.