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)
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
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`.
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/externals/design-sdk b/externals/design-sdk
index 1212ba4a..6c54a532 160000
--- a/externals/design-sdk
+++ b/externals/design-sdk
@@ -1 +1 @@
-Subproject commit 1212ba4aafa1c08d372faa3d15ab2f5b72eb16ac
+Subproject commit 6c54a532cec8a4ca016a43a8d75461e0a961ff13
diff --git a/externals/reflect-core b/externals/reflect-core
index c5dc2966..7e792b1f 160000
--- a/externals/reflect-core
+++ b/externals/reflect-core
@@ -1 +1 @@
-Subproject commit c5dc2966f333a1f4749a6aa3acec606a3dd0848d
+Subproject commit 7e792b1fff930f3b1f49956b9ab2f9d40aa6f682
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/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/builder-web-core/widgets-native/html-svg/index.ts b/packages/builder-web-core/widgets-native/html-svg/index.ts
index e6abd37c..1315134d 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,19 @@ export class SvgElement extends StylableJsxWidget {
};
}
+ // @override
+ get finalStyle() {
+ const super_finalStyle = super.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.)
+ // it can also be 100%
+ width: css.length(this.width),
+ height: css.length(this.height),
+ };
+ }
+
jsxConfig(): StylableJSXElementConfig | UnstylableJSXElementConfig {
return {
type: "tag-and-attr",
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..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
@@ -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;
@@ -73,16 +73,29 @@ 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,
// ------------------------------------------
- "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 +112,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. (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 c14a42c7..7d15a886 100644
--- a/packages/designto-token/main.ts
+++ b/packages/designto-token/main.ts
@@ -270,6 +270,21 @@ 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
+ // 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 (txtlen < 30) {
+ node.width = node.width + 2;
+ }
+ // FIXME: ---------------------------------------------------------------------------------
+
tokenizedTarget = tokenizeText.fromText(node as nodes.ReflectTextNode);
break;
@@ -306,7 +321,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:
@@ -314,11 +335,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..23d7a5f4 100644
--- a/packages/designto-token/token-border/index.ts
+++ b/packages/designto-token/token-border/index.ts
@@ -1,9 +1,38 @@
import { retrievePrimaryColor } from "@design-sdk/core/utils";
-import { ReflectSceneNode } 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) {
+ const strokes = node.strokes;
+ if (!strokes || strokes.length === 0) {
+ return undefined;
+ }
+ // 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([_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;
+ }
+}
+
function fromNode(node: ReflectSceneNode) {
if ("strokes" in node) {
if (!node.strokes || node.strokes.length === 0) {
@@ -12,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),
});
}
}
@@ -21,27 +52,47 @@ function fromStrokes(
strokes: ReadonlyArray,
{
width,
+ dashed,
}: {
/**
* a stroke height
*/
width: number;
+ dashed: boolean;
}
) {
// 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
+ // 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;
}
}
+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,
+ fromLineNode: fromLineNode,
};
diff --git a/packages/designto-token/token-container/index.ts b/packages/designto-token/token-container/index.ts
index c721bf6c..d92dccc8 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({
@@ -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),
@@ -30,7 +44,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),
});
@@ -45,4 +59,5 @@ function fromEllipse(ellipse: nodes.ReflectEllipseNode): core.Container {
export const tokenizeContainer = {
fromRectangle: fromRectangle,
fromEllipse: fromEllipse,
+ fromLine: fromLine,
};
diff --git a/packages/designto-token/token-layout/index.ts b/packages/designto-token/token-layout/index.ts
index daf2a61a..908617bc 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,
@@ -249,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(
@@ -278,7 +280,7 @@ function stackChild({
container: nodes.ReflectSceneNode;
wchild: core.Widget;
}) {
- const constraint = {
+ let constraint = {
left: undefined,
top: undefined,
right: undefined,
@@ -290,16 +292,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 +317,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 +351,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,
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,
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..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
@@ -7,8 +7,19 @@ 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.
+ // see docs/figma-rotation.md
+ "transform-origin": "top left",
});
return child;
}