-
-
Notifications
You must be signed in to change notification settings - Fork 132
/
api.js
154 lines (139 loc) · 3.94 KB
/
api.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
* Extended StyleSheet API
*/
import {StyleSheet} from 'react-native';
import Sheet from './sheet';
import Style from './style';
import Value from './value';
import vars from './replacers/vars';
import mq from './replacers/media-queries';
import child from './child';
const BUILD_EVENT = 'build';
export default class EStyleSheet {
/**
* Constructor
*/
constructor() {
this.child = child;
this.builded = false;
this.sheets = [];
this.globalVars = null;
this.listeners = {};
this._proxyToOriginal();
}
/**
* Creates stylesheet that will be calculated after build
* @param {Object} obj
* @returns {Object}
*/
create(obj) {
const sheet = new Sheet(obj);
// todo: add options param to allow create dynamic stylesheets that should not be stored
this.sheets.push(sheet);
if (this.builded) {
sheet.calc(this.globalVars);
}
return sheet.getResult();
}
/**
* Builds all created stylesheets with passed variables
* @param {Object} [rawGlobalVars]
*/
build(rawGlobalVars) {
this.builded = true;
this._calcGlobalVars(rawGlobalVars);
this._calcSheets();
this._callListeners(BUILD_EVENT);
}
/**
* Calculates particular value. For some values you need to pass prop (e.g. percent)
* @param {*} expr
* @param {String} [prop]
* @returns {*}
*/
value(expr, prop) {
let varsArr = this.globalVars ? [this.globalVars] : [];
return new Value(expr, prop, varsArr).calc();
}
/**
* Subscribe to event. Currently only 'build' event is supported.
* @param {String} event
* @param {Function} listener
*/
subscribe(event, listener) {
this._assertSubscriptionParams(event, listener);
this.listeners[BUILD_EVENT] = this.listeners[BUILD_EVENT] || [];
this.listeners[BUILD_EVENT].push(listener);
if (this.builded) {
listener();
}
}
/**
* Unsubscribe from event. Currently only 'build' event is supported.
* @param {String} event
* @param {Function} listener
*/
unsubscribe(event, listener) {
this._assertSubscriptionParams(event, listener);
if (this.listeners[BUILD_EVENT]) {
this.listeners[BUILD_EVENT] = this.listeners[BUILD_EVENT].filter(item => item !== listener);
}
}
/**
* Clears all cached styles.
*/
clearCache() {
this.sheets.forEach(sheet => sheet.clearCache());
}
// todo: move global vars stuff to separate module
_calcGlobalVars(rawGlobalVars) {
if (rawGlobalVars) {
this._checkGlobalVars(rawGlobalVars);
// $theme is system variable used for caching
rawGlobalVars.$theme = rawGlobalVars.$theme || 'default';
this.globalVars = new Style(rawGlobalVars, [rawGlobalVars]).calc().calculatedVars;
}
}
_calcSheets() {
this.sheets.forEach(sheet => sheet.calc(this.globalVars));
}
_callListeners(event) {
if (Array.isArray(this.listeners[event])) {
this.listeners[event].forEach(listener => listener());
}
}
_proxyToOriginal() {
// see: https://facebook.github.io/react-native/docs/stylesheet.html
const props = [
'setStyleAttributePreprocessor',
'hairlineWidth',
'absoluteFill',
'absoluteFillObject',
'flatten',
];
props.forEach(prop => {
Object.defineProperty(this, prop, {
get: () => StyleSheet[prop],
enumerable: true,
});
});
}
_checkGlobalVars(rawGlobalVars) {
Object.keys(rawGlobalVars).forEach(key => {
if (!vars.isVar(key) && !mq.isMediaQuery(key)) {
throw new Error(
`EStyleSheet.build() params should contain global variables (start with $) ` +
`or media queries (start with @media). Got '${key}'.`
);
}
});
}
_assertSubscriptionParams(event, listener) {
if (event !== BUILD_EVENT) {
throw new Error(`Only '${BUILD_EVENT}' event is currently supported.`);
}
if (typeof listener !== 'function') {
throw new Error('Listener should be a function.');
}
}
}