diff --git a/change/@adaptive-web-adaptive-ui-designer-figma-c4faed57-4c53-4767-b0eb-1c18daf9c8e6.json b/change/@adaptive-web-adaptive-ui-designer-figma-c4faed57-4c53-4767-b0eb-1c18daf9c8e6.json new file mode 100644 index 00000000..e1388dd6 --- /dev/null +++ b/change/@adaptive-web-adaptive-ui-designer-figma-c4faed57-4c53-4767-b0eb-1c18daf9c8e6.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Improved Figma CLI", + "packageName": "@adaptive-web/adaptive-ui-designer-figma", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@adaptive-web-adaptive-ui-e4bc13b2-9613-478b-943f-91caad3db43d.json b/change/@adaptive-web-adaptive-ui-e4bc13b2-9613-478b-943f-91caad3db43d.json new file mode 100644 index 00000000..06c079d7 --- /dev/null +++ b/change/@adaptive-web-adaptive-ui-e4bc13b2-9613-478b-943f-91caad3db43d.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Improved Figma CLI", + "packageName": "@adaptive-web/adaptive-ui", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/adaptive-ui-designer-figma-plugin/src/core/code-gen.ts b/packages/adaptive-ui-designer-figma-plugin/src/core/code-gen.ts index 95bf2ba7..ba08501b 100644 --- a/packages/adaptive-ui-designer-figma-plugin/src/core/code-gen.ts +++ b/packages/adaptive-ui-designer-figma-plugin/src/core/code-gen.ts @@ -104,7 +104,8 @@ export class CodeGen { #genStyleRuleCode(componentName: string, styleRule: StyleRule): string { let targetOut = ""; - const contextCondition = styleRule.contextCondition ? ` contextCondition: ${componentName}Anatomy.conditions.${camelCase(styleRule.contextCondition)},\n` : ""; + // HACK: The model has changed but this class never fully handled conditions yet anyway. Revisit this when the model changes are complete. + const contextCondition = ""; // styleRule.contextCondition ? ` contextCondition: ${componentName}Anatomy.conditions.${camelCase(styleRule.contextCondition)},\n` : ""; const part = styleRule.part ? ` part: ${componentName}Anatomy.parts.${camelCase(styleRule.part)},\n` : ""; if (contextCondition || part) { targetOut = ` target: {\n${part}${contextCondition} },\n`; diff --git a/packages/adaptive-ui-designer-figma-plugin/src/figma/node.ts b/packages/adaptive-ui-designer-figma-plugin/src/figma/node.ts index 30658861..04464a9a 100644 --- a/packages/adaptive-ui-designer-figma-plugin/src/figma/node.ts +++ b/packages/adaptive-ui-designer-figma-plugin/src/figma/node.ts @@ -1,8 +1,9 @@ -import { Shadow, StyleProperty } from "@adaptive-web/adaptive-ui"; import { type Color, modeLrgb, modeRgb, parse, type Rgb, useMode, wcagLuminance } from "culori/fn"; +import { Shadow, StyleProperty } from "@adaptive-web/adaptive-ui"; import { Controller, STYLE_REMOVE } from "@adaptive-web/adaptive-ui-designer-core"; import { AppliedStyleModules, AppliedStyleValues, PluginNodeData } from "@adaptive-web/adaptive-ui-designer-core"; import { focusIndicatorNodeName, PluginNode, State, StatesState } from "@adaptive-web/adaptive-ui-designer-core"; +import { FIGMA_SHARED_DATA_NAMESPACE } from "@adaptive-web/adaptive-ui-designer-figma"; import { colorToRgba, variantBooleanHelper } from "./utility.js"; const rgb = useMode(modeRgb); @@ -105,8 +106,6 @@ function canHaveChildren(node: BaseNode): node is ].some((test: (node: BaseNode) => boolean) => test(node)); } -const FIGMA_SHARED_DATA_NAMESPACE: string = "adaptive_ui"; - export class FigmaPluginNode extends PluginNode { public id: string; public type: string; diff --git a/packages/adaptive-ui-designer-figma/src/cli/anatomy.ts b/packages/adaptive-ui-designer-figma/src/cli/anatomy.ts index edb0927b..60c00952 100644 --- a/packages/adaptive-ui-designer-figma/src/cli/anatomy.ts +++ b/packages/adaptive-ui-designer-figma/src/cli/anatomy.ts @@ -1,10 +1,11 @@ /* eslint @typescript-eslint/naming-convention: off */ import fs from 'fs/promises'; import path from 'path'; +import { SerializableAnatomy } from '@adaptive-web/adaptive-ui'; import type * as FigmaRestAPI from '@figma/rest-api-spec'; import { kebabCase } from 'change-case'; import { parseNode } from '../lib/node-parser.js'; -import { Anatomy, type SerializableAnatomy, } from '../lib/anatomy.js'; +import { Anatomy } from '../lib/anatomy.js'; import { ILibraryConfig } from './library-config.js'; import { ILogger } from './logger.js'; @@ -15,11 +16,9 @@ export interface IAnatomyConfiguration { export class AnatomyConfiguration implements IAnatomyConfiguration { public readonly name: string; - #anatomy: SerializableAnatomy; private constructor(anatomy: SerializableAnatomy, public readonly path: string) { this.name = anatomy.name; - this.#anatomy = anatomy; } public static async create( @@ -35,13 +34,18 @@ export class AnatomyConfiguration implements IAnatomyConfiguration { await fs.stat(configurationPath); logger.neutral(`Anatomy file for ${name} already exists! Using existing anatomy.`); anatomy = JSON.parse((await fs.readFile(configurationPath)).toString()) as unknown as SerializableAnatomy; - } catch { - logger.success(`Writing anatomy file for ${name}.`); - const nodeAnatomy = Anatomy.fromPluginUINodeData(parseNode(node)) - const nodeAnatomyFileData = JSON.stringify(nodeAnatomy, null, 2); - anatomy = JSON.parse(nodeAnatomyFileData); - await fs.mkdir(path.parse(configurationPath).dir, { recursive: true }); // ensure dir exists or fs.writeFile will throw - await fs.writeFile(configurationPath, nodeAnatomyFileData); + } catch (e) { + if ((e as Error).message.startsWith("ENOENT")) { + logger.success(`Writing anatomy file for ${name}.`); + const nodeAnatomy = Anatomy.fromPluginUINodeData(parseNode(node)) + const nodeAnatomyFileData = JSON.stringify(nodeAnatomy, null, 2); + anatomy = JSON.parse(nodeAnatomyFileData); + await fs.mkdir(path.parse(configurationPath).dir, { recursive: true }); // ensure dir exists or fs.writeFile will throw + await fs.writeFile(configurationPath, nodeAnatomyFileData); + } else { + logger.warn(`Anatomy file error: ${(e as Error).message}.`); + throw e; + } } return new AnatomyConfiguration(anatomy, configurationPath); diff --git a/packages/adaptive-ui-designer-figma/src/cli/figma-rest-client.ts b/packages/adaptive-ui-designer-figma/src/cli/figma-rest-client.ts index d56de1d4..690838f6 100644 --- a/packages/adaptive-ui-designer-figma/src/cli/figma-rest-client.ts +++ b/packages/adaptive-ui-designer-figma/src/cli/figma-rest-client.ts @@ -416,7 +416,7 @@ export interface FigmaRESTClient { */ getFileComponents( pathParams: FigmaRestAPI.GetFileComponentsPathParams, - queryParams: FigmaRestAPI.GetFilePathParams + queryParams?: FigmaRestAPI.GetFilePathParams ): Promise; /** diff --git a/packages/adaptive-ui-designer-figma/src/cli/main.ts b/packages/adaptive-ui-designer-figma/src/cli/main.ts index 2268e0c4..da3621d6 100644 --- a/packages/adaptive-ui-designer-figma/src/cli/main.ts +++ b/packages/adaptive-ui-designer-figma/src/cli/main.ts @@ -47,7 +47,14 @@ async function main({ library }: ProgramOptions) { }); logger.neutral('Requesting Figma Library.'); - const libraryComponentsResponse = await client.getFileComponentSets(libraryConfig.file); + const libraryComponentSetsResponse = await client.getFileComponentSets(libraryConfig.file); + + if (libraryComponentSetsResponse.error || libraryComponentSetsResponse.status !== 200) { + logger.fail(`Accessing Figma library failed with status code ${libraryComponentSetsResponse.status}`); + process.exit(1); + } + + const libraryComponentsResponse = await client.getFileComponents(libraryConfig.file); if (libraryComponentsResponse.error || libraryComponentsResponse.status !== 200) { logger.fail(`Accessing Figma library failed with status code ${libraryComponentsResponse.status}`); @@ -56,7 +63,19 @@ async function main({ library }: ProgramOptions) { logger.success('Your library was successfully retrieved!'); - const { component_sets: allComponents } = libraryComponentsResponse.meta; + const { component_sets: libraryComponentSets } = libraryComponentSetsResponse.meta; + const { components: libraryComponents } = libraryComponentsResponse.meta; + + // The file components endpoint returns _all_ components including within a set, filter those out. + const uniqueComponents = libraryComponents.filter(component => + // Also filter out components which aren't in a container frame (assume they are helper/utility for now) + component.containing_frame !== undefined && + libraryComponentSets.find(componentSet => + componentSet.containing_frame?.nodeId === component.containing_frame?.nodeId || + componentSet.node_id === component.containing_frame?.nodeId + ) === undefined + ); + const allComponents = libraryComponentSets.concat(uniqueComponents); const componentNames = allComponents.map((value) => value.name).sort(alphabetize); const pickComponentsRequest = { diff --git a/packages/adaptive-ui-designer-figma/src/lib/anatomy.ts b/packages/adaptive-ui-designer-figma/src/lib/anatomy.ts index 054689ba..e93aadc9 100644 --- a/packages/adaptive-ui-designer-figma/src/lib/anatomy.ts +++ b/packages/adaptive-ui-designer-figma/src/lib/anatomy.ts @@ -1,8 +1,13 @@ /* eslint-disable max-len */ -import { camelCase, kebabCase } from "change-case"; +import { kebabCase } from "change-case"; import { Interactivity, InteractivityDefinition, + SerializableAnatomy, + SerializableBooleanCondition, + SerializableStringCondition, + SerializableStyleRule, + SerializableToken, } from "@adaptive-web/adaptive-ui"; import { AdditionalDataKeys, type PluginUINodeData } from "@adaptive-web/adaptive-ui-designer-core"; @@ -15,33 +20,6 @@ function makeClassName(value: string) { // TODO: All of these type definitions will be merged into the core AUI package, including simple interface and serialization support. -export type SerializableBooleanCondition = string; - -export type SerializableStringCondition = Record; - -export type SerializableCondition = SerializableBooleanCondition | SerializableStringCondition; - -export interface SerializableToken { - target: string; - tokenID: string; -} - -export interface SerializableStyleRule { - contextCondition?: string; - part?: string; - styles?: string[]; - tokens?: SerializableToken[]; -} - -export interface SerializableAnatomy { - name: string; - context: string; - interactivity?: InteractivityDefinition; - conditions: Record; - parts: Record; - styleRules: SerializableStyleRule[]; -} - export abstract class Condition { constructor( public readonly name: string, @@ -91,17 +69,16 @@ export class Token { } export class StyleRule { - contextCondition?: string; - part: string = ""; + contextCondition?: Record; + part?: string; styles: Set = new Set(); tokens: Set = new Set(); toJSON(): SerializableStyleRule { - const contextCondition = typeof this.contextCondition === "string" ? "." + kebabCase(this.contextCondition) : undefined; return { - contextCondition, - part: this.part || "", - styles: Array.from(this.styles), + contextCondition: this.contextCondition, + part: this.part, + styles: this.styles.size === 0 ? undefined : Array.from(this.styles), tokens: this.tokens.size === 0 ? undefined : Array.from(this.tokens).map(token => token.toJSON()), }; } @@ -119,12 +96,12 @@ export class Anatomy implements Anatomy { const conditions = Array.from(this.conditions.entries()).reduce((prev, next) => { prev[next[0]] = next[1].toJSON() return prev - }, {} as SerializableAnatomy['conditions']) + }, {} as SerializableAnatomy["conditions"]) const parts = Array.from(this.parts.entries()).reduce((prev, current) => { prev[current[0]] = makeClassName(current[1]); return prev; - }, {} as SerializableAnatomy['parts']) + }, {} as SerializableAnatomy["parts"]) return { name: this.name, @@ -167,7 +144,7 @@ function parseComponent(node: PluginUINodeData): Anatomy { if (node.type === "COMPONENT_SET") { if (node.children.length === 1) { // Unlikely case - walkNode(node.children[0], componentName, "", anatomy); + walkNode(node.children[0], componentName, undefined, anatomy); } else { // Parse the component names into property and value sets const properties = new Map>(); @@ -205,13 +182,13 @@ function parseComponent(node: PluginUINodeData): Anatomy { }); // Handler for a single Component within a Set - const nodeHandler = (name: string, property: string): void => { + const nodeHandler = (name: string, property: string, value: string | boolean): void => { const found = node.children.find(node => node.name.toLowerCase() === name.toLowerCase()); if (!found) { - // console.warn(`Expected component ${name}, property ${property}, not found`); + console.warn(`Expected component ${name}, property ${property}, not found`); } else { - // console.log(" found node", name); - walkNode(found, componentName, property, anatomy); + // console.log("Handling node", {nodeName: name, condition: [property, value]}); + walkNode(found, componentName, { [property]: value }, anatomy); } }; @@ -231,14 +208,14 @@ function parseComponent(node: PluginUINodeData): Anatomy { const replaceProperty = `${property}=${condition.values[0]}`; condition.values.forEach(value => { const name = defaultName.replace(replaceProperty, `${property}=${value}`); - nodeHandler(name, camelCase(`${property} ${value}`)); + nodeHandler(name, property, value); }); } }); // If there were no string conditions, process the first component as the default "baseline" if (!foundString) { - walkNode(node.children[0], componentName, "", anatomy); + walkNode(node.children[0], componentName, undefined, anatomy); } // Handle boolean condition "true" values @@ -246,12 +223,12 @@ function parseComponent(node: PluginUINodeData): Anatomy { if (condition instanceof BooleanCondition) { // Assume false is the default condition, find the `true` component const name = defaultName.replace(`${property}=false`, `${property}=true`); - nodeHandler(name, property); + nodeHandler(name, property, true); } }); } } else { - walkNode(node, componentName, "", anatomy); + walkNode(node, componentName, undefined, anatomy); } return anatomy; @@ -262,7 +239,7 @@ function cleanNodeName(nodeName: string): string { return nodeName.replace(/[^\x20-\x7F]/g, "").trim(); } -function walkNode(node: PluginUINodeData, componentName: string, condition: string, anatomy: Anatomy): void { +function walkNode(node: PluginUINodeData, componentName: string, condition: Record | undefined, anatomy: Anatomy): void { const nodeName = cleanNodeName(node.name); if (nodeName === "Focus indicator") { @@ -270,36 +247,38 @@ function walkNode(node: PluginUINodeData, componentName: string, condition: stri return; } + if (node.type === "INSTANCE" && nodeName !== componentName) { + // TODO: This is too simplified, but it addresses many nested component issues for now. + return; + } + if (!node.name.endsWith(ignoreLayerName)) { // TODO, not only frames, but what? if (node.type === "FRAME" && nodeName !== componentName) { - anatomy.parts.add(node.name); - } - - const styleRule = new StyleRule(); - - if (condition) { - styleRule.contextCondition = condition; + anatomy.parts.add(nodeName); } - node.appliedStyleModules.forEach(style => { - const styleVariableName = style; - styleRule.styles.add(styleVariableName); - }); + if (node.appliedStyleModules.length > 0 || node.appliedDesignTokens.size > 0) { + const styleRule = new StyleRule(); - node.appliedDesignTokens.forEach((token, target) => { - const tokenRef = token.tokenID; - styleRule.tokens.add(new Token(target, tokenRef)); - }); + if (condition) { + styleRule.contextCondition = condition; + } + + node.appliedStyleModules.forEach(style => { + styleRule.styles.add(style); + }); - if (nodeName !== componentName) { - styleRule.part = nodeName; - } + node.appliedDesignTokens.forEach((token, target) => { + const tokenRef = token.tokenID; + styleRule.tokens.add(new Token(target, tokenRef)); + }); - if (styleRule.styles.size > 0 || styleRule.tokens.size > 0) { if (nodeName !== componentName) { - anatomy.parts.add(node.name) + anatomy.parts.add(nodeName) + styleRule.part = nodeName; } + anatomy.styleRules.add(styleRule); } } diff --git a/packages/adaptive-ui-designer-figma/src/lib/constants.ts b/packages/adaptive-ui-designer-figma/src/lib/constants.ts new file mode 100644 index 00000000..ac9c1723 --- /dev/null +++ b/packages/adaptive-ui-designer-figma/src/lib/constants.ts @@ -0,0 +1,4 @@ +/** + * The plugin data namespace used for Adaptive UI. + */ +export const FIGMA_SHARED_DATA_NAMESPACE: string = "adaptive_ui"; diff --git a/packages/adaptive-ui-designer-figma/src/lib/index.ts b/packages/adaptive-ui-designer-figma/src/lib/index.ts index 618dc3c0..7fba644c 100644 --- a/packages/adaptive-ui-designer-figma/src/lib/index.ts +++ b/packages/adaptive-ui-designer-figma/src/lib/index.ts @@ -1 +1,2 @@ -export { Anatomy, Condition, BooleanCondition, StringCondition, StyleRule } from "./anatomy.js"; \ No newline at end of file +export { Anatomy, Condition, BooleanCondition, StringCondition, StyleRule } from "./anatomy.js"; +export { FIGMA_SHARED_DATA_NAMESPACE } from "./constants.js"; diff --git a/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts b/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts index fe92e851..b570d1aa 100644 --- a/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts +++ b/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts @@ -10,16 +10,17 @@ import { PluginNodeData, PluginUINodeData, } from "@adaptive-web/adaptive-ui-designer-core"; +import { FIGMA_SHARED_DATA_NAMESPACE } from "./constants.js"; -const SHARED_PLUGIN_DATA_KEY = "adaptive_ui"; function getPluginData(node: T, key: K): string | null { const data = node.sharedPluginData; - if (data && (data as any)[SHARED_PLUGIN_DATA_KEY]) { - return (data as any)[SHARED_PLUGIN_DATA_KEY][key]; + if (data && (data as any)[FIGMA_SHARED_DATA_NAMESPACE]) { + return (data as any)[FIGMA_SHARED_DATA_NAMESPACE][key]; } return null; } + function hasChildren(node: T): node is FigmaRestAPI.HasChildrenTrait & T { return "children" in node; } @@ -33,7 +34,7 @@ export function parseNode(node: FigmaRestAPI.Node): PluginUINodeData { const children = hasChildren(node) ? node.children : []; const additionalData: AdditionalData = new AdditionalData(); // Where do I get data for this? - if (node.type === "COMPONENT_SET") { + if (node.type === "COMPONENT" || node.type === "COMPONENT_SET") { additionalData.set(AdditionalDataKeys.codeGenName, node.name); } @@ -59,4 +60,4 @@ export function parseNode(node: FigmaRestAPI.Node): PluginUINodeData { designTokens: new DesignTokenValues(), // Intentionally empty inheritedDesignTokens: new DesignTokenValues(), // Intentionally empty }; -} \ No newline at end of file +} diff --git a/packages/adaptive-ui/docs/api-report.md b/packages/adaptive-ui/docs/api-report.md index 6d32bba4..2a3c0d63 100644 --- a/packages/adaptive-ui/docs/api-report.md +++ b/packages/adaptive-ui/docs/api-report.md @@ -496,7 +496,7 @@ export type SerializableStringCondition = Record; // @beta (undocumented) export interface SerializableStyleRule { // (undocumented) - contextCondition?: string; + contextCondition?: Record; // (undocumented) part?: string; // (undocumented) diff --git a/packages/adaptive-ui/src/bin/aui.ts b/packages/adaptive-ui/src/bin/aui.ts index 1a048a0b..924a07c4 100644 --- a/packages/adaptive-ui/src/bin/aui.ts +++ b/packages/adaptive-ui/src/bin/aui.ts @@ -9,20 +9,23 @@ import { Command } from 'commander'; import { glob } from "glob"; import { ElementStylesRenderer } from '../core/modules/element-styles-renderer.js'; import { + BooleanCondition, ComponentAnatomy, ComponentConditions, ComponentParts, SerializableAnatomy, + SerializableStyleRule, + StringCondition, StyleModuleTarget, StyleRules - } from '../core/modules/types.js'; +} from '../core/modules/types.js'; import { DesignTokenRegistry, StyleProperties, StyleRule, Styles, TypedCSSDesignToken - } from "../core/index.js"; +} from "../core/index.js"; const program = new Command(); @@ -55,7 +58,8 @@ program.command('compile-style ') process.exit(0); }) -program.command("compile-styles ").description("") +program.command("compile-styles ") + .description("") .option("-a , --anatomy ", "The name of the anatomy exports. This option supports wildcards.", "anatomy") .option("-s , --styles ", "The name of the styles exports. This option supports wildcards.", "styles") .option("-e , --extension ", "The file extension of the file to write.", ".css") @@ -71,22 +75,22 @@ program.command("compile-styles ").description("") process.exit(0); }) }); -program.command("compile-json-anatomy ") -.description("Compile a stylesheet from a JSON anatomy") -.action(async (path: string) => { - const data = (await fsp.readFile(path)).toString(); - await import("../reference/index.js"); - - const jsonData = JSON.parse(data); - const compiler = new SheetCompilerImpl(); - const sheet = jsonToAUIStyleSheet(jsonData); - const compiledSheet = compiler.compile(sheet); - const formatted = await prettier.format(compiledSheet, { filepath: "foo.css" }); - process.stdout.write("/* This file is generated. Do not edit directly */\n", ); - process.stdout.write(formatted) - process.stdout.end(); -}); +program.command("compile-json-anatomy ") + .description("Compile a stylesheet from a JSON anatomy") + .action(async (path: string) => { + const data = (await fsp.readFile(path)).toString(); + await import("../reference/index.js"); + + const jsonData = JSON.parse(data); + const compiler = new SheetCompilerImpl(); + const sheet = jsonToAUIStyleSheet(jsonData); + const compiledSheet = compiler.compile(sheet); + const formatted = await prettier.format(compiledSheet, { filepath: "foo.css" }); + process.stdout.write("/* This file is generated. Do not edit directly */\n",); + process.stdout.write(formatted) + process.stdout.end(); + }); program.parse() @@ -201,41 +205,56 @@ class SheetCompilerImpl implements SheetCompiler { .reduce((prev: string, curr: string) => prev.concat(curr), ""); } } + +function createCondition(obj: SerializableAnatomy, style: SerializableStyleRule): string | undefined { + if (style.contextCondition) { + const conditionSelectors = Object.entries(style.contextCondition).map(entry => { + const conditionKey = entry[0]; + const value = entry[1]; + + const condition = obj.conditions[conditionKey]; + if (typeof value === "string") { + return (condition as StringCondition)[value]; + } else { + return condition as BooleanCondition; + } + }); + return conditionSelectors.join(""); + } +} + function jsonToAUIStyleSheet(obj: SerializableAnatomy): AUIStyleSheet { - const sheet:AUIStyleSheet = { + const sheet: AUIStyleSheet = { anatomy: { conditions: obj.conditions, parts: obj.parts, - interactivity: obj.interactivity + interactivity: obj.interactivity, }, rules: obj.styleRules.map(style => { - - const styles = style.styles?.map(name => { - return Styles.Shared.get(name)! + const styles = style.styles?.map(name => { + return Styles.Shared.get(name)!; }); + const properties = style.tokens?.reduce((prev, current) => { prev[current.target] = DesignTokenRegistry.Shared.get(current.tokenID) as TypedCSSDesignToken return prev; - }, {} as StyleProperties) + }, {} as StyleProperties); - // TODO this is incomplete - // TODO add const target: StyleModuleTarget = { - part: style.part ? obj.parts[style.part] : undefined, context: obj.context, - // TODO This should be a lookup like `part` above - also update in StyleRule.toJSON - contextCondition: style.contextCondition, - } + contextCondition: createCondition(obj, style), + part: style.part ? obj.parts[style.part] : undefined, + }; const rule: StyleRule = { + target, styles, properties, - target, - } + }; return rule; - }) + }), } return sheet; -} \ No newline at end of file +} diff --git a/packages/adaptive-ui/src/core/modules/types.ts b/packages/adaptive-ui/src/core/modules/types.ts index 2497a537..590901d8 100644 --- a/packages/adaptive-ui/src/core/modules/types.ts +++ b/packages/adaptive-ui/src/core/modules/types.ts @@ -431,10 +431,12 @@ export type StyleRules = Array; * @beta */ export type SerializableBooleanCondition = string; + /** * @beta */ export type SerializableStringCondition = Record; + /** * @beta */ @@ -447,15 +449,17 @@ export interface SerializableToken { target: string, tokenID: string } + /** * @beta */ export interface SerializableStyleRule { - contextCondition?: string; + contextCondition?: Record; part?: string, styles?: string[], tokens?: SerializableToken[]; } + /** * @beta */ @@ -466,4 +470,4 @@ export interface SerializableAnatomy{ conditions: Record, parts: Record, styleRules: SerializableStyleRule[] -} \ No newline at end of file +}