fix: nesting platform functions in css variables

This commit is contained in:
Mark Lawlor
2022-09-28 09:10:43 +10:00
parent 23bed41991
commit 689a9ac1ba
5 changed files with 188 additions and 72 deletions

View File

@@ -16,8 +16,6 @@ const expectStyle = (style: string, config?: Partial<Config>, css?: string) => {
`@tailwind components;@tailwind utilities;${css ?? ""}`
);
console.log(createOptions);
return expect(createOptions);
};
@@ -36,6 +34,68 @@ type OutputObject =
type CaseOutput = Atom | CreateOptions | OutputObject;
const cases: Record<string, CaseOutput> = {
"text-white": {
css: `:root {
--number: 255;
--string: string;
--unit: 123vw;
--rgb: rgb(255, 255, 255);
--default-value: var(--value, 2);
--rgb-var: rgb(255, 255, var(--number));
--inline-theme-value: var(--error-color,platformColor(ios__systemRed,android__colorError,default__red));
font-size: 16;
padding: 1px;
}`,
output: {
":root": {
variables: [
{
"--number": 255,
"--string": "string",
"--rem": 16,
"--rgb": "rgb(255, 255, 255)",
"--unit": {
function: "vw",
values: [123],
},
"--rgb-var": {
function: "inbuilt",
values: [
"rgb",
255,
255,
{
function: "var",
values: ["--number"],
},
],
},
"--default-value": {
function: "var",
values: ["--value", 2],
},
"--inline-theme-value": {
function: "var",
values: [
"--error-color",
{
function: "platformColor",
values: [
"ios__systemRed",
"android__colorError",
"default__red",
],
},
],
},
},
],
},
"text-white": {
styles: [{ color: "#fff" }],
},
},
},
// "text-apply": {
// config: {
// plugins: [
@@ -95,30 +155,30 @@ const cases: Record<string, CaseOutput> = {
// styles: [{ marginLeft: 8, marginTop: 8 }],
// },
// },
"text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": {
css: `:root { --hue: 255; }`,
output: {
":root": {
variables: [{ "--hue": 255 }],
},
"text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": {
styles: [
{
color: {
function: "inbuilt",
values: [
"hsl",
{ function: "var", values: ["--hue"] },
{ function: "var", values: ["--saturation"] },
{ function: "var", values: ["--lightness"] },
],
},
},
],
topics: ["--hue", "--saturation", "--lightness"],
},
},
},
// "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": {
// css: `:root { --hue: 255; }`,
// output: {
// ":root": {
// variables: [{ "--hue": 255 }],
// },
// "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": {
// styles: [
// {
// color: {
// function: "inbuilt",
// values: [
// "hsl",
// { function: "var", values: ["--hue"] },
// { function: "var", values: ["--saturation"] },
// { function: "var", values: ["--lightness"] },
// ],
// },
// },
// ],
// topics: ["--hue", "--saturation", "--lightness"],
// },
// },
// },
// "w-screen": {
// styles: [{ width: { function: "vw", values: [100] } }],
// },

View File

@@ -1,9 +1,10 @@
/* eslint-disable unicorn/no-lonely-if */
import { Block, CssNode, Declaration, walk } from "css-tree";
import { Atom, AtomStyle, StyleWithFunction } from "../style-sheet";
import { Block, CssNode, Declaration, walk, parse } from "css-tree";
import { Atom, StyleWithFunction, VariableValue } from "../style-sheet";
import { validProperties } from "./valid-styles";
import { TransformsStyle } from "react-native";
import { flatten } from "./flatten";
export type StylesAndTopics = Required<
Pick<Atom, "styles" | "topics" | "variables">
@@ -58,7 +59,20 @@ export function getDeclarations(block: Block) {
default: {
if (node.value.type === "Raw") {
if (node.property.startsWith("--")) {
atom.variables.push({ [node.property]: node.value.value.trim() });
const ast = parse(node.value.value.trim(), { context: "value" });
if (ast.type !== "Value") {
return;
}
const value = parseStyleValue(ast.children.toArray()[0], []);
if (typeof value === "object" && !("function" in value)) {
return;
}
if (value !== undefined) {
atom.variables.push({ [node.property]: value });
}
}
} else {
return pushStyle(
@@ -72,17 +86,9 @@ export function getDeclarations(block: Block) {
},
});
let styles: AtomStyle = {};
for (const style of atom.styles) {
for (const [key, value] of Object.entries(style)) {
styles = setValue(styles, key, value);
}
}
return {
...atom,
styles,
styles: flatten(atom.styles),
};
}
@@ -116,10 +122,11 @@ function parseStyleValue(
node: StyleValue | null | undefined,
topics: string[]
): StyleValue | StyleValue[] | undefined {
if (!node) return [];
if (!node) return;
if (typeof node === "string") {
return node;
const maybeNumber = Number.parseFloat(node);
return Number.isNaN(maybeNumber) ? node : maybeNumber;
}
if (typeof node === "number") {
@@ -163,16 +170,54 @@ function parseStyleValue(
switch (node.name) {
case "pixelRatio":
return { function: "pixelRatio", values: [] };
case "platformColor": {
const children = node.children
.toArray()
.map((child) => parseStyleValue(child, topics))
.filter((child) => Boolean(child));
return {
function: "platformColor",
values: children as unknown as VariableValue[],
};
}
case "var": {
const value = parseStyleValue(node.children.shift()?.data, topics);
const children = node.children.toArray();
const variableName = parseStyleValue(children[0], topics);
if (typeof value !== "string") return [];
if (typeof variableName !== "string") return [];
topics.push(value);
const values: StyleWithFunction["values"] = [variableName];
topics.push(variableName);
if (children.length === 3) {
const defaultChild = children[2];
if (defaultChild.type === "Raw") {
const ast = parse(defaultChild.value, {
context: "value",
});
if (ast.type === "Value") {
const defaultValue = parseStyleValue(
ast.children.toArray()[0],
[]
);
if (typeof defaultValue === "object") {
if ("function" in defaultValue) {
values.push(defaultValue);
}
} else if (defaultValue) {
values.push(defaultValue);
}
}
}
}
return {
function: "var",
values: [value],
values,
};
}
default: {
@@ -204,26 +249,6 @@ function parseStyleValue(
) as unknown as Transform;
}
function setValue<T extends Record<string, unknown>>(
object: T,
is: string | string[],
value: unknown
): T {
if (typeof is == "string") {
return setValue<T>(object, is.split("."), value);
} else if (is.length == 1) {
(object as Record<string, unknown>)[is[0]] = value;
return object;
} else {
(object as Record<string, unknown>)[is[0]] = setValue<T>(
(object[is[0]] || {}) as T,
is.slice(1),
value
);
return object;
}
}
function border(atom: StylesAndTopics, node: Declaration) {
if (node.value.type !== "Value") {
return;

View File

@@ -8,6 +8,7 @@ import { CreateOptions } from "../style-sheet";
import { parseMediaQuery, MediaQueryMeta } from "./media-query";
import { getDeclarations } from "./declarations";
import { getSelector } from "./selector";
import { flatten } from "./flatten";
const skip = (walk as unknown as Record<string, unknown>).skip;
@@ -105,13 +106,11 @@ function addRule(
// Invalid selector, skip it
if (!selector) return;
if (selector === ":root") {
createOptions[selector] ??= { variables };
return;
}
if (selector === "dark") {
createOptions[selector] ??= { variables };
if (selector === ":root" || selector === "dark") {
if (styles.fontSize) {
variables.push({ "--rem": styles.fontSize });
}
createOptions[selector] ??= { variables: [flatten(variables)] };
return;
}

View File

@@ -0,0 +1,32 @@
export function flatten<T extends Record<string, unknown>>(
objectArray: T[]
): T {
let returnObject = {} as T;
for (const object of objectArray) {
for (const [key, value] of Object.entries(object)) {
returnObject = setValue(returnObject, key, value);
}
}
return returnObject;
}
function setValue<T extends Record<string, unknown>>(
object: T,
is: string | string[],
value: unknown
): T {
if (typeof is == "string") {
return setValue<T>(object, is.split("."), value);
} else if (is.length == 1) {
(object as Record<string, unknown>)[is[0]] = value;
return object;
} else {
(object as Record<string, unknown>)[is[0]] = setValue<T>(
(object[is[0]] || {}) as T,
is.slice(1),
value
);
return object;
}
}

View File

@@ -24,7 +24,7 @@ export type AtomStyle = {
export type StyleWithFunction = {
function: string;
values: Array<StyleWithFunction | string | number>;
values: Array<VariableValue>;
};
export type VariableValue =