@webstudio-is/css-engine 0.91.0 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__generated__/types.js +1 -0
- package/lib/core/compare-media.js +2 -4
- package/{src/core/compare-media.test.ts → lib/core/compare-media.test.js} +7 -9
- package/lib/core/create-css-engine.js +2 -4
- package/lib/core/css-engine.js +2 -4
- package/lib/core/css-engine.stories.js +48 -0
- package/{src/core/css-engine.test.ts → lib/core/css-engine.test.js} +51 -88
- package/lib/core/equal-media.js +2 -4
- package/{src/core/equal-media.test.ts → lib/core/equal-media.test.js} +1 -3
- package/lib/core/find-applicable-media.js +2 -4
- package/{src/core/find-applicable-media.test.ts → lib/core/find-applicable-media.test.js} +7 -8
- package/lib/core/index.js +2 -4
- package/lib/core/match-media.js +2 -4
- package/{src/core/match-media.test.ts → lib/core/match-media.test.js} +1 -3
- package/lib/core/rules.js +5 -10
- package/lib/core/style-element.js +2 -4
- package/lib/core/style-sheet.js +2 -4
- package/lib/core/to-property.js +2 -4
- package/{src/core/to-property.test.ts → lib/core/to-property.test.js} +1 -1
- package/lib/core/to-value.js +2 -4
- package/{src/core/to-value.test.ts → lib/core/to-value.test.js} +21 -32
- package/lib/index.js +2 -0
- package/lib/schema.js +98 -0
- package/lib/types/__generated__/types.d.ts +2 -0
- package/lib/types/core/css-engine.d.ts +1 -1
- package/lib/types/core/rules.d.ts +2 -2
- package/lib/types/core/to-property.d.ts +1 -1
- package/lib/types/core/to-value.d.ts +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/schema.d.ts +3233 -0
- package/package.json +12 -15
- package/lib/cjs/core/compare-media.js +0 -38
- package/lib/cjs/core/create-css-engine.js +0 -27
- package/lib/cjs/core/css-engine.js +0 -122
- package/lib/cjs/core/equal-media.js +0 -26
- package/lib/cjs/core/find-applicable-media.js +0 -33
- package/lib/cjs/core/index.js +0 -32
- package/lib/cjs/core/match-media.js +0 -28
- package/lib/cjs/core/rules.js +0 -187
- package/lib/cjs/core/style-element.js +0 -61
- package/lib/cjs/core/style-sheet.js +0 -36
- package/lib/cjs/core/to-property.js +0 -40
- package/lib/cjs/core/to-value.js +0 -98
- package/lib/cjs/index.js +0 -18
- package/lib/cjs/package.json +0 -1
- package/src/core/compare-media.ts +0 -30
- package/src/core/create-css-engine.ts +0 -5
- package/src/core/css-engine.stories.tsx +0 -48
- package/src/core/css-engine.ts +0 -128
- package/src/core/equal-media.ts +0 -5
- package/src/core/find-applicable-media.ts +0 -20
- package/src/core/index.ts +0 -15
- package/src/core/match-media.ts +0 -8
- package/src/core/rules.ts +0 -182
- package/src/core/style-element.ts +0 -38
- package/src/core/style-sheet.ts +0 -15
- package/src/core/to-property.ts +0 -12
- package/src/core/to-value.ts +0 -108
- package/src/index.ts +0 -1
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var to_property_exports = {};
|
|
30
|
-
__export(to_property_exports, {
|
|
31
|
-
toProperty: () => toProperty
|
|
32
|
-
});
|
|
33
|
-
module.exports = __toCommonJS(to_property_exports);
|
|
34
|
-
var import_hyphenate_style_name = __toESM(require("hyphenate-style-name"), 1);
|
|
35
|
-
const toProperty = (property) => {
|
|
36
|
-
if (property === "backgroundClip") {
|
|
37
|
-
return "-webkit-background-clip";
|
|
38
|
-
}
|
|
39
|
-
return (0, import_hyphenate_style_name.default)(property);
|
|
40
|
-
};
|
package/lib/cjs/core/to-value.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var to_value_exports = {};
|
|
20
|
-
__export(to_value_exports, {
|
|
21
|
-
toValue: () => toValue
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(to_value_exports);
|
|
24
|
-
var import_error_utils = require("@webstudio-is/error-utils");
|
|
25
|
-
var import_fonts = require("@webstudio-is/fonts");
|
|
26
|
-
const fallbackTransform = (styleValue) => {
|
|
27
|
-
if (styleValue.type === "fontFamily") {
|
|
28
|
-
const firstFontFamily = styleValue.value[0];
|
|
29
|
-
const fallbacks = import_fonts.SYSTEM_FONTS.get(firstFontFamily);
|
|
30
|
-
const fontFamily = [...styleValue.value];
|
|
31
|
-
if (Array.isArray(fallbacks)) {
|
|
32
|
-
fontFamily.push(...fallbacks);
|
|
33
|
-
} else {
|
|
34
|
-
fontFamily.push(import_fonts.DEFAULT_FONT_FALLBACK);
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
type: "fontFamily",
|
|
38
|
-
value: fontFamily
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
const toValue = (styleValue, transformValue) => {
|
|
43
|
-
if (styleValue === void 0) {
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
const transformedValue = transformValue?.(styleValue) ?? fallbackTransform(styleValue);
|
|
47
|
-
const value = transformedValue ?? styleValue;
|
|
48
|
-
if (value.type === "unit") {
|
|
49
|
-
return value.value + (value.unit === "number" ? "" : value.unit);
|
|
50
|
-
}
|
|
51
|
-
if (value.type === "fontFamily") {
|
|
52
|
-
return value.value.join(", ");
|
|
53
|
-
}
|
|
54
|
-
if (value.type === "var") {
|
|
55
|
-
const fallbacks = [];
|
|
56
|
-
for (const fallback of value.fallbacks) {
|
|
57
|
-
fallbacks.push(toValue(fallback, transformValue));
|
|
58
|
-
}
|
|
59
|
-
const fallbacksString = fallbacks.length > 0 ? `, ${fallbacks.join(", ")}` : "";
|
|
60
|
-
return `var(--${value.value}${fallbacksString})`;
|
|
61
|
-
}
|
|
62
|
-
if (value.type === "keyword") {
|
|
63
|
-
return value.value;
|
|
64
|
-
}
|
|
65
|
-
if (value.type === "invalid") {
|
|
66
|
-
return value.value;
|
|
67
|
-
}
|
|
68
|
-
if (value.type === "unset") {
|
|
69
|
-
return value.value;
|
|
70
|
-
}
|
|
71
|
-
if (value.type === "rgb") {
|
|
72
|
-
return `rgba(${value.r}, ${value.g}, ${value.b}, ${value.alpha})`;
|
|
73
|
-
}
|
|
74
|
-
if (value.type === "image") {
|
|
75
|
-
if (value.hidden || value.value.type !== "url") {
|
|
76
|
-
return "none";
|
|
77
|
-
}
|
|
78
|
-
return `url(${value.value.url})`;
|
|
79
|
-
}
|
|
80
|
-
if (value.type === "unparsed") {
|
|
81
|
-
if (value.hidden) {
|
|
82
|
-
return "none";
|
|
83
|
-
}
|
|
84
|
-
return value.value;
|
|
85
|
-
}
|
|
86
|
-
if (value.type === "layers") {
|
|
87
|
-
const valueString = value.value.filter(
|
|
88
|
-
(layer) => "hidden" in layer === false || "hidden" in layer && layer.hidden === false
|
|
89
|
-
).map((layer) => {
|
|
90
|
-
return toValue(layer, transformValue);
|
|
91
|
-
}).join(", ");
|
|
92
|
-
return valueString === "" ? "none" : valueString;
|
|
93
|
-
}
|
|
94
|
-
if (value.type === "tuple") {
|
|
95
|
-
return value.value.map((value2) => toValue(value2, transformValue)).join(" ");
|
|
96
|
-
}
|
|
97
|
-
return (0, import_error_utils.captureError)(new Error("Unknown value type"), value);
|
|
98
|
-
};
|
package/lib/cjs/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __copyProps = (to, from, except, desc) => {
|
|
7
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
-
for (let key of __getOwnPropNames(from))
|
|
9
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
-
}
|
|
12
|
-
return to;
|
|
13
|
-
};
|
|
14
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
15
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
-
var src_exports = {};
|
|
17
|
-
module.exports = __toCommonJS(src_exports);
|
|
18
|
-
__reExport(src_exports, require("./core"), module.exports);
|
package/lib/cjs/package.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"type":"commonjs"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { MediaRuleOptions } from "./rules";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Sort by minWidth descending or maxWidth ascending
|
|
5
|
-
* We want media querries with bigger minWidth to override the smaller once, but the smaller maxWidth to override the bigger once.
|
|
6
|
-
*/
|
|
7
|
-
export const compareMedia = (
|
|
8
|
-
optionA: MediaRuleOptions,
|
|
9
|
-
optionB: MediaRuleOptions
|
|
10
|
-
) => {
|
|
11
|
-
// Ensures a media with no min/max is always first
|
|
12
|
-
if (optionA.minWidth === undefined && optionA.maxWidth === undefined) {
|
|
13
|
-
return -1;
|
|
14
|
-
}
|
|
15
|
-
if (optionB.minWidth === undefined && optionB.maxWidth === undefined) {
|
|
16
|
-
return 1;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Both are defined by minWidth, put the bigger one first
|
|
20
|
-
if (optionA.minWidth !== undefined && optionB.minWidth !== undefined) {
|
|
21
|
-
return optionA.minWidth - optionB.minWidth;
|
|
22
|
-
}
|
|
23
|
-
// Both are defined by maxWidth, put the smaller one first
|
|
24
|
-
if (optionA.maxWidth !== undefined && optionB.maxWidth !== undefined) {
|
|
25
|
-
return optionB.maxWidth - optionA.maxWidth;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Media with maxWith should render before minWith just to have the same sorting visually in the UI as in CSSOM.
|
|
29
|
-
return "minWidth" in optionA ? 1 : -1;
|
|
30
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { CssEngine } from "./css-engine";
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
component: "CssEngine",
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const style0 = {
|
|
8
|
-
color: { type: "keyword", value: "red" },
|
|
9
|
-
} as const;
|
|
10
|
-
|
|
11
|
-
const mediaRuleOptions0 = { minWidth: 0 } as const;
|
|
12
|
-
const mediaId = "0";
|
|
13
|
-
|
|
14
|
-
export const Basic = () => {
|
|
15
|
-
const engine = new CssEngine({ name: "test" });
|
|
16
|
-
engine.addMediaRule(mediaId, mediaRuleOptions0);
|
|
17
|
-
const rule = engine.addStyleRule(".test", {
|
|
18
|
-
style: style0,
|
|
19
|
-
breakpoint: "0",
|
|
20
|
-
});
|
|
21
|
-
engine.render();
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<div className="test">Should be red</div>
|
|
25
|
-
<button
|
|
26
|
-
onClick={() => {
|
|
27
|
-
rule.styleMap.set("color", { type: "keyword", value: "green" });
|
|
28
|
-
engine.render();
|
|
29
|
-
}}
|
|
30
|
-
>
|
|
31
|
-
Make it green
|
|
32
|
-
</button>
|
|
33
|
-
<button
|
|
34
|
-
onClick={() => {
|
|
35
|
-
engine.addStyleRule(".test", {
|
|
36
|
-
style: {
|
|
37
|
-
backgroundColor: { type: "keyword", value: "yellow" },
|
|
38
|
-
},
|
|
39
|
-
breakpoint: "0",
|
|
40
|
-
});
|
|
41
|
-
engine.render();
|
|
42
|
-
}}
|
|
43
|
-
>
|
|
44
|
-
Add rule with yellow background
|
|
45
|
-
</button>
|
|
46
|
-
</>
|
|
47
|
-
);
|
|
48
|
-
};
|
package/src/core/css-engine.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import type { Style } from "@webstudio-is/css-data";
|
|
2
|
-
import {
|
|
3
|
-
FontFaceRule,
|
|
4
|
-
MediaRule,
|
|
5
|
-
PlaintextRule,
|
|
6
|
-
StyleRule,
|
|
7
|
-
type FontFaceOptions,
|
|
8
|
-
type MediaRuleOptions,
|
|
9
|
-
} from "./rules";
|
|
10
|
-
import { compareMedia } from "./compare-media";
|
|
11
|
-
import { StyleElement } from "./style-element";
|
|
12
|
-
import { StyleSheet } from "./style-sheet";
|
|
13
|
-
import type { TransformValue } from "./to-value";
|
|
14
|
-
|
|
15
|
-
const defaultMediaRuleId = "__default-media-rule__";
|
|
16
|
-
|
|
17
|
-
type CssRule = {
|
|
18
|
-
style: Style;
|
|
19
|
-
breakpoint?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type CssEngineOptions = { name?: string };
|
|
23
|
-
|
|
24
|
-
export class CssEngine {
|
|
25
|
-
#element;
|
|
26
|
-
#mediaRules: Map<string, MediaRule> = new Map();
|
|
27
|
-
#plainRules: Map<string, PlaintextRule> = new Map();
|
|
28
|
-
#fontFaceRules: Array<FontFaceRule> = [];
|
|
29
|
-
#sheet: StyleSheet;
|
|
30
|
-
#isDirty = false;
|
|
31
|
-
#cssText = "";
|
|
32
|
-
constructor({ name }: CssEngineOptions) {
|
|
33
|
-
this.#element = new StyleElement(name);
|
|
34
|
-
this.#sheet = new StyleSheet(this.#element);
|
|
35
|
-
}
|
|
36
|
-
addMediaRule(id: string, options?: MediaRuleOptions) {
|
|
37
|
-
let mediaRule = this.#mediaRules.get(id);
|
|
38
|
-
if (mediaRule === undefined) {
|
|
39
|
-
mediaRule = new MediaRule(options);
|
|
40
|
-
this.#mediaRules.set(id, mediaRule);
|
|
41
|
-
this.#isDirty = true;
|
|
42
|
-
return mediaRule;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (options) {
|
|
46
|
-
mediaRule.options = options;
|
|
47
|
-
this.#isDirty = true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return mediaRule;
|
|
51
|
-
}
|
|
52
|
-
addStyleRule(
|
|
53
|
-
selectorText: string,
|
|
54
|
-
rule: CssRule,
|
|
55
|
-
transformValue?: TransformValue
|
|
56
|
-
) {
|
|
57
|
-
const mediaRule = this.addMediaRule(rule.breakpoint || defaultMediaRuleId);
|
|
58
|
-
this.#isDirty = true;
|
|
59
|
-
const styleRule = new StyleRule(selectorText, rule.style, transformValue);
|
|
60
|
-
styleRule.onChange = this.#onChangeRule;
|
|
61
|
-
if (mediaRule === undefined) {
|
|
62
|
-
// Should be impossible to reach.
|
|
63
|
-
throw new Error("No media rule found");
|
|
64
|
-
}
|
|
65
|
-
mediaRule.insertRule(styleRule);
|
|
66
|
-
return styleRule;
|
|
67
|
-
}
|
|
68
|
-
addPlaintextRule(cssText: string) {
|
|
69
|
-
const rule = this.#plainRules.get(cssText);
|
|
70
|
-
if (rule !== undefined) {
|
|
71
|
-
return rule;
|
|
72
|
-
}
|
|
73
|
-
this.#isDirty = true;
|
|
74
|
-
return this.#plainRules.set(cssText, new PlaintextRule(cssText));
|
|
75
|
-
}
|
|
76
|
-
addFontFaceRule(options: FontFaceOptions) {
|
|
77
|
-
this.#isDirty = true;
|
|
78
|
-
return this.#fontFaceRules.push(new FontFaceRule(options));
|
|
79
|
-
}
|
|
80
|
-
clear() {
|
|
81
|
-
this.#mediaRules.clear();
|
|
82
|
-
this.#plainRules.clear();
|
|
83
|
-
this.#fontFaceRules = [];
|
|
84
|
-
this.#isDirty = true;
|
|
85
|
-
}
|
|
86
|
-
render() {
|
|
87
|
-
this.#element.mount();
|
|
88
|
-
// This isn't going to do anything if the `cssText` hasn't changed.
|
|
89
|
-
this.#sheet.replaceSync(this.cssText);
|
|
90
|
-
}
|
|
91
|
-
unmount() {
|
|
92
|
-
this.#element.unmount();
|
|
93
|
-
}
|
|
94
|
-
setAttribute(name: string, value: string) {
|
|
95
|
-
this.#element.setAttribute(name, value);
|
|
96
|
-
}
|
|
97
|
-
getAttribute(name: string) {
|
|
98
|
-
return this.#element.getAttribute(name);
|
|
99
|
-
}
|
|
100
|
-
get cssText() {
|
|
101
|
-
if (this.#isDirty === false) {
|
|
102
|
-
return this.#cssText;
|
|
103
|
-
}
|
|
104
|
-
this.#isDirty = false;
|
|
105
|
-
const css: Array<string> = [];
|
|
106
|
-
|
|
107
|
-
css.push(...this.#fontFaceRules.map((rule) => rule.cssText));
|
|
108
|
-
for (const plaintextRule of this.#plainRules.values()) {
|
|
109
|
-
css.push(plaintextRule.cssText);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const sortedMediaRules = Array.from(this.#mediaRules.values()).sort(
|
|
113
|
-
(ruleA, ruleB) => compareMedia(ruleA.options, ruleB.options)
|
|
114
|
-
);
|
|
115
|
-
for (const mediaRule of sortedMediaRules) {
|
|
116
|
-
const { cssText } = mediaRule;
|
|
117
|
-
if (cssText !== "") {
|
|
118
|
-
css.push(cssText);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
this.#cssText = css.join("\n");
|
|
122
|
-
return this.#cssText;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
#onChangeRule = () => {
|
|
126
|
-
this.#isDirty = true;
|
|
127
|
-
};
|
|
128
|
-
}
|
package/src/core/equal-media.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { compareMedia } from "./compare-media";
|
|
2
|
-
import { matchMedia } from "./match-media";
|
|
3
|
-
import type { MediaRuleOptions } from "./rules";
|
|
4
|
-
|
|
5
|
-
// Find media rule that matches the given width when rendered.
|
|
6
|
-
export const findApplicableMedia = <Media extends MediaRuleOptions>(
|
|
7
|
-
media: Array<Media>,
|
|
8
|
-
width: number
|
|
9
|
-
) => {
|
|
10
|
-
const sortedMedia = [...media]
|
|
11
|
-
.sort(compareMedia)
|
|
12
|
-
// Reverse order is needed because the last rule in CSSOM has higher source order specificity.
|
|
13
|
-
.reverse();
|
|
14
|
-
|
|
15
|
-
for (const options of sortedMedia) {
|
|
16
|
-
if (matchMedia(options, width)) {
|
|
17
|
-
return options;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
package/src/core/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export { CssEngine } from "./css-engine";
|
|
2
|
-
export type {
|
|
3
|
-
AnyRule,
|
|
4
|
-
StyleRule,
|
|
5
|
-
MediaRule,
|
|
6
|
-
PlaintextRule,
|
|
7
|
-
FontFaceRule,
|
|
8
|
-
} from "./rules";
|
|
9
|
-
export * from "./create-css-engine";
|
|
10
|
-
export * from "./to-value";
|
|
11
|
-
export * from "./to-property";
|
|
12
|
-
export * from "./match-media";
|
|
13
|
-
export * from "./equal-media";
|
|
14
|
-
export * from "./compare-media";
|
|
15
|
-
export * from "./find-applicable-media";
|
package/src/core/match-media.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { MediaRuleOptions } from "./rules";
|
|
2
|
-
|
|
3
|
-
// This will match a breakpoint that has no min/max.
|
|
4
|
-
export const matchMedia = (options: MediaRuleOptions, width: number) => {
|
|
5
|
-
const minWidth = options.minWidth ?? Number.MIN_SAFE_INTEGER;
|
|
6
|
-
const maxWidth = options.maxWidth ?? Number.MAX_SAFE_INTEGER;
|
|
7
|
-
return width >= minWidth && width <= maxWidth;
|
|
8
|
-
};
|
package/src/core/rules.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import type { Style, StyleProperty, StyleValue } from "@webstudio-is/css-data";
|
|
2
|
-
import { toValue, type TransformValue } from "./to-value";
|
|
3
|
-
import { toProperty } from "./to-property";
|
|
4
|
-
|
|
5
|
-
class StylePropertyMap {
|
|
6
|
-
#styleMap: Map<StyleProperty, StyleValue | undefined> = new Map();
|
|
7
|
-
#isDirty = false;
|
|
8
|
-
#string = "";
|
|
9
|
-
#indent = 0;
|
|
10
|
-
#transformValue?: TransformValue;
|
|
11
|
-
onChange?: () => void;
|
|
12
|
-
constructor(transformValue?: TransformValue) {
|
|
13
|
-
this.#transformValue = transformValue;
|
|
14
|
-
}
|
|
15
|
-
setTransformer(transformValue: TransformValue) {
|
|
16
|
-
this.#transformValue = transformValue;
|
|
17
|
-
}
|
|
18
|
-
set(property: StyleProperty, value?: StyleValue) {
|
|
19
|
-
this.#styleMap.set(property, value);
|
|
20
|
-
this.#isDirty = true;
|
|
21
|
-
this.onChange?.();
|
|
22
|
-
}
|
|
23
|
-
has(property: StyleProperty) {
|
|
24
|
-
return this.#styleMap.has(property);
|
|
25
|
-
}
|
|
26
|
-
get size() {
|
|
27
|
-
return this.#styleMap.size;
|
|
28
|
-
}
|
|
29
|
-
keys() {
|
|
30
|
-
return this.#styleMap.keys();
|
|
31
|
-
}
|
|
32
|
-
delete(property: StyleProperty) {
|
|
33
|
-
this.#styleMap.delete(property);
|
|
34
|
-
this.#isDirty = true;
|
|
35
|
-
this.onChange?.();
|
|
36
|
-
}
|
|
37
|
-
clear() {
|
|
38
|
-
this.#styleMap.clear();
|
|
39
|
-
this.#isDirty = true;
|
|
40
|
-
this.onChange?.();
|
|
41
|
-
}
|
|
42
|
-
toString({ indent = 0 } = {}) {
|
|
43
|
-
if (this.#isDirty === false && indent === this.#indent) {
|
|
44
|
-
return this.#string;
|
|
45
|
-
}
|
|
46
|
-
this.#indent = indent;
|
|
47
|
-
const block: Array<string> = [];
|
|
48
|
-
const spaces = " ".repeat(indent);
|
|
49
|
-
for (const [property, value] of this.#styleMap) {
|
|
50
|
-
if (value === undefined) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
block.push(
|
|
54
|
-
`${spaces}${toProperty(property)}: ${toValue(
|
|
55
|
-
value,
|
|
56
|
-
this.#transformValue
|
|
57
|
-
)}`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
this.#string = block.join(";\n");
|
|
61
|
-
this.#isDirty = false;
|
|
62
|
-
return this.#string;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class StyleRule {
|
|
67
|
-
styleMap;
|
|
68
|
-
selectorText;
|
|
69
|
-
onChange?: () => void;
|
|
70
|
-
constructor(
|
|
71
|
-
selectorText: string,
|
|
72
|
-
style: Style,
|
|
73
|
-
transformValue?: TransformValue
|
|
74
|
-
) {
|
|
75
|
-
this.styleMap = new StylePropertyMap(transformValue);
|
|
76
|
-
this.selectorText = selectorText;
|
|
77
|
-
let property: StyleProperty;
|
|
78
|
-
for (property in style) {
|
|
79
|
-
this.styleMap.set(property, style[property]);
|
|
80
|
-
}
|
|
81
|
-
this.styleMap.onChange = this.#onChange;
|
|
82
|
-
}
|
|
83
|
-
#onChange = () => {
|
|
84
|
-
this.onChange?.();
|
|
85
|
-
};
|
|
86
|
-
get cssText() {
|
|
87
|
-
return this.toString();
|
|
88
|
-
}
|
|
89
|
-
toString(options = { indent: 0 }) {
|
|
90
|
-
const spaces = " ".repeat(options.indent);
|
|
91
|
-
return `${spaces}${this.selectorText} {\n${this.styleMap.toString({
|
|
92
|
-
indent: options.indent + 2,
|
|
93
|
-
})}\n${spaces}}`;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export type MediaRuleOptions = {
|
|
98
|
-
minWidth?: number;
|
|
99
|
-
maxWidth?: number;
|
|
100
|
-
mediaType?: "all" | "screen" | "print";
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export class MediaRule {
|
|
104
|
-
options: MediaRuleOptions;
|
|
105
|
-
rules: Array<StyleRule | PlaintextRule> = [];
|
|
106
|
-
#mediaType;
|
|
107
|
-
constructor(options: MediaRuleOptions = {}) {
|
|
108
|
-
this.options = options;
|
|
109
|
-
this.#mediaType = options.mediaType ?? "all";
|
|
110
|
-
}
|
|
111
|
-
insertRule(rule: StyleRule | PlaintextRule) {
|
|
112
|
-
this.rules.push(rule);
|
|
113
|
-
return rule;
|
|
114
|
-
}
|
|
115
|
-
get cssText() {
|
|
116
|
-
return this.toString();
|
|
117
|
-
}
|
|
118
|
-
toString() {
|
|
119
|
-
if (this.rules.length === 0) {
|
|
120
|
-
return "";
|
|
121
|
-
}
|
|
122
|
-
const rules = [];
|
|
123
|
-
for (const rule of this.rules) {
|
|
124
|
-
rules.push(rule.toString({ indent: 2 }));
|
|
125
|
-
}
|
|
126
|
-
let conditionText = "";
|
|
127
|
-
const { minWidth, maxWidth } = this.options;
|
|
128
|
-
if (minWidth !== undefined) {
|
|
129
|
-
conditionText = ` and (min-width: ${minWidth}px)`;
|
|
130
|
-
}
|
|
131
|
-
if (maxWidth !== undefined) {
|
|
132
|
-
conditionText += ` and (max-width: ${maxWidth}px)`;
|
|
133
|
-
}
|
|
134
|
-
return `@media ${this.#mediaType}${conditionText} {\n${rules.join(
|
|
135
|
-
"\n"
|
|
136
|
-
)}\n}`;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export class PlaintextRule {
|
|
141
|
-
cssText;
|
|
142
|
-
styleMap = new StylePropertyMap();
|
|
143
|
-
constructor(cssText: string) {
|
|
144
|
-
this.cssText = cssText;
|
|
145
|
-
}
|
|
146
|
-
toString() {
|
|
147
|
-
return this.cssText;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export type FontFaceOptions = {
|
|
152
|
-
fontFamily: string;
|
|
153
|
-
fontStyle?: "normal" | "italic" | "oblique";
|
|
154
|
-
fontWeight?: number | string;
|
|
155
|
-
fontDisplay: "swap" | "auto" | "block" | "fallback" | "optional";
|
|
156
|
-
src: string;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export class FontFaceRule {
|
|
160
|
-
options: FontFaceOptions;
|
|
161
|
-
constructor(options: FontFaceOptions) {
|
|
162
|
-
this.options = options;
|
|
163
|
-
}
|
|
164
|
-
get cssText() {
|
|
165
|
-
return this.toString();
|
|
166
|
-
}
|
|
167
|
-
toString() {
|
|
168
|
-
const decls = [];
|
|
169
|
-
const { fontFamily, fontStyle, fontWeight, fontDisplay, src } =
|
|
170
|
-
this.options;
|
|
171
|
-
decls.push(
|
|
172
|
-
`font-family: ${/\s/.test(fontFamily) ? `"${fontFamily}"` : fontFamily}`
|
|
173
|
-
);
|
|
174
|
-
decls.push(`font-style: ${fontStyle}`);
|
|
175
|
-
decls.push(`font-weight: ${fontWeight}`);
|
|
176
|
-
decls.push(`font-display: ${fontDisplay}`);
|
|
177
|
-
decls.push(`src: ${src}`);
|
|
178
|
-
return `@font-face {\n ${decls.join("; ")};\n}`;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export type AnyRule = StyleRule | MediaRule | PlaintextRule | FontFaceRule;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export class StyleElement {
|
|
2
|
-
#element?: HTMLStyleElement;
|
|
3
|
-
#name: string;
|
|
4
|
-
constructor(name = "") {
|
|
5
|
-
this.#name = name;
|
|
6
|
-
}
|
|
7
|
-
get isMounted() {
|
|
8
|
-
return this.#element?.parentElement != null;
|
|
9
|
-
}
|
|
10
|
-
mount() {
|
|
11
|
-
if (this.isMounted === false) {
|
|
12
|
-
this.#element = document.createElement("style");
|
|
13
|
-
this.#element.setAttribute("data-webstudio", this.#name);
|
|
14
|
-
document.head.appendChild(this.#element);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
unmount() {
|
|
18
|
-
if (this.isMounted) {
|
|
19
|
-
this.#element?.parentElement?.removeChild(this.#element);
|
|
20
|
-
this.#element = undefined;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
render(cssText: string) {
|
|
24
|
-
if (this.#element) {
|
|
25
|
-
this.#element.textContent = cssText;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
setAttribute(name: string, value: string) {
|
|
29
|
-
if (this.#element) {
|
|
30
|
-
this.#element.setAttribute(name, value);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
getAttribute(name: string) {
|
|
34
|
-
if (this.#element) {
|
|
35
|
-
return this.#element.getAttribute(name);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/core/style-sheet.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { StyleElement } from "./style-element";
|
|
2
|
-
|
|
3
|
-
export class StyleSheet {
|
|
4
|
-
#cssText = "";
|
|
5
|
-
#element;
|
|
6
|
-
constructor(element: StyleElement) {
|
|
7
|
-
this.#element = element;
|
|
8
|
-
}
|
|
9
|
-
replaceSync(cssText: string) {
|
|
10
|
-
if (cssText !== this.#cssText) {
|
|
11
|
-
this.#cssText = cssText;
|
|
12
|
-
this.#element.render(cssText);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
package/src/core/to-property.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { StyleProperty } from "@webstudio-is/css-data";
|
|
2
|
-
import hyphenate from "hyphenate-style-name";
|
|
3
|
-
|
|
4
|
-
export const toProperty = (property: StyleProperty) => {
|
|
5
|
-
// Currently its a non-standard property and is only officially supported via -webkit- prefix.
|
|
6
|
-
// Safari illegally supports it without the prefix.
|
|
7
|
-
// https://caniuse.com/background-clip-text
|
|
8
|
-
if (property === "backgroundClip") {
|
|
9
|
-
return "-webkit-background-clip";
|
|
10
|
-
}
|
|
11
|
-
return hyphenate(property);
|
|
12
|
-
};
|