Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ES5 compatible by using an object instead of Map and adding a polyfill for WeakMap #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ It's smart enough to not remove class names added in mutlipe places, and to main
npm install --save react-global-style
```

You may need a polyfill for the ES6 features `Map` and `WeakMap`. These are included in [babel-polyfill], which many apps already use.

[babel-polyfill]: https://babeljs.io/docs/usage/polyfill/

## Usage

```jsx
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,11 @@
"bugs": {
"url": "https://github.com/brigand/react-global-style/issues"
},
"homepage": "https://github.com/brigand/react-global-style#readme"
"homepage": "https://github.com/brigand/react-global-style#readme",
"jest": {
"collectCoverageFrom": [
"src/**/*",
"!src/WeakMap.js"
]
}
}
34 changes: 18 additions & 16 deletions src/ReactGlobalStyle.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const React = require('react');

// polyfill WeakMap if it's not defined.
require('./WeakMap');

// ed = elementData, we use getEd a lot
// WeakMap for edge cases where many elements are targeted
// Not a big deal if it's shimmed as Map
Expand All @@ -9,38 +12,38 @@ export const getEd = (element = document.body) => {
return ed.get(element);
}

const classCounts = new Map();
const styleLevels = new Map();
const classCounts = {};
const styleLevels = {};

const incrClassCount = (key) => {
if (classCounts.has(key)) {
classCounts.set(key, classCounts.get(key) + 1);
if (classCounts[key] !== undefined) {
classCounts[key] = classCounts[key] + 1;
} else {
classCounts.set(key, 1);
classCounts[key] = 1;
}
};

const decrClassCount = (key) => {
classCounts.set(key, classCounts.get(key) - 1);
classCounts[key] = classCounts[key] - 1;
};

const addStyleLevel = (key, value) => {
if (styleLevels.has(key)) {
styleLevels.set(key, styleLevels.get(key).concat([value]));
}
if (styleLevels[key] !== undefined) {
styleLevels[key] = styleLevels[key].concat([value]);
}
else {
const items = [value];
if (element.style[key]) items.unshift(element.style[key]);
styleLevels.set(key, items);
styleLevels[key] = items;
}
}

const removeStyleLevel = (key, value) => {
if (styleLevels.has(key)) {
const styles = styleLevels.get(key);
if (styleLevels[key] !== undefined) {
const styles = styleLevels[key];
const newStyles = styles.slice(0, -1);

styleLevels.set(key, newStyles);
styleLevels[key] = newStyles;
}
}

Expand Down Expand Up @@ -94,7 +97,7 @@ export default class ReactGlobalStyle extends React.Component {
oldClasses.forEach((b) => {
if (classes.indexOf(b) === -1) {
this.ed().decrClassCount(b);
if (!this.ed().classCounts.get(b)) {
if (!this.ed().classCounts[b]) {
this.el().classList.remove(b);
}
}
Expand All @@ -118,7 +121,7 @@ export default class ReactGlobalStyle extends React.Component {
// Remove the style by peeling back a layer
if (!style[b] && style[b] !== 0) {
ed.removeStyleLevel(b);
const levels = ed.styleLevels.get(b);
const levels = ed.styleLevels[b];
if (levels && levels.length) {
el.style[b] = levels[levels.length - 1];
} else {
Expand All @@ -144,4 +147,3 @@ export default class ReactGlobalStyle extends React.Component {
}
render() { return null; }
}

47 changes: 47 additions & 0 deletions src/WeakMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2012 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* https://github.com/Polymer/WeakMap/blob/master/LICENSE
*/

if (typeof WeakMap === 'undefined') {
(function() {
var defineProperty = Object.defineProperty;
var counter = Date.now() % 1e9;

var WeakMap = function() {
this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
};

WeakMap.prototype = {
set: function(key, value) {
var entry = key[this.name];
if (entry && entry[0] === key)
entry[1] = value;
else
defineProperty(key, this.name, {value: [key, value], writable: true});
return this;
},
get: function(key) {
var entry;
return (entry = key[this.name]) && entry[0] === key ?
entry[1] : undefined;
},
delete: function(key) {
var entry = key[this.name];
if (!entry) return false;
var hasValue = entry[0] === key;
entry[0] = entry[1] = undefined;
return hasValue;
},
has: function(key) {
var entry = key[this.name];
if (!entry) return false;
return entry[0] === key;
}
};

window.WeakMap = WeakMap;
})();
}
36 changes: 18 additions & 18 deletions src/__tests__/ReactGlobalStyle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,59 @@ const setup = () => {
describe(`classCounts`, () => {
it(`works for first run`, () => {
const ed = setup();
expect([...ed.classCounts]).toEqual([]);
expect(ed.classCounts).toEqual({});
ed.incrClassCount(`foo`);
expect([...ed.classCounts]).toEqual([[`foo`, 1]]);
expect(ed.classCounts).toEqual({ foo: 1 });
});

it(`works for second run`, () => {
const ed = setup();
ed.incrClassCount(`foo`);
ed.incrClassCount(`foo`);
expect([...ed.classCounts]).toEqual([[`foo`, 2]]);
expect(ed.classCounts).toEqual({ foo: 2 });
});

it(`removes`, () => {
const ed = setup();
ed.incrClassCount(`foo`);
ed.incrClassCount(`foo`);
ed.decrClassCount(`foo`);
expect([...ed.classCounts]).toEqual([[`foo`, 1]]);
expect(ed.classCounts).toEqual({ foo: 1 });
});
});

describe(`styleLevels`, () => {
it(`works for first run`, () => {
const ed = setup();
expect([...ed.styleLevels]).toEqual([]);
expect(ed.styleLevels).toEqual({});
ed.addStyleLevel(`color`, `red`);
expect([...ed.styleLevels]).toEqual([[`color`, [`red`]]]);
expect(ed.styleLevels).toEqual({ color: [`red`] });
});

it(`works for multiple levels`, () => {
const ed = setup();
expect([...ed.styleLevels]).toEqual([]);
expect(ed.styleLevels).toEqual({});
ed.addStyleLevel(`color`, `red`);
ed.addStyleLevel(`width`, `5px`);
ed.addStyleLevel(`color`, `blue`);
expect([...ed.styleLevels]).toEqual([
[`color`, [`red`, `blue`]],
[`width`, [`5px`]],
]);
expect(ed.styleLevels).toEqual({
color: [`red`, `blue`],
width: [`5px`],
});
ed.removeStyleLevel(`color`);
expect([...ed.styleLevels]).toEqual([
[`color`, [`red`]],
[`width`, [`5px`]],
]);
expect(ed.styleLevels).toEqual({
color: [`red`],
width: [`5px`],
});
});

it(`works with existing styles`, () => {
document.body.style.color = 'red';
const ed = setup();
ed.addStyleLevel('color', 'green');
expect([...ed.styleLevels]).toEqual([
['color', ['red', 'green']],
]);
expect(ed.styleLevels).toEqual({
'color': ['red', 'green'],
});
document.body.style.color = '';
});
});
Expand Down