Skip to content

Commit

Permalink
#27
Browse files Browse the repository at this point in the history
  • Loading branch information
garronej committed Oct 16, 2021
1 parent e13760a commit 693716a
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 20 deletions.
122 changes: 118 additions & 4 deletions src/cssAndCx.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import "./tools/polyfills/Array.prototype.find";
import { Polyfill as WeakMap } from "./tools/polyfills/WeakMap";
import { classnames } from "./tools/classnames";
import type { Cx, Css } from "./types";
import type { Cx, Css, CSSObject } from "./types";
import { serializeStyles } from "@emotion/serialize";
import type { RegisteredCache } from "@emotion/serialize";
import { insertStyles, getRegisteredStyles } from "@emotion/utils";
import { useGuaranteedMemo } from "./tools/useGuaranteedMemo";
import type { EmotionCache } from "@emotion/cache";
import { useTssEmotionCache } from "./cache";
import type { CSSObjectTssSpecials } from "./types";
import { matchCSSObject } from "./types";

const refPropertyName: keyof CSSObjectTssSpecials = "ref";

Expand Down Expand Up @@ -67,13 +70,40 @@ export const { createCssAndCx } = (() => {

const serialized = serializeStyles(args, cache.registered);
insertStyles(cache, serialized, false);
return `${cache.key}-${serialized.name}${
const className = `${cache.key}-${serialized.name}${
ref === undefined ? "" : ` ${ref}`
}`;

scope: {
const arg = args[0];

if (!matchCSSObject(arg)) {
break scope;
}

increaseSpecificityToTakePrecedenceOverMediaQuerries.saveClassNameCSSObjectMapping(
cache,
className,
arg,
);
}

return className;
};

const cx: Cx = (...args) =>
merge(cache.registered, css, classnames(args));
const cx: Cx = (...args) => {
const className = classnames(args);

const feat27FixedClassnames =
increaseSpecificityToTakePrecedenceOverMediaQuerries.fixClassName(
cache,
className,
css,
);

return merge(cache.registered, css, feat27FixedClassnames);
//return merge(cache.registered, css, className);
};

return { css, cx };
}
Expand All @@ -92,3 +122,87 @@ export function useCssAndCx() {

return { css, cx };
}

// https://github.com/garronej/tss-react/issues/27
const increaseSpecificityToTakePrecedenceOverMediaQuerries = (() => {
const cssObjectMapByCache = new WeakMap<
EmotionCache,
Map<string, CSSObject>
>();

return {
"saveClassNameCSSObjectMapping": (
cache: EmotionCache,
className: string,
cssObject: CSSObject,
) => {
let cssObjectMap = cssObjectMapByCache.get(cache);

if (cssObjectMap === undefined) {
cssObjectMap = new Map();
cssObjectMapByCache.set(cache, cssObjectMap);
}

cssObjectMap.set(className, cssObject);
},
"fixClassName": (() => {
function fix(
classNameCSSObjects: [
string /*className*/,
CSSObject | undefined,
][],
): (string | CSSObject)[] {
let isThereAnyMediaQueriesInPreviousClasses = false;

return classNameCSSObjects.map(([className, cssObject]) => {
if (cssObject === undefined) {
return className;
}

let out: string | CSSObject;

if (!isThereAnyMediaQueriesInPreviousClasses) {
out = className;

if (
Object.keys(cssObject).find(key =>
key.startsWith("@media"),
) !== undefined
) {
isThereAnyMediaQueriesInPreviousClasses = true;
}
} else {
out = {
"&&": cssObject,
};
}

return out;
});
}

return (
cache: EmotionCache,
className: string,
css: Css,
): string => {
const cssObjectMap = cssObjectMapByCache.get(cache);

return classnames(
fix(
className
.split(" ")
.map(className => [
className,
cssObjectMap?.get(className),
]),
).map(classNameOrCSSObject =>
typeof classNameOrCSSObject === "string"
? className
: css(classNameOrCSSObject),
),
);
};
})(),
};
})();
2 changes: 1 addition & 1 deletion src/makeStyles.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fromEntries } from "./tools/Object.fromEntries";
import { fromEntries } from "./tools/polyfills/Object.fromEntries";
import { objectKeys } from "./tools/objectKeys";
import type { CSSObject } from "./types";
import { useCssAndCx } from "./cssAndCx";
Expand Down
13 changes: 13 additions & 0 deletions src/test/apps/muiV4ssr/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,19 @@ const { App } = (() => {
<MyAnchorStyled href="https://exampe.com">
Background should be limegreen
</MyAnchorStyled>
<div className={cx(
css({
"@media screen and (min-width: 1px)": {
"backgroundColor": "red"
},
"height": 50
}),
css({
"backgroundColor": "lightgreen"
})
)}>
background should be lightgreen
</div>
</div>
</>
);
Expand Down
13 changes: 13 additions & 0 deletions src/test/apps/spa/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,19 @@ export function App(props: { className?: string; }) {
<MyAnchorStyled href="https://exampe.com">
Background should be limegreen
</MyAnchorStyled>
<div className={cx(
css({
"@media screen and (min-width: 1px)": {
"backgroundColor": "red"
},
"height": 50
}),
css({
"backgroundColor": "lightgreen"
})
)}>
background should be lightgreen
</div>
</div>
</>
);
Expand Down
15 changes: 0 additions & 15 deletions src/test/apps/ssr/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,21 +277,6 @@ const { App } = (() => {
<MyAnchorStyled href="https://exampe.com">
Background should be limegreen
</MyAnchorStyled>
<div className={cx(
css({
"@media screen and (min-width: 1px)": {
"backgroundColor": "red"
},
"height": 50
}),
css({
"&&": {
"backgroundColor": "lightgreen"
}
})
)}>
background should be lightgreen
</div>
<div className={cx(
css({
"@media screen and (min-width: 1px)": {
Expand Down
52 changes: 52 additions & 0 deletions src/tools/polyfills/Array.prototype.find.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable prefer-rest-params */
/* eslint-disable no-var */

// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, "find", {
value: function (predicate: any) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}

var o = Object(this);

// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;

// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== "function") {
throw new TypeError("predicate must be a function");
}

// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];

// 5. Let k be 0.
var k = 0;

// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}

// 7. Return undefined.
return undefined;
},
configurable: true,
writable: true,
});
}

export {};
57 changes: 57 additions & 0 deletions src/tools/polyfills/Map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export interface LightMap<K, V> {
[Symbol.toStringTag]: string;
has(key: K): boolean;
get(key: K): V | undefined;
set(key: K, value: V): this;
delete(key: K): boolean;
keys(): Iterable<K>;
}

export class LightMapImpl<K, V> implements LightMap<K, V> {
[Symbol.toStringTag]: string;

private readonly record: [K, V][] = [];

public has(key: K): boolean {
return this.record.map(([_key]) => _key).indexOf(key) >= 0;
}

public get(key: K): V | undefined {
const [entry] = this.record.filter(([_key]) => _key === key);
if (entry === undefined) {
return undefined;
}

return entry[1];
}

public set(key: K, value: V) {
const [entry] = this.record.filter(([_key]) => _key === key);
if (entry === undefined) {
this.record.push([key, value]);
} else {
entry[1] = value;
}

return this;
}

public delete(key: K): boolean {
const index = this.record.map(([key]) => key).indexOf(key);

if (index < 0) {
return false;
}

this.record.splice(index, 1);

return true;
}

public keys(): Iterable<K> {
return this.record.map(([key]) => key);
}
}

export const Polyfill: { new <K, V>(): LightMap<K, V> } =
typeof Map !== "undefined" ? Map : LightMapImpl;
File renamed without changes.
6 changes: 6 additions & 0 deletions src/tools/polyfills/WeakMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/ban-types */

import { Polyfill as MapPolyfill } from "./Map";

export const Polyfill: { new <K extends object, V>(): WeakMap<K, V> } =
typeof WeakMap !== "undefined" ? WeakMap : MapPolyfill;
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,14 @@ export { CxArg };

//SEE: https://github.com/emotion-js/emotion/pull/2276
export type Cx = (...classNames: CxArg[]) => string;

export function matchCSSObject(
arg: TemplateStringsArray | CSSInterpolation,
): arg is CSSObject {
return (
arg instanceof Object &&
!("styles" in arg) &&
!("length" in arg) &&
!("__emotion_styles" in arg)
);
}

0 comments on commit 693716a

Please sign in to comment.