diff --git a/docs/flutter-svg-support.md b/docs/flutter-svg-support.md new file mode 100644 index 00000000..ffe44def --- /dev/null +++ b/docs/flutter-svg-support.md @@ -0,0 +1,17 @@ +# Flutter does not have a SVG support. + +**Use `flutter_svg`** + +> Can't run on dartpad. + +```dart + +``` + +**Use `Image.network()`** + +> Can run on dartpad, requires hosting within svg to png conversion. + +```dart + +``` diff --git a/editor/components/app-runner/app-runner.tsx b/editor/components/app-runner/app-runner.tsx index 2ff35610..5611a557 100644 --- a/editor/components/app-runner/app-runner.tsx +++ b/editor/components/app-runner/app-runner.tsx @@ -77,15 +77,12 @@ function DedicatedFrameworkRunner({ switch (platform) { case "flutter": return ( -
- +
+
); case "react": diff --git a/editor/pages/figma/to-flutter.tsx b/editor/pages/figma/to-flutter.tsx index 144ce575..c46bf71e 100644 --- a/editor/pages/figma/to-flutter.tsx +++ b/editor/pages/figma/to-flutter.tsx @@ -1,5 +1,13 @@ import React, { useEffect, useState } from "react"; import { designTo } from "@designto/code"; +import styled from "@emotion/styled"; +import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository"; +import { + ImageRepository, + MainImageRepository, +} from "@design-sdk/core/assets-repository"; +import { output } from "@designto/config"; +import { tokenize } from "@designto/token"; import { utils_dart } from "../../utils"; import { DefaultEditorWorkspaceLayout } from "../../layout/default-editor-workspace-layout"; import { LayerHierarchy } from "../../components/editor-hierarchy"; @@ -8,15 +16,8 @@ import { WorkspaceContentPanelGridLayout, } from "../../layout/panel"; import { PreviewAndRunPanel } from "../../components/preview-and-run"; -import styled from "@emotion/styled"; import { useDesign } from "../../query-hooks"; import { MonacoEditor } from "../../components/code-editor"; -import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository"; -import { - ImageRepository, - MainImageRepository, -} from "@design-sdk/core/assets-repository"; -import { output } from "@designto/config"; import LoadingLayout from "../../layout/loading-overlay"; export default function FigmaToFlutterPage() { @@ -26,7 +27,6 @@ export default function FigmaToFlutterPage() { useEffect(() => { if (design) { const { reflect, url, node, file } = design; - const { id, name } = reflect; // ------------------------------------------------------------ // other platforms are not supported yet @@ -39,13 +39,10 @@ export default function FigmaToFlutterPage() { ) ); // ------------------------------------------------------------ - designTo .flutter({ input: { - id: id, - name: name, - design: reflect, + widget: tokenize(reflect), }, asset_config: { asset_repository: MainImageRepository.instance }, }) diff --git a/editor/pages/to-code/index.tsx b/editor/pages/to-code/index.tsx index d42be1fa..82dc9486 100644 --- a/editor/pages/to-code/index.tsx +++ b/editor/pages/to-code/index.tsx @@ -65,7 +65,7 @@ export default function DesignToCodeUniversalPage() { return ; } - const { code, name: componentName } = result; + const { code, scaffold, name: componentName } = result; const runner_platform = get_runner_platform(framework_config); return ( @@ -84,7 +84,7 @@ export default function DesignToCodeUniversalPage() { { const token = tokenize(input.design); - + const _tokenized_widget_input = { widget: token }; switch (framework.framework) { case "vanilla": - return designToVanilla({ input: { widget: token }, asset_config }); + return designToVanilla({ input: _tokenized_widget_input, asset_config }); case "react": - return designToReact({ input: { widget: token }, asset_config }); + return designToReact({ input: _tokenized_widget_input, asset_config }); case "flutter": - return designToFlutter({ input, asset_config }); + return designToFlutter({ input: _tokenized_widget_input, asset_config }); } throw `The framework "${framework}" is not supported at this point.`; return; @@ -78,38 +77,31 @@ export async function designToFlutter({ input, asset_config, }: { - input: input.IDesignInput; + input: { widget: Widget }; asset_config?: AssetsConfig; }): Promise { await Promise.resolve(); - const flutterAppBuild = toflutter.buildApp(input.design); - const widget = flutterAppBuild?.widget; - const app = - widget && - toflutter.makeApp({ - widget: widget, - scrollable: flutterAppBuild.scrollable, - }); + const flutterwidget = toflutter.buildFlutterWidget(input.widget); + const flutterapp = toflutter.buildFlutterApp(flutterwidget); - let widgetCode = widget?.build()?.finalize(); - let rootAppCode = composeAppWithHome(app); // ------------------------------------------------------------------------ // finilize temporary assets // this should be placed somewhere else if (asset_config?.asset_repository && !asset_config.skip_asset_replacement) { const assets = await fetch_all_assets(asset_config?.asset_repository); - rootAppCode = dangerous_temporary_asset_replacer(rootAppCode, assets); - widgetCode = dangerous_temporary_asset_replacer(widgetCode, assets); + flutterapp.scaffold.raw = dangerous_temporary_asset_replacer( + flutterapp.scaffold.raw, + assets + ); + flutterapp.code.raw = dangerous_temporary_asset_replacer( + flutterapp.code.raw, + assets + ); } // ------------------------------------------------------------------------ - return { - code: { raw: widgetCode }, - scaffold: { raw: rootAppCode }, - id: input.id, - name: input.name, - }; + return flutterapp; } export function designToVue(input: input.IDesignInput): output.ICodeOutput { diff --git a/packages/designto-flutter/utils/make-as-safe-single.ts b/packages/designto-flutter/_utils/array-utils.ts similarity index 68% rename from packages/designto-flutter/utils/make-as-safe-single.ts rename to packages/designto-flutter/_utils/array-utils.ts index 9c982817..3db65500 100644 --- a/packages/designto-flutter/utils/make-as-safe-single.ts +++ b/packages/designto-flutter/_utils/array-utils.ts @@ -1,4 +1,11 @@ import * as flutter from "@flutter-builder/flutter"; +export function makeSafelyAsList(maybeList: Array | T): Array { + if (Array.isArray(maybeList)) { + return maybeList; + } else { + return [maybeList]; + } +} export function makeSaflyAsSingle( maybeWidget: Array | flutter.Widget diff --git a/packages/designto-flutter/_utils/index.ts b/packages/designto-flutter/_utils/index.ts new file mode 100644 index 00000000..2b4722cf --- /dev/null +++ b/packages/designto-flutter/_utils/index.ts @@ -0,0 +1,3 @@ +export * from "./round-double"; +export * from "./array-utils"; +export * from "./size-utils"; diff --git a/packages/designto-flutter/convert/double.convert.ts b/packages/designto-flutter/_utils/round-double.ts similarity index 100% rename from packages/designto-flutter/convert/double.convert.ts rename to packages/designto-flutter/_utils/round-double.ts diff --git a/packages/designto-flutter/convert/size.convert.ts b/packages/designto-flutter/_utils/size-utils.ts old mode 100755 new mode 100644 similarity index 100% rename from packages/designto-flutter/convert/size.convert.ts rename to packages/designto-flutter/_utils/size-utils.ts diff --git a/packages/designto-flutter/case-handling/handle-flutter-data-image/README.md b/packages/designto-flutter/case-handling/handle-flutter-data-image/README.md new file mode 100644 index 00000000..9278b933 --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-flutter-data-image/README.md @@ -0,0 +1,14 @@ +# Image.memory handling + +Unlike web, using base64 encoded image string requires extra steps on flutter + +```dart +import 'dart:convert'; +import 'package:flutter/widgets.dart'; + +Image.memory(base64Decode(base64String)); +``` + +### References + +- https://stackoverflow.com/questions/46145472/how-to-convert-base64-string-into-image-with-flutter diff --git a/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/README.md b/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/README.md new file mode 100644 index 00000000..bb6ab235 --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/README.md @@ -0,0 +1,3 @@ +# Flutter svg support - [Learn more at docs](../../../../docs/flutter-svg-support.md) + +- https://github.com/lovell/sharp diff --git a/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/index.ts b/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/index.ts new file mode 100644 index 00000000..e43d4f51 --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-flutter-svg-as-image/index.ts @@ -0,0 +1,12 @@ +import { VectorWidget } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +/** + * @deprecated - not implemented + * @param vector + */ +export function flutter_handle_svg_vector_as_bitmap_converted( + vector: VectorWidget +): flutter.Widget { + throw `svg to png conversion is not supported yet.`; +} diff --git a/packages/designto-flutter/case-handling/handle-nested-stack/README.md b/packages/designto-flutter/case-handling/handle-nested-stack/README.md new file mode 100644 index 00000000..21a5b29f --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-nested-stack/README.md @@ -0,0 +1,126 @@ +# Nested stack in flutter + +Nested stack (stack under positioned under stack) requires width and height. +This, + +```dart +Stack( + children: [ + Positioned( + left: 795, + top: 163, + child: Stack( + children: [ + Positioned( + left: 0, + top: 83, + child: Text( + "BeoPlay S3 draws heavily on geometry as a design reference. But also, it is reminiscent from the lines of two shapes embracing each other.", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w400, + fontFamily: "Roboto", + ), + ), + ), + Positioned( + left: 0, + top: 23, + child: Text( + "B.play Homeaaaa Speaker ", + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w700, + fontFamily: "Poppins", + ), + ), + ), + Positioned( + left: 0, + top: 0, + child: Text( + "SPEAKERS", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + fontFamily: "Poppins", + ), + ), + ), + + /// stack requires empty non positioned widget to work properly. refer: https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 + Container(), + ], + ), + ), + + /// stack requires empty non positioned widget to work properly. refer: https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 + Container(), + ], +); +``` + +Tobe + +```dart +Stack( + children: [ + Positioned( + left: 795, + top: 163, + child: Container( + width: MediaQuery.of(context).width, /// <----------- + height: MediaQuery.of(context).height, /// <----------- + child: Stack( + children: [ + Positioned( + left: 0, + top: 83, + child: Text( + "BeoPlay S3 draws heavily on geometry as a design reference. But also, it is reminiscent from the lines of two shapes embracing each other.", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w400, + fontFamily: "Roboto", + ), + ), + ), + Positioned( + left: 0, + top: 23, + child: Text( + "B.play Homeaaaa Speaker ", + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w700, + fontFamily: "Poppins", + ), + ), + ), + Positioned( + left: 0, + top: 0, + child: Text( + "SPEAKERS", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + fontFamily: "Poppins", + ), + ), + ), + + /// stack requires empty non positioned widget to work properly. refer: https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 + Container(), + ], + ), + ), + ), + + /// stack requires empty non positioned widget to work properly. refer: https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 + Container(), + ], +); +``` + +- https://stackoverflow.com/a/52936983/5463235 diff --git a/packages/designto-flutter/case-handling/handle-nested-stack/index.ts b/packages/designto-flutter/case-handling/handle-nested-stack/index.ts new file mode 100644 index 00000000..404e1326 --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-nested-stack/index.ts @@ -0,0 +1,11 @@ +import * as flutter from "@flutter-builder/flutter"; + +export function handle_flutter_case_nested_positioned_stack( + stack: flutter.Stack +): flutter.Container { + return new flutter.Container({ + width: flutter.MediaQuery.of().size.width, + height: flutter.MediaQuery.of().size.height, + child: stack, + }); +} diff --git a/packages/designto-flutter/case-handling/handle-no-size-stack-children/README.md b/packages/designto-flutter/case-handling/handle-no-size-stack-children/README.md new file mode 100644 index 00000000..3c01d032 --- /dev/null +++ b/packages/designto-flutter/case-handling/handle-no-size-stack-children/README.md @@ -0,0 +1,3 @@ +# Flutter requires empty container if stack has no size specified or children is only positioned widgets. + +- https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 diff --git a/packages/designto-flutter/utils/make-as-safe-list.ts b/packages/designto-flutter/case-handling/handle-no-size-stack-children/index.ts similarity index 61% rename from packages/designto-flutter/utils/make-as-safe-list.ts rename to packages/designto-flutter/case-handling/handle-no-size-stack-children/index.ts index 9000dcc9..1024070b 100644 --- a/packages/designto-flutter/utils/make-as-safe-list.ts +++ b/packages/designto-flutter/case-handling/handle-no-size-stack-children/index.ts @@ -1,15 +1,12 @@ import * as flutter from "@flutter-builder/flutter"; +import { makeSafelyAsList } from "../../_utils"; -export function makeSafelyAsList(maybeList: Array | T): Array { - if (Array.isArray(maybeList)) { - return maybeList; - } else { - return [maybeList]; - } -} - -// https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 -export function makeSafelyAsStackList( +/** + * https://github.com/flutter/flutter/issues/49631#issuecomment-582090992 + * @param maybeWidgets + * @returns + */ +export function handle_flutter_case_no_size_stack_children( maybeWidgets: Array | flutter.Widget ): Array { const list = makeSafelyAsList(maybeWidgets); diff --git a/packages/designto-flutter/case-handling/index.ts b/packages/designto-flutter/case-handling/index.ts new file mode 100644 index 00000000..0de995da --- /dev/null +++ b/packages/designto-flutter/case-handling/index.ts @@ -0,0 +1,3 @@ +export * from "./handle-nested-stack"; +export * from "./handle-no-size-stack-children"; +export * from "./handle-flutter-svg-as-image"; diff --git a/packages/designto-flutter/convert/index.ts b/packages/designto-flutter/convert/index.ts deleted file mode 100644 index a29d978f..00000000 --- a/packages/designto-flutter/convert/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { convertToSize } from "./size.convert"; -export { convertToSize }; -// ---- -export * from "./double.convert"; diff --git a/packages/designto-flutter/core-type-mappers/README.md b/packages/designto-flutter/core-type-mappers/README.md deleted file mode 100644 index 3d6067b9..00000000 --- a/packages/designto-flutter/core-type-mappers/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Core type mappers - -> reflect core types to flutter types (simple mappers, without logics) diff --git a/packages/designto-flutter/core-type-mappers/index.ts b/packages/designto-flutter/core-type-mappers/index.ts deleted file mode 100644 index 90fbf38e..00000000 --- a/packages/designto-flutter/core-type-mappers/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./alignment.mapper"; -export * from "./cross-axis-alignment.mapper"; -export * from "./text-align.mapper"; -export * from "./main-axis-alignment.mapper"; -export * from "./main-axis-size.mapper"; -export * from "./radius.mapper"; diff --git a/packages/designto-flutter/core-type-mappers/main-axis-size.mapper.ts b/packages/designto-flutter/core-type-mappers/main-axis-size.mapper.ts deleted file mode 100644 index 63d2cace..00000000 --- a/packages/designto-flutter/core-type-mappers/main-axis-size.mapper.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { MainAxisSize } from "@flutter-builder/flutter"; -import { FigmaLayoutGrow } from "@design-sdk/figma-types"; - -export function mapMainAxisSize(layoutGrow: FigmaLayoutGrow): MainAxisSize { - if (layoutGrow === 0) { - return MainAxisSize.min; - } else { - return MainAxisSize.max; - } -} diff --git a/packages/designto-flutter/dart-ui/dart-ui-color.ts b/packages/designto-flutter/dart-ui/dart-ui-color.ts new file mode 100644 index 00000000..b36a7bf0 --- /dev/null +++ b/packages/designto-flutter/dart-ui/dart-ui-color.ts @@ -0,0 +1,16 @@ +import { Color } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; +import { ColorFormat, converters } from "@reflect-ui/core"; + +/** + * this does not support named colors yet. + * @param color : Reflect#Color; + * @returns + */ +export function color(color: Color): flutter.Color { + const hex = converters.color.convertReflectColorToUniversal( + color, + ColorFormat.hex + ); + return flutter.Color.fromHex(hex); +} diff --git a/packages/designto-flutter/dart-ui/dart-ui-offset.ts b/packages/designto-flutter/dart-ui/dart-ui-offset.ts new file mode 100644 index 00000000..512432f2 --- /dev/null +++ b/packages/designto-flutter/dart-ui/dart-ui-offset.ts @@ -0,0 +1,19 @@ +import { Offset } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function offset(o: Offset): flutter.Offset { + const { dx, dy } = o; + if (requiredOffset(o)) { + return new flutter.Offset(dx, dy); + } +} + +/** + * returns undefined, if offset is redundant. + */ +function requiredOffset(offset: Offset): Offset { + if (offset.dx == 0 && offset.dy == 0) { + return undefined; + } + return offset; +} diff --git a/packages/designto-flutter/core-type-mappers/radius.mapper.ts b/packages/designto-flutter/dart-ui/dart-ui-radius.ts similarity index 85% rename from packages/designto-flutter/core-type-mappers/radius.mapper.ts rename to packages/designto-flutter/dart-ui/dart-ui-radius.ts index 03047c4a..4acbaa19 100644 --- a/packages/designto-flutter/core-type-mappers/radius.mapper.ts +++ b/packages/designto-flutter/dart-ui/dart-ui-radius.ts @@ -2,7 +2,7 @@ import * as flutter from "@flutter-builder/flutter"; import { IRadius } from "@reflect-ui/core"; import { roundNumber } from "@reflect-ui/uiutils"; -export function mapRadius(radius: IRadius): flutter.Radius { +export function radius(radius: IRadius): flutter.Radius { if (typeof radius == "number") { return flutter.Radius.circular(roundNumber(radius)); } else { diff --git a/packages/designto-flutter/core-type-mappers/text-align.mapper.ts b/packages/designto-flutter/dart-ui/dart-ui-text-align.ts similarity index 91% rename from packages/designto-flutter/core-type-mappers/text-align.mapper.ts rename to packages/designto-flutter/dart-ui/dart-ui-text-align.ts index 3e55920e..7c421dc0 100644 --- a/packages/designto-flutter/core-type-mappers/text-align.mapper.ts +++ b/packages/designto-flutter/dart-ui/dart-ui-text-align.ts @@ -1,7 +1,7 @@ import * as core from "@reflect-ui/core"; import * as flutter from "@flutter-builder/flutter"; -export function mapTextAlign(textalign: core.TextAlign): flutter.TextAlign { +export function textAlign(textalign: core.TextAlign): flutter.TextAlign { // only undefined in testing // we are not handling "left" align, because it will be set by default. diff --git a/packages/designto-flutter/dart-ui/index.ts b/packages/designto-flutter/dart-ui/index.ts new file mode 100644 index 00000000..fdc4535f --- /dev/null +++ b/packages/designto-flutter/dart-ui/index.ts @@ -0,0 +1,4 @@ +export * from "./dart-ui-offset"; +export * from "./dart-ui-radius"; +export * from "./dart-ui-text-align"; +export * from "./dart-ui-color"; diff --git a/packages/designto-flutter/index.ts b/packages/designto-flutter/index.ts old mode 100755 new mode 100644 index f133acd6..e4763d10 --- a/packages/designto-flutter/index.ts +++ b/packages/designto-flutter/index.ts @@ -1,285 +1,35 @@ -import { nodes } from "@design-sdk/core"; -import { TextBuilder, WidgetBuilder } from "./widget-builders"; +import { Widget } from "@reflect-ui/core"; +import { buildFlutterWidgetFromTokens } from "./tokens-to-flutter-widget"; +import { flutter as config } from "@designto/config"; import * as flutter from "@flutter-builder/flutter"; -import { makeSafelyAsStackList } from "./utils/make-as-safe-list"; -import { makeFlutterDivider } from "./make/make-flutter-divider"; -import { detectIf } from "@reflect-ui/detection"; -import { makeFlatButton } from "./make/make-flutter-flat-button"; -import { makeDetectedIcon } from "./make/make-flutter-icon"; -import { makeIllustImage } from "./make/make-flutter-image"; -import { makeRowColumn } from "./make/make-flutter-column-row"; -import { makeStack } from "./make/make-flutter-stack"; -import { Axis as ReflectAxis } from "@reflect-ui/core/lib"; -import { makeChip } from "./make/make-flutter-chip"; -import { array, roundNumber } from "@reflect-ui/uiutils"; -import { output } from "@designto/config"; -import { tokenizeDivider } from "@designto/token"; - -type FlutterScreenOutput = output.ScreenOutput; +import { composeAppWithHome } from "@flutter-builder/flutter"; +import { makeApp } from "./make/make-flutter-material-app"; +import { formatCode as dartformat } from "dart-style"; + +export function buildFlutterApp( + widget: flutter.Widget +): config.FlutterComponentOutput { + const app = + widget && + makeApp({ + widget: widget, + scrollable: true, + }); -let parentId = ""; + let widgetCode = dartformat(widget?.build()?.finalize()).code; + let rootAppCode = composeAppWithHome(app); -// the target root widget tree -let targetId: string; -let scrollable: boolean; -export function buildApp( - sceneNode: nodes.ReflectSceneNode -): FlutterScreenOutput { - scrollable = true; // init to true. - targetId = sceneNode.id; - const rootWidget = generateRootWidget(sceneNode); - console.log("flutter widget build result is..", rootWidget); return { - widget: rootWidget, - scrollable: scrollable, + id: "flutterapp", + name: "flutterapp", + code: { raw: widgetCode }, + scaffold: { raw: rootAppCode }, }; } -function generateRootWidget( - sceneNode: nodes.ReflectSceneNode, - parentIdSrc: string = "" -): flutter.Widget { - parentId = parentIdSrc; - _log_current_node(sceneNode); - let result = flutterWidgetGenerator(sceneNode); - - if (Array.isArray(result)) { - // this won't happen - throw "result cannot be in array form."; - } - - return result; -} - -function flutterWidgetGenerator( - sceneNode: ReadonlyArray | nodes.ReflectSceneNode -): Array | flutter.Widget { - console.log(`flutterWidgetGenerator handling scene node -`, sceneNode); - if (Array.isArray(sceneNode) && sceneNode.length > 0) { - // explicit type casting - sceneNode = sceneNode as ReadonlyArray; - - // count of input nodes - const sceneLen = sceneNode.length; - - // initialize output widgets array - let widgets: Array = []; - - console.log( - `widget generator:: - targetting list of nodes children of parent:{${sceneNode[0].parent?.toString()}}. - total count of ${sceneLen}` - ); - - sceneNode.forEach((node, index) => { - _log_current_node(node); - widgets.push(handleNode(node)); - - // if the parent is an AutoLayout, and itemSpacing is set, add a SizedBox between items. - widgets.push(addSpacingIfNeeded(node, index, sceneLen)); - }); - - // filter empty widgets - widgets = widgets.filter((w) => array.filters.notEmpty(w)); - if (widgets.length == 1) { - // TODO - inspect this logic - is it safe? - // console.log("flutterWidgetGenerator complete", widgets[0]) - return widgets[0]; - } - // console.log("flutterWidgetGenerator complete", widgets) - return widgets; - } else { - // explicit type casting - sceneNode = sceneNode as nodes.ReflectSceneNode; - console.log( - `widget generator:: - targetting single node ${sceneNode.toString()} - this is child of ${sceneNode.parent?.toString()}` - ); - - return handleNode(sceneNode); - } - - function handleNode(node: nodes.ReflectSceneNode): flutter.Widget { - _log_current_node(node); - console.log( - `starting handling node ${node.toString()} type of ${node.type}` - ); - - const chipDetectionResult = detectIf.chip(node); - if (chipDetectionResult.result) { - console.log("detected:: this node is detected as Chip", node.name); - return makeChip(chipDetectionResult.data); - } - - const buttonDetectionResult = detectIf.button(node); - if (buttonDetectionResult.result) { - console.log("detected:: this node is detected as button.", node.name); - return makeFlatButton(buttonDetectionResult.data); - } - - const iconDetectionResult = detectIf.icon(node); - if (iconDetectionResult.result) { - console.log("detected:: this node is detected as an icon.", node.name); - return makeDetectedIcon(iconDetectionResult.data); - } - - const illustDetectionResult = detectIf.illust(node); - if (illustDetectionResult.result) { - console.log("detected:: this node is detected as an illust.", node.name); - return makeIllustImage(node); - } - - if ( - node instanceof nodes.ReflectRectangleNode || - node instanceof nodes.ReflectEllipseNode - ) { - console.log( - `this node ${node.toString()} is a rect || ellipse. making it as a empty container` - ); - return flutterContainer(node, undefined); - } else if (node instanceof nodes.ReflectLineNode) { - const _divider_detection_result = detectIf.divider(node); - if (_divider_detection_result.result) { - console.log( - `this node ${node.toString()} is a line. making it as a divider` - ); - const dividerwidget = tokenizeDivider.fromManifest( - node, - _divider_detection_result.data - ); - return makeFlutterDivider(dividerwidget); - } - } else if (node instanceof nodes.ReflectGroupNode) { - console.log( - `this node ${node.toString()} is a group. handling with group handler` - ); - return flutterGroupHandler(node); - } else if (node instanceof nodes.ReflectFrameNode) { - return flutterFrame(node); - } else if (node instanceof nodes.ReflectTextNode) { - return flutterText(node); - } - } -} - -function _log_current_node(node: { id: string }) { - // console.log( - // ` - // ---------- - // flutter app generation: targetting current processing node to ${node.id} - // ---------- - // ` - // ); -} - -function flutterGroupHandler(node: nodes.ReflectGroupNode): flutter.Widget { - // console.log( - // `group handler :: making ${node} as a stack with its children count of ${node.childrenCount}` - // ); - - return flutterContainer( - node, - makeStack(flutterWidgetGenerator(node.children) as []) - ); -} - -function flutterContainer( - node: - | nodes.ReflectFrameNode - | nodes.ReflectGroupNode - | nodes.ReflectRectangleNode - | nodes.ReflectEllipseNode, - child?: flutter.Widget -): flutter.Widget { - const builder = new WidgetBuilder({ child: child, node: node }); - - const isBuildRoot = targetId === node.id; - const sizeOptions = isBuildRoot - ? { - size: new flutter.Size( - flutter.MediaQuery.of().size.width, - undefined - ).addComment( - 'container building for target root node. making the width with "MediaQuery.of().size.width"' - ), - } - : undefined; - - builder - .wrapWithContainer(sizeOptions) - .blendWithAttributes() - .positionInParent(parentId); - return builder.child; -} - -function flutterText(node: nodes.ReflectTextNode): flutter.Widget { - const builder = new TextBuilder({ - child: undefined, - node: node, - }); - - builder.createText().blendWithAttributes().positionInParent(parentId); - - return builder.child; -} - -function flutterFrame(node: nodes.ReflectFrameNode): flutter.Widget { - // console.log(`start handling frame node ${node.toString()} and its children`); - - const children = flutterWidgetGenerator(node.children); - - if (node.children.length === 1) { - // if there is only one child, there is no need for Container or Row. Padding works indepdently of them. - return flutterContainer(node, children as flutter.Widget); - } else if (node.layoutMode !== undefined) { - const rowColumn = makeRowColumn(node, children as Array); - return flutterContainer(node, rowColumn); - } else { - // node.layoutMode === "NONE" && node.children.length > 1 - // children needs to be absolute - - // region - // if currently handled node is root node, and it's outcome is stack, then make it not scrollable. (singlechildscrollview with stack usage is not resolved, remaining as issue.) - if (node.id == targetId) { - scrollable = false; - } - // endregion - - return flutterContainer( - node, - new flutter.Stack({ - children: makeSafelyAsStackList(children), - }) - ); +export function buildFlutterWidget(widget: Widget) { + if (!widget) { + throw "A valid reflect widget manifest should be passed as an input. none was passed."; } + return buildFlutterWidgetFromTokens(widget); } - -function addSpacingIfNeeded( - node: nodes.ReflectSceneNode, - index: number, - length: number -): flutter.Widget | undefined { - if ( - node.parent instanceof nodes.ReflectFrameNode && - node.parent.layoutMode !== undefined - ) { - // check if itemSpacing is set and if it isn't the last value. - // Don't add the SizedBox at last value. In Figma, itemSpacing CAN be negative; here it can't. - if (node.parent.itemSpacing > 0 && index < length - 1) { - if (node.parent.layoutMode === ReflectAxis.horizontal) { - return new flutter.SizedBox({ - width: roundNumber(node.parent.itemSpacing), - }); - } else { - // node.parent.layoutMode === "VERTICAL" - return new flutter.SizedBox({ - height: roundNumber(node.parent.itemSpacing), - }); - } - } - } - return undefined; -} - -export * from "./make/make-flutter-material-app"; diff --git a/packages/designto-flutter/legacy-archives/iterative-builder.ts b/packages/designto-flutter/legacy-archives/iterative-builder.ts new file mode 100755 index 00000000..b316c9f3 --- /dev/null +++ b/packages/designto-flutter/legacy-archives/iterative-builder.ts @@ -0,0 +1,285 @@ +import { nodes } from "@design-sdk/core"; +import { TextBuilder, WidgetBuilder } from "../widget-builders"; +import * as flutter from "@flutter-builder/flutter"; +import { makeSafelyAsStackList } from "../utils/make-as-safe-list"; +import { makeFlutterDivider } from "../make/make-flutter-divider"; +import { detectIf } from "@reflect-ui/detection"; +import { makeFlatButton } from "../make/make-flutter-flat-button"; +import { makeDetectedIcon } from "../make/make-flutter-icon"; +import { makeIllustImage } from "../make/make-flutter-image"; +import { makeRowColumn } from "../make/make-flutter-column-row"; +import { makeStack } from "../make/make-flutter-stack"; +import { Axis as ReflectAxis } from "@reflect-ui/core/lib"; +import { makeChip } from "../make/make-flutter-chip"; +import { array, roundNumber } from "@reflect-ui/uiutils"; +import { output } from "@designto/config"; +import { tokenizeDivider } from "@designto/token"; + +type FlutterScreenOutput = output.ScreenOutput; + +let parentId = ""; + +// the target root widget tree +let targetId: string; +let scrollable: boolean; +export function buildApp( + sceneNode: nodes.ReflectSceneNode +): FlutterScreenOutput { + scrollable = true; // init to true. + targetId = sceneNode.id; + const rootWidget = generateRootWidget(sceneNode); + console.log("flutter widget build result is..", rootWidget); + return { + widget: rootWidget, + scrollable: scrollable, + }; +} + +function generateRootWidget( + sceneNode: nodes.ReflectSceneNode, + parentIdSrc: string = "" +): flutter.Widget { + parentId = parentIdSrc; + _log_current_node(sceneNode); + let result = flutterWidgetGenerator(sceneNode); + + if (Array.isArray(result)) { + // this won't happen + throw "result cannot be in array form."; + } + + return result; +} + +function flutterWidgetGenerator( + sceneNode: ReadonlyArray | nodes.ReflectSceneNode +): Array | flutter.Widget { + console.log(`flutterWidgetGenerator handling scene node -`, sceneNode); + if (Array.isArray(sceneNode) && sceneNode.length > 0) { + // explicit type casting + sceneNode = sceneNode as ReadonlyArray; + + // count of input nodes + const sceneLen = sceneNode.length; + + // initialize output widgets array + let widgets: Array = []; + + console.log( + `widget generator:: + targetting list of nodes children of parent:{${sceneNode[0].parent?.toString()}}. + total count of ${sceneLen}` + ); + + sceneNode.forEach((node, index) => { + _log_current_node(node); + widgets.push(handleNode(node)); + + // if the parent is an AutoLayout, and itemSpacing is set, add a SizedBox between items. + widgets.push(addSpacingIfNeeded(node, index, sceneLen)); + }); + + // filter empty widgets + widgets = widgets.filter((w) => array.filters.notEmpty(w)); + if (widgets.length == 1) { + // TODO - inspect this logic - is it safe? + // console.log("flutterWidgetGenerator complete", widgets[0]) + return widgets[0]; + } + // console.log("flutterWidgetGenerator complete", widgets) + return widgets; + } else { + // explicit type casting + sceneNode = sceneNode as nodes.ReflectSceneNode; + console.log( + `widget generator:: + targetting single node ${sceneNode.toString()} + this is child of ${sceneNode.parent?.toString()}` + ); + + return handleNode(sceneNode); + } + + function handleNode(node: nodes.ReflectSceneNode): flutter.Widget { + _log_current_node(node); + console.log( + `starting handling node ${node.toString()} type of ${node.type}` + ); + + const chipDetectionResult = detectIf.chip(node); + if (chipDetectionResult.result) { + console.log("detected:: this node is detected as Chip", node.name); + return makeChip(chipDetectionResult.data); + } + + const buttonDetectionResult = detectIf.button(node); + if (buttonDetectionResult.result) { + console.log("detected:: this node is detected as button.", node.name); + return makeFlatButton(buttonDetectionResult.data); + } + + const iconDetectionResult = detectIf.icon(node); + if (iconDetectionResult.result) { + console.log("detected:: this node is detected as an icon.", node.name); + return makeDetectedIcon(iconDetectionResult.data); + } + + const illustDetectionResult = detectIf.illust(node); + if (illustDetectionResult.result) { + console.log("detected:: this node is detected as an illust.", node.name); + return makeIllustImage(node); + } + + if ( + node instanceof nodes.ReflectRectangleNode || + node instanceof nodes.ReflectEllipseNode + ) { + console.log( + `this node ${node.toString()} is a rect || ellipse. making it as a empty container` + ); + return flutterContainer(node, undefined); + } else if (node instanceof nodes.ReflectLineNode) { + const _divider_detection_result = detectIf.divider(node); + if (_divider_detection_result.result) { + console.log( + `this node ${node.toString()} is a line. making it as a divider` + ); + const dividerwidget = tokenizeDivider.fromManifest( + node, + _divider_detection_result.data + ); + return makeFlutterDivider(dividerwidget); + } + } else if (node instanceof nodes.ReflectGroupNode) { + console.log( + `this node ${node.toString()} is a group. handling with group handler` + ); + return flutterGroupHandler(node); + } else if (node instanceof nodes.ReflectFrameNode) { + return flutterFrame(node); + } else if (node instanceof nodes.ReflectTextNode) { + return flutterText(node); + } + } +} + +function _log_current_node(node: { id: string }) { + // console.log( + // ` + // ---------- + // flutter app generation: targetting current processing node to ${node.id} + // ---------- + // ` + // ); +} + +function flutterGroupHandler(node: nodes.ReflectGroupNode): flutter.Widget { + // console.log( + // `group handler :: making ${node} as a stack with its children count of ${node.childrenCount}` + // ); + + return flutterContainer( + node, + makeStack(flutterWidgetGenerator(node.children) as []) + ); +} + +function flutterContainer( + node: + | nodes.ReflectFrameNode + | nodes.ReflectGroupNode + | nodes.ReflectRectangleNode + | nodes.ReflectEllipseNode, + child?: flutter.Widget +): flutter.Widget { + const builder = new WidgetBuilder({ child: child, node: node }); + + const isBuildRoot = targetId === node.id; + const sizeOptions = isBuildRoot + ? { + size: new flutter.Size( + flutter.MediaQuery.of().size.width, + undefined + ).addComment( + 'container building for target root node. making the width with "MediaQuery.of().size.width"' + ), + } + : undefined; + + builder + .wrapWithContainer(sizeOptions) + .blendWithAttributes() + .positionInParent(parentId); + return builder.child; +} + +function flutterText(node: nodes.ReflectTextNode): flutter.Widget { + const builder = new TextBuilder({ + child: undefined, + node: node, + }); + + builder.createText().blendWithAttributes().positionInParent(parentId); + + return builder.child; +} + +function flutterFrame(node: nodes.ReflectFrameNode): flutter.Widget { + // console.log(`start handling frame node ${node.toString()} and its children`); + + const children = flutterWidgetGenerator(node.children); + + if (node.children.length === 1) { + // if there is only one child, there is no need for Container or Row. Padding works indepdently of them. + return flutterContainer(node, children as flutter.Widget); + } else if (node.layoutMode !== undefined) { + const rowColumn = makeRowColumn(node, children as Array); + return flutterContainer(node, rowColumn); + } else { + // node.layoutMode === "NONE" && node.children.length > 1 + // children needs to be absolute + + // region + // if currently handled node is root node, and it's outcome is stack, then make it not scrollable. (singlechildscrollview with stack usage is not resolved, remaining as issue.) + if (node.id == targetId) { + scrollable = false; + } + // endregion + + return flutterContainer( + node, + new flutter.Stack({ + children: makeSafelyAsStackList(children), + }) + ); + } +} + +function addSpacingIfNeeded( + node: nodes.ReflectSceneNode, + index: number, + length: number +): flutter.Widget | undefined { + if ( + node.parent instanceof nodes.ReflectFrameNode && + node.parent.layoutMode !== undefined + ) { + // check if itemSpacing is set and if it isn't the last value. + // Don't add the SizedBox at last value. In Figma, itemSpacing CAN be negative; here it can't. + if (node.parent.itemSpacing > 0 && index < length - 1) { + if (node.parent.layoutMode === ReflectAxis.horizontal) { + return new flutter.SizedBox({ + width: roundNumber(node.parent.itemSpacing), + }); + } else { + // node.parent.layoutMode === "VERTICAL" + return new flutter.SizedBox({ + height: roundNumber(node.parent.itemSpacing), + }); + } + } + } + return undefined; +} + +export * from "../make/make-flutter-material-app"; diff --git a/packages/designto-flutter/legacy-archives/rect.interpret.ts b/packages/designto-flutter/legacy-archives/rect.interpret.ts index a5f75868..fa36f31b 100644 --- a/packages/designto-flutter/legacy-archives/rect.interpret.ts +++ b/packages/designto-flutter/legacy-archives/rect.interpret.ts @@ -5,7 +5,6 @@ import { Color, } from "@flutter-builder/flutter"; import { makeColorFromRGBO } from "../make"; -import { interpretGradient } from "../interpreter/gradient.interpret"; import { interpretBorderRadius } from "./border-radius.interpret"; import { roundNumber } from "@reflect-ui/uiutils"; import { ReflectRectangleNode } from "@design-sdk/core"; @@ -15,6 +14,8 @@ import { Paint, SolidPaint, } from "@design-sdk/figma"; +import * as painting from "../painting"; +import { tokenize_gradient } from "../../designto-token"; export function interpretRect(rect: ReflectRectangleNode): Container { const fills = rect.fills as Array; @@ -46,7 +47,8 @@ export function interpretRect(rect: ReflectRectangleNode): Container { break; case BackgroundType.gradient: const gradientFill = singleFill as GradientPaint; - gradient = interpretGradient(gradientFill); + const g = tokenize_gradient(gradientFill); + gradient = painting.linearGradient(g); break; case BackgroundType.image: const imageFill = singleFill as ImagePaint; @@ -65,7 +67,8 @@ export function interpretRect(rect: ReflectRectangleNode): Container { break; case BackgroundType.gradient: const gradientFill = primaryFill as GradientPaint; - gradient = interpretGradient(gradientFill); + const g = tokenize_gradient(gradientFill); + gradient = painting.linearGradient(g); break; case BackgroundType.image: const imageFill = primaryFill as ImagePaint; diff --git a/packages/designto-flutter/length/README.md b/packages/designto-flutter/length/README.md new file mode 100644 index 00000000..aba8e23e --- /dev/null +++ b/packages/designto-flutter/length/README.md @@ -0,0 +1,8 @@ +# Special operator. + +Convert css length syntax to flutter size calculation snippet. + +```dart +// e.g - calc(100vh - 500px) +MediaQuery.of(context).size.height - 500; +``` diff --git a/packages/designto-flutter/length/calc.ts b/packages/designto-flutter/length/calc.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/designto-flutter/length/index.ts b/packages/designto-flutter/length/index.ts new file mode 100644 index 00000000..8a0b9dfe --- /dev/null +++ b/packages/designto-flutter/length/index.ts @@ -0,0 +1,2 @@ +// export * from "./calc"; +export * from "./length"; diff --git a/packages/designto-flutter/length/length.ts b/packages/designto-flutter/length/length.ts new file mode 100644 index 00000000..ccbf5ca3 --- /dev/null +++ b/packages/designto-flutter/length/length.ts @@ -0,0 +1,58 @@ +import { Axis, DimensionLength } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function length(d: DimensionLength, a?: Axis) { + if (d === undefined) { + return; + } + + if (typeof d === "string") { + if (d === "match-screen-size") { + switch (a) { + case Axis.horizontal: + return flutter.MediaQuery.of().size.width; + case Axis.vertical: + return flutter.MediaQuery.of().size.height; + } + throw new Error("Invalid axis"); + } + + if (d.endsWith("px")) { + return parseFloat(d) as flutter.double; + } + + if (d.endsWith("vw")) { + throw "not supported"; + // return vw(parseFloat(d)); + } + + if (d.endsWith("vh")) { + throw "not supported"; + // return vh(parseFloat(d)); + } + + if (d.endsWith("%")) { + throw "% not supported"; + // return percent(d as `${number}%`); + } + + // this case, the d value is already processed by other css builders. + else { + return d; + } + } else if (typeof d === "number") { + return d; + } + + if (d.type == "calc") { + throw "calc not supported"; + // return calc(d.operations, a); + } + + if (d.type == "op") { + //@ts-ignore + return operation(d, a); + } + + throw `no matching length type found. "${JSON.stringify(d)}" was givven`; +} diff --git a/packages/designto-flutter/make/index.ts b/packages/designto-flutter/make/index.ts index 2a69b61d..c658b99f 100644 --- a/packages/designto-flutter/make/index.ts +++ b/packages/designto-flutter/make/index.ts @@ -1,10 +1,6 @@ -import { makeBorderRadius } from "./make-flutter-border-radius"; -import { makeBorder } from "./make-flutter-border"; -import { makeBoxShadow } from "./make-flutter-box-shadow"; +import { boxShadow } from "../painting/painting-box-shadow"; import { makeColor, makeColorFromRGBO } from "./make-flutter-color"; -import { makeEdgeInsets } from "./make-flutter-edge-insets"; import { makeShape } from "./make-flutter-shape-border"; -import { makeTextStyle } from "./make-flutter-text-style"; import { makeText } from "./make-flutter-text"; import { makeBoxDecoration, @@ -12,15 +8,11 @@ import { } from "./make-flutter-box-decoration"; export { - makeBorderRadius, - makeBorder, - makeBoxShadow, + boxShadow as makeBoxShadow, makeBoxDecoration, makeBoxDecorationColorBg as makeBoxDecorationBg, makeColor, makeColorFromRGBO, - makeEdgeInsets, makeShape, - makeTextStyle, makeText, }; diff --git a/packages/designto-flutter/make/make-flutter-border-radius.ts b/packages/designto-flutter/make/make-flutter-border-radius.ts deleted file mode 100644 index 970df4d6..00000000 --- a/packages/designto-flutter/make/make-flutter-border-radius.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as flutter from "@flutter-builder/flutter"; -import { - ReflectEllipseNode, - IReflectRectangleCornerMixin, -} from "@design-sdk/core"; -import { mapRadius } from "../core-type-mappers"; -import * as core from "@reflect-ui/core"; -import { roundNumber } from "@reflect-ui/uiutils"; - -export function makeBorderRadius( - node: IReflectRectangleCornerMixin -): flutter.BorderRadiusGeometry { - if (node instanceof ReflectEllipseNode) return undefined; - if (node.cornerRadius === undefined || node.cornerRadius.all === 0) { - return undefined; - } - - return node.cornerRadius.all !== undefined - ? flutter.BorderRadius.circular( - roundNumber(node.cornerRadius.all as number) - ) - : _makePartialBorderRadius(node.cornerRadius); -} - -function _makePartialBorderRadius(cornerRadius: core.BorderRadiusManifest) { - const _oneofRadiusIsHasValue = [ - cornerRadius.tl, - cornerRadius.tr, - cornerRadius.bl, - cornerRadius.br, - ].some((i) => i !== undefined); - - if (_oneofRadiusIsHasValue) { - return flutter.BorderRadius.only({ - topLeft: mapRadius(cornerRadius.tl), - topRight: mapRadius(cornerRadius.tr), - bottomLeft: mapRadius(cornerRadius.bl), - bottomRight: mapRadius(cornerRadius.br), - }); - } else { - // if none of each corner radius contains value (if every value is empty) do not return a value. - return; - } -} diff --git a/packages/designto-flutter/make/make-flutter-border-side.ts b/packages/designto-flutter/make/make-flutter-border-side.ts deleted file mode 100644 index aa4f6884..00000000 --- a/packages/designto-flutter/make/make-flutter-border-side.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ReflectDefaultShapeMixin } from "@design-sdk/core"; -import * as flutter from "@flutter-builder/flutter"; -import { roundNumber } from "@reflect-ui/uiutils"; -import { makeColor } from "./make-flutter-color"; - -export function makeBorderSide(node: ReflectDefaultShapeMixin) { - // TODO -> move this as member method - if (!node.strokes || node.strokes.length === 0) { - return undefined; - } - - return new flutter.BorderSide({ - color: makeColor(node.strokes), - width: roundNumber(node.strokeWeight), - }); -} diff --git a/packages/designto-flutter/make/make-flutter-border.ts b/packages/designto-flutter/make/make-flutter-border.ts deleted file mode 100644 index 9464b0cc..00000000 --- a/packages/designto-flutter/make/make-flutter-border.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as flutter from "@flutter-builder/flutter"; -import { ReflectDefaultShapeMixin, ReflectSceneNode } from "@design-sdk/core"; -import { roundNumber } from "@reflect-ui/uiutils"; -import { makeColor } from "./make-flutter-color"; - -// generate the border, when it exists -export function makeBorder(node: ReflectSceneNode): flutter.Border { - if (node instanceof ReflectDefaultShapeMixin) { - if (!node.strokes || node.strokes.length === 0) { - return undefined; - } - - // generate the border, when it should exist - return node.strokeWeight - ? flutter.Border.all({ - color: makeColor(node.strokes), - width: roundNumber(node.strokeWeight), - }) - : undefined; - } -} diff --git a/packages/designto-flutter/make/make-flutter-box-decoration.ts b/packages/designto-flutter/make/make-flutter-box-decoration.ts index 10c6a2b5..2b60aca1 100644 --- a/packages/designto-flutter/make/make-flutter-box-decoration.ts +++ b/packages/designto-flutter/make/make-flutter-box-decoration.ts @@ -3,15 +3,9 @@ import { Figma } from "@design-sdk/figma"; import { retrieveFill } from "@design-sdk/core/utils"; import * as flutter from "@flutter-builder/flutter"; import { interpretImageFill } from "../interpreter/image.interpret"; -import { makeBorderRadius } from "./make-flutter-border-radius"; -import { makeBorder } from "./make-flutter-border"; -import { makeBoxShadow } from "./make-flutter-box-shadow"; -import { - makeColorFromRGBO, - makeFlutterColorFromReflectColor, -} from "./make-flutter-color"; -import { tokenize_gradient } from "@designto/token"; -import { mapAlignment } from "../core-type-mappers"; +import * as painting from "../painting"; +import { makeColorFromRGBO } from "./make-flutter-color"; +import { tokenizeBorder, tokenize_gradient } from "@designto/token"; type DecorationBackgroundLike = | flutter.Color @@ -24,9 +18,10 @@ export function makeBoxDecoration( | nodes.ReflectEllipseNode | nodes.ReflectFrameNode ): flutter.BoxDecoration | flutter.Color { - const decorationBorder = makeBorder(node); - const decorationBoxShadow = makeBoxShadow(node); - const decorationBorderRadius = makeBorderRadius(node); + const _border = tokenizeBorder.fromNode(node); + const decorationBorder = painting.border(_border); + const decorationBoxShadow = painting.boxShadow(node.shadows); + const decorationBorderRadius = painting.borderRadius(node.cornerRadius); /// /// ---------------------------------------------------------------- @@ -119,12 +114,7 @@ export function makeBoxDecorationColorBg( return undefined; case "GRADIENT_LINEAR": const g = tokenize_gradient(fill as Figma.GradientPaint); - return new flutter.LinearGradient({ - begin: mapAlignment(g.begin), - end: mapAlignment(g.end), - colors: g.colors.map((c) => makeFlutterColorFromReflectColor(c)), - stops: g.stops, - }); + return painting.linearGradient(g); case "SOLID": return makeColorFromRGBO(fill.color, opacity); default: diff --git a/packages/designto-flutter/make/make-flutter-box-shadow.ts b/packages/designto-flutter/make/make-flutter-box-shadow.ts deleted file mode 100644 index 7e6054ec..00000000 --- a/packages/designto-flutter/make/make-flutter-box-shadow.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as flutter from "@flutter-builder/flutter"; -import { ReflectSceneNode } from "@design-sdk/core"; -import { Figma } from "@design-sdk/figma"; -import { roundNumber } from "@reflect-ui/uiutils"; -import { makeColorFromRGBO } from "./make-flutter-color"; - -export function makeBoxShadow( - node: ReflectSceneNode -): Array { - let boxShadows: Array = []; - - if (node.effects?.length > 0) { - const shadows: Array = node.effects.filter( - (d): d is Figma.ShadowEffect => - (d.type === "DROP_SHADOW" || d.type === "INNER_SHADOW") && d.visible - ); - - // if no shadow filtered available, return undefined - if (shadows.length == 0) { - return undefined; - } - - shadows.forEach(function (d: Figma.ShadowEffect) { - let boxShadow: flutter.BoxShadow; - if (d.type == "DROP_SHADOW") { - boxShadow = new flutter.BoxShadow({ - color: makeColorFromRGBO(d.color, d.color.a), - blurRadius: requiredNumber(d.radius), - spreadRadius: requiredNumber(d.spread), - offset: requiredOffset(new flutter.Offset(d.offset.x, d.offset.y)), - }); - } else if (d.type == "INNER_SHADOW") { - // handling inner shadow - // https://stackoverflow.com/a/55096682/5463235 - - boxShadow = new flutter.BoxShadow({ - color: makeColorFromRGBO(d.color, d.color.a), - blurRadius: requiredNumber(d.radius), - // multiply -1 * blur for spread - // TODO inspect this logic again. - spreadRadius: requiredNumber((d.spread + d.radius) * -1), - offset: requiredOffset(new flutter.Offset(d.offset.x, d.offset.y)), - }); - } - - boxShadows.push(boxShadow); - }); - } - - // return undefined if array is empty, since it's not needed. - return boxShadows.length > 0 ? boxShadows : undefined; -} - -function requiredNumber(number: number): number { - const rounded = roundNumber(number); - if (rounded == 0) { - return undefined; - } - return rounded; -} - -/** - * returns undefined, if offset is redundant. - */ -function requiredOffset(offset: flutter.Offset): flutter.Offset { - if (offset.dx == 0 && offset.dy == 0) { - return undefined; - } - return offset; -} diff --git a/packages/designto-flutter/make/make-flutter-chip.ts b/packages/designto-flutter/make/make-flutter-chip.ts index 5cae0d51..ddd54065 100644 --- a/packages/designto-flutter/make/make-flutter-chip.ts +++ b/packages/designto-flutter/make/make-flutter-chip.ts @@ -1,18 +1,21 @@ +import { retrievePrimaryColor } from "@design-sdk/core/utils"; import * as flutter from "@flutter-builder/flutter"; import { manifests } from "@reflect-ui/detection"; -import { makeBorderRadius } from "."; -import { makeBorderSide } from "./make-flutter-border-side"; +import { borderside } from "../painting/painting-border-side"; import { makeColor } from "./make-flutter-color"; +import * as painting from "../painting"; export function makeChip(manifest: manifests.DetectedChipManifest) { - console.log({ manifest }); var content = new flutter.Text(manifest.content?.text); const color: flutter.Color = makeColor(manifest.base.fills); const textColor: flutter.Color = makeColor(manifest.content?.fills); const height = manifest.base.height; const shape = new flutter.RoundedRectangleBorder({ - borderRadius: makeBorderRadius(manifest.base), - side: makeBorderSide(manifest.base), + borderRadius: painting.borderRadius(manifest.base.cornerRadius), + side: borderside({ + color: retrievePrimaryColor(manifest.base.strokes), + width: manifest.base.strokeWeight, + }), }); const onSelected = flutter.Snippet.fromStatic( '(){ print("Chip onSelected"); }' diff --git a/packages/designto-flutter/make/make-flutter-color.ts b/packages/designto-flutter/make/make-flutter-color.ts index 40980491..e0b77afd 100644 --- a/packages/designto-flutter/make/make-flutter-color.ts +++ b/packages/designto-flutter/make/make-flutter-color.ts @@ -1,24 +1,8 @@ import { Figma } from "@design-sdk/figma"; import { retrieveFill } from "@design-sdk/core/utils"; import * as flutter from "@flutter-builder/flutter"; -import * as core from "@reflect-ui/core"; import { converters } from "@reflect-ui/core/lib"; -/** - * this does not support named colors yet. - * @param color : Reflect#Color; - * @returns - */ -export function makeFlutterColorFromReflectColor( - color: core.Color -): flutter.Color { - const hex = converters.color.convertReflectColorToUniversal( - color, - core.ColorFormat.hex - ); - return flutter.Color.fromHex(hex); -} - /** * Retrieve the SOLID color for Flutter when existent, otherwise "" */ diff --git a/packages/designto-flutter/make/make-flutter-column-row.ts b/packages/designto-flutter/make/make-flutter-column-row.ts index a4988aa4..81f6ba26 100644 --- a/packages/designto-flutter/make/make-flutter-column-row.ts +++ b/packages/designto-flutter/make/make-flutter-column-row.ts @@ -1,12 +1,9 @@ import { ReflectFrameNode } from "@design-sdk/core"; import * as flutter from "@flutter-builder/flutter"; import { Axis as ReflectAxis } from "@reflect-ui/core/lib"; -import { - mapCrossAxisAlignment, - mapMainAxisAlignment, - mapMainAxisSize, -} from "../core-type-mappers"; -import { makeSafelyAsList } from "../utils/make-as-safe-list"; +import * as dartui from "../dart-ui"; +import * as rendering from "../rendering"; +import { makeSafelyAsList } from "../utils"; export type RowOrColumn = "Row" | "Column"; export function makeRowColumn( @@ -17,9 +14,17 @@ export function makeRowColumn( const rowOrColumn: RowOrColumn = node.layoutMode === ReflectAxis.horizontal ? "Row" : "Column"; - const _mainAxisAlignment = mapMainAxisAlignment(node.mainAxisAlignment); - const _mainAxisSize: flutter.MainAxisSize = mapMainAxisSize(node.layoutGrow); - const _crossAxisAlignment = mapCrossAxisAlignment(node.crossAxisAlignment); + const _mainAxisAlignment = rendering.mainAxisAlignment( + node.mainAxisAlignment + ); + const _mainAxisSize: flutter.MainAxisSize = rendering.mainAxisSize( + // FIXME: + // @ts-ignore + node.layoutGrow + ); + const _crossAxisAlignment = rendering.crossAxisAlignment( + node.crossAxisAlignment + ); // safely make childeren as list type children = makeSafelyAsList(children); diff --git a/packages/designto-flutter/make/make-flutter-divider.ts b/packages/designto-flutter/make/make-flutter-divider.ts index 4ab3d27b..bf89c151 100644 --- a/packages/designto-flutter/make/make-flutter-divider.ts +++ b/packages/designto-flutter/make/make-flutter-divider.ts @@ -1,10 +1,8 @@ import { nodes } from "@design-sdk/core"; import * as core from "@reflect-ui/core"; import * as flutter from "@flutter-builder/flutter"; -import { - makeColor, - makeFlutterColorFromReflectColor, -} from "./make-flutter-color"; +import { makeColor } from "./make-flutter-color"; +import * as dartui from "../dart-ui"; /** * @@ -20,7 +18,7 @@ export function makeFlutterDivider( // Case 3 has no stroke, height > 0 && has fill return new flutter.Divider({ height: divider.height, - color: makeFlutterColorFromReflectColor(divider.color), + color: dartui.color(divider.color), thickness: divider.thickness, indent: divider.indent, endIndent: divider.endIndent, diff --git a/packages/designto-flutter/make/make-flutter-edge-insets.ts b/packages/designto-flutter/make/make-flutter-edge-insets.ts deleted file mode 100755 index 92ca982c..00000000 --- a/packages/designto-flutter/make/make-flutter-edge-insets.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { nodes, utils } from "@design-sdk/core"; -import * as flutter from "@flutter-builder/flutter"; - -// This must happen before Stack or after the Positioned, but not before. -export function makeEdgeInsets( - node: nodes.ReflectSceneNode -): flutter.EdgeInsetsGeometry { - if (!("layoutMode" in node)) { - return undefined; - } - - const padding = utils.commonPadding(node); - - if (!padding) { - return undefined; - } - - if ("all" in padding) { - return flutter.EdgeInsets.all(padding.all); - } - - // horizontal and vertical, as the default AutoLayout - if ( - padding.horizontal + padding.vertical !== 0 && - padding.top + padding.bottom + padding.left + padding.right === 0 - ) { - const propHorizontalPadding: number = - padding.horizontal > 0 ? padding.horizontal : undefined; - - const propVerticalPadding: number = - padding.vertical > 0 ? padding.vertical : undefined; - - return flutter.EdgeInsets.symmetric({ - horizontal: propHorizontalPadding, - vertical: propVerticalPadding, - }); - } - - // all padding to 0 does not require padding. - if ( - padding.left === 0 && - padding.right === 0 && - padding.top === 0 && - padding.bottom === 0 - ) { - return undefined; - } - - return flutter.EdgeInsets.only({ - left: getOnlyIfNotZero(padding.left || padding.horizontal), - right: getOnlyIfNotZero(padding.right || padding.horizontal), - top: getOnlyIfNotZero(padding.top || padding.vertical), - bottom: getOnlyIfNotZero(padding.bottom || padding.vertical), - }); -} - -function getOnlyIfNotZero(value): number { - if (value === 0) { - return; - } - return value; -} diff --git a/packages/designto-flutter/make/make-flutter-flat-button.ts b/packages/designto-flutter/make/make-flutter-flat-button.ts index a154572f..b18cc1fc 100644 --- a/packages/designto-flutter/make/make-flutter-flat-button.ts +++ b/packages/designto-flutter/make/make-flutter-flat-button.ts @@ -1,10 +1,11 @@ import { manifests } from "@reflect-ui/detection"; import * as flutter from "@flutter-builder/flutter"; import { makeColor } from "./make-flutter-color"; -import { makeBorderRadius } from "./make-flutter-border-radius"; -import { makeBorderSide } from "./make-flutter-border-side"; +import { borderRadius } from "../painting/painting-border-radius"; +import { borderside } from "../painting/painting-border-side"; import { makeDetectedIcon } from "./make-flutter-icon"; import { onPressed } from "../static-snippets"; +import { retrievePrimaryColor } from "@design-sdk/core/utils"; export function makeFlatButton(manifest: manifests.DetectedButtonManifest) { const text = new flutter.Text(manifest.text?.text); @@ -13,8 +14,11 @@ export function makeFlatButton(manifest: manifests.DetectedButtonManifest) { const minWidth = manifest.base.width; const height = manifest.base.height; const shape = new flutter.RoundedRectangleBorder({ - borderRadius: makeBorderRadius(manifest.base), - side: makeBorderSide(manifest.base), + borderRadius: borderRadius(manifest.base.cornerRadius), + side: borderside({ + color: retrievePrimaryColor(manifest.base.strokes), + width: manifest.base.strokeWeight, + }), }); if (manifest.icon) { diff --git a/packages/designto-flutter/make/make-flutter-icon.ts b/packages/designto-flutter/make/make-flutter-icon.ts index 260b8ec1..061f61ff 100644 --- a/packages/designto-flutter/make/make-flutter-icon.ts +++ b/packages/designto-flutter/make/make-flutter-icon.ts @@ -6,7 +6,7 @@ import { interpretFlutterMaterialIconData, } from "../interpreter/icon.interpreter"; import { Color, IconManifest, MdiConfig } from "@reflect-ui/core"; -import { makeFlutterColorFromReflectColor } from "./make-flutter-color"; +import * as dartui from "../dart-ui"; import { DetectedIconData } from "@reflect-ui/detection/lib/icon.detection"; type FlutterDynamicIconLike = flutter.Icon | flutter.Image; @@ -72,7 +72,7 @@ export function makeIcon({ }) { return new flutter.Icon(icon, { size: size, - color: makeFlutterColorFromReflectColor(color), + color: dartui.color(color), }); } diff --git a/packages/designto-flutter/make/make-flutter-shape-border.ts b/packages/designto-flutter/make/make-flutter-shape-border.ts index de432e84..0d69cb37 100644 --- a/packages/designto-flutter/make/make-flutter-shape-border.ts +++ b/packages/designto-flutter/make/make-flutter-shape-border.ts @@ -1,7 +1,7 @@ import * as flutter from "@flutter-builder/flutter"; -import { nodes } from "@design-sdk/core"; -import { makeBorderRadius } from "./make-flutter-border-radius"; +import * as painting from "../painting"; import { makeColor } from "./make-flutter-color"; +import { nodes } from "@design-sdk/core"; /** * [Flutter#ShapeBorder](https://api.flutter.dev/flutter/painting/ShapeBorder-class.html) @@ -16,7 +16,7 @@ export function makeShape( | nodes.ReflectFrameNode ): flutter.ShapeBorder { const strokeColor = makeColor(node.strokes); - const side: flutter.Border = + const side: flutter.BorderSide = strokeColor && node.strokeWeight > 0 ? new flutter.BorderSide({ width: node.strokeWeight, @@ -32,6 +32,6 @@ export function makeShape( return new flutter.RoundedRectangleBorder({ side: side, - borderRadius: makeBorderRadius(node), + borderRadius: painting.borderRadius(node.cornerRadius), }); } diff --git a/packages/designto-flutter/make/make-flutter-text-style.ts b/packages/designto-flutter/make/make-flutter-text-style.ts index 18fb8086..12e27b6c 100644 --- a/packages/designto-flutter/make/make-flutter-text-style.ts +++ b/packages/designto-flutter/make/make-flutter-text-style.ts @@ -1,9 +1,10 @@ import * as flutter from "@flutter-builder/flutter"; -import * as core from "@reflect-ui/core"; -import { nodes } from "@design-sdk/core"; -import { makeColor } from "."; +// import * as core from "@reflect-ui/core"; +// import { nodes } from "@design-sdk/core"; +// import { makeColor } from "."; import { TextStyleRepository } from "@design-sdk/figma"; -import { roundDouble } from "../convert"; +// import { roundDouble } from "../convert"; +// import { fontStyle, mapTextDecoration } from "../core-type-mappers"; /** * get the code of Text#style (text-style) via the name of the defined textstyle. @@ -17,27 +18,8 @@ function getThemedTextStyleByName(textStyleName: string): flutter.TextStyle { return flutter.Theme.of().textTheme[styleDef] as flutter.TextStyle; } -export function makeTextStyleFromDesign( - style: core.ITextStyle -): flutter.TextStyle { - let decoration: flutter.TextDecoration = makeTextDecoration(style.decoration); - const fontFamily: string = style.fontFamily; - const fontWeight: flutter.FontWeight = flutter.FontWeight[style.fontWeight]; - // percentage is not supported - const letterSpacing = style.letterSpacing; - const fontStyle = makeFontStyle(style.fontStyle); - - return new flutter.TextStyle({ - fontSize: style.fontSize, - fontWeight: fontWeight, - fontFamily: fontFamily, - fontStyle: fontStyle, - letterSpacing: letterSpacing, - decoration: decoration, - }); -} - // TODO lineSpacing +/* - use tokenized text manifest instead. export function makeTextStyle(node: nodes.ReflectTextNode): flutter.TextStyle { const fontColor: flutter.Color = makeColor(node.fills); @@ -46,10 +28,10 @@ export function makeTextStyle(node: nodes.ReflectTextNode): flutter.TextStyle { fontSize = node.fontSize; } - const decoration: flutter.TextDecoration = makeTextDecoration( + const decoration: flutter.TextDecoration = mapTextDecoration( node.textStyle.decoration ); - let fontStyle: flutter.FontStyle = makeFontStyle(node.textStyle.fontStyle); + let fontStyle: flutter.FontStyle = mapFontStyle(node.textStyle.fontStyle); let fontFamily: string; if (node.textStyle) { @@ -88,25 +70,4 @@ export function makeTextStyle(node: nodes.ReflectTextNode): flutter.TextStyle { decoration: decoration, }); } - -export function makeFontStyle(style: core.FontStyle): flutter.FontStyle { - switch (style) { - case core.FontStyle.italic: - return flutter.FontStyle.italic as flutter.Snippet; - case core.FontStyle.normal: - return; // not returning any value, since normal is a default value. - } -} - -export function makeTextDecoration( - textDecoration: core.TextDecoration -): flutter.TextDecoration { - if (!textDecoration) { - return; - } - let decoration: flutter.TextDecoration; - if (textDecoration === core.TextDecoration.underline) { - decoration = flutter.TextDecoration.underline as flutter.Snippet; - } - return decoration; -} + */ diff --git a/packages/designto-flutter/make/make-flutter-text-theme.ts b/packages/designto-flutter/make/make-flutter-text-theme.ts index a6f1da66..effb0eb7 100644 --- a/packages/designto-flutter/make/make-flutter-text-theme.ts +++ b/packages/designto-flutter/make/make-flutter-text-theme.ts @@ -1,11 +1,11 @@ import { TextStyleRepository, TextThemeStyles } from "@design-sdk/figma"; import * as flutter from "@flutter-builder/flutter"; -import { makeTextStyleFromDesign } from "./make-flutter-text-style"; +import * as painting from "../painting"; export function makeTextTheme(): flutter.TextTheme { function buildTextStyle(style: TextThemeStyles): flutter.TextStyle { try { - return makeTextStyleFromDesign( + return painting.textStyle( TextStyleRepository.getDefaultDesignTextStyleFromRegistry(style) ); } catch (e) { diff --git a/packages/designto-flutter/make/make-flutter-text.ts b/packages/designto-flutter/make/make-flutter-text.ts index e1e4bc3a..30fe30c7 100644 --- a/packages/designto-flutter/make/make-flutter-text.ts +++ b/packages/designto-flutter/make/make-flutter-text.ts @@ -1,8 +1,8 @@ import * as flutter from "@flutter-builder/flutter"; import { nodes } from "@design-sdk/figma"; -import { makeTextStyle } from "./make-flutter-text-style"; -import { mapTextAlign } from "../core-type-mappers"; import { escapeDartString } from "@coli.codes/escape-string"; +import * as dartui from "../dart-ui"; +import * as painting from "../painting"; /** * [Flutter#Text](https://flutter.dev/docs/development/ui/widgets/text) @@ -11,7 +11,7 @@ import { escapeDartString } from "@coli.codes/escape-string"; * @param node text node from desing */ export function makeText(node: nodes.ReflectTextNode): flutter.Text { - const textAlign = mapTextAlign(node.textAlign); + const textAlign = dartui.textAlign(node.textAlign); //#region get text content let text = node.text; @@ -28,13 +28,14 @@ export function makeText(node: nodes.ReflectTextNode): flutter.Text { case "ORIGINAL": break; } - //#endregion + const escapedText = escapeDartString(text); - const textStyle = makeTextStyle(node); + // throw "not used"; + // const textStyle = painting.textStyle(node); return new flutter.Text(escapedText, { - style: textStyle, + // style: textStyle, textAlign: textAlign, }); } diff --git a/packages/designto-flutter/painting/index.ts b/packages/designto-flutter/painting/index.ts new file mode 100644 index 00000000..0dcb3e0a --- /dev/null +++ b/packages/designto-flutter/painting/index.ts @@ -0,0 +1,13 @@ +export * from "./painting-alignment"; +export * from "./painting-border-radius"; +export * from "./painting-edge-insets"; +export * from "./painting-box-shape"; +export * from "./painting-box-shadow"; +export * from "./painting-border"; +export * from "./painting-border-side"; +export * from "./painting-text-style"; +export * from "./painting-text-decoration"; +export * from "./painting-font-style"; +export * from "./painting-linear-gradient"; +export * from "./painting-vertical-direction"; +export * from "./painting-box-decoration"; diff --git a/packages/designto-flutter/core-type-mappers/alignment.mapper.ts b/packages/designto-flutter/painting/painting-alignment.ts similarity index 93% rename from packages/designto-flutter/core-type-mappers/alignment.mapper.ts rename to packages/designto-flutter/painting/painting-alignment.ts index f153708a..1019dfe5 100644 --- a/packages/designto-flutter/core-type-mappers/alignment.mapper.ts +++ b/packages/designto-flutter/painting/painting-alignment.ts @@ -1,7 +1,7 @@ import { Alignment } from "@reflect-ui/core"; import * as flutter from "@flutter-builder/flutter"; -export function mapAlignment(a: Alignment): flutter.Alignment { +export function alignment(a: Alignment): flutter.Alignment { // TODO: Alignemt value comparison won't work. (not tested) switch (a) { case Alignment.center: { diff --git a/packages/designto-flutter/painting/painting-border-radius.ts b/packages/designto-flutter/painting/painting-border-radius.ts new file mode 100644 index 00000000..ad07c063 --- /dev/null +++ b/packages/designto-flutter/painting/painting-border-radius.ts @@ -0,0 +1,35 @@ +import * as flutter from "@flutter-builder/flutter"; +import { roundNumber } from "@reflect-ui/uiutils"; +import * as dartui from "../dart-ui"; +import { BorderRadius, BorderRadiusManifest } from "@reflect-ui/core"; + +export function borderRadius(br: BorderRadius): flutter.BorderRadiusGeometry { + if (br === undefined || br.all === 0) { + return undefined; + } + + return br.all !== undefined + ? flutter.BorderRadius.circular(roundNumber(br.all as number)) + : _partialBorderRadius(br); +} + +function _partialBorderRadius(cornerRadius: BorderRadiusManifest) { + const _oneofRadiusIsHasValue = [ + cornerRadius.tl, + cornerRadius.tr, + cornerRadius.bl, + cornerRadius.br, + ].some((i) => i !== undefined); + + if (_oneofRadiusIsHasValue) { + return flutter.BorderRadius.only({ + topLeft: dartui.radius(cornerRadius.tl), + topRight: dartui.radius(cornerRadius.tr), + bottomLeft: dartui.radius(cornerRadius.bl), + bottomRight: dartui.radius(cornerRadius.br), + }); + } else { + // if none of each corner radius contains value (if every value is empty) do not return a value. + return; + } +} diff --git a/packages/designto-flutter/painting/painting-border-side.ts b/packages/designto-flutter/painting/painting-border-side.ts new file mode 100644 index 00000000..d7fbdb10 --- /dev/null +++ b/packages/designto-flutter/painting/painting-border-side.ts @@ -0,0 +1,11 @@ +import * as flutter from "@flutter-builder/flutter"; +import type { Color } from "@reflect-ui/core"; +import { roundNumber } from "@reflect-ui/uiutils"; +import * as dartui from "../dart-ui"; + +export function borderside({ color, width }: { color: Color; width: number }) { + return new flutter.BorderSide({ + color: dartui.color(color), + width: roundNumber(width), + }); +} diff --git a/packages/designto-flutter/painting/painting-border.ts b/packages/designto-flutter/painting/painting-border.ts new file mode 100644 index 00000000..94ece86b --- /dev/null +++ b/packages/designto-flutter/painting/painting-border.ts @@ -0,0 +1,41 @@ +import * as flutter from "@flutter-builder/flutter"; +import { Border } from "@reflect-ui/core"; +import { borderside } from "./painting-border-side"; + +// generate the border, when it exists +export function border(border: Border): flutter.Border { + if (!border) { + return; + } + + if ( + // at least one side of the border's width shoul be higher than 0. + border.left?.width || + border.right?.width || + border.top?.width || + border.bottom?.width + ) { + return new flutter.Border({ + left: border.left ? borderside(border.left) : flutter.BorderSide.none, + top: border.top ? borderside(border.top) : flutter.BorderSide.none, + right: border.right ? borderside(border.right) : flutter.BorderSide.none, + bottom: border.bottom + ? borderside(border.bottom) + : flutter.BorderSide.none, + }); + } + + // TODO: support shorthands. + // 1. flutter.Border.all + // 2. flutter.Border.fromBorderSide + // 3. flutter.Border.symmetric + + // border. + // generate the border, when it should exist + // return node.strokeWeight + // ? flutter.Border.all({ + // color: makeColor(node.strokes), + // width: roundNumber(node.strokeWeight), + // }) + // : undefined; +} diff --git a/packages/designto-flutter/painting/painting-box-decoration.ts b/packages/designto-flutter/painting/painting-box-decoration.ts new file mode 100644 index 00000000..6fcf9b5f --- /dev/null +++ b/packages/designto-flutter/painting/painting-box-decoration.ts @@ -0,0 +1,49 @@ +import { BoxDecoration } from "@flutter-builder/flutter"; +import { Color } from "@reflect-ui/core"; +import { Background } from "@reflect-ui/core/lib/background"; +import * as dartui from "../dart-ui"; + +function fromColor(color: Color): BoxDecoration { + return new BoxDecoration({ + color: dartui.color(color), + }); +} + +function fromBackground(b: Background): BoxDecoration { + if (!b) { + return; + } + + if (Array.isArray(b)) { + throw "multiple bg not supported"; + } else { + switch (b.type) { + case "gradient": { + console.error("gradient bg not ready"); + break; + } + case "graphics": { + console.error("graphics bg not ready"); + break; + } + case "solid-color": { + return fromColor(b as Color); + } + } + } +} + +function fromGradient(): BoxDecoration { + throw "not ready."; +} + +function fromImage(): BoxDecoration { + throw "not ready."; +} + +export const boxDecorationPart = { + fromColor: fromColor, + fromBackground: fromBackground, + fromGradient: fromGradient, + fromImage: fromImage, +}; diff --git a/packages/designto-flutter/painting/painting-box-shadow.ts b/packages/designto-flutter/painting/painting-box-shadow.ts new file mode 100644 index 00000000..5c77dee8 --- /dev/null +++ b/packages/designto-flutter/painting/painting-box-shadow.ts @@ -0,0 +1,50 @@ +import * as flutter from "@flutter-builder/flutter"; +import * as dartui from "../dart-ui"; +import { roundNumber } from "@reflect-ui/uiutils"; +import { BoxShadowManifest } from "@reflect-ui/core"; + +export function boxShadow( + shadows: ReadonlyArray +): Array { + // if no shadow filtered available, return undefined + if (shadows.length == 0) { + return undefined; + } + + const boxShadows: Array = shadows.map( + (d: BoxShadowManifest) => { + return new flutter.BoxShadow({ + color: dartui.color(d.color), + blurRadius: requiredNumber(d.blurRadius), + spreadRadius: requiredNumber(d.spreadRadius), + offset: dartui.offset(d.offset), + }); + + // if (d.type == "INNER_SHADOW") { + // handling inner shadow + // https://stackoverflow.com/a/55096682/5463235 + // inner shadow disabled. + // -------------------------------- + // return new flutter.BoxShadow({ + // color: makeColorFromRGBO(d.color, d.color.a), + // blurRadius: requiredNumber(d.radius), + // // multiply -1 * blur for spread + // // TODO inspect this logic again. + // spreadRadius: requiredNumber((d.spread + d.radius) * -1), + // offset: requiredOffset(new flutter.Offset(d.offset.x, d.offset.y)), + // }); + // } + } + ); + + // return undefined if array is empty, since it's not needed. + return boxShadows.length > 0 ? boxShadows : undefined; +} + +function requiredNumber(number: number): number { + const rounded = roundNumber(number); + if (rounded == 0) { + return undefined; + } + return rounded; +} diff --git a/packages/designto-flutter/painting/painting-box-shape.ts b/packages/designto-flutter/painting/painting-box-shape.ts new file mode 100644 index 00000000..cf15d893 --- /dev/null +++ b/packages/designto-flutter/painting/painting-box-shape.ts @@ -0,0 +1,17 @@ +import { BoxShape } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function boxshape(shape: BoxShape): flutter.BoxShape { + if (!shape) { + return; + } + + switch (shape) { + case BoxShape.circle: + return flutter.BoxShape.circle as flutter.BoxShape; + case BoxShape.rectangle: + return flutter.BoxShape.rectangle as flutter.BoxShape; + default: + throw new Error("Unknown BoxShape: " + shape); + } +} diff --git a/packages/designto-flutter/painting/painting-edge-insets.ts b/packages/designto-flutter/painting/painting-edge-insets.ts new file mode 100644 index 00000000..dd7251d1 --- /dev/null +++ b/packages/designto-flutter/painting/painting-edge-insets.ts @@ -0,0 +1,49 @@ +import { EdgeInsets } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function edgeinsets(ei: EdgeInsets): flutter.EdgeInsets { + if (!ei) { + return undefined; + } + + if ("all" in ei) { + // missing usage of commonPadding(); + return flutter.EdgeInsets.all((ei as any).all); + } + + // horizontal and vertical, as the default AutoLayout + if ( + ei.horizontal + ei.vertical !== 0 && + ei.top + ei.bottom + ei.left + ei.right === 0 + ) { + const propHorizontalPadding: number = + ei.horizontal > 0 ? ei.horizontal : undefined; + + const propVerticalPadding: number = + ei.vertical > 0 ? ei.vertical : undefined; + + return flutter.EdgeInsets.symmetric({ + horizontal: propHorizontalPadding, + vertical: propVerticalPadding, + }); + } + + // all padding to 0 does not require padding. + if (ei.left === 0 && ei.right === 0 && ei.top === 0 && ei.bottom === 0) { + return undefined; + } + + return flutter.EdgeInsets.only({ + left: getOnlyIfNotZero(ei.left || ei.horizontal), + right: getOnlyIfNotZero(ei.right || ei.horizontal), + top: getOnlyIfNotZero(ei.top || ei.vertical), + bottom: getOnlyIfNotZero(ei.bottom || ei.vertical), + }); +} + +function getOnlyIfNotZero(value): number { + if (value === 0) { + return; + } + return value; +} diff --git a/packages/designto-flutter/painting/painting-font-style.ts b/packages/designto-flutter/painting/painting-font-style.ts new file mode 100644 index 00000000..cea909a1 --- /dev/null +++ b/packages/designto-flutter/painting/painting-font-style.ts @@ -0,0 +1,11 @@ +import { FontStyle } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function fontStyle(style: FontStyle): flutter.FontStyle { + switch (style) { + case FontStyle.italic: + return flutter.FontStyle.italic as flutter.Snippet; + case FontStyle.normal: + return; // not returning any value, since normal is a default value. + } +} diff --git a/packages/designto-flutter/painting/painting-linear-gradient.ts b/packages/designto-flutter/painting/painting-linear-gradient.ts new file mode 100644 index 00000000..fcc4cb28 --- /dev/null +++ b/packages/designto-flutter/painting/painting-linear-gradient.ts @@ -0,0 +1,13 @@ +import { LinearGradient } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; +import { alignment } from "./painting-alignment"; +import * as dartui from "../dart-ui"; + +export function linearGradient(g: LinearGradient): flutter.LinearGradient { + return new flutter.LinearGradient({ + begin: alignment(g.begin), + end: alignment(g.end), + colors: g.colors.map((c) => dartui.color(c)), + stops: g.stops, + }); +} diff --git a/packages/designto-flutter/painting/painting-text-decoration.ts b/packages/designto-flutter/painting/painting-text-decoration.ts new file mode 100644 index 00000000..d7bbcc02 --- /dev/null +++ b/packages/designto-flutter/painting/painting-text-decoration.ts @@ -0,0 +1,15 @@ +import { TextDecoration } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function textDecoration( + textDecoration: TextDecoration +): flutter.TextDecoration { + if (!textDecoration) { + return; + } + let decoration: flutter.TextDecoration; + if (textDecoration === TextDecoration.underline) { + decoration = flutter.TextDecoration.underline as flutter.Snippet; + } + return decoration; +} diff --git a/packages/designto-flutter/painting/painting-text-style.ts b/packages/designto-flutter/painting/painting-text-style.ts new file mode 100644 index 00000000..577a22ad --- /dev/null +++ b/packages/designto-flutter/painting/painting-text-style.ts @@ -0,0 +1,21 @@ +import type { ITextStyle } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; +import { textDecoration } from "./painting-text-decoration"; +import { fontStyle } from "./painting-font-style"; +import * as dartui from "../dart-ui"; + +export function textStyle(style: ITextStyle): flutter.TextStyle { + const { fontFamily, letterSpacing } = style; + let decoration: flutter.TextDecoration = textDecoration(style.decoration); + const fontWeight: flutter.FontWeight = flutter.FontWeight[style.fontWeight]; + + return new flutter.TextStyle({ + fontSize: style.fontSize, + fontWeight: fontWeight, + fontFamily: fontFamily, + color: dartui.color(style.color), + fontStyle: fontStyle(style.fontStyle), + letterSpacing: letterSpacing, // percentage is not supported + decoration: decoration, + }); +} diff --git a/packages/designto-flutter/painting/painting-vertical-direction.ts b/packages/designto-flutter/painting/painting-vertical-direction.ts new file mode 100644 index 00000000..3fc48ea2 --- /dev/null +++ b/packages/designto-flutter/painting/painting-vertical-direction.ts @@ -0,0 +1,14 @@ +import { VerticalDirection } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function verticalDirection( + vd: VerticalDirection +): flutter.VerticalDirection { + switch (vd) { + case VerticalDirection.up: + return flutter.VerticalDirection.up as flutter.VerticalDirection; + case VerticalDirection.down: + return flutter.VerticalDirection.down as flutter.VerticalDirection; + } + throw new Error(`unknown vertical direction ${vd}`); +} diff --git a/packages/designto-flutter/rendering/index.ts b/packages/designto-flutter/rendering/index.ts new file mode 100644 index 00000000..41e9e480 --- /dev/null +++ b/packages/designto-flutter/rendering/index.ts @@ -0,0 +1,3 @@ +export * from "./rendering-cross-axis-alignment"; +export * from "./rendering-main-axis-alignment"; +export * from "./rendering-main-axis-size"; diff --git a/packages/designto-flutter/core-type-mappers/cross-axis-alignment.mapper.ts b/packages/designto-flutter/rendering/rendering-cross-axis-alignment.ts similarity index 94% rename from packages/designto-flutter/core-type-mappers/cross-axis-alignment.mapper.ts rename to packages/designto-flutter/rendering/rendering-cross-axis-alignment.ts index def38069..09c1efd9 100644 --- a/packages/designto-flutter/core-type-mappers/cross-axis-alignment.mapper.ts +++ b/packages/designto-flutter/rendering/rendering-cross-axis-alignment.ts @@ -4,7 +4,7 @@ import * as core from "@reflect-ui/core/lib"; * returns CrossAxisAlignment by counterAxisAlignItems * @param crossAxisAlignItems */ -export function mapCrossAxisAlignment( +export function crossAxisAlignment( crossAxisAlignItems: core.CrossAxisAlignment ): CrossAxisAlignment { switch (crossAxisAlignItems) { diff --git a/packages/designto-flutter/core-type-mappers/main-axis-alignment.mapper.ts b/packages/designto-flutter/rendering/rendering-main-axis-alignment.ts similarity index 94% rename from packages/designto-flutter/core-type-mappers/main-axis-alignment.mapper.ts rename to packages/designto-flutter/rendering/rendering-main-axis-alignment.ts index 882f33eb..d3a63a96 100644 --- a/packages/designto-flutter/core-type-mappers/main-axis-alignment.mapper.ts +++ b/packages/designto-flutter/rendering/rendering-main-axis-alignment.ts @@ -1,7 +1,7 @@ import { MainAxisAlignment, Snippet } from "@flutter-builder/flutter"; import { MainAxisAlignment as ReflectMainAxisAlignment } from "@reflect-ui/core/lib"; -export function mapMainAxisAlignment( +export function mainAxisAlignment( mainAxisAlignemt: ReflectMainAxisAlignment ): MainAxisAlignment { switch (mainAxisAlignemt) { diff --git a/packages/designto-flutter/rendering/rendering-main-axis-size.ts b/packages/designto-flutter/rendering/rendering-main-axis-size.ts new file mode 100644 index 00000000..be6f5371 --- /dev/null +++ b/packages/designto-flutter/rendering/rendering-main-axis-size.ts @@ -0,0 +1,11 @@ +import { MainAxisSize } from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; + +export function mainAxisSize(m: MainAxisSize): flutter.MainAxisSize { + switch (m) { + case MainAxisSize.max: + return flutter.MainAxisSize.max; + case MainAxisSize.min: + return flutter.MainAxisSize.min; + } +} diff --git a/packages/designto-flutter/tokens-to-flutter-widget/index.ts b/packages/designto-flutter/tokens-to-flutter-widget/index.ts new file mode 100644 index 00000000..077dab0f --- /dev/null +++ b/packages/designto-flutter/tokens-to-flutter-widget/index.ts @@ -0,0 +1,373 @@ +import * as core from "@reflect-ui/core"; +import * as flutter from "@flutter-builder/flutter"; +import * as painting from "../painting"; +import * as rendering from "../rendering"; +import * as dartui from "../dart-ui"; +import { tokens as special, t2t } from "@designto/token"; +import { MainImageRepository } from "@design-sdk/core/assets-repository"; +import { Axis, BoxShape } from "@reflect-ui/core"; +import { escapeDartString } from "@coli.codes/escape-string"; +import { boxDecorationPart } from "../painting"; +import { + flutter_handle_svg_vector_as_bitmap_converted, + handle_flutter_case_nested_positioned_stack, + handle_flutter_case_no_size_stack_children, +} from "../case-handling"; + +export function buildFlutterWidgetFromTokens( + widget: core.Widget +): flutter.Widget { + const composed = compose(widget, { + is_root: true, + }); + + if (process.env.NODE_ENV === "development") { + console.info("dev::", "final flutter token composed", composed); + } + + return composed; +} + +function compose(widget: core.Widget, context: { is_root: boolean }) { + const handleChildren = (children: core.Widget[]): flutter.Widget[] => { + return children?.map((c) => { + return handleChild(c); + }); + }; + + const handleChild = (child: core.Widget): flutter.Widget => { + return compose(child, { ...context, is_root: false }); + }; + + const _remove_width_height_if_root_wh = { + width: context.is_root ? undefined : widget.width, + height: context.is_root ? undefined : widget.height, + }; + + const default_props_for_layout = { + ...widget, + ..._remove_width_height_if_root_wh, + }; + + const flex_props = (f: core.Flex) => { + return { + mainAxisAlignment: rendering.mainAxisAlignment(f.mainAxisAlignment), + crossAxisAlignment: rendering.crossAxisAlignment(f.crossAxisAlignment), + mainAxisSize: rendering.mainAxisSize(f.mainAxisSize), + verticalDirection: painting.verticalDirection(f.verticalDirection), + }; + }; + // const _key = keyFromWidget(widget); + + let thisFlutterWidget: flutter.Widget; + if (widget instanceof core.Column) { + const children = compose_item_spacing_children(widget.children, { + itemspacing: widget.itemSpacing, + axis: widget.direction, + }); + thisFlutterWidget = new flutter.Column({ + ...default_props_for_layout, + ...flex_props(widget), + children: children, + // key: _key, + }); + } else if (widget instanceof core.Row) { + const children = compose_item_spacing_children(widget.children, { + itemspacing: widget.itemSpacing, + axis: widget.direction, + }); + thisFlutterWidget = new flutter.Row({ + ...default_props_for_layout, + ...flex_props(widget), + children: children, + // key: _key, + }); + } else if (widget instanceof core.Flex) { + // FIXME: FLEX not supported yet. + // thisFlutterWidget = new flutter.Flex({ + // // direction: widget.direction, + // // ...widget, + // // ...default_props_for_layout, + // children: handleChildren(widget.children), + // // key: _key, + // }); + } else if (widget instanceof core.Stack) { + const _remove_overflow_if_root_overflow = { + clipBehavior: context.is_root + ? undefined + : (widget as core.Stack).clipBehavior, + }; + + const children = handle_flutter_case_no_size_stack_children( + handleChildren(widget.children as []) + ); + const stack = new flutter.Stack({ + ...default_props_for_layout, + ..._remove_overflow_if_root_overflow, + children: children, + // key: _key, + }); + if (!context.is_root) { + thisFlutterWidget = handle_flutter_case_nested_positioned_stack(stack); + } else { + thisFlutterWidget = stack; + } + } else if (widget instanceof core.SingleChildScrollView) { + const _child = handleChild(widget.child); + thisFlutterWidget = new flutter.SingleChildScrollView({ + // TODO: map axis + // scrollDirection: widget.direction, + child: _child, + }); + } else if (widget instanceof core.Positioned) { + const _tmp_length_convert = (l) => { + return l as number; + }; + + const _child = handleChild(widget.child); + if (_child) { + thisFlutterWidget = new flutter.Positioned({ + left: widget.left && _tmp_length_convert(widget.left), + right: widget.right && _tmp_length_convert(widget.right), + top: widget.top && _tmp_length_convert(widget.top), + bottom: widget.bottom && _tmp_length_convert(widget.bottom), + child: _child, + }); + // ------------------------------------- + // override w & h with position provided w/h + if (_child instanceof flutter.Container) { + _child.width = widget.width; + _child.height = widget.height; + } + // ------------------------------------- + } + } else if (widget instanceof core.Opacity) { + thisFlutterWidget = new flutter.Opacity({ + opacity: widget.opacity, + child: handleChild(widget.child), + }); + } + // ----- region clip path ------ + else if (widget instanceof core.ClipRRect) { + // FIXME: flutter clip rrect support is not ready. + thisFlutterWidget = handleChild(widget.child); + } else if (widget instanceof core.ClipPath) { + // FIXME: flutter clip path support is not ready. + thisFlutterWidget = handleChild(widget.child); + } + // ----- endregion clip path ------ + else if (widget instanceof core.Text) { + const _escaped_dart_string = escapeDartString(widget.data); + thisFlutterWidget = new flutter.Text(_escaped_dart_string, { + ...widget, + style: painting.textStyle(widget.style), + /** explicit assignment - field name is different */ + // key: _key, + }); + } else if (widget instanceof core.VectorWidget) { + const id = widget.key.id; + // use widget as baked image. + thisFlutterWidget = handleChild(t2t.vector_token_to_image_token(widget)); + // TODO: convert vector data to bitmap, host the image, than load. + // thisFlutterWidget = flutter_handle_svg_vector_as_bitmap_converted(widget); + } else if (widget instanceof core.ImageWidget) { + thisFlutterWidget = flutter.Image.network(widget.src, { + width: widget.width, + height: widget.height, + // fit?: BoxFit; + // key: _key, + }); + } else if (widget instanceof core.IconWidget) { + // TODO: not ready - svg & named icon not supported + + switch ((widget.icon as core.IconData)._type) { + case "named-icon": { + const _tmp_icon_as_img = MainImageRepository.instance + .get("fill-later-assets") + .addImage({ + key: widget.key.id, + }); + + thisFlutterWidget = flutter.Image.network( + _tmp_icon_as_img.url || + /*fallback*/ "https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png", // TODO: change this + { + width: widget.width, + height: widget.height, + // fit?: BoxFit; + // key: _key, + } + ); + break; + } + case "remote-uri": { + thisFlutterWidget = flutter.Image.network(widget.icon.uri, { + width: widget.size, + height: widget.size, + semanticLabel: "icon", + // key: _key, + }); + break; + } + } + } + + // execution order matters - some above widgets inherits from Container, this shall be handled at the last. + else if (widget instanceof core.Container) { + // flutter cannot set both shape circle & border radius. + let _deco_part_shape_and_border_radius = {}; + if (widget.shape == BoxShape.circle) { + _deco_part_shape_and_border_radius = { + shape: painting.boxshape(widget.shape), + borderRadius: undefined, + }; + } else { + _deco_part_shape_and_border_radius = { + borderRadius: painting.borderRadius(widget.borderRadius), + shape: painting.boxshape(widget.shape), + }; + } + + const _deco_part_bg = boxDecorationPart.fromBackground(widget.background); + + thisFlutterWidget = new flutter.Container({ + padding: painting.edgeinsets(widget.padding), + margin: painting.edgeinsets(widget.margin), + width: widget.width, + height: widget.height, + decoration: new flutter.BoxDecoration({ + border: painting.border(widget.border), + ..._deco_part_shape_and_border_radius, + ..._deco_part_bg, + // TODO: + // boxShadow: + // background: + }), + // key: _key, + }); + // thisFlutterWidget.x = widget.x; + // thisFlutterWidget.y = widget.y; + // thisFlutterWidget.background = widget.background; + } + + // ------------------------------------- + // special tokens + // ------------------------------------- + else if (widget instanceof special.Stretched) { + let remove_size; + switch (widget.axis) { + case Axis.horizontal: + remove_size = "height"; + break; + case Axis.vertical: + remove_size = "width"; + break; + } + + thisFlutterWidget = handleChild(widget.child); + thisFlutterWidget = wrap_with_sized_and_inject_size(thisFlutterWidget, { + [remove_size]: undefined, // Double.infinity, + }); + } + // ------------------------------------- + + // ------------------------------------- + // end of logic gate + // ------------------------------------- + else { + // todo - handle case more specific + thisFlutterWidget = new flutter.ErrorWidget.withDetails({ + // key: _key, + message: `The input design was not handled. "${ + widget.key.originName + }" type of "${widget._type}" - ${JSON.stringify(widget.key)}`, + }); + } + // ------------------------------------- + // ------------------------------------- + + // post extending - do not abuse this + if (context.is_root) { + // TODO: add overflow x hide handling by case. + } + + return thisFlutterWidget; +} + +/** + * children under col / row with item spacing on each between with sizedbox. + * ``` + * s = SizedBox() + * | 1 | 2 | 3 | 4 | + * | 1 | s | 2 | s | 3 | s | 4 + * ``` + */ +function compose_item_spacing_children( + children: core.Widget[], + args: { + itemspacing: number; + axis: Axis; + } +) { + let injection = undefined; + if (args.itemspacing) { + const wh = args.axis === Axis.horizontal ? "width" : "height"; + injection = new flutter.SizedBox({ + [wh]: args.itemspacing, + }); + } + return compoes_children_with_injection(children, injection); +} + +/** + * children under col / row with item spacing on each between. + * ``` + * | 1 | 2 | 3 | 4 | + * | 1 | x | 2 | x | 3 | x | 4 + * ``` + */ +function compoes_children_with_injection( + children: core.Widget[], + between?: flutter.Widget +): flutter.Widget[] { + const composedchildren = children.map((c) => { + return compose(c, { + is_root: false, + }); + }); + + const result: flutter.Widget[] = []; + composedchildren.forEach((c, i) => { + result.push(c); + if (between) { + if (i !== children.length - 1) { + result.push(between); + } + } + }); + return result; + // const result = array.reduce((r, a) => r.concat(a, 0), [0]); +} + +function wrap_with_sized_and_inject_size( + widget: flutter.Widget, + size: { + width?: flutter.double; + height?: flutter.double; + } +) { + if ( + widget instanceof flutter.Container || + widget instanceof flutter.SizedBox + ) { + size.width && (widget.width = size.width); + size.height && (widget.height = size.height); + return widget; + } else { + return new flutter.SizedBox({ + child: widget, + width: size.width, + height: size.height, + }); + } +} diff --git a/packages/designto-flutter/wrappers/container.wrap.ts b/packages/designto-flutter/wrappers/container.wrap.ts index 41af7abb..086e23b9 100755 --- a/packages/designto-flutter/wrappers/container.wrap.ts +++ b/packages/designto-flutter/wrappers/container.wrap.ts @@ -1,10 +1,10 @@ -import { convertToSize } from "../convert"; +import { convertToSize } from "../_utils"; import { nodes } from "@design-sdk/core"; -import { makeEdgeInsets } from "../make"; import { array } from "@reflect-ui/uiutils"; import * as flutter from "@flutter-builder/flutter"; import { makeBoxDecoration } from "../make/make-flutter-box-decoration"; -import { roundDouble } from "../convert/double.convert"; +import { roundDouble } from "../_utils"; +import * as painting from "../painting"; export function wrapWithContainer( node: @@ -38,7 +38,7 @@ export function wrapWithContainer( let _padding: flutter.EdgeInsetsGeometry; if (node instanceof nodes.ReflectFrameNode) { - _padding = makeEdgeInsets(node); + _padding = painting.edgeinsets(node.padding); } // Container is a container if [_size] or [_boxDecoration] are set. diff --git a/packages/designto-flutter/wrappers/material.wrap.ts b/packages/designto-flutter/wrappers/material.wrap.ts index 3137455b..291b19a1 100755 --- a/packages/designto-flutter/wrappers/material.wrap.ts +++ b/packages/designto-flutter/wrappers/material.wrap.ts @@ -1,10 +1,10 @@ -import { convertToSize } from "../convert/size.convert"; +import { convertToSize } from "../_utils"; import { Figma, nodes } from "@design-sdk/figma"; import { converters } from "@reflect-ui/core/lib"; import * as flutter from "@flutter-builder/flutter"; -import { makeColor } from "../make/color.make"; -import { makeShape as makeShape } from "../make/shape.make"; -import { makeBorderRadius } from "../make/border-radius.make"; +// import { makeColor } from "../make/color.make"; +// import { makeShape as makeShape } from "../make/shape.make"; +// import { makeBorderRadius } from "../make/border-radius.make"; import { wrapWithPadding } from "./padding.wrap"; // https://api.flutter.dev/flutter/material/Material-class.html diff --git a/packages/designto-flutter/wrappers/positioned.wrap.ts b/packages/designto-flutter/wrappers/positioned.wrap.ts index 095bf391..9e78879d 100755 --- a/packages/designto-flutter/wrappers/positioned.wrap.ts +++ b/packages/designto-flutter/wrappers/positioned.wrap.ts @@ -1,7 +1,7 @@ import * as flutter from "@flutter-builder/flutter"; import { utils, nodes } from "@design-sdk/core"; import { roundNumber } from "@reflect-ui/uiutils"; -import { makeSaflyAsSingle } from "../utils/make-as-safe-single"; +import { makeSaflyAsSingle } from "../_utils"; export function wrapWithPositioned( node: nodes.ReflectSceneNode, child: flutter.Widget, diff --git a/packages/designto-token/index.ts b/packages/designto-token/index.ts index 17f30341..2da72e3a 100644 --- a/packages/designto-token/index.ts +++ b/packages/designto-token/index.ts @@ -15,3 +15,6 @@ export { tokenizeMasking } from "./token-masking"; // simple atomics export * from "./token-gradient"; + +// t2t +export * as t2t from "./token-to-token"; diff --git a/packages/designto-token/token-border/index.ts b/packages/designto-token/token-border/index.ts index 74d78258..de638e7e 100644 --- a/packages/designto-token/token-border/index.ts +++ b/packages/designto-token/token-border/index.ts @@ -9,9 +9,11 @@ function fromNode(node: ReflectSceneNode) { if (!node.strokes || node.strokes.length === 0) { return undefined; } - return fromStrokes(node.strokes, { - width: node.strokeWeight, - }); + if ("strokeWeight" in node) { + return fromStrokes(node.strokes, { + width: node.strokeWeight, + }); + } } } diff --git a/packages/designto-token/token-to-token/README.md b/packages/designto-token/token-to-token/README.md new file mode 100644 index 00000000..3958e9f3 --- /dev/null +++ b/packages/designto-token/token-to-token/README.md @@ -0,0 +1 @@ +# Post tokenization token to token conversions diff --git a/packages/designto-token/token-to-token/index.ts b/packages/designto-token/token-to-token/index.ts new file mode 100644 index 00000000..a0ac122d --- /dev/null +++ b/packages/designto-token/token-to-token/index.ts @@ -0,0 +1 @@ +export * from "./vector-token-to-image-token"; diff --git a/packages/designto-token/token-to-token/vector-token-to-image-token.ts b/packages/designto-token/token-to-token/vector-token-to-image-token.ts new file mode 100644 index 00000000..5171aa34 --- /dev/null +++ b/packages/designto-token/token-to-token/vector-token-to-image-token.ts @@ -0,0 +1,24 @@ +import { MainImageRepository } from "@design-sdk/core/assets-repository"; +import { ImageWidget, VectorWidget } from "@reflect-ui/core"; + +/** + * convert vector widget to image widget, ignoring that widget is already tokenized to vector widget + * + * this is dangerous since the vector token is already tokenized, we cannot ensure if the vector token's key will maintain its identity. + * @param vector + * @returns + */ +export function vector_token_to_image_token(vector: VectorWidget): ImageWidget { + const _tmp_img = MainImageRepository.instance + .get("fill-later-assets") + .addImage({ + key: vector.key.id, + }); + + return new ImageWidget({ + key: vector.key, + src: _tmp_img.url, + width: vector.width, + height: vector.height, + }); +} diff --git a/packages/reflect-core b/packages/reflect-core index 27f82724..3d78b933 160000 --- a/packages/reflect-core +++ b/packages/reflect-core @@ -1 +1 @@ -Subproject commit 27f82724a64bf740279668d00725d8d1f1d9da90 +Subproject commit 3d78b933f5bce0486e955cc0668e211ff325b838 diff --git a/yarn.lock b/yarn.lock index a2968de1..1d6365b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1504,10 +1504,10 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== -"@flutter-builder/flutter@^2.5.0-f2": - version "2.5.0-f2" - resolved "https://registry.yarnpkg.com/@flutter-builder/flutter/-/flutter-2.5.0-f2.tgz#eea915ff7ae9bc75959b55c3a3bcf2b07e86814a" - integrity sha512-fq/299yw3fyFapRorHFNYGo2tOCrcD+qQ+MxGJxQv0VzdrlJd77WX0mWHIOoVHpBvvQtPGB9l/dFerdgd87JqA== +"@flutter-builder/flutter@^2.5.0-f3": + version "2.5.0-f3" + resolved "https://registry.yarnpkg.com/@flutter-builder/flutter/-/flutter-2.5.0-f3.tgz#91e993d420a97f39d5a9bd781ac43ac969388edd" + integrity sha512-5UVI31fwEBjHk79ECWmqGSLviC+ezEe+n5mRK+IoWiQ6+VXNftUJPZR0mjX7wEpCJ7obyqeH8p8gXV8Ejot05g== dependencies: "@abraham/reflection" "^0.8.0" dart-style "^1.3.2-dev"