@telus-uds/components-base 1.69.0 → 1.70.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/CHANGELOG.md +10 -2
- package/lib/utils/index.js +12 -0
- package/lib/utils/ssr-media-query/create-stylesheet.js +76 -0
- package/lib/utils/ssr-media-query/hash.js +19 -0
- package/lib/utils/ssr-media-query/index.js +19 -0
- package/lib/utils/ssr-media-query/utils/common.js +25 -0
- package/lib/utils/ssr-media-query/utils/create-declaration-block.js +24 -0
- package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +34 -0
- package/lib/utils/ssr-media-query/utils/hyphenate-style-name.js +19 -0
- package/lib/utils/ssr-media-query/utils/inject.js +36 -0
- package/lib/utils/ssr.js +2 -1
- package/lib-module/utils/index.js +1 -0
- package/lib-module/utils/ssr-media-query/create-stylesheet.js +68 -0
- package/lib-module/utils/ssr-media-query/hash.js +13 -0
- package/lib-module/utils/ssr-media-query/index.js +6 -0
- package/lib-module/utils/ssr-media-query/utils/common.js +15 -0
- package/lib-module/utils/ssr-media-query/utils/create-declaration-block.js +16 -0
- package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +30 -0
- package/lib-module/utils/ssr-media-query/utils/hyphenate-style-name.js +12 -0
- package/lib-module/utils/ssr-media-query/utils/inject.js +26 -0
- package/lib-module/utils/ssr.js +3 -1
- package/package.json +2 -1
- package/src/utils/index.js +1 -1
- package/src/utils/ssr-media-query/create-stylesheet.js +61 -0
- package/src/utils/ssr-media-query/hash.js +16 -0
- package/src/utils/ssr-media-query/index.js +8 -0
- package/src/utils/ssr-media-query/utils/common.js +20 -0
- package/src/utils/ssr-media-query/utils/create-declaration-block.js +21 -0
- package/src/utils/ssr-media-query/utils/create-media-query-styles.js +31 -0
- package/src/utils/ssr-media-query/utils/hyphenate-style-name.js +15 -0
- package/src/utils/ssr-media-query/utils/inject.js +30 -0
- package/src/utils/ssr.jsx +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# Change Log - @telus-uds/components-base
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Mon, 27 Nov 2023 21:13:27 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## 1.70.0
|
|
8
|
+
|
|
9
|
+
Mon, 27 Nov 2023 21:13:27 GMT
|
|
10
|
+
|
|
11
|
+
### Minor changes
|
|
12
|
+
|
|
13
|
+
- Add SSR support for media queries (wlsdud194@hotmail.com)
|
|
14
|
+
|
|
7
15
|
## 1.69.0
|
|
8
16
|
|
|
9
|
-
Sat, 18 Nov 2023 02:
|
|
17
|
+
Sat, 18 Nov 2023 02:32:49 GMT
|
|
10
18
|
|
|
11
19
|
### Minor changes
|
|
12
20
|
|
package/lib/utils/index.js
CHANGED
|
@@ -168,6 +168,18 @@ Object.keys(_props).forEach(function (key) {
|
|
|
168
168
|
}
|
|
169
169
|
});
|
|
170
170
|
});
|
|
171
|
+
var _ssrMediaQuery = require("./ssr-media-query");
|
|
172
|
+
Object.keys(_ssrMediaQuery).forEach(function (key) {
|
|
173
|
+
if (key === "default" || key === "__esModule") return;
|
|
174
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
175
|
+
if (key in exports && exports[key] === _ssrMediaQuery[key]) return;
|
|
176
|
+
Object.defineProperty(exports, key, {
|
|
177
|
+
enumerable: true,
|
|
178
|
+
get: function () {
|
|
179
|
+
return _ssrMediaQuery[key];
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
171
183
|
var _info = _interopRequireDefault(require("./info"));
|
|
172
184
|
var _useCopy = _interopRequireDefault(require("./useCopy"));
|
|
173
185
|
var _useHash = _interopRequireDefault(require("./useHash"));
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _cssMediaquery = _interopRequireDefault(require("css-mediaquery"));
|
|
8
|
+
var _Dimensions = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Dimensions"));
|
|
9
|
+
var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
|
|
10
|
+
var _inject = require("./utils/inject");
|
|
11
|
+
var _createDeclarationBlock = _interopRequireDefault(require("./utils/create-declaration-block"));
|
|
12
|
+
var _hash = _interopRequireDefault(require("./hash"));
|
|
13
|
+
var _common = require("./utils/common");
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
const createStyleSheet = stylesWithQuery => {
|
|
16
|
+
if (!stylesWithQuery) return {
|
|
17
|
+
ids: {},
|
|
18
|
+
styles: {},
|
|
19
|
+
fullStyles: {}
|
|
20
|
+
};
|
|
21
|
+
let cleanStyles;
|
|
22
|
+
let ids = {};
|
|
23
|
+
Object.keys(stylesWithQuery).forEach(key => {
|
|
24
|
+
if (!(stylesWithQuery !== null && stylesWithQuery !== void 0 && stylesWithQuery[key])) return;
|
|
25
|
+
const mediaQueriesAndPseudoClasses = Object.keys(stylesWithQuery[key]).filter(_common.isMediaOrPseudo);
|
|
26
|
+
if (_Platform.default.OS === 'web') {
|
|
27
|
+
cleanStyles = (0, _common.deepClone)(stylesWithQuery);
|
|
28
|
+
mediaQueriesAndPseudoClasses.forEach(query => {
|
|
29
|
+
var _ids;
|
|
30
|
+
const css = (0, _createDeclarationBlock.default)(stylesWithQuery[key][query]);
|
|
31
|
+
const stringHash = `rnmq-${(0, _hash.default)(`${key}${query}${css}`)}`;
|
|
32
|
+
const rule = (0, _common.createCssRule)(query, stringHash, css);
|
|
33
|
+
(0, _inject.addCss)(`${stringHash}`, rule);
|
|
34
|
+
delete cleanStyles[key][query];
|
|
35
|
+
ids = {
|
|
36
|
+
...ids,
|
|
37
|
+
[key]: `${(_ids = ids) !== null && _ids !== void 0 && _ids[key] ? `${ids[key]} ` : ''}${stringHash}`
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
cleanStyles = JSON.parse(JSON.stringify(stylesWithQuery));
|
|
42
|
+
mediaQueriesAndPseudoClasses.forEach(str => {
|
|
43
|
+
if ((0, _common.isMedia)(str)) {
|
|
44
|
+
const mqStr = str.replace('@media', '');
|
|
45
|
+
const {
|
|
46
|
+
width,
|
|
47
|
+
height
|
|
48
|
+
} = _Dimensions.default.get('window');
|
|
49
|
+
const isMatchingMediaQuery = _cssMediaquery.default.match(mqStr, {
|
|
50
|
+
width,
|
|
51
|
+
height,
|
|
52
|
+
orientation: width > height ? 'landscape' : 'portrait',
|
|
53
|
+
'aspect-ratio': width / height
|
|
54
|
+
});
|
|
55
|
+
if (isMatchingMediaQuery) {
|
|
56
|
+
cleanStyles = {
|
|
57
|
+
...cleanStyles,
|
|
58
|
+
[key]: {
|
|
59
|
+
...cleanStyles[key],
|
|
60
|
+
...stylesWithQuery[key][str]
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
delete cleanStyles[key][str];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
ids,
|
|
71
|
+
styles: cleanStyles,
|
|
72
|
+
fullStyles: stylesWithQuery
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
var _default = createStyleSheet;
|
|
76
|
+
exports.default = _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = hash;
|
|
7
|
+
/* eslint-disable no-bitwise */
|
|
8
|
+
function hash(text) {
|
|
9
|
+
if (!text) {
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
let hashValue = 5381;
|
|
13
|
+
let index = text.length - 1;
|
|
14
|
+
while (index) {
|
|
15
|
+
hashValue = hashValue * 33 ^ text.charCodeAt(index);
|
|
16
|
+
index -= 1;
|
|
17
|
+
}
|
|
18
|
+
return (hashValue >>> 0).toString(16);
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.StyleSheet = void 0;
|
|
7
|
+
Object.defineProperty(exports, "createMediaQueryStyles", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _createMediaQueryStyles.default;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _createStylesheet = _interopRequireDefault(require("./create-stylesheet"));
|
|
14
|
+
var _createMediaQueryStyles = _interopRequireDefault(require("./utils/create-media-query-styles"));
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
const StyleSheet = {
|
|
17
|
+
create: _createStylesheet.default
|
|
18
|
+
};
|
|
19
|
+
exports.StyleSheet = StyleSheet;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isPseudo = exports.isMediaOrPseudo = exports.isMedia = exports.deepClone = exports.createCssRule = void 0;
|
|
7
|
+
const isMedia = query => query.indexOf('@media') === 0;
|
|
8
|
+
exports.isMedia = isMedia;
|
|
9
|
+
const isPseudo = query => query.indexOf(':') === 0;
|
|
10
|
+
exports.isPseudo = isPseudo;
|
|
11
|
+
const isMediaOrPseudo = query => isMedia(query) || isPseudo(query);
|
|
12
|
+
exports.isMediaOrPseudo = isMediaOrPseudo;
|
|
13
|
+
const deepClone = obj => JSON.parse(JSON.stringify(obj));
|
|
14
|
+
exports.deepClone = deepClone;
|
|
15
|
+
const createCssRule = (query, stringHash, css) => {
|
|
16
|
+
let rule;
|
|
17
|
+
const dataMediaSelector = `[data-media~="${stringHash}"]`;
|
|
18
|
+
if (isMedia(query)) {
|
|
19
|
+
rule = `${query} {${dataMediaSelector} ${css}}`;
|
|
20
|
+
} else {
|
|
21
|
+
rule = `${dataMediaSelector}${query} ${css}`;
|
|
22
|
+
}
|
|
23
|
+
return rule;
|
|
24
|
+
};
|
|
25
|
+
exports.createCssRule = createCssRule;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _createReactDOMStyle = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet/compiler/createReactDOMStyle"));
|
|
8
|
+
var _prefixStyles = _interopRequireDefault(require("react-native-web/dist/cjs/modules/prefixStyles"));
|
|
9
|
+
var _hyphenateStyleName = _interopRequireDefault(require("./hyphenate-style-name"));
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
const createDeclarationBlock = style => {
|
|
12
|
+
const domStyle = (0, _prefixStyles.default)((0, _createReactDOMStyle.default)(style));
|
|
13
|
+
const declarationsString = Object.keys(domStyle).map(property => {
|
|
14
|
+
const value = domStyle[property];
|
|
15
|
+
const prop = (0, _hyphenateStyleName.default)(property);
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map(v => `${prop}:${v}`).join(';');
|
|
18
|
+
}
|
|
19
|
+
return `${prop}:${value} !important`;
|
|
20
|
+
}).sort().join(';');
|
|
21
|
+
return `{${declarationsString};}`;
|
|
22
|
+
};
|
|
23
|
+
var _default = createDeclarationBlock;
|
|
24
|
+
exports.default = _default;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _systemConstants = require("@telus-uds/system-constants");
|
|
8
|
+
/**
|
|
9
|
+
* @typedef { Object } CssStyles
|
|
10
|
+
* @typedef { Record<"xs" | "sm" | "md" | "lg" | "xl", CssStyles> } ViewportStyles
|
|
11
|
+
* @typedef { Record<String, CssStyles> } MediaQueryStyles
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Generates media query styles based on specified viewport styles.
|
|
15
|
+
* @param {ViewportStyles} viewportStyles
|
|
16
|
+
* @returns { MediaQueryStyles }
|
|
17
|
+
*/
|
|
18
|
+
function createMediaQueryStyles(viewportStyles) {
|
|
19
|
+
const viewportsArray = Object.keys(viewportStyles);
|
|
20
|
+
const mediaQueries = Object.entries(viewportStyles).reduce((acc, _ref) => {
|
|
21
|
+
let [viewport, styles] = _ref;
|
|
22
|
+
const minWidth = _systemConstants.viewports.map.get(viewport);
|
|
23
|
+
const nextViewport = viewportsArray[viewportsArray.indexOf(viewport) + 1];
|
|
24
|
+
const maxWidth = _systemConstants.viewports.map.get(nextViewport);
|
|
25
|
+
const mediaQuery = `@media (min-width: ${minWidth}px)${maxWidth ? ` and (max-width: ${maxWidth}px)` : ''}`;
|
|
26
|
+
return {
|
|
27
|
+
...acc,
|
|
28
|
+
[mediaQuery]: styles
|
|
29
|
+
};
|
|
30
|
+
}, {});
|
|
31
|
+
return mediaQueries;
|
|
32
|
+
}
|
|
33
|
+
var _default = createMediaQueryStyles;
|
|
34
|
+
exports.default = _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
const uppercasePattern = /[A-Z]/g;
|
|
8
|
+
const msPattern = /^ms-/;
|
|
9
|
+
const cache = {};
|
|
10
|
+
const toHyphenLower = match => `-${match.toLowerCase()}`;
|
|
11
|
+
const hyphenateStyleName = name => {
|
|
12
|
+
if (Object.prototype.hasOwnProperty.call(cache, name)) {
|
|
13
|
+
return cache[name];
|
|
14
|
+
}
|
|
15
|
+
const hName = name.replace(uppercasePattern, toHyphenLower);
|
|
16
|
+
return cache[name] === msPattern.test(hName) ? `-${hName}` : hName;
|
|
17
|
+
};
|
|
18
|
+
var _default = hyphenateStyleName;
|
|
19
|
+
exports.default = _default;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.hasCss = exports.flush = exports.addCss = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
const rules = {};
|
|
11
|
+
const hasCss = (id, text) => {
|
|
12
|
+
var _rules$id$text, _rules$id$text$includ;
|
|
13
|
+
return !!rules[id] && !!((_rules$id$text = rules[id].text) !== null && _rules$id$text !== void 0 && (_rules$id$text$includ = _rules$id$text.includes) !== null && _rules$id$text$includ !== void 0 && _rules$id$text$includ.call(_rules$id$text, text));
|
|
14
|
+
};
|
|
15
|
+
exports.hasCss = hasCss;
|
|
16
|
+
const addCss = (id, text) => {
|
|
17
|
+
if (!hasCss(id, text)) {
|
|
18
|
+
var _rules$id;
|
|
19
|
+
rules[id] = (rules === null || rules === void 0 ? void 0 : rules[id]) || {};
|
|
20
|
+
rules[id].text = (((_rules$id = rules[id]) === null || _rules$id === void 0 ? void 0 : _rules$id.text) || '') + text;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.addCss = addCss;
|
|
24
|
+
const flush = () => {
|
|
25
|
+
if (_Platform.default.OS === 'web') {
|
|
26
|
+
return /*#__PURE__*/_react.default.createElement('style', {
|
|
27
|
+
id: 'rnmq',
|
|
28
|
+
key: 'rnmq',
|
|
29
|
+
dangerouslySetInnerHTML: {
|
|
30
|
+
__html: Object.keys(rules).map(key => rules[key].text).join('\n')
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return {};
|
|
35
|
+
};
|
|
36
|
+
exports.flush = flush;
|
package/lib/utils/ssr.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.ssrStyles = exports.getSSRStyles = exports.getReactNativeWebSSRStyles = exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireDefault(require("react"));
|
|
8
8
|
var _AppRegistry = _interopRequireDefault(require("react-native-web/dist/cjs/exports/AppRegistry"));
|
|
9
|
+
var _inject = require("./ssr-media-query/utils/inject");
|
|
9
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
12
|
/** @typedef {import('react').ComponentType} ReactComponent */
|
|
@@ -102,7 +103,7 @@ const ssrStyles = function () {
|
|
|
102
103
|
for (var _len = arguments.length, existingStyles = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
103
104
|
existingStyles[_key] = arguments[_key];
|
|
104
105
|
}
|
|
105
|
-
return [...existingStyles, ...styleGetters.flatMap(getter => getter())];
|
|
106
|
+
return [...existingStyles, ...styleGetters.flatMap(getter => getter()), (0, _inject.flush)()];
|
|
106
107
|
}
|
|
107
108
|
};
|
|
108
109
|
};
|
|
@@ -4,6 +4,7 @@ export * from './children';
|
|
|
4
4
|
export * from './input';
|
|
5
5
|
export * from './pressability';
|
|
6
6
|
export * from './props';
|
|
7
|
+
export * from './ssr-media-query';
|
|
7
8
|
export { default as info } from './info';
|
|
8
9
|
export { default as useCopy } from './useCopy';
|
|
9
10
|
export { default as useHash } from './useHash';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import mediaQuery from 'css-mediaquery';
|
|
2
|
+
import Dimensions from "react-native-web/dist/exports/Dimensions";
|
|
3
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
4
|
+
import { addCss } from './utils/inject';
|
|
5
|
+
import createDeclarationBlock from './utils/create-declaration-block';
|
|
6
|
+
import hash from './hash';
|
|
7
|
+
import { isMediaOrPseudo, isMedia, deepClone, createCssRule } from './utils/common';
|
|
8
|
+
const createStyleSheet = stylesWithQuery => {
|
|
9
|
+
if (!stylesWithQuery) return {
|
|
10
|
+
ids: {},
|
|
11
|
+
styles: {},
|
|
12
|
+
fullStyles: {}
|
|
13
|
+
};
|
|
14
|
+
let cleanStyles;
|
|
15
|
+
let ids = {};
|
|
16
|
+
Object.keys(stylesWithQuery).forEach(key => {
|
|
17
|
+
if (!(stylesWithQuery !== null && stylesWithQuery !== void 0 && stylesWithQuery[key])) return;
|
|
18
|
+
const mediaQueriesAndPseudoClasses = Object.keys(stylesWithQuery[key]).filter(isMediaOrPseudo);
|
|
19
|
+
if (Platform.OS === 'web') {
|
|
20
|
+
cleanStyles = deepClone(stylesWithQuery);
|
|
21
|
+
mediaQueriesAndPseudoClasses.forEach(query => {
|
|
22
|
+
var _ids;
|
|
23
|
+
const css = createDeclarationBlock(stylesWithQuery[key][query]);
|
|
24
|
+
const stringHash = `rnmq-${hash(`${key}${query}${css}`)}`;
|
|
25
|
+
const rule = createCssRule(query, stringHash, css);
|
|
26
|
+
addCss(`${stringHash}`, rule);
|
|
27
|
+
delete cleanStyles[key][query];
|
|
28
|
+
ids = {
|
|
29
|
+
...ids,
|
|
30
|
+
[key]: `${(_ids = ids) !== null && _ids !== void 0 && _ids[key] ? `${ids[key]} ` : ''}${stringHash}`
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
cleanStyles = JSON.parse(JSON.stringify(stylesWithQuery));
|
|
35
|
+
mediaQueriesAndPseudoClasses.forEach(str => {
|
|
36
|
+
if (isMedia(str)) {
|
|
37
|
+
const mqStr = str.replace('@media', '');
|
|
38
|
+
const {
|
|
39
|
+
width,
|
|
40
|
+
height
|
|
41
|
+
} = Dimensions.get('window');
|
|
42
|
+
const isMatchingMediaQuery = mediaQuery.match(mqStr, {
|
|
43
|
+
width,
|
|
44
|
+
height,
|
|
45
|
+
orientation: width > height ? 'landscape' : 'portrait',
|
|
46
|
+
'aspect-ratio': width / height
|
|
47
|
+
});
|
|
48
|
+
if (isMatchingMediaQuery) {
|
|
49
|
+
cleanStyles = {
|
|
50
|
+
...cleanStyles,
|
|
51
|
+
[key]: {
|
|
52
|
+
...cleanStyles[key],
|
|
53
|
+
...stylesWithQuery[key][str]
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
delete cleanStyles[key][str];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
ids,
|
|
64
|
+
styles: cleanStyles,
|
|
65
|
+
fullStyles: stylesWithQuery
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export default createStyleSheet;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
2
|
+
export default function hash(text) {
|
|
3
|
+
if (!text) {
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
let hashValue = 5381;
|
|
7
|
+
let index = text.length - 1;
|
|
8
|
+
while (index) {
|
|
9
|
+
hashValue = hashValue * 33 ^ text.charCodeAt(index);
|
|
10
|
+
index -= 1;
|
|
11
|
+
}
|
|
12
|
+
return (hashValue >>> 0).toString(16);
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const isMedia = query => query.indexOf('@media') === 0;
|
|
2
|
+
const isPseudo = query => query.indexOf(':') === 0;
|
|
3
|
+
const isMediaOrPseudo = query => isMedia(query) || isPseudo(query);
|
|
4
|
+
const deepClone = obj => JSON.parse(JSON.stringify(obj));
|
|
5
|
+
const createCssRule = (query, stringHash, css) => {
|
|
6
|
+
let rule;
|
|
7
|
+
const dataMediaSelector = `[data-media~="${stringHash}"]`;
|
|
8
|
+
if (isMedia(query)) {
|
|
9
|
+
rule = `${query} {${dataMediaSelector} ${css}}`;
|
|
10
|
+
} else {
|
|
11
|
+
rule = `${dataMediaSelector}${query} ${css}`;
|
|
12
|
+
}
|
|
13
|
+
return rule;
|
|
14
|
+
};
|
|
15
|
+
export { isMedia, isPseudo, isMediaOrPseudo, deepClone, createCssRule };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import createReactDOMStyle from 'react-native-web/dist/cjs/exports/StyleSheet/compiler/createReactDOMStyle';
|
|
2
|
+
import prefixStyles from 'react-native-web/dist/cjs/modules/prefixStyles';
|
|
3
|
+
import hyphenateStyleName from './hyphenate-style-name';
|
|
4
|
+
const createDeclarationBlock = style => {
|
|
5
|
+
const domStyle = prefixStyles(createReactDOMStyle(style));
|
|
6
|
+
const declarationsString = Object.keys(domStyle).map(property => {
|
|
7
|
+
const value = domStyle[property];
|
|
8
|
+
const prop = hyphenateStyleName(property);
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return value.map(v => `${prop}:${v}`).join(';');
|
|
11
|
+
}
|
|
12
|
+
return `${prop}:${value} !important`;
|
|
13
|
+
}).sort().join(';');
|
|
14
|
+
return `{${declarationsString};}`;
|
|
15
|
+
};
|
|
16
|
+
export default createDeclarationBlock;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { viewports } from '@telus-uds/system-constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef { Object } CssStyles
|
|
5
|
+
* @typedef { Record<"xs" | "sm" | "md" | "lg" | "xl", CssStyles> } ViewportStyles
|
|
6
|
+
* @typedef { Record<String, CssStyles> } MediaQueryStyles
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates media query styles based on specified viewport styles.
|
|
11
|
+
* @param {ViewportStyles} viewportStyles
|
|
12
|
+
* @returns { MediaQueryStyles }
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
function createMediaQueryStyles(viewportStyles) {
|
|
16
|
+
const viewportsArray = Object.keys(viewportStyles);
|
|
17
|
+
const mediaQueries = Object.entries(viewportStyles).reduce((acc, _ref) => {
|
|
18
|
+
let [viewport, styles] = _ref;
|
|
19
|
+
const minWidth = viewports.map.get(viewport);
|
|
20
|
+
const nextViewport = viewportsArray[viewportsArray.indexOf(viewport) + 1];
|
|
21
|
+
const maxWidth = viewports.map.get(nextViewport);
|
|
22
|
+
const mediaQuery = `@media (min-width: ${minWidth}px)${maxWidth ? ` and (max-width: ${maxWidth}px)` : ''}`;
|
|
23
|
+
return {
|
|
24
|
+
...acc,
|
|
25
|
+
[mediaQuery]: styles
|
|
26
|
+
};
|
|
27
|
+
}, {});
|
|
28
|
+
return mediaQueries;
|
|
29
|
+
}
|
|
30
|
+
export default createMediaQueryStyles;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const uppercasePattern = /[A-Z]/g;
|
|
2
|
+
const msPattern = /^ms-/;
|
|
3
|
+
const cache = {};
|
|
4
|
+
const toHyphenLower = match => `-${match.toLowerCase()}`;
|
|
5
|
+
const hyphenateStyleName = name => {
|
|
6
|
+
if (Object.prototype.hasOwnProperty.call(cache, name)) {
|
|
7
|
+
return cache[name];
|
|
8
|
+
}
|
|
9
|
+
const hName = name.replace(uppercasePattern, toHyphenLower);
|
|
10
|
+
return cache[name] === msPattern.test(hName) ? `-${hName}` : hName;
|
|
11
|
+
};
|
|
12
|
+
export default hyphenateStyleName;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
3
|
+
const rules = {};
|
|
4
|
+
export const hasCss = (id, text) => {
|
|
5
|
+
var _rules$id$text, _rules$id$text$includ;
|
|
6
|
+
return !!rules[id] && !!((_rules$id$text = rules[id].text) !== null && _rules$id$text !== void 0 && (_rules$id$text$includ = _rules$id$text.includes) !== null && _rules$id$text$includ !== void 0 && _rules$id$text$includ.call(_rules$id$text, text));
|
|
7
|
+
};
|
|
8
|
+
export const addCss = (id, text) => {
|
|
9
|
+
if (!hasCss(id, text)) {
|
|
10
|
+
var _rules$id;
|
|
11
|
+
rules[id] = (rules === null || rules === void 0 ? void 0 : rules[id]) || {};
|
|
12
|
+
rules[id].text = (((_rules$id = rules[id]) === null || _rules$id === void 0 ? void 0 : _rules$id.text) || '') + text;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const flush = () => {
|
|
16
|
+
if (Platform.OS === 'web') {
|
|
17
|
+
return /*#__PURE__*/React.createElement('style', {
|
|
18
|
+
id: 'rnmq',
|
|
19
|
+
key: 'rnmq',
|
|
20
|
+
dangerouslySetInnerHTML: {
|
|
21
|
+
__html: Object.keys(rules).map(key => rules[key].text).join('\n')
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return {};
|
|
26
|
+
};
|
package/lib-module/utils/ssr.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import AppRegistry from "react-native-web/dist/exports/AppRegistry";
|
|
3
|
+
import { flush } from './ssr-media-query/utils/inject';
|
|
3
4
|
/** @typedef {import('react').ComponentType} ReactComponent */
|
|
4
5
|
/** @typedef {import('react').ReactElement} ReactElement */
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Returns object with `renderApp` and `getStyles` functions.
|
|
7
9
|
* Weave these into your app's server-side render process:
|
|
@@ -94,7 +96,7 @@ export const ssrStyles = function () {
|
|
|
94
96
|
for (var _len = arguments.length, existingStyles = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
95
97
|
existingStyles[_key] = arguments[_key];
|
|
96
98
|
}
|
|
97
|
-
return [...existingStyles, ...styleGetters.flatMap(getter => getter())];
|
|
99
|
+
return [...existingStyles, ...styleGetters.flatMap(getter => getter()), flush()];
|
|
98
100
|
}
|
|
99
101
|
};
|
|
100
102
|
};
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@telus-uds/system-constants": "^1.3.0",
|
|
14
14
|
"@telus-uds/system-theme-tokens": "^2.46.0",
|
|
15
15
|
"airbnb-prop-types": "^2.16.0",
|
|
16
|
+
"css-mediaquery": "^0.1.2",
|
|
16
17
|
"lodash.debounce": "^4.0.8",
|
|
17
18
|
"lodash.merge": "^4.6.2",
|
|
18
19
|
"lodash.throttle": "^4.1.1",
|
|
@@ -73,5 +74,5 @@
|
|
|
73
74
|
"standard-engine": {
|
|
74
75
|
"skip": true
|
|
75
76
|
},
|
|
76
|
-
"version": "1.
|
|
77
|
+
"version": "1.70.0"
|
|
77
78
|
}
|
package/src/utils/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export * from './children'
|
|
|
4
4
|
export * from './input'
|
|
5
5
|
export * from './pressability'
|
|
6
6
|
export * from './props'
|
|
7
|
-
|
|
7
|
+
export * from './ssr-media-query'
|
|
8
8
|
export { default as info } from './info'
|
|
9
9
|
export { default as useCopy } from './useCopy'
|
|
10
10
|
export { default as useHash } from './useHash'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import mediaQuery from 'css-mediaquery'
|
|
2
|
+
import { Dimensions, Platform } from 'react-native'
|
|
3
|
+
import { addCss } from './utils/inject'
|
|
4
|
+
import createDeclarationBlock from './utils/create-declaration-block'
|
|
5
|
+
import hash from './hash'
|
|
6
|
+
import { isMediaOrPseudo, isMedia, deepClone, createCssRule } from './utils/common'
|
|
7
|
+
|
|
8
|
+
const createStyleSheet = (stylesWithQuery) => {
|
|
9
|
+
if (!stylesWithQuery) return { ids: {}, styles: {}, fullStyles: {} }
|
|
10
|
+
let cleanStyles
|
|
11
|
+
let ids = {}
|
|
12
|
+
Object.keys(stylesWithQuery).forEach((key) => {
|
|
13
|
+
if (!stylesWithQuery?.[key]) return
|
|
14
|
+
|
|
15
|
+
const mediaQueriesAndPseudoClasses = Object.keys(stylesWithQuery[key]).filter(isMediaOrPseudo)
|
|
16
|
+
if (Platform.OS === 'web') {
|
|
17
|
+
cleanStyles = deepClone(stylesWithQuery)
|
|
18
|
+
mediaQueriesAndPseudoClasses.forEach((query) => {
|
|
19
|
+
const css = createDeclarationBlock(stylesWithQuery[key][query])
|
|
20
|
+
const stringHash = `rnmq-${hash(`${key}${query}${css}`)}`
|
|
21
|
+
const rule = createCssRule(query, stringHash, css)
|
|
22
|
+
|
|
23
|
+
addCss(`${stringHash}`, rule)
|
|
24
|
+
delete cleanStyles[key][query]
|
|
25
|
+
|
|
26
|
+
ids = {
|
|
27
|
+
...ids,
|
|
28
|
+
[key]: `${ids?.[key] ? `${ids[key]} ` : ''}${stringHash}`
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
} else {
|
|
32
|
+
cleanStyles = JSON.parse(JSON.stringify(stylesWithQuery))
|
|
33
|
+
mediaQueriesAndPseudoClasses.forEach((str) => {
|
|
34
|
+
if (isMedia(str)) {
|
|
35
|
+
const mqStr = str.replace('@media', '')
|
|
36
|
+
const { width, height } = Dimensions.get('window')
|
|
37
|
+
|
|
38
|
+
const isMatchingMediaQuery = mediaQuery.match(mqStr, {
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
orientation: width > height ? 'landscape' : 'portrait',
|
|
42
|
+
'aspect-ratio': width / height
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (isMatchingMediaQuery) {
|
|
46
|
+
cleanStyles = {
|
|
47
|
+
...cleanStyles,
|
|
48
|
+
[key]: { ...cleanStyles[key], ...stylesWithQuery[key][str] }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
delete cleanStyles[key][str]
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return { ids, styles: cleanStyles, fullStyles: stylesWithQuery }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default createStyleSheet
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
2
|
+
export default function hash(text) {
|
|
3
|
+
if (!text) {
|
|
4
|
+
return ''
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let hashValue = 5381
|
|
8
|
+
let index = text.length - 1
|
|
9
|
+
|
|
10
|
+
while (index) {
|
|
11
|
+
hashValue = (hashValue * 33) ^ text.charCodeAt(index)
|
|
12
|
+
index -= 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return (hashValue >>> 0).toString(16)
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const isMedia = (query) => query.indexOf('@media') === 0
|
|
2
|
+
const isPseudo = (query) => query.indexOf(':') === 0
|
|
3
|
+
const isMediaOrPseudo = (query) => isMedia(query) || isPseudo(query)
|
|
4
|
+
|
|
5
|
+
const deepClone = (obj) => JSON.parse(JSON.stringify(obj))
|
|
6
|
+
|
|
7
|
+
const createCssRule = (query, stringHash, css) => {
|
|
8
|
+
let rule
|
|
9
|
+
const dataMediaSelector = `[data-media~="${stringHash}"]`
|
|
10
|
+
|
|
11
|
+
if (isMedia(query)) {
|
|
12
|
+
rule = `${query} {${dataMediaSelector} ${css}}`
|
|
13
|
+
} else {
|
|
14
|
+
rule = `${dataMediaSelector}${query} ${css}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return rule
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { isMedia, isPseudo, isMediaOrPseudo, deepClone, createCssRule }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import createReactDOMStyle from 'react-native-web/dist/cjs/exports/StyleSheet/compiler/createReactDOMStyle'
|
|
2
|
+
import prefixStyles from 'react-native-web/dist/cjs/modules/prefixStyles'
|
|
3
|
+
import hyphenateStyleName from './hyphenate-style-name'
|
|
4
|
+
|
|
5
|
+
const createDeclarationBlock = (style) => {
|
|
6
|
+
const domStyle = prefixStyles(createReactDOMStyle(style))
|
|
7
|
+
const declarationsString = Object.keys(domStyle)
|
|
8
|
+
.map((property) => {
|
|
9
|
+
const value = domStyle[property]
|
|
10
|
+
const prop = hyphenateStyleName(property)
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value.map((v) => `${prop}:${v}`).join(';')
|
|
13
|
+
}
|
|
14
|
+
return `${prop}:${value} !important`
|
|
15
|
+
})
|
|
16
|
+
.sort()
|
|
17
|
+
.join(';')
|
|
18
|
+
return `{${declarationsString};}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default createDeclarationBlock
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { viewports } from '@telus-uds/system-constants'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef { Object } CssStyles
|
|
5
|
+
* @typedef { Record<"xs" | "sm" | "md" | "lg" | "xl", CssStyles> } ViewportStyles
|
|
6
|
+
* @typedef { Record<String, CssStyles> } MediaQueryStyles
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates media query styles based on specified viewport styles.
|
|
11
|
+
* @param {ViewportStyles} viewportStyles
|
|
12
|
+
* @returns { MediaQueryStyles }
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
function createMediaQueryStyles(viewportStyles) {
|
|
16
|
+
const viewportsArray = Object.keys(viewportStyles)
|
|
17
|
+
const mediaQueries = Object.entries(viewportStyles).reduce((acc, [viewport, styles]) => {
|
|
18
|
+
const minWidth = viewports.map.get(viewport)
|
|
19
|
+
const nextViewport = viewportsArray[viewportsArray.indexOf(viewport) + 1]
|
|
20
|
+
const maxWidth = viewports.map.get(nextViewport)
|
|
21
|
+
|
|
22
|
+
const mediaQuery = `@media (min-width: ${minWidth}px)${
|
|
23
|
+
maxWidth ? ` and (max-width: ${maxWidth}px)` : ''
|
|
24
|
+
}`
|
|
25
|
+
return { ...acc, [mediaQuery]: styles }
|
|
26
|
+
}, {})
|
|
27
|
+
|
|
28
|
+
return mediaQueries
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default createMediaQueryStyles
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const uppercasePattern = /[A-Z]/g
|
|
2
|
+
const msPattern = /^ms-/
|
|
3
|
+
const cache = {}
|
|
4
|
+
|
|
5
|
+
const toHyphenLower = (match) => `-${match.toLowerCase()}`
|
|
6
|
+
|
|
7
|
+
const hyphenateStyleName = (name) => {
|
|
8
|
+
if (Object.prototype.hasOwnProperty.call(cache, name)) {
|
|
9
|
+
return cache[name]
|
|
10
|
+
}
|
|
11
|
+
const hName = name.replace(uppercasePattern, toHyphenLower)
|
|
12
|
+
return cache[name] === msPattern.test(hName) ? `-${hName}` : hName
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default hyphenateStyleName
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Platform } from 'react-native'
|
|
3
|
+
|
|
4
|
+
const rules = {}
|
|
5
|
+
|
|
6
|
+
export const hasCss = (id, text) => {
|
|
7
|
+
return !!rules[id] && !!rules[id].text?.includes?.(text)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const addCss = (id, text) => {
|
|
11
|
+
if (!hasCss(id, text)) {
|
|
12
|
+
rules[id] = rules?.[id] || {}
|
|
13
|
+
rules[id].text = (rules[id]?.text || '') + text
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const flush = () => {
|
|
18
|
+
if (Platform.OS === 'web') {
|
|
19
|
+
return React.createElement('style', {
|
|
20
|
+
id: 'rnmq',
|
|
21
|
+
key: 'rnmq',
|
|
22
|
+
dangerouslySetInnerHTML: {
|
|
23
|
+
__html: Object.keys(rules)
|
|
24
|
+
.map((key) => rules[key].text)
|
|
25
|
+
.join('\n')
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
return {}
|
|
30
|
+
}
|
package/src/utils/ssr.jsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { AppRegistry } from 'react-native'
|
|
3
|
+
import { flush } from './ssr-media-query/utils/inject'
|
|
3
4
|
/** @typedef {import('react').ComponentType} ReactComponent */
|
|
4
5
|
/** @typedef {import('react').ReactElement} ReactElement */
|
|
5
6
|
|
|
@@ -80,7 +81,8 @@ export const ssrStyles = (appName = 'UDS app', { styleGetters = [], collectStyle
|
|
|
80
81
|
*/
|
|
81
82
|
getStyles: (...existingStyles) => {
|
|
82
83
|
if (!hasAppRendered) throw new Error('Called getStyles before renderApp in ssrStyles')
|
|
83
|
-
|
|
84
|
+
|
|
85
|
+
return [...existingStyles, ...styleGetters.flatMap((getter) => getter()), flush()]
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
}
|