From 2f3c300c79348aa4587d8f59b2170d54dfc7fcb5 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Mon, 14 Mar 2022 17:47:03 +0900 Subject: [PATCH 01/15] split positioning logic --- packages/designto-token/token-layout/index.ts | 257 +++++++++++------- 1 file changed, 155 insertions(+), 102 deletions(-) diff --git a/packages/designto-token/token-layout/index.ts b/packages/designto-token/token-layout/index.ts index daf2a61a..f011c7ce 100644 --- a/packages/designto-token/token-layout/index.ts +++ b/packages/designto-token/token-layout/index.ts @@ -1,5 +1,6 @@ import { nodes, ReflectSceneNodeType } from "@design-sdk/core"; import { layoutAlignToReflectMainAxisSize } from "@design-sdk/figma-node-conversion"; +import type { Constraints } from "@design-sdk/figma-types"; import * as core from "@reflect-ui/core"; import { Axis, @@ -278,7 +279,7 @@ function stackChild({ container: nodes.ReflectSceneNode; wchild: core.Widget; }) { - const constraint = { + let constraint = { left: undefined, top: undefined, right: undefined, @@ -290,16 +291,11 @@ function stackChild({ const _unwrappedChild: IWHStyleWidget = unwrappedChild( child ) as IWHStyleWidget; - const wh = { + let wh = { width: _unwrappedChild.width, height: _unwrappedChild.height, }; - const _l = ogchild.x; - const _r = container.width - (ogchild.x + ogchild.width); - const _t = ogchild.y; - const _b = container.height - (ogchild.y + ogchild.height); - /** * "MIN": Left or Top * "MAX": Right or Bottom @@ -320,101 +316,25 @@ function stackChild({ ); // throw `${ogchild.toString()} has no constraints. this can happen when node under group item tokenization is incomplete. this is engine's error.`; } else { - switch (ogchild.constraints.horizontal) { - case "MIN": - constraint.left = _l; - break; - case "MAX": - constraint.right = _r; - break; - case "SCALE": /** scale fallbacks to stretch */ - case "STRETCH": - constraint.left = _l; - constraint.right = _r; - wh.width = undefined; // no fixed width - break; - case "CENTER": - const half_w = ogchild.width / 2; - const centerdiff = - // center of og - half_w + - ogchild.x - - // center of frame - container.width / 2; - constraint.left = { - type: "calc", - operations: { - type: "op", - left: { - type: "calc", - operations: { - type: "op", - left: "50%", - op: "+", - right: centerdiff, - }, - }, - op: "-", // this part is different - right: half_w, - }, - }; - // --- we can also specify the right, but left is enough. - // constraint.right = { - // type: "calc", - // operations: { - // left: { - // type: "calc", - // operations: { left: "50%", op: "+", right: centerdiff }, - // }, - // op: "+", // this part is different - // right: half, - // }, - // }; - break; - } - switch (ogchild.constraints.vertical) { - case "MIN": - constraint.top = _t; - break; - case "MAX": - constraint.bottom = _b; - break; - case "SCALE": /** scale fallbacks to stretch */ - case "STRETCH": - constraint.top = _t; - constraint.bottom = _b; - wh.height = undefined; - break; - case "CENTER": - const half_height = ogchild.height / 2; - const container_snapshot_center = container.height / 2; - const child_snapshot_center = half_height + ogchild.y; - - const centerdiff = - // center of og - child_snapshot_center - - // center of frame - container_snapshot_center; - - constraint.top = { - type: "calc", - operations: { - type: "op", - left: { - type: "calc", - operations: { - type: "op", - left: "50%", - op: "+", - right: centerdiff, - }, - }, - op: "-", // this part is different - right: half_height, - }, - }; - break; - } + const _l = ogchild.x; + const _r = container.width - (ogchild.x + ogchild.width); + const _t = ogchild.y; + const _b = container.height - (ogchild.y + ogchild.height); + + const res = handlePositioning({ + constraints: ogchild.constraints, + pos: { l: _l, t: _t, b: _b, r: _r, x: ogchild.x, y: ogchild.y }, + width: ogchild.width, + height: ogchild.height, + containerWidth: container.width, + containerHeight: container.height, + }); + + constraint = res.constraint; + wh = { + ...wh, + ...res.wh, + }; } // console.log("positioning based on constraints", { wh, constraint, child }); @@ -430,6 +350,139 @@ function stackChild({ }); } +/** + * calculates the position & constraints based on the input. + * @param + * @returns + */ +function handlePositioning({ + constraints, + pos, + width, + height, + containerWidth, + containerHeight, +}: { + constraints: Constraints; + pos: { l: number; r: number; t: number; b: number; x: number; y: number }; + width: number; + height: number; + containerWidth: number; + containerHeight: number; +}): { + constraint; + wh: { + width?: number; + height?: number; + }; +} { + const constraint = { + left: undefined, + top: undefined, + right: undefined, + bottom: undefined, + }; + const wh = { width, height }; + + switch (constraints.horizontal) { + case "MIN": + constraint.left = pos.l; + break; + case "MAX": + constraint.right = pos.r; + break; + case "SCALE": /** scale fallbacks to stretch */ + case "STRETCH": + constraint.left = pos.l; + constraint.right = pos.r; + wh.width = undefined; // no fixed width + break; + case "CENTER": + const half_w = width / 2; + const centerdiff = + // center of og + half_w + + pos.x - + // center of frame + containerWidth / 2; + constraint.left = { + type: "calc", + operations: { + type: "op", + left: { + type: "calc", + operations: { + type: "op", + left: "50%", + op: "+", + right: centerdiff, + }, + }, + op: "-", // this part is different + right: half_w, + }, + }; + // --- we can also specify the right, but left is enough. + // constraint.right = { + // type: "calc", + // operations: { + // left: { + // type: "calc", + // operations: { left: "50%", op: "+", right: centerdiff }, + // }, + // op: "+", // this part is different + // right: half, + // }, + // }; + break; + } + switch (constraints.vertical) { + case "MIN": + constraint.top = pos.t; + break; + case "MAX": + constraint.bottom = pos.b; + break; + case "SCALE": /** scale fallbacks to stretch */ + case "STRETCH": + constraint.top = pos.t; + constraint.bottom = pos.b; + wh.height = undefined; + break; + case "CENTER": + const half_height = height / 2; + const container_snapshot_center = containerHeight / 2; + const child_snapshot_center = half_height + pos.y; + + const centerdiff = + // center of og + child_snapshot_center - + // center of frame + container_snapshot_center; + + constraint.top = { + type: "calc", + operations: { + type: "op", + left: { + type: "calc", + operations: { + type: "op", + left: "50%", + op: "+", + right: centerdiff, + }, + }, + op: "-", // this part is different + right: half_height, + }, + }; + break; + } + + return { constraint, wh }; +} + function fromGroup( group: nodes.ReflectGroupNode, children: RuntimeChildrenInput, From 949b969076b2ac9139fe754a45f9a03970739d26 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Mon, 14 Mar 2022 19:19:24 +0900 Subject: [PATCH 02/15] add support for elliptical radius on ellipse border radius --- packages/builder-css-styles/border-radius/index.ts | 5 +++-- packages/designto-token/token-container/index.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/builder-css-styles/border-radius/index.ts b/packages/builder-css-styles/border-radius/index.ts index c1566e76..4ed99418 100644 --- a/packages/builder-css-styles/border-radius/index.ts +++ b/packages/builder-css-styles/border-radius/index.ts @@ -60,10 +60,11 @@ export function borderRadius(r: BorderRadiusManifest): CSSProperties { "border-radius": px(r.all), }; } else { - console.warn("elliptical border radius not supported"); + // example - https://codepen.io/Mahe76/pen/ExZbdro return { - "border-radius": px(r.all.x), + "border-radius": `${px(r.all.x)} / ${px(r.all.y)}`, }; + // TODO: support short handed version - `50%` } } else { return { diff --git a/packages/designto-token/token-container/index.ts b/packages/designto-token/token-container/index.ts index c721bf6c..d0abe440 100644 --- a/packages/designto-token/token-container/index.ts +++ b/packages/designto-token/token-container/index.ts @@ -4,7 +4,7 @@ import { tokenizeBackground } from "../token-background"; import { BoxShape } from "@reflect-ui/core/lib/box-shape"; import { keyFromNode } from "../key"; import { tokenizeBorder } from "../token-border"; -import { BoxShadowManifest } from "@reflect-ui/core"; +import { BorderRadius, BoxShadowManifest } from "@reflect-ui/core"; function fromRectangle(rect: nodes.ReflectRectangleNode): core.Container { const container = new core.Container({ @@ -30,7 +30,7 @@ function fromEllipse(ellipse: nodes.ReflectEllipseNode): core.Container { height: ellipse.height, boxShadow: ellipse.shadows as BoxShadowManifest[], border: tokenizeBorder.fromNode(ellipse), - borderRadius: { all: Math.max(ellipse.width, ellipse.height) / 2 }, + borderRadius: BorderRadius.all({ x: ellipse.width, y: ellipse.height }), // this is equivalant to css "50%" background: tokenizeBackground.fromFills(ellipse.fills), }); From 8dbaead0ec417a1ab18b04e7aecca591fce6cdf4 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Mon, 14 Mar 2022 20:04:41 +0900 Subject: [PATCH 03/15] add proper line support - fixes the border side handling of the line node --- externals/reflect-core | 2 +- packages/designto-token/main.ts | 6 ++-- packages/designto-token/token-border/index.ts | 28 +++++++++++++++++-- .../designto-token/token-container/index.ts | 15 ++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/externals/reflect-core b/externals/reflect-core index c5dc2966..692ded59 160000 --- a/externals/reflect-core +++ b/externals/reflect-core @@ -1 +1 @@ -Subproject commit c5dc2966f333a1f4749a6aa3acec606a3dd0848d +Subproject commit 692ded5998d82b7940ff03933d7e80eef9ea22b7 diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts index c14a42c7..51ec4c42 100644 --- a/packages/designto-token/main.ts +++ b/packages/designto-token/main.ts @@ -314,11 +314,9 @@ function handle_by_types( break; case nodes.ReflectSceneNodeType.line: - // FIXME: this is a temporary fallback. line should be handled with unique handler. (using rect's handler instead.) - tokenizedTarget = tokenizeContainer.fromRectangle(node as any); + tokenizedTarget = tokenizeContainer.fromLine(node as any); + // tokenizedTarget = tokenizeDivider.fromLine(_line); break; - // const _line = node as nodes.ReflectLineNode; - // tokenizedTarget = tokenizeDivider.fromLine(_line); default: console.error(`${node["type"]} is not yet handled by "@designto/token"`); diff --git a/packages/designto-token/token-border/index.ts b/packages/designto-token/token-border/index.ts index de638e7e..aba5eb04 100644 --- a/packages/designto-token/token-border/index.ts +++ b/packages/designto-token/token-border/index.ts @@ -1,9 +1,30 @@ import { retrievePrimaryColor } from "@design-sdk/core/utils"; -import { ReflectSceneNode } from "@design-sdk/figma-node"; +import type { ReflectSceneNode, ReflectLineNode } from "@design-sdk/figma-node"; import { Figma } from "@design-sdk/figma-types"; /** remove figma dependency */ import { Border } from "@reflect-ui/core"; import { roundNumber } from "@reflect-ui/uiutils"; +function fromLineNode(node: ReflectLineNode) { + const strokes = node.strokes; + if (!strokes || strokes.length === 0) { + return undefined; + } + // guard invisible borders + if (strokes.filter(visible).length > 0) { + // generate the border, when it should exist + // if width is 0, then we don't want to generate a border + return node.strokeWeight + ? new Border({ + // it's a line so we only add border to a single side. + top: { + color: retrievePrimaryColor(strokes), + width: node.strokeWeight, // do not round number + }, + }) + : undefined; + } +} + function fromNode(node: ReflectSceneNode) { if ("strokes" in node) { if (!node.strokes || node.strokes.length === 0) { @@ -29,7 +50,7 @@ function fromStrokes( } ) { // guard invisible borders - if (strokes.filter((s) => s.visible).length > 0) { + if (strokes.filter(visible).length > 0) { // generate the border, when it should exist // if width is 0, then we don't want to generate a border return width @@ -41,7 +62,10 @@ function fromStrokes( } } +const visible = (s) => s.visible; + export const tokenizeBorder = { fromStrokes: fromStrokes, fromNode: fromNode, + fromLineNode: fromLineNode, }; diff --git a/packages/designto-token/token-container/index.ts b/packages/designto-token/token-container/index.ts index d0abe440..d92dccc8 100644 --- a/packages/designto-token/token-container/index.ts +++ b/packages/designto-token/token-container/index.ts @@ -23,6 +23,20 @@ function fromRectangle(rect: nodes.ReflectRectangleNode): core.Container { return container; } +function fromLine(line: nodes.ReflectLineNode): core.Container { + const container = new core.Container({ + key: keyFromNode(line), + width: line.width, + height: 0, + boxShadow: line.shadows as BoxShadowManifest[], + border: tokenizeBorder.fromLineNode(line), + }); + + container.x = line.x; + container.y = line.y; + return container; +} + function fromEllipse(ellipse: nodes.ReflectEllipseNode): core.Container { const container = new core.Container({ key: keyFromNode(ellipse), @@ -45,4 +59,5 @@ function fromEllipse(ellipse: nodes.ReflectEllipseNode): core.Container { export const tokenizeContainer = { fromRectangle: fromRectangle, fromEllipse: fromEllipse, + fromLine: fromLine, }; From c1031d9cfbc6e3a2a9d9bb2df9bb7a88b0f233c4 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Mon, 14 Mar 2022 21:33:44 +0900 Subject: [PATCH 04/15] add dashed stroke support (stroke gaps are not supported) --- externals/design-sdk | 2 +- externals/reflect-core | 2 +- packages/builder-css-styles/border/index.ts | 4 ++- packages/designto-token/token-border/index.ts | 35 ++++++++++++++++--- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/externals/design-sdk b/externals/design-sdk index 1212ba4a..96331eb7 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit 1212ba4aafa1c08d372faa3d15ab2f5b72eb16ac +Subproject commit 96331eb7010e1fe996105d990bf3b94fa8890a22 diff --git a/externals/reflect-core b/externals/reflect-core index 692ded59..50fc154c 160000 --- a/externals/reflect-core +++ b/externals/reflect-core @@ -1 +1 @@ -Subproject commit 692ded5998d82b7940ff03933d7e80eef9ea22b7 +Subproject commit 50fc154cc98817a8ee6138401e99eb55f67239aa diff --git a/packages/builder-css-styles/border/index.ts b/packages/builder-css-styles/border/index.ts index 0d51b3c3..4576467d 100644 --- a/packages/builder-css-styles/border/index.ts +++ b/packages/builder-css-styles/border/index.ts @@ -33,5 +33,7 @@ export function border(border: Border): CSSProperties { } export function borderSide(borderSide: BorderSide): CSSProperty.Border { - return `solid ${px(borderSide.width)} ${color(borderSide.color)}`; + return `${borderSide.style ?? "solid"} ${px(borderSide.width)} ${color( + borderSide.color + )}`; } diff --git a/packages/designto-token/token-border/index.ts b/packages/designto-token/token-border/index.ts index aba5eb04..23d7a5f4 100644 --- a/packages/designto-token/token-border/index.ts +++ b/packages/designto-token/token-border/index.ts @@ -1,7 +1,11 @@ import { retrievePrimaryColor } from "@design-sdk/core/utils"; -import type { ReflectSceneNode, ReflectLineNode } from "@design-sdk/figma-node"; +import type { + ReflectSceneNode, + ReflectLineNode, + IReflectGeometryMixin, +} from "@design-sdk/figma-node"; import { Figma } from "@design-sdk/figma-types"; /** remove figma dependency */ -import { Border } from "@reflect-ui/core"; +import { Border, BorderStyle } from "@reflect-ui/core"; import { roundNumber } from "@reflect-ui/uiutils"; function fromLineNode(node: ReflectLineNode) { @@ -11,14 +15,18 @@ function fromLineNode(node: ReflectLineNode) { } // guard invisible borders if (strokes.filter(visible).length > 0) { + const _p_stroke = strokes.find(visible); + // generate the border, when it should exist // if width is 0, then we don't want to generate a border return node.strokeWeight ? new Border({ // it's a line so we only add border to a single side. top: { - color: retrievePrimaryColor(strokes), + color: retrievePrimaryColor([_p_stroke]), width: node.strokeWeight, // do not round number + // the dashed border support is incomplete. we cannot support dashed gap since the platform does not support it. + style: isDashed(node) ? BorderStyle.dashed : BorderStyle.solid, }, }) : undefined; @@ -33,6 +41,8 @@ function fromNode(node: ReflectSceneNode) { if ("strokeWeight" in node) { return fromStrokes(node.strokes, { width: node.strokeWeight, + // the dashed border support is incomplete. we cannot support dashed gap since the platform does not support it. + dashed: isDashed(node), }); } } @@ -42,21 +52,26 @@ function fromStrokes( strokes: ReadonlyArray, { width, + dashed, }: { /** * a stroke height */ width: number; + dashed: boolean; } ) { // guard invisible borders if (strokes.filter(visible).length > 0) { // generate the border, when it should exist // if width is 0, then we don't want to generate a border + // using the first stroke. since css3 standard does not support multiple borders (as well as flutter) + const _p_stroke = strokes.find(visible); return width ? Border.all({ - color: retrievePrimaryColor(strokes), + color: retrievePrimaryColor([_p_stroke]), width: roundNumber(width), + style: dashed ? BorderStyle.dashed : BorderStyle.solid, }) : undefined; } @@ -64,6 +79,18 @@ function fromStrokes( const visible = (s) => s.visible; +/** + * + * returns if the node's border is dashed or not + * + * - when solid, the dashPattern is empty - `[]` + * - when dashed, the dashPattern will be like - `[6, 6]` + * @param s + * @returns + */ +const isDashed = (s: IReflectGeometryMixin): boolean => + s.dashPattern?.length > 0; + export const tokenizeBorder = { fromStrokes: fromStrokes, fromNode: fromNode, From 8abcfaa712833fd2d89bf80364ab10de268602bf Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 11:14:58 +0900 Subject: [PATCH 05/15] fix rotation & relative transform mapping & css gen - applies to vanilla, react, and other web based platforms --- externals/design-sdk | 2 +- .../compose-wrapped-with-rotation.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/externals/design-sdk b/externals/design-sdk index 96331eb7..aec79e18 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit 96331eb7010e1fe996105d990bf3b94fa8890a22 +Subproject commit aec79e1838eb902a25bedf466f555ea371855787 diff --git a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts index 0f953325..a52960ca 100644 --- a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts +++ b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts @@ -7,8 +7,18 @@ export function compose_wrapped_with_rotation( child_composer: Composer ) { const child = child_composer(widget.child); + + const r = widget.rotation; + // don't apply rotation if it's 0 + if (Math.abs(r) === 360 || Math.abs(r) === 360) { + return child; + } + child.extendStyle({ - transform: css.rotation(widget.rotation), + // rotation data needs to be inverted + transform: css.rotation(-widget.rotation), + // this is where the figma's rotation data is originated from. + "transform-origin": "top left", }); return child; } From 493d5503d23874adc545d6e85f9563457237fb53 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 11:22:46 +0900 Subject: [PATCH 06/15] update figma rotation docs --- docs/figma-rotation.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/figma-rotation.md b/docs/figma-rotation.md index f6657af7..ed23d84a 100644 --- a/docs/figma-rotation.md +++ b/docs/figma-rotation.md @@ -12,6 +12,37 @@ revision: 1 > Figma rotation from [figma plugin docs](https://www.figma.com/plugin-docs/api/properties/nodes-rotation/#docsNav) +## [Note] The rotation value needs to be inverted on the client side. + +to rotate something to the clockwise direction, + +- figma: -n +- client: +n (css) + +the transform origin + +- figma: not specified (always top left) +- client: top left + +**so the conversion from figma to css will be like below** + +``` +# figma +{ + x: 100, + y: 100, + rotation: 10, +} + +# css +.rotate-n { + top: 100; + left: 100; + transform: rotate(-10deg); + transform-origin: top left; +} +``` + ## Transform? While figma and other major design tools has both transform value, and explicit rotation value (which can be calculated from transform value), The intuitive way to represent a rotation value is by using a `Rotation` token. Overall all figma node properties, the only two property that has impact to final transform (based on css) is `scale` and `rotation`. From d769ffde9659496b8e224c41266d176dd0c87d31 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 13:49:40 +0900 Subject: [PATCH 07/15] add customized ellipse handling --- packages/designto-token/main.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts index 51ec4c42..05c3ea7b 100644 --- a/packages/designto-token/main.ts +++ b/packages/designto-token/main.ts @@ -306,7 +306,13 @@ function handle_by_types( break; case nodes.ReflectSceneNodeType.ellipse: - tokenizedTarget = tokenizeContainer.fromEllipse(node); + if (node.arcData.startingAngle === 0 && node.arcData.innerRadius === 0) { + // a standard ellipse + tokenizedTarget = tokenizeContainer.fromEllipse(node); + } else { + // a customized ellipse, most likely to be part of a graphical element. + tokenizedTarget = tokenizeGraphics.fromAnyNode(node); + } break; case nodes.ReflectSceneNodeType.boolean_operation: From b890289c03cfb7a7680e02a8c33c070593b7cd5f Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 13:54:24 +0900 Subject: [PATCH 08/15] add proposal docs --- docs/css-multiple-background.md | 60 +++++++++++++++++++++++++++++++++ docs/css-text-gradient.md | 26 ++++++++++++++ docs/css-text-vertical-align.md | 11 ++++++ 3 files changed, 97 insertions(+) create mode 100644 docs/css-multiple-background.md create mode 100644 docs/css-text-gradient.md create mode 100644 docs/css-text-vertical-align.md diff --git a/docs/css-multiple-background.md b/docs/css-multiple-background.md new file mode 100644 index 00000000..f940998d --- /dev/null +++ b/docs/css-multiple-background.md @@ -0,0 +1,60 @@ +--- +title: "CSS How to handle multiple background fills" +version: 0.1.0 +revision: 1 +--- + +# How to handle multiple background fills + +## Definition of `"multiple background fills"` + +- one or none active solid fill +- one or more gradient fill above solid fill +- one or more image fill + +## Possible combinations + +single solid fill + +```css +._1 { + background: #fff; +} +._2 { + background-color: #fff; +} +``` + +single solid fill with single gradient fill + +```css +._1 { + background-color: #fff; + background-image: linear-gradient(to bottom, #fff, #fff); +} + +._2 { + background: #fff; + background-image: linear-gradient(to bottom, #fff, #fff); +} +``` + +no solid fill with single gradient fill + +```css +._1 { + background: linear-gradient(to bottom, #fff, #fff); +} + +._2 { + background-image: linear-gradient(to bottom, #fff, #fff); +} +``` + +no solid fill with multiple gradient fill + +```css +._1 { + background: linear-gradient(to bottom, #fff, #fff), linear-gradient(to bottom, #fff, #fff); +} +``` diff --git a/docs/css-text-gradient.md b/docs/css-text-gradient.md new file mode 100644 index 00000000..dac84d3e --- /dev/null +++ b/docs/css-text-gradient.md @@ -0,0 +1,26 @@ +--- +title: "CSS Gradient on text layer" +version: 0.1.0 +revision: 1 +--- + +# CSS - Gradient on Text Layer + +Applying a gradient to a text fill is quite different from simply giving a color to a text. +Yet hooray CSS, it is much more simple than other platforms (flutter, android, ...) + +**How to** + +```css +h1 { + font-size: 72px; + background: -webkit-linear-gradient(#eee, #333); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +``` + +### References + +- https://cssgradient.io/blog/css-gradient-text/ +- https://github.com/gridaco/designto-code/issues/84 diff --git a/docs/css-text-vertical-align.md b/docs/css-text-vertical-align.md new file mode 100644 index 00000000..2151eded --- /dev/null +++ b/docs/css-text-vertical-align.md @@ -0,0 +1,11 @@ +--- +title: "CSS Vertically align text content" +version: 0.1.0 +revision: 1 +--- + +# Vertical align of text content + +### References + +- https://stackoverflow.com/questions/8865458/how-do-i-vertically-center-text-with-css From c89040136b3f592f812c64cb7a8ea53140ecaa7f Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 14:28:22 +0900 Subject: [PATCH 09/15] add blendmode support proposal doc --- docs/css-blend-mode.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 docs/css-blend-mode.md diff --git a/docs/css-blend-mode.md b/docs/css-blend-mode.md new file mode 100644 index 00000000..e709a7a8 --- /dev/null +++ b/docs/css-blend-mode.md @@ -0,0 +1,16 @@ +# CSS - Blend mode + +## Supported types + +``` +normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity +``` + +## Warning Lack of browser support on SVG element. + +Applying `mix-blend-mode` to SVG element is limited in some browsers (safari & mobile browsers) See - https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode#browser_compatibility + +### References + +- [`mix-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) +- [`background-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-blend-mode) From cd5a78a7376bfbccf631b8675bcd8f3f1bdb48ac Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 14:40:15 +0900 Subject: [PATCH 10/15] svg sizing hotfix --- .../builder-web-core/widgets-native/html-svg/index.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/builder-web-core/widgets-native/html-svg/index.ts b/packages/builder-web-core/widgets-native/html-svg/index.ts index e6abd37c..6bdfc909 100644 --- a/packages/builder-web-core/widgets-native/html-svg/index.ts +++ b/packages/builder-web-core/widgets-native/html-svg/index.ts @@ -200,6 +200,17 @@ export class SvgElement extends StylableJsxWidget { }; } + // @override + get finalStyle() { + return { + ...super.finalStyle, + // TODO: this is a hot fix of svg & path's constraint handling + // width & height for the svg must be preserved. (it wont' follow the constraints anyway.) + width: css.length(this.width), + height: css.length(this.height), + }; + } + jsxConfig(): StylableJSXElementConfig | UnstylableJSXElementConfig { return { type: "tag-and-attr", From b494398f0fed2c44c4f3df7364910cd0149e8dcc Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 14:55:18 +0900 Subject: [PATCH 11/15] bump design sdk --- externals/design-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/design-sdk b/externals/design-sdk index aec79e18..3cb3f5ec 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit aec79e1838eb902a25bedf466f555ea371855787 +Subproject commit 3cb3f5ece3cee7536636602ad3a7103c4b1d03d5 From f52dbfbff5d4da3c6ccf44ada31b55b8dfff0fba Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 14:55:21 +0900 Subject: [PATCH 12/15] cleaning --- .../editor-canvas/canvas/canvas.tsx | 1 + .../editor-canvas/canvas/hud-surface.tsx | 5 +- .../overlay/hover-outline-hightlight.tsx | 51 ++++++----------- editor-packages/editor-canvas/overlay/math.ts | 2 + .../overlay/overlay-container.tsx | 30 ++++++++++ .../overlay/readonly-select-hightlight.tsx | 55 ++++++------------- .../widgets-native/html-svg/index.ts | 4 +- .../compose-wrapped-with-rotation.ts | 1 + 8 files changed, 73 insertions(+), 76 deletions(-) create mode 100644 editor-packages/editor-canvas/overlay/overlay-container.tsx diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx index 051517b0..595f8cad 100644 --- a/editor-packages/editor-canvas/canvas/canvas.tsx +++ b/editor-packages/editor-canvas/canvas/canvas.tsx @@ -320,6 +320,7 @@ export function Canvas({ ).map((h) => ({ id: h.id, xywh: [h.absoluteX, h.absoluteY, h.width, h.height], + rotation: h.rotation, })) : [] } diff --git a/editor-packages/editor-canvas/canvas/hud-surface.tsx b/editor-packages/editor-canvas/canvas/hud-surface.tsx index 05d21f9f..193414c9 100644 --- a/editor-packages/editor-canvas/canvas/hud-surface.tsx +++ b/editor-packages/editor-canvas/canvas/hud-surface.tsx @@ -22,6 +22,7 @@ export interface DisplayNodeMeta { absoluteY: number; width: number; height: number; + rotation: number; } export function HudSurface({ @@ -41,7 +42,7 @@ export function HudSurface({ }: { offset: XY; zoom: number; - highlights: { id: string; xywh: XYWH }[]; + highlights: { id: string; xywh: XYWH; rotation: number }[]; labelDisplayNodes: DisplayNodeMeta[]; selectedNodes: DisplayNodeMeta[]; hide: boolean; @@ -102,6 +103,7 @@ export function HudSurface({ key={h.id} type="xywhr" xywh={h.xywh} + rotation={h.rotation} zoom={zoom} width={2} /> @@ -121,6 +123,7 @@ export function HudSurface({ key={s.id} type="xywhr" xywh={xywh} + rotation={s.rotation} zoom={zoom} width={1} /> diff --git a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx index 068e2266..b5b9b6db 100644 --- a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx +++ b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx @@ -2,46 +2,27 @@ import React from "react"; import { color_layer_highlight } from "../theme"; import { get_boinding_box } from "./math"; import { OulineSide } from "./outline-side"; +import { OverlayContainer } from "./overlay-container"; import type { OutlineProps } from "./types"; export function HoverOutlineHighlight({ width = 1, ...props }: OutlineProps) { - const { xywh, zoom } = props; + const { xywh, zoom, rotation } = props; const bbox = get_boinding_box({ xywh, scale: zoom }); const wh: [number, number] = [xywh[2], xywh[3]]; + const vprops = { + wh: wh, + zoom: props.zoom, + width: width, + box: bbox, + color: color_layer_highlight, + }; + return ( - <> - - - - - + + + + + + ); } diff --git a/editor-packages/editor-canvas/overlay/math.ts b/editor-packages/editor-canvas/overlay/math.ts index 67fc2190..9c391b42 100644 --- a/editor-packages/editor-canvas/overlay/math.ts +++ b/editor-packages/editor-canvas/overlay/math.ts @@ -6,6 +6,8 @@ export function get_boinding_box({ scale: number; }): [number, number, number, number] { const [x, y, w, h] = xywh; + + // return the bounding box in [number, number, number, number] form with givven x, y, w, h, rotation and scale. const [x1, y1, x2, y2] = [ x * scale, y * scale, diff --git a/editor-packages/editor-canvas/overlay/overlay-container.tsx b/editor-packages/editor-canvas/overlay/overlay-container.tsx new file mode 100644 index 00000000..73bbaf7b --- /dev/null +++ b/editor-packages/editor-canvas/overlay/overlay-container.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +/** + * @default - TODO: rotation not supported + * @returns + */ +export function OverlayContainer({ + xywh, + rotation = 0, + children, +}: { + xywh: [number, number, number, number]; + rotation: number; + children: React.ReactNode; +}) { + // const [x, y, w, h] = xywh; + return ( +
+ {children} +
+ ); +} diff --git a/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx b/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx index 8dea6a5a..27ab07db 100644 --- a/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx +++ b/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx @@ -3,12 +3,13 @@ import type { OutlineProps } from "./types"; import { color_layer_readonly_highlight } from "../theme"; import { get_boinding_box } from "./math"; import { OulineSide } from "./outline-side"; +import { OverlayContainer } from "./overlay-container"; export function ReadonlySelectHightlight({ width = 1, ...props }: OutlineProps) { - const { xywh, zoom } = props; + const { xywh, zoom, rotation } = props; const bbox = get_boinding_box({ xywh, scale: zoom }); const wh: [number, number] = [xywh[2], xywh[3]]; @@ -16,12 +17,16 @@ export function ReadonlySelectHightlight({ const handle_size = 3; const dot_size = 4; + const sideprops = { + wh: wh, + zoom: props.zoom, + width: width, + box: bbox, + color: color_layer_readonly_highlight, + }; + return ( -
+ <> <> - - - - + + + + -
+ ); } diff --git a/packages/builder-web-core/widgets-native/html-svg/index.ts b/packages/builder-web-core/widgets-native/html-svg/index.ts index 6bdfc909..1315134d 100644 --- a/packages/builder-web-core/widgets-native/html-svg/index.ts +++ b/packages/builder-web-core/widgets-native/html-svg/index.ts @@ -202,10 +202,12 @@ export class SvgElement extends StylableJsxWidget { // @override get finalStyle() { + const super_finalStyle = super.finalStyle; return { - ...super.finalStyle, + ...super_finalStyle, // TODO: this is a hot fix of svg & path's constraint handling // width & height for the svg must be preserved. (it wont' follow the constraints anyway.) + // it can also be 100% width: css.length(this.width), height: css.length(this.height), }; diff --git a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts index a52960ca..110303b2 100644 --- a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts +++ b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-rotation.ts @@ -18,6 +18,7 @@ export function compose_wrapped_with_rotation( // rotation data needs to be inverted transform: css.rotation(-widget.rotation), // this is where the figma's rotation data is originated from. + // see docs/figma-rotation.md "transform-origin": "top left", }); return child; From a7bc8e35faeb843e9fbc6d2611a0ca94cb56ea4a Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 16:36:27 +0900 Subject: [PATCH 13/15] fix text sizing (with dangerous static logic overrides) --- externals/design-sdk | 2 +- .../widgets-native/html-text-element/index.ts | 31 ++++++++++++++++--- packages/designto-token/main.ts | 10 ++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/externals/design-sdk b/externals/design-sdk index 3cb3f5ec..633440ea 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit 3cb3f5ece3cee7536636602ad3a7103c4b1d03d5 +Subproject commit 633440eac4f7acca06133d1f48cd97ad2bd3a71f diff --git a/packages/builder-web-core/widgets-native/html-text-element/index.ts b/packages/builder-web-core/widgets-native/html-text-element/index.ts index 8611aaee..2e05feca 100644 --- a/packages/builder-web-core/widgets-native/html-text-element/index.ts +++ b/packages/builder-web-core/widgets-native/html-text-element/index.ts @@ -14,7 +14,7 @@ import { Dynamic } from "@reflect-ui/core/lib/_utility-types"; * You can select wich element to render with `elementPreference`. - choose between h1 ~ h6, p, span, etc. */ export class Text extends TextChildWidget { - _type: "Text"; + _type: "Text" = "Text"; // text properties data: Dynamic; @@ -74,15 +74,27 @@ export class Text extends TextChildWidget { "text-decoration": css.textDecoration(this.textStyle.decoration), "text-shadow": css.textShadow(this.textStyle.textShadow), // ------------------------------------------ - "min-height": css.px(this.height), - // TODO: do not specify width when parent is a flex container. - // Also flex: 1 is required to make the text wrap. - width: css.px(this.width), + ...textWH({ width: this.width, height: this.height }), }; return textStyle; } + get finalStyle() { + const superFinalStyle = super.finalStyle; + // TODO: this is a dirty fix ------------------------------------------------ + // the text's width should not be overriden by the constraint's preference. + if (this.width === undefined) { + delete superFinalStyle["width"]; + } + if (this.height === undefined) { + delete superFinalStyle["height"]; + } + // -------------------------------------------------------------------------- + + return { ...superFinalStyle }; + } + jsxConfig(): StylableJSXElementConfig { return { type: "tag-and-attr", @@ -99,3 +111,12 @@ const __get_dedicated_element_tag = (t?: WebTextElement | undefined) => { return __default_element_tag; } }; + +function textWH({ width, height }: { width: number; height: number }) { + return { + // TODO: do not specify width when parent is a flex container. + // Also flex: 1 is required to make the text wrap. + width: css.px(width), + "min-height": css.px(height), + }; +} diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts index 05c3ea7b..d3c82217 100644 --- a/packages/designto-token/main.ts +++ b/packages/designto-token/main.ts @@ -270,6 +270,16 @@ function handle_by_types( break; case nodes.ReflectSceneNodeType.text: + // FIXME: aberation handling (remove me if required) -------------------------------- + // FIXME: this is for giving extra space for text so it won't break line accidently. + // FIXME: consider applying this only to a preview build + if (node.data.length <= 6 && node.data.length > 2) { + node.width = node.width + 1; + } else if (node.data.length < 30) { + node.width = node.width + 2; + } + // FIXME: --------------------------------------------------------------------------------- + tokenizedTarget = tokenizeText.fromText(node as nodes.ReflectTextNode); break; From d1a5c0dd4aa070dc6ffd6e1e88261a37f76e43d5 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Tue, 15 Mar 2022 16:59:33 +0900 Subject: [PATCH 14/15] add support for building text with text case (text-transform) (only for css platforms) --- externals/design-sdk | 2 +- externals/reflect-core | 2 +- .../builder-web-core/widgets-native/html-text-element/index.ts | 1 + packages/designto-token/token-text/index.ts | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/externals/design-sdk b/externals/design-sdk index 633440ea..6c54a532 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit 633440eac4f7acca06133d1f48cd97ad2bd3a71f +Subproject commit 6c54a532cec8a4ca016a43a8d75461e0a961ff13 diff --git a/externals/reflect-core b/externals/reflect-core index 50fc154c..7e792b1f 160000 --- a/externals/reflect-core +++ b/externals/reflect-core @@ -1 +1 @@ -Subproject commit 50fc154cc98817a8ee6138401e99eb55f67239aa +Subproject commit 7e792b1fff930f3b1f49956b9ab2f9d40aa6f682 diff --git a/packages/builder-web-core/widgets-native/html-text-element/index.ts b/packages/builder-web-core/widgets-native/html-text-element/index.ts index 2e05feca..fd4626ec 100644 --- a/packages/builder-web-core/widgets-native/html-text-element/index.ts +++ b/packages/builder-web-core/widgets-native/html-text-element/index.ts @@ -73,6 +73,7 @@ export class Text extends TextChildWidget { "text-align": this.textAlign, "text-decoration": css.textDecoration(this.textStyle.decoration), "text-shadow": css.textShadow(this.textStyle.textShadow), + "text-transform": this.textStyle.textTransform, // ------------------------------------------ ...textWH({ width: this.width, height: this.height }), }; diff --git a/packages/designto-token/token-text/index.ts b/packages/designto-token/token-text/index.ts index 5f5db2fc..57bda4e7 100644 --- a/packages/designto-token/token-text/index.ts +++ b/packages/designto-token/token-text/index.ts @@ -47,6 +47,7 @@ export function fromText(node: nodes.ReflectTextNode): RenderedText { color: node.primaryColor, lineHeight: node.lineHeight, letterSpacing: node.letterSpacing, + textTransform: node.textCase, textShadow: node.shadows as TextShadowManifest[], }), ...wh, From 2cb5fc406833d7ba705a71cc4ebe0cc9c737efe3 Mon Sep 17 00:00:00 2001 From: softmarshmallow Date: Fri, 18 Mar 2022 02:59:32 +0900 Subject: [PATCH 15/15] small fixes to text width optimization --- .../widgets-native/html-text-element/index.ts | 4 ++-- packages/designto-token/main.ts | 9 +++++++-- packages/designto-token/token-layout/index.ts | 3 ++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/builder-web-core/widgets-native/html-text-element/index.ts b/packages/builder-web-core/widgets-native/html-text-element/index.ts index fd4626ec..384295d8 100644 --- a/packages/builder-web-core/widgets-native/html-text-element/index.ts +++ b/packages/builder-web-core/widgets-native/html-text-element/index.ts @@ -115,8 +115,8 @@ const __get_dedicated_element_tag = (t?: WebTextElement | undefined) => { function textWH({ width, height }: { width: number; height: number }) { return { - // TODO: do not specify width when parent is a flex container. - // Also flex: 1 is required to make the text wrap. + // TODO: do not specify width when parent is a flex container. (set as 100%) + // Also flex-grow: 1 is required to make the text wrap. width: css.px(width), "min-height": css.px(height), }; diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts index d3c82217..7d15a886 100644 --- a/packages/designto-token/main.ts +++ b/packages/designto-token/main.ts @@ -273,9 +273,14 @@ function handle_by_types( // FIXME: aberation handling (remove me if required) -------------------------------- // FIXME: this is for giving extra space for text so it won't break line accidently. // FIXME: consider applying this only to a preview build - if (node.data.length <= 6 && node.data.length > 2) { + // TODO: change logic to word count. + const wordcount = node.data.split(" ").length; + const txtlen = node.data.length; + if (wordcount <= 1) { + /* skip, since there is no word break */ + } else if (txtlen <= 6 && wordcount <= 2) { node.width = node.width + 1; - } else if (node.data.length < 30) { + } else if (txtlen < 30) { node.width = node.width + 2; } // FIXME: --------------------------------------------------------------------------------- diff --git a/packages/designto-token/token-layout/index.ts b/packages/designto-token/token-layout/index.ts index f011c7ce..908617bc 100644 --- a/packages/designto-token/token-layout/index.ts +++ b/packages/designto-token/token-layout/index.ts @@ -250,7 +250,8 @@ function find_original(ogchildren: Array, of: Widget) { // target the unwrapped child c.id === (_unwrappedChild && _unwrappedChild.key.id) || // target the widget itself - some widgets are not wrapped, yet being converted to a container-like (e.g. maskier) - c.id === of.key.id + c.id === of.key.id || + c.id === of.key.id.split(".")[0] // {id}.positioned or {id}.scroll-wrap TODO: this logic can cause problem later on. ); if (!ogchild) { console.error(