@webstudio-is/css-engine 0.51.0 → 0.52.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.
Files changed (45) hide show
  1. package/lib/cjs/core/compare-media.js +32 -0
  2. package/lib/cjs/core/{css-engine.cjs → css-engine.js} +5 -1
  3. package/lib/cjs/core/equal-media.js +26 -0
  4. package/lib/cjs/core/{index.cjs → index.js} +2 -0
  5. package/lib/cjs/core/{rules.cjs → rules.js} +0 -9
  6. package/lib/cjs/package.json +1 -0
  7. package/lib/core/compare-media.js +12 -0
  8. package/lib/core/css-engine.js +5 -1
  9. package/lib/core/equal-media.js +6 -0
  10. package/lib/core/index.js +2 -0
  11. package/lib/core/rules.js +0 -9
  12. package/lib/types/core/compare-media.d.ts +6 -0
  13. package/lib/types/core/compare-media.test.d.ts +1 -0
  14. package/lib/types/core/create-css-engine.d.ts +2 -0
  15. package/lib/types/core/css-engine.d.ts +19 -0
  16. package/lib/types/core/css-engine.stories.d.ts +6 -0
  17. package/lib/types/core/css-engine.test.d.ts +1 -0
  18. package/lib/types/core/equal-media.d.ts +2 -0
  19. package/lib/types/core/equal-media.test.d.ts +1 -0
  20. package/lib/types/core/index.d.ts +7 -0
  21. package/lib/types/core/match-media.d.ts +2 -0
  22. package/lib/types/core/match-media.test.d.ts +1 -0
  23. package/lib/types/core/rules.d.ts +51 -0
  24. package/lib/types/core/style-element.d.ts +10 -0
  25. package/lib/types/core/style-sheet.d.ts +6 -0
  26. package/lib/types/core/to-property.d.ts +2 -0
  27. package/lib/types/core/to-property.test.d.ts +1 -0
  28. package/lib/types/core/to-value.d.ts +6 -0
  29. package/lib/types/core/to-value.test.d.ts +1 -0
  30. package/lib/types/index.d.ts +1 -0
  31. package/package.json +6 -5
  32. package/src/core/compare-media.test.ts +40 -0
  33. package/src/core/compare-media.ts +18 -0
  34. package/src/core/css-engine.ts +6 -1
  35. package/src/core/equal-media.test.ts +31 -0
  36. package/src/core/equal-media.ts +5 -0
  37. package/src/core/index.ts +2 -0
  38. package/src/core/rules.ts +0 -12
  39. /package/lib/cjs/core/{create-css-engine.cjs → create-css-engine.js} +0 -0
  40. /package/lib/cjs/core/{match-media.cjs → match-media.js} +0 -0
  41. /package/lib/cjs/core/{style-element.cjs → style-element.js} +0 -0
  42. /package/lib/cjs/core/{style-sheet.cjs → style-sheet.js} +0 -0
  43. /package/lib/cjs/core/{to-property.cjs → to-property.js} +0 -0
  44. /package/lib/cjs/core/{to-value.cjs → to-value.js} +0 -0
  45. /package/lib/cjs/{index.cjs → index.js} +0 -0
@@ -0,0 +1,32 @@
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 compare_media_exports = {};
20
+ __export(compare_media_exports, {
21
+ compareMedia: () => compareMedia
22
+ });
23
+ module.exports = __toCommonJS(compare_media_exports);
24
+ const compareMedia = (optionA, optionB) => {
25
+ if (optionA?.minWidth !== void 0 && optionB?.minWidth !== void 0) {
26
+ return optionA.minWidth - optionB.minWidth;
27
+ }
28
+ if (optionA?.maxWidth !== void 0 && optionB?.maxWidth !== void 0) {
29
+ return optionB.maxWidth - optionA.maxWidth;
30
+ }
31
+ return "minWidth" in optionA ? 1 : -1;
32
+ };
@@ -40,6 +40,7 @@ __export(css_engine_exports, {
40
40
  });
41
41
  module.exports = __toCommonJS(css_engine_exports);
42
42
  var import_rules = require("./rules");
43
+ var import_compare_media = require("./compare-media");
43
44
  var import_style_element = require("./style-element");
44
45
  var import_style_sheet = require("./style-sheet");
45
46
  var _element, _mediaRules, _plainRules, _fontFaceRules, _sheet, _isDirty, _cssText, _onChangeRule;
@@ -120,7 +121,10 @@ class CssEngine {
120
121
  for (const plaintextRule of __privateGet(this, _plainRules).values()) {
121
122
  css.push(plaintextRule.cssText);
122
123
  }
123
- for (const mediaRule of import_rules.MediaRule.sort(__privateGet(this, _mediaRules).values())) {
124
+ const sortedMediaRules = Array.from(__privateGet(this, _mediaRules).values()).sort(
125
+ (ruleA, ruleB) => (0, import_compare_media.compareMedia)(ruleA.options, ruleB.options)
126
+ );
127
+ for (const mediaRule of sortedMediaRules) {
124
128
  const { cssText } = mediaRule;
125
129
  if (cssText !== "") {
126
130
  css.push(cssText);
@@ -0,0 +1,26 @@
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 equal_media_exports = {};
20
+ __export(equal_media_exports, {
21
+ equalMedia: () => equalMedia
22
+ });
23
+ module.exports = __toCommonJS(equal_media_exports);
24
+ const equalMedia = (left, right) => {
25
+ return left.minWidth === right.minWidth && left.maxWidth === right.maxWidth;
26
+ };
@@ -26,3 +26,5 @@ var import_css_engine = require("./css-engine");
26
26
  __reExport(core_exports, require("./create-css-engine"), module.exports);
27
27
  __reExport(core_exports, require("./to-value"), module.exports);
28
28
  __reExport(core_exports, require("./match-media"), module.exports);
29
+ __reExport(core_exports, require("./equal-media"), module.exports);
30
+ __reExport(core_exports, require("./compare-media"), module.exports);
@@ -116,15 +116,6 @@ class MediaRule {
116
116
  this.options = options;
117
117
  __privateSet(this, _mediaType, options.mediaType ?? "all");
118
118
  }
119
- // Sort media rules by minWidth.
120
- // Needed to ensure that more specific media rules are inserted after less specific ones.
121
- // So that they get a higher specificity.
122
- // @todo remove this once we have ability to control order from the UI
123
- static sort(mediaRules) {
124
- return Array.from(mediaRules).sort((ruleA, ruleB) => {
125
- return (ruleA.options.minWidth ?? -Number.MAX_SAFE_INTEGER) - (ruleB.options.minWidth ?? -Number.MAX_SAFE_INTEGER);
126
- });
127
- }
128
119
  insertRule(rule) {
129
120
  this.rules.push(rule);
130
121
  return rule;
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,12 @@
1
+ const compareMedia = (optionA, optionB) => {
2
+ if (optionA?.minWidth !== void 0 && optionB?.minWidth !== void 0) {
3
+ return optionA.minWidth - optionB.minWidth;
4
+ }
5
+ if (optionA?.maxWidth !== void 0 && optionB?.maxWidth !== void 0) {
6
+ return optionB.maxWidth - optionA.maxWidth;
7
+ }
8
+ return "minWidth" in optionA ? 1 : -1;
9
+ };
10
+ export {
11
+ compareMedia
12
+ };
@@ -23,6 +23,7 @@ import {
23
23
  PlaintextRule,
24
24
  StyleRule
25
25
  } from "./rules";
26
+ import { compareMedia } from "./compare-media";
26
27
  import { StyleElement } from "./style-element";
27
28
  import { StyleSheet } from "./style-sheet";
28
29
  const defaultMediaRuleId = "__default-media-rule__";
@@ -102,7 +103,10 @@ class CssEngine {
102
103
  for (const plaintextRule of __privateGet(this, _plainRules).values()) {
103
104
  css.push(plaintextRule.cssText);
104
105
  }
105
- for (const mediaRule of MediaRule.sort(__privateGet(this, _mediaRules).values())) {
106
+ const sortedMediaRules = Array.from(__privateGet(this, _mediaRules).values()).sort(
107
+ (ruleA, ruleB) => compareMedia(ruleA.options, ruleB.options)
108
+ );
109
+ for (const mediaRule of sortedMediaRules) {
106
110
  const { cssText } = mediaRule;
107
111
  if (cssText !== "") {
108
112
  css.push(cssText);
@@ -0,0 +1,6 @@
1
+ const equalMedia = (left, right) => {
2
+ return left.minWidth === right.minWidth && left.maxWidth === right.maxWidth;
3
+ };
4
+ export {
5
+ equalMedia
6
+ };
package/lib/core/index.js CHANGED
@@ -2,6 +2,8 @@ import { CssEngine } from "./css-engine";
2
2
  export * from "./create-css-engine";
3
3
  export * from "./to-value";
4
4
  export * from "./match-media";
5
+ export * from "./equal-media";
6
+ export * from "./compare-media";
5
7
  export {
6
8
  CssEngine
7
9
  };
package/lib/core/rules.js CHANGED
@@ -90,15 +90,6 @@ class MediaRule {
90
90
  this.options = options;
91
91
  __privateSet(this, _mediaType, options.mediaType ?? "all");
92
92
  }
93
- // Sort media rules by minWidth.
94
- // Needed to ensure that more specific media rules are inserted after less specific ones.
95
- // So that they get a higher specificity.
96
- // @todo remove this once we have ability to control order from the UI
97
- static sort(mediaRules) {
98
- return Array.from(mediaRules).sort((ruleA, ruleB) => {
99
- return (ruleA.options.minWidth ?? -Number.MAX_SAFE_INTEGER) - (ruleB.options.minWidth ?? -Number.MAX_SAFE_INTEGER);
100
- });
101
- }
102
93
  insertRule(rule) {
103
94
  this.rules.push(rule);
104
95
  return rule;
@@ -0,0 +1,6 @@
1
+ import type { MediaRuleOptions } from "./rules";
2
+ /**
3
+ * Sort by minWidth descending or maxWidth ascending
4
+ * We want media querries with bigger minWidth to override the smaller once, but the smaller maxWidth to override the bigger once.
5
+ */
6
+ export declare const compareMedia: (optionA: MediaRuleOptions, optionB: MediaRuleOptions) => number;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { CssEngine, type CssEngineOptions } from "./css-engine";
2
+ export declare const createCssEngine: (options?: CssEngineOptions) => CssEngine;
@@ -0,0 +1,19 @@
1
+ import type { CssRule } from "@webstudio-is/css-data";
2
+ import { MediaRule, PlaintextRule, StyleRule, type FontFaceOptions, type MediaRuleOptions } from "./rules";
3
+ export type CssEngineOptions = {
4
+ name?: string;
5
+ };
6
+ export declare class CssEngine {
7
+ #private;
8
+ constructor({ name }: CssEngineOptions);
9
+ addMediaRule(id: string, options?: MediaRuleOptions): MediaRule;
10
+ addStyleRule(selectorText: string, rule: CssRule): StyleRule;
11
+ addPlaintextRule(cssText: string): PlaintextRule | Map<string, PlaintextRule>;
12
+ addFontFaceRule(options: FontFaceOptions): number;
13
+ clear(): void;
14
+ render(): void;
15
+ unmount(): void;
16
+ setAttribute(name: string, value: string): void;
17
+ getAttribute(name: string): string | null | undefined;
18
+ get cssText(): string;
19
+ }
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ declare const _default: {
3
+ component: string;
4
+ };
5
+ export default _default;
6
+ export declare const Basic: () => JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { MediaRuleOptions } from "./rules";
2
+ export declare const equalMedia: (left: MediaRuleOptions, right: MediaRuleOptions) => boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ export { CssEngine } from "./css-engine";
2
+ export type { AnyRule, StyleRule, MediaRule, PlaintextRule, FontFaceRule, } from "./rules";
3
+ export * from "./create-css-engine";
4
+ export * from "./to-value";
5
+ export * from "./match-media";
6
+ export * from "./equal-media";
7
+ export * from "./compare-media";
@@ -0,0 +1,2 @@
1
+ import type { MediaRuleOptions } from "./rules";
2
+ export declare const matchMedia: (options: MediaRuleOptions, width: number) => boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import type { Style, StyleProperty, StyleValue } from "@webstudio-is/css-data";
2
+ declare class StylePropertyMap {
3
+ #private;
4
+ onChange?: () => void;
5
+ set(property: StyleProperty, value?: StyleValue): void;
6
+ has(property: StyleProperty): boolean;
7
+ keys(): IterableIterator<"color" | "left" | "right" | "top" | "bottom" | "contain" | "clip" | "content" | "filter" | "float" | "fontFamily" | "width" | "height" | `--${string}` | "accentColor" | "alignContent" | "alignItems" | "alignSelf" | "alignTracks" | "animationComposition" | "animationDelay" | "animationDirection" | "animationDuration" | "animationFillMode" | "animationIterationCount" | "animationName" | "animationPlayState" | "animationTimingFunction" | "animationTimeline" | "appearance" | "aspectRatio" | "backdropFilter" | "backfaceVisibility" | "backgroundAttachment" | "backgroundBlendMode" | "backgroundClip" | "backgroundColor" | "backgroundImage" | "backgroundOrigin" | "backgroundPosition" | "backgroundPositionX" | "backgroundPositionY" | "backgroundRepeat" | "backgroundSize" | "blockOverflow" | "blockSize" | "borderBlockColor" | "borderBlockStyle" | "borderBlockWidth" | "borderBlockEndColor" | "borderBlockEndStyle" | "borderBlockEndWidth" | "borderBlockStartColor" | "borderBlockStartStyle" | "borderBlockStartWidth" | "borderBottomColor" | "borderBottomLeftRadius" | "borderBottomRightRadius" | "borderBottomStyle" | "borderBottomWidth" | "borderCollapse" | "borderEndEndRadius" | "borderEndStartRadius" | "borderImageOutset" | "borderImageRepeat" | "borderImageSlice" | "borderImageSource" | "borderImageWidth" | "borderInlineColor" | "borderInlineStyle" | "borderInlineWidth" | "borderInlineEndColor" | "borderInlineEndStyle" | "borderInlineEndWidth" | "borderInlineStartColor" | "borderInlineStartStyle" | "borderInlineStartWidth" | "borderLeftColor" | "borderLeftStyle" | "borderLeftWidth" | "borderRightColor" | "borderRightStyle" | "borderRightWidth" | "borderSpacing" | "borderStartEndRadius" | "borderStartStartRadius" | "borderTopColor" | "borderTopLeftRadius" | "borderTopRightRadius" | "borderTopStyle" | "borderTopWidth" | "boxDecorationBreak" | "boxShadow" | "boxSizing" | "breakAfter" | "breakBefore" | "breakInside" | "captionSide" | "caretColor" | "caretShape" | "clear" | "clipPath" | "printColorAdjust" | "colorScheme" | "columnCount" | "columnFill" | "columnGap" | "columnRuleColor" | "columnRuleStyle" | "columnRuleWidth" | "columnSpan" | "columnWidth" | "containIntrinsicBlockSize" | "containIntrinsicHeight" | "containIntrinsicInlineSize" | "containIntrinsicWidth" | "contentVisibility" | "counterIncrement" | "counterReset" | "counterSet" | "cursor" | "direction" | "display" | "emptyCells" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "fontFeatureSettings" | "fontKerning" | "fontLanguageOverride" | "fontOpticalSizing" | "fontVariationSettings" | "fontSize" | "fontSizeAdjust" | "fontStretch" | "fontStyle" | "fontSynthesis" | "fontVariant" | "fontVariantAlternates" | "fontVariantCaps" | "fontVariantEastAsian" | "fontVariantLigatures" | "fontVariantNumeric" | "fontVariantPosition" | "fontWeight" | "forcedColorAdjust" | "gridAutoColumns" | "gridAutoFlow" | "gridAutoRows" | "gridColumnEnd" | "gridColumnStart" | "gridRowEnd" | "gridRowStart" | "gridTemplateAreas" | "gridTemplateColumns" | "gridTemplateRows" | "hangingPunctuation" | "hyphenateCharacter" | "hyphens" | "imageOrientation" | "imageRendering" | "imageResolution" | "initialLetter" | "initialLetterAlign" | "inlineSize" | "inputSecurity" | "insetBlockEnd" | "insetBlockStart" | "insetInlineEnd" | "insetInlineStart" | "isolation" | "justifyContent" | "justifyItems" | "justifySelf" | "justifyTracks" | "letterSpacing" | "lineBreak" | "lineClamp" | "lineHeight" | "lineHeightStep" | "listStyleImage" | "listStylePosition" | "listStyleType" | "marginBlockEnd" | "marginBlockStart" | "marginBottom" | "marginInlineEnd" | "marginInlineStart" | "marginLeft" | "marginRight" | "marginTop" | "marginTrim" | "maskBorderMode" | "maskBorderOutset" | "maskBorderRepeat" | "maskBorderSlice" | "maskBorderSource" | "maskBorderWidth" | "maskClip" | "maskComposite" | "maskImage" | "maskMode" | "maskOrigin" | "maskPosition" | "maskRepeat" | "maskSize" | "maskType" | "masonryAutoFlow" | "mathDepth" | "mathShift" | "mathStyle" | "maxBlockSize" | "maxHeight" | "maxInlineSize" | "maxLines" | "maxWidth" | "minBlockSize" | "minHeight" | "minInlineSize" | "minWidth" | "mixBlendMode" | "objectFit" | "objectPosition" | "offsetAnchor" | "offsetDistance" | "offsetPath" | "offsetPosition" | "offsetRotate" | "opacity" | "order" | "orphans" | "outlineColor" | "outlineOffset" | "outlineStyle" | "outlineWidth" | "overflow" | "overflowAnchor" | "overflowBlock" | "overflowClipMargin" | "overflowInline" | "overflowWrap" | "overflowX" | "overflowY" | "overscrollBehavior" | "overscrollBehaviorBlock" | "overscrollBehaviorInline" | "overscrollBehaviorX" | "overscrollBehaviorY" | "paddingBlockEnd" | "paddingBlockStart" | "paddingBottom" | "paddingInlineEnd" | "paddingInlineStart" | "paddingLeft" | "paddingRight" | "paddingTop" | "pageBreakAfter" | "pageBreakBefore" | "pageBreakInside" | "paintOrder" | "perspective" | "perspectiveOrigin" | "pointerEvents" | "position" | "quotes" | "resize" | "rotate" | "rowGap" | "rubyAlign" | "rubyMerge" | "rubyPosition" | "scale" | "scrollbarColor" | "scrollbarGutter" | "scrollbarWidth" | "scrollBehavior" | "scrollMarginBlockStart" | "scrollMarginBlockEnd" | "scrollMarginBottom" | "scrollMarginInlineStart" | "scrollMarginInlineEnd" | "scrollMarginLeft" | "scrollMarginRight" | "scrollMarginTop" | "scrollPaddingBlockStart" | "scrollPaddingBlockEnd" | "scrollPaddingBottom" | "scrollPaddingInlineStart" | "scrollPaddingInlineEnd" | "scrollPaddingLeft" | "scrollPaddingRight" | "scrollPaddingTop" | "scrollSnapAlign" | "scrollSnapStop" | "scrollSnapType" | "scrollTimelineAxis" | "scrollTimelineName" | "shapeImageThreshold" | "shapeMargin" | "shapeOutside" | "tabSize" | "tableLayout" | "textAlign" | "textAlignLast" | "textCombineUpright" | "textDecorationColor" | "textDecorationLine" | "textDecorationSkip" | "textDecorationSkipInk" | "textDecorationStyle" | "textDecorationThickness" | "textEmphasisColor" | "textEmphasisPosition" | "textEmphasisStyle" | "textIndent" | "textJustify" | "textOrientation" | "textOverflow" | "textRendering" | "textShadow" | "textSizeAdjust" | "textTransform" | "textUnderlineOffset" | "textUnderlinePosition" | "touchAction" | "transform" | "transformBox" | "transformOrigin" | "transformStyle" | "transitionDelay" | "transitionDuration" | "transitionProperty" | "transitionTimingFunction" | "translate" | "unicodeBidi" | "userSelect" | "verticalAlign" | "visibility" | "whiteSpace" | "widows" | "willChange" | "wordBreak" | "wordSpacing" | "wordWrap" | "writingMode" | "zIndex">;
8
+ delete(property: StyleProperty): void;
9
+ clear(): void;
10
+ toString(): string;
11
+ }
12
+ export declare class StyleRule {
13
+ #private;
14
+ styleMap: StylePropertyMap;
15
+ selectorText: string;
16
+ onChange?: () => void;
17
+ constructor(selectorText: string, style: Style);
18
+ get cssText(): string;
19
+ }
20
+ export type MediaRuleOptions = {
21
+ minWidth?: number;
22
+ maxWidth?: number;
23
+ mediaType?: "all" | "screen" | "print";
24
+ };
25
+ export declare class MediaRule {
26
+ #private;
27
+ options: MediaRuleOptions;
28
+ rules: Array<StyleRule | PlaintextRule>;
29
+ constructor(options?: MediaRuleOptions);
30
+ insertRule(rule: StyleRule | PlaintextRule): StyleRule | PlaintextRule;
31
+ get cssText(): string;
32
+ }
33
+ export declare class PlaintextRule {
34
+ cssText: string;
35
+ styleMap: StylePropertyMap;
36
+ constructor(cssText: string);
37
+ }
38
+ export type FontFaceOptions = {
39
+ fontFamily: string;
40
+ fontStyle?: "normal" | "italic" | "oblique";
41
+ fontWeight?: number | string;
42
+ fontDisplay: "swap" | "auto" | "block" | "fallback" | "optional";
43
+ src: string;
44
+ };
45
+ export declare class FontFaceRule {
46
+ options: FontFaceOptions;
47
+ constructor(options: FontFaceOptions);
48
+ get cssText(): string;
49
+ }
50
+ export type AnyRule = StyleRule | MediaRule | PlaintextRule | FontFaceRule;
51
+ export {};
@@ -0,0 +1,10 @@
1
+ export declare class StyleElement {
2
+ #private;
3
+ constructor(name?: string);
4
+ get isMounted(): boolean;
5
+ mount(): void;
6
+ unmount(): void;
7
+ render(cssText: string): void;
8
+ setAttribute(name: string, value: string): void;
9
+ getAttribute(name: string): string | null | undefined;
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { StyleElement } from "./style-element";
2
+ export declare class StyleSheet {
3
+ #private;
4
+ constructor(element: StyleElement);
5
+ replaceSync(cssText: string): void;
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { StyleProperty } from "@webstudio-is/css-data";
2
+ export declare const toProperty: (property: StyleProperty) => string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { StyleValue } from "@webstudio-is/css-data";
2
+ type ToCssOptions = {
3
+ withFallback: boolean;
4
+ };
5
+ export declare const toValue: (value?: StyleValue, options?: ToCssOptions) => string;
6
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/css-engine",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "description": "CSS Renderer for Webstudio",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -9,7 +9,7 @@
9
9
  "hyphenate-style-name": "^1.0.4",
10
10
  "react": "^17.0.2",
11
11
  "react-dom": "^17.0.2",
12
- "@webstudio-is/fonts": "^0.51.0"
12
+ "@webstudio-is/fonts": "^0.52.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@jest/globals": "^29.3.1",
@@ -18,8 +18,8 @@
18
18
  "@types/react": "^17.0.24",
19
19
  "@types/react-dom": "^17.0.9",
20
20
  "jest": "^29.3.1",
21
- "typescript": "4.9.5",
22
- "@webstudio-is/css-data": "^0.51.0",
21
+ "typescript": "5.0.3",
22
+ "@webstudio-is/css-data": "^0.52.0",
23
23
  "@webstudio-is/jest-config": "^1.0.2",
24
24
  "@webstudio-is/scripts": "^0.0.0",
25
25
  "@webstudio-is/storybook-config": "^0.0.0",
@@ -29,7 +29,7 @@
29
29
  "source": "./src/index.ts",
30
30
  "import": "./lib/index.js"
31
31
  },
32
- "types": "src/index.ts",
32
+ "types": "lib/types/index.d.ts",
33
33
  "files": [
34
34
  "lib/*",
35
35
  "src/*",
@@ -43,6 +43,7 @@
43
43
  "checks": "pnpm typecheck && pnpm lint && pnpm test",
44
44
  "dev": "build-package --watch",
45
45
  "build": "build-package",
46
+ "dts": "tsc --emitDeclarationOnly --declaration --declarationDir lib/types",
46
47
  "test": "NODE_OPTIONS=--experimental-vm-modules jest",
47
48
  "lint": "eslint ./src --ext .ts,.tsx --max-warnings 0",
48
49
  "storybook:run": "start-storybook -p 6006",
@@ -0,0 +1,40 @@
1
+ import { describe, test, expect } from "@jest/globals";
2
+ import { compareMedia } from "./compare-media";
3
+
4
+ describe("Compare media", () => {
5
+ test("mobile first", () => {
6
+ const initial = [
7
+ { minWidth: 0 },
8
+ { minWidth: 768 },
9
+ { minWidth: 1024 },
10
+ { minWidth: 1280 },
11
+ ];
12
+ expect(initial.sort(compareMedia)).toStrictEqual(initial);
13
+ });
14
+
15
+ test("random minWidth", () => {
16
+ const initial = [{ minWidth: 0 }, { minWidth: 3 }, { minWidth: 2 }];
17
+ const sorted = [{ minWidth: 0 }, { minWidth: 2 }, { minWidth: 3 }];
18
+ expect(initial.sort(compareMedia)).toStrictEqual(sorted);
19
+ });
20
+
21
+ test("webflow", () => {
22
+ const initial = [
23
+ { maxWidth: 991 },
24
+ { maxWidth: 767 },
25
+ { maxWidth: 479 },
26
+ { minWidth: 1280 },
27
+ { minWidth: 1440 },
28
+ { minWidth: 1920 },
29
+ ];
30
+ const sorted = [
31
+ { maxWidth: 991 },
32
+ { maxWidth: 767 },
33
+ { maxWidth: 479 },
34
+ { minWidth: 1280 },
35
+ { minWidth: 1440 },
36
+ { minWidth: 1920 },
37
+ ];
38
+ expect(initial.sort(compareMedia)).toStrictEqual(sorted);
39
+ });
40
+ });
@@ -0,0 +1,18 @@
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
+ if (optionA?.minWidth !== undefined && optionB?.minWidth !== undefined) {
12
+ return optionA.minWidth - optionB.minWidth;
13
+ }
14
+ if (optionA?.maxWidth !== undefined && optionB?.maxWidth !== undefined) {
15
+ return optionB.maxWidth - optionA.maxWidth;
16
+ }
17
+ return "minWidth" in optionA ? 1 : -1;
18
+ };
@@ -7,6 +7,7 @@ import {
7
7
  type FontFaceOptions,
8
8
  type MediaRuleOptions,
9
9
  } from "./rules";
10
+ import { compareMedia } from "./compare-media";
10
11
  import { StyleElement } from "./style-element";
11
12
  import { StyleSheet } from "./style-sheet";
12
13
 
@@ -90,7 +91,11 @@ export class CssEngine {
90
91
  for (const plaintextRule of this.#plainRules.values()) {
91
92
  css.push(plaintextRule.cssText);
92
93
  }
93
- for (const mediaRule of MediaRule.sort(this.#mediaRules.values())) {
94
+
95
+ const sortedMediaRules = Array.from(this.#mediaRules.values()).sort(
96
+ (ruleA, ruleB) => compareMedia(ruleA.options, ruleB.options)
97
+ );
98
+ for (const mediaRule of sortedMediaRules) {
94
99
  const { cssText } = mediaRule;
95
100
  if (cssText !== "") {
96
101
  css.push(cssText);
@@ -0,0 +1,31 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+ import { equalMedia } from "./equal-media";
3
+
4
+ describe("equalMedia", () => {
5
+ test("minWidth", () => {
6
+ expect(equalMedia({ minWidth: 100 }, { minWidth: 10 })).toBe(false);
7
+ expect(equalMedia({ minWidth: 100 }, { minWidth: 100 })).toBe(true);
8
+ expect(equalMedia({ minWidth: 100 }, { minWidth: 101 })).toBe(false);
9
+ });
10
+
11
+ test("maxWidth", () => {
12
+ expect(equalMedia({ maxWidth: 100 }, { maxWidth: 101 })).toBe(false);
13
+ expect(equalMedia({ maxWidth: 100 }, { maxWidth: 100 })).toBe(true);
14
+ expect(equalMedia({ maxWidth: 100 }, { maxWidth: 10 })).toBe(false);
15
+ });
16
+
17
+ test("minWidth and maxWidth", () => {
18
+ expect(equalMedia({ maxWidth: 100, minWidth: 10 }, { maxWidth: 100 })).toBe(
19
+ false
20
+ );
21
+ expect(equalMedia({ maxWidth: 100, minWidth: 10 }, { minWidth: 10 })).toBe(
22
+ false
23
+ );
24
+ expect(
25
+ equalMedia(
26
+ { maxWidth: 100, minWidth: 10 },
27
+ { maxWidth: 100, minWidth: 10 }
28
+ )
29
+ ).toBe(true);
30
+ });
31
+ });
@@ -0,0 +1,5 @@
1
+ import type { MediaRuleOptions } from "./rules";
2
+
3
+ export const equalMedia = (left: MediaRuleOptions, right: MediaRuleOptions) => {
4
+ return left.minWidth === right.minWidth && left.maxWidth === right.maxWidth;
5
+ };
package/src/core/index.ts CHANGED
@@ -9,3 +9,5 @@ export type {
9
9
  export * from "./create-css-engine";
10
10
  export * from "./to-value";
11
11
  export * from "./match-media";
12
+ export * from "./equal-media";
13
+ export * from "./compare-media";
package/src/core/rules.ts CHANGED
@@ -73,18 +73,6 @@ export type MediaRuleOptions = {
73
73
  };
74
74
 
75
75
  export class MediaRule {
76
- // Sort media rules by minWidth.
77
- // Needed to ensure that more specific media rules are inserted after less specific ones.
78
- // So that they get a higher specificity.
79
- // @todo remove this once we have ability to control order from the UI
80
- static sort(mediaRules: Iterable<MediaRule>) {
81
- return Array.from(mediaRules).sort((ruleA, ruleB) => {
82
- return (
83
- (ruleA.options.minWidth ?? -Number.MAX_SAFE_INTEGER) -
84
- (ruleB.options.minWidth ?? -Number.MAX_SAFE_INTEGER)
85
- );
86
- });
87
- }
88
76
  options: MediaRuleOptions;
89
77
  rules: Array<StyleRule | PlaintextRule> = [];
90
78
  #mediaType;
File without changes
File without changes