Skip to content

Commit

Permalink
refactor: organize css functions
Browse files Browse the repository at this point in the history
  • Loading branch information
juanrgm committed May 29, 2022
1 parent 5f16d05 commit e74745b
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 130 deletions.
6 changes: 6 additions & 0 deletions .changeset/five-pens-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@suid/css": patch
"@suid/system": patch
---

Refactor `css` package
4 changes: 4 additions & 0 deletions packages/css/src/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ export function isVar(value: string) {
return value.startsWith("--");
}

export function isPrivateVar(value: string) {
return value.startsWith("__");
}

export function isSelector(value: string) {
return /[^a-z-]/i.test(value) && !isVar(value);
}
Expand Down
2 changes: 0 additions & 2 deletions packages/css/src/cache.ts

This file was deleted.

50 changes: 0 additions & 50 deletions packages/css/src/createStyle.ts

This file was deleted.

58 changes: 58 additions & 0 deletions packages/css/src/createStyleObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import render from "./render";
import randomString from "@suid/utils/randomString";
import resolveFunction from "@suid/utils/resolveFunction";
import toArray from "@suid/utils/toArray";

export type StyleObject = {
id: string;
name: string;
className: string;
rules: string;
};

export type StyleObjectCache = Map<string, StyleObject>;

export type StyleProps =
| Record<string, any>
| string
| (Record<string, any> | string)[]
| (() => Record<string, any> | string | (Record<string, any> | string)[]);

export type StyleObjectOptions = {
name: string;
props: StyleProps;
extraProperties?: Record<string, (value: any) => any>;
cache?: StyleObjectCache;
};

function create(name: string, rules: string) {
const id = randomString().slice(0, 6);
return {
id,
name: name,
className: `${name}-${id}`,
rules: rules.replaceAll(`$id`, `${id}`),
};
}

function createStyleObject(options: StyleObjectOptions) {
const className = `${options.name}-$id`;
const propsValues = toArray(resolveFunction(options.props));
const rules = propsValues
.map((v) =>
typeof v === "string"
? `.${className} {\n${v}\n}`
: render(v, [`.${className}`], {
extraProperties: options.extraProperties,
}).join("\n")
)
.join("\n");

const styleObject = options.cache?.get(rules) || create(options.name, rules);

if (options.cache) options.cache.set(rules, styleObject);

return styleObject;
}

export default createStyleObject;
28 changes: 28 additions & 0 deletions packages/css/src/dom/appendStyleElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import createStyleElement from "./createStyleElement";
import registerStyleElementUsage from "./registerStyleElementUsage";
import setStyleElementText from "./setStyleElementText";

function appendStyleElement(
css: string | string[],
id?: string | false
): {
element: HTMLStyleElement;
id: string | false | undefined;
} {
if (Array.isArray(css)) css = css.join("\n");
const head = document.head || document.getElementsByTagName("head")[0];
const prevElement = id && document.getElementById(id);
if (prevElement && prevElement instanceof HTMLStyleElement) {
setStyleElementText(prevElement, css);
registerStyleElementUsage(prevElement);
return { element: prevElement, id };
} else {
if (prevElement) prevElement.remove();
const element = createStyleElement(css, id);
registerStyleElementUsage(element);
head.appendChild(element);
return { element, id };
}
}

export default appendStyleElement;
11 changes: 11 additions & 0 deletions packages/css/src/dom/createStyleElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import setStyleElementText from "./setStyleElementText";

function createStyleElement(css: string, id?: string | false) {
const element = document.createElement("style");
if (id) element.setAttribute("id", id);
element.type = "text/css";
setStyleElementText(element, css);
return element;
}

export default createStyleElement;
5 changes: 5 additions & 0 deletions packages/css/src/dom/findStyleElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function findStyleElement(id: string) {
return document.getElementById(id) as HTMLStyleElement | undefined;
}

export default findStyleElement;
7 changes: 7 additions & 0 deletions packages/css/src/dom/registerStyleElementUsage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function registerStyleElementUsage(style: HTMLStyleElement) {
let uses = Number(style.getAttribute("data-uses"));
uses++;
style.setAttribute("data-uses", uses.toString());
}

export default registerStyleElementUsage;
10 changes: 10 additions & 0 deletions packages/css/src/dom/setStyleElementText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function setStyleElementText(element: HTMLStyleElement, text: string) {
if ("styleSheet" in element) {
(element as any)["styleSheet"].cssText = text;
} else {
element.innerText = "";
element.appendChild(document.createTextNode(text));
}
}

export default setStyleElementText;
11 changes: 11 additions & 0 deletions packages/css/src/dom/unregisterStyleElementUsage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function unregisterStyleElementUsage(style: HTMLStyleElement) {
let uses = Number(style.getAttribute("data-uses"));
uses--;
if (uses <= 0) {
style.remove();
} else {
style.setAttribute("data-uses", uses.toString());
}
}

export default unregisterStyleElementUsage;
5 changes: 4 additions & 1 deletion packages/css/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isMediaQuery,
isSelector,
isVar,
isPrivateVar,
} from "./assert";
import snakeCase from "@suid/utils/snakeCase";

Expand Down Expand Up @@ -44,7 +45,9 @@ function render(
const rules: string[] = [];
for (let propKey in css) {
const propValue = css[propKey];
if (isGlobalSelector(propKey)) {
if (isPrivateVar(propKey)) {
continue;
} else if (isGlobalSelector(propKey)) {
for (const selector in propValue) {
rules.push(
...renderSelector(selector, propValue[selector], [], options)
Expand Down
59 changes: 0 additions & 59 deletions packages/css/src/style-element.ts

This file was deleted.

39 changes: 21 additions & 18 deletions packages/system/src/createSxClass.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import mergeSxObjects from "./mergeSxObjects";
import SxProps from "./sxProps";
import createStyle from "@suid/css/createStyle";
import {
appendStyle,
findStyle,
subscribeStyle,
unsubscribeStyle,
} from "@suid/css/style-element";
import createStyleObject, { StyleObject } from "@suid/css/createStyleObject";
import appendStyleElement from "@suid/css/dom/appendStyleElement";
import findStyleElement from "@suid/css/dom/findStyleElement";
import registerStyleElementUsage from "@suid/css/dom/registerStyleElementUsage";
import unregisterStyleElementUsage from "@suid/css/dom/unregisterStyleElementUsage";
import { createRenderEffect, createSignal, onCleanup } from "solid-js";

export const resolvedPropKey = "__resolved";

const cache = new Map<string, StyleObject>();

function createSxClass(value: () => SxProps | undefined) {
const [name, setName] = createSignal("");
let styleElement: HTMLStyleElement | undefined;
createRenderEffect<
{ className?: string; styleElement?: HTMLStyleElement } | undefined
>((prevResult) => {
const v = value();
let result: ReturnType<typeof createStyle> | undefined;
let styleObject: StyleObject | undefined;
if (v) {
const styles = (Array.isArray(v) ? v : [v])
// https://github.com/microsoft/TypeScript/issues/44408
Expand All @@ -32,39 +32,42 @@ function createSxClass(value: () => SxProps | undefined) {
}, {});

delete css.name;
delete css[resolvedPropKey];

result = createStyle({
styleObject = createStyleObject({
name: "css",
props: css,
cache,
});

styleElement = findStyle(result.cacheId);
styleElement = findStyleElement(styleObject.id);

if (styleElement) {
subscribeStyle(styleElement);
registerStyleElementUsage(styleElement);
} else {
styleElement = appendStyle(result.rules, result.cacheId).element;
styleElement = appendStyleElement(
styleObject.rules,
styleObject.id
).element;
}
}

if (prevResult?.styleElement) {
unsubscribeStyle(prevResult.styleElement);
unregisterStyleElementUsage(prevResult.styleElement);
}

if (typeof result?.className === "string") {
setName(result.className);
if (typeof styleObject?.className === "string") {
setName(styleObject.className);
} else {
setName("");
}

return {
className: result?.className,
className: styleObject?.className,
styleElement,
};
}, undefined);
onCleanup(() => {
if (styleElement) unsubscribeStyle(styleElement);
if (styleElement) unregisterStyleElementUsage(styleElement);
});
return name;
}
Expand Down

0 comments on commit e74745b

Please sign in to comment.