@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/lib/utils/index.js +12 -0
  3. package/lib/utils/ssr-media-query/create-stylesheet.js +76 -0
  4. package/lib/utils/ssr-media-query/hash.js +19 -0
  5. package/lib/utils/ssr-media-query/index.js +19 -0
  6. package/lib/utils/ssr-media-query/utils/common.js +25 -0
  7. package/lib/utils/ssr-media-query/utils/create-declaration-block.js +24 -0
  8. package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +34 -0
  9. package/lib/utils/ssr-media-query/utils/hyphenate-style-name.js +19 -0
  10. package/lib/utils/ssr-media-query/utils/inject.js +36 -0
  11. package/lib/utils/ssr.js +2 -1
  12. package/lib-module/utils/index.js +1 -0
  13. package/lib-module/utils/ssr-media-query/create-stylesheet.js +68 -0
  14. package/lib-module/utils/ssr-media-query/hash.js +13 -0
  15. package/lib-module/utils/ssr-media-query/index.js +6 -0
  16. package/lib-module/utils/ssr-media-query/utils/common.js +15 -0
  17. package/lib-module/utils/ssr-media-query/utils/create-declaration-block.js +16 -0
  18. package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +30 -0
  19. package/lib-module/utils/ssr-media-query/utils/hyphenate-style-name.js +12 -0
  20. package/lib-module/utils/ssr-media-query/utils/inject.js +26 -0
  21. package/lib-module/utils/ssr.js +3 -1
  22. package/package.json +2 -1
  23. package/src/utils/index.js +1 -1
  24. package/src/utils/ssr-media-query/create-stylesheet.js +61 -0
  25. package/src/utils/ssr-media-query/hash.js +16 -0
  26. package/src/utils/ssr-media-query/index.js +8 -0
  27. package/src/utils/ssr-media-query/utils/common.js +20 -0
  28. package/src/utils/ssr-media-query/utils/create-declaration-block.js +21 -0
  29. package/src/utils/ssr-media-query/utils/create-media-query-styles.js +31 -0
  30. package/src/utils/ssr-media-query/utils/hyphenate-style-name.js +15 -0
  31. package/src/utils/ssr-media-query/utils/inject.js +30 -0
  32. 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 Sat, 18 Nov 2023 02:25:11 GMT and should not be manually modified.
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:25:11 GMT
17
+ Sat, 18 Nov 2023 02:32:49 GMT
10
18
 
11
19
  ### Minor changes
12
20
 
@@ -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,6 @@
1
+ import createStyleSheet from './create-stylesheet';
2
+ import createMediaQueryStyles from './utils/create-media-query-styles';
3
+ const StyleSheet = {
4
+ create: createStyleSheet
5
+ };
6
+ export { StyleSheet, createMediaQueryStyles };
@@ -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
+ };
@@ -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.69.0"
77
+ "version": "1.70.0"
77
78
  }
@@ -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,8 @@
1
+ import createStyleSheet from './create-stylesheet'
2
+ import createMediaQueryStyles from './utils/create-media-query-styles'
3
+
4
+ const StyleSheet = {
5
+ create: createStyleSheet
6
+ }
7
+
8
+ export { StyleSheet, createMediaQueryStyles }
@@ -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
- return [...existingStyles, ...styleGetters.flatMap((getter) => getter())]
84
+
85
+ return [...existingStyles, ...styleGetters.flatMap((getter) => getter()), flush()]
84
86
  }
85
87
  }
86
88
  }