@telus-uds/components-base 1.68.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 (38) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/lib/ExpandCollapse/Panel.js +1 -1
  3. package/lib/TextInput/TextInputBase.js +20 -17
  4. package/lib/utils/index.js +12 -0
  5. package/lib/utils/ssr-media-query/create-stylesheet.js +76 -0
  6. package/lib/utils/ssr-media-query/hash.js +19 -0
  7. package/lib/utils/ssr-media-query/index.js +19 -0
  8. package/lib/utils/ssr-media-query/utils/common.js +25 -0
  9. package/lib/utils/ssr-media-query/utils/create-declaration-block.js +24 -0
  10. package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +34 -0
  11. package/lib/utils/ssr-media-query/utils/hyphenate-style-name.js +19 -0
  12. package/lib/utils/ssr-media-query/utils/inject.js +36 -0
  13. package/lib/utils/ssr.js +2 -1
  14. package/lib-module/ExpandCollapse/Panel.js +1 -1
  15. package/lib-module/TextInput/TextInputBase.js +20 -17
  16. package/lib-module/utils/index.js +1 -0
  17. package/lib-module/utils/ssr-media-query/create-stylesheet.js +68 -0
  18. package/lib-module/utils/ssr-media-query/hash.js +13 -0
  19. package/lib-module/utils/ssr-media-query/index.js +6 -0
  20. package/lib-module/utils/ssr-media-query/utils/common.js +15 -0
  21. package/lib-module/utils/ssr-media-query/utils/create-declaration-block.js +16 -0
  22. package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +30 -0
  23. package/lib-module/utils/ssr-media-query/utils/hyphenate-style-name.js +12 -0
  24. package/lib-module/utils/ssr-media-query/utils/inject.js +26 -0
  25. package/lib-module/utils/ssr.js +3 -1
  26. package/package.json +3 -2
  27. package/src/ExpandCollapse/Panel.jsx +1 -1
  28. package/src/TextInput/TextInputBase.jsx +16 -8
  29. package/src/utils/index.js +1 -1
  30. package/src/utils/ssr-media-query/create-stylesheet.js +61 -0
  31. package/src/utils/ssr-media-query/hash.js +16 -0
  32. package/src/utils/ssr-media-query/index.js +8 -0
  33. package/src/utils/ssr-media-query/utils/common.js +20 -0
  34. package/src/utils/ssr-media-query/utils/create-declaration-block.js +21 -0
  35. package/src/utils/ssr-media-query/utils/create-media-query-styles.js +31 -0
  36. package/src/utils/ssr-media-query/utils/hyphenate-style-name.js +15 -0
  37. package/src/utils/ssr-media-query/utils/inject.js +30 -0
  38. package/src/utils/ssr.jsx +3 -1
package/CHANGELOG.md CHANGED
@@ -1,12 +1,35 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Wed, 15 Nov 2023 18:34:44 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
+
15
+ ## 1.69.0
16
+
17
+ Sat, 18 Nov 2023 02:32:49 GMT
18
+
19
+ ### Minor changes
20
+
21
+ - updating notification stories (srikanthkhari@gmail.com)
22
+ - Bump @telus-uds/system-theme-tokens to v2.46.0
23
+
24
+ ### Patches
25
+
26
+ - `TermsAndConditions`: Fix unexpected state handling in `ExpandCollapse.Panel` (shahzaibkhalidmalik@outlook.com)
27
+ - fixes to getIcon method for card variant in TextInput (email not defined)
28
+ - removing koodo rebrand iconset from supports (srikanthkhari@gmail.com)
29
+
7
30
  ## 1.68.0
8
31
 
9
- Wed, 15 Nov 2023 18:34:44 GMT
32
+ Wed, 15 Nov 2023 18:45:58 GMT
10
33
 
11
34
  ### Minor changes
12
35
 
@@ -174,7 +174,7 @@ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) =>
174
174
  },
175
175
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
176
176
  style: selectContainerStyles(themeTokens),
177
- children: children
177
+ children: isExpanded ? children : null
178
178
  })
179
179
  })
180
180
  })]
@@ -152,26 +152,29 @@ const getIcon = function () {
152
152
  visaIcon,
153
153
  masterCardIcon
154
154
  } = arguments.length > 1 ? arguments[1] : undefined;
155
- const cardType = {
156
- 1: {
157
- icon: visaIcon,
158
- testID: 'visa'
159
- },
160
- 2: {
161
- icon: amexIcon,
162
- testID: 'amex'
163
- },
164
- 4: {
165
- icon: masterCardIcon,
166
- testID: 'mastercard'
167
- }
168
- };
169
- const firstDigit = cardNumber ? cardNumber[0] : '';
170
- const defaultIcon = {
155
+ const sanitizedCardNumber = cardNumber.replace(/[^0-9]/g, '');
156
+ const firstFourDigits = sanitizedCardNumber.slice(0, 4);
157
+ const number = Number(firstFourDigits);
158
+ const visaRange = number >= 4000 && number <= 4999;
159
+ const amexRange = number >= 3400 && number <= 3499 || number >= 3700 && number <= 3799;
160
+ const masterCardRange = number >= 5100 && number <= 5599 || number >= 2221 && number <= 2720;
161
+ let selectedIcon = {
171
162
  icon: defaultCreditIcon,
172
163
  testID: 'default'
173
164
  };
174
- const selectedIcon = cardNumber.length > 4 ? cardType[firstDigit] || defaultIcon : defaultIcon;
165
+ const objVisaIcon = {
166
+ icon: visaIcon,
167
+ testID: 'visa'
168
+ };
169
+ const objAmexIcon = {
170
+ icon: amexIcon,
171
+ testID: 'amex'
172
+ };
173
+ const objMasterCardIcon = {
174
+ icon: masterCardIcon,
175
+ testID: 'mastercard'
176
+ };
177
+ if (visaRange) selectedIcon = objVisaIcon;else if (amexRange) selectedIcon = objAmexIcon;else if (masterCardRange) selectedIcon = objMasterCardIcon;
175
178
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
176
179
  icon: selectedIcon.icon,
177
180
  variant: {
@@ -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
  };
@@ -166,7 +166,7 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
166
166
  },
167
167
  children: /*#__PURE__*/_jsx(View, {
168
168
  style: selectContainerStyles(themeTokens),
169
- children: children
169
+ children: isExpanded ? children : null
170
170
  })
171
171
  })
172
172
  })]
@@ -144,26 +144,29 @@ const getIcon = function () {
144
144
  visaIcon,
145
145
  masterCardIcon
146
146
  } = arguments.length > 1 ? arguments[1] : undefined;
147
- const cardType = {
148
- 1: {
149
- icon: visaIcon,
150
- testID: 'visa'
151
- },
152
- 2: {
153
- icon: amexIcon,
154
- testID: 'amex'
155
- },
156
- 4: {
157
- icon: masterCardIcon,
158
- testID: 'mastercard'
159
- }
160
- };
161
- const firstDigit = cardNumber ? cardNumber[0] : '';
162
- const defaultIcon = {
147
+ const sanitizedCardNumber = cardNumber.replace(/[^0-9]/g, '');
148
+ const firstFourDigits = sanitizedCardNumber.slice(0, 4);
149
+ const number = Number(firstFourDigits);
150
+ const visaRange = number >= 4000 && number <= 4999;
151
+ const amexRange = number >= 3400 && number <= 3499 || number >= 3700 && number <= 3799;
152
+ const masterCardRange = number >= 5100 && number <= 5599 || number >= 2221 && number <= 2720;
153
+ let selectedIcon = {
163
154
  icon: defaultCreditIcon,
164
155
  testID: 'default'
165
156
  };
166
- const selectedIcon = cardNumber.length > 4 ? cardType[firstDigit] || defaultIcon : defaultIcon;
157
+ const objVisaIcon = {
158
+ icon: visaIcon,
159
+ testID: 'visa'
160
+ };
161
+ const objAmexIcon = {
162
+ icon: amexIcon,
163
+ testID: 'amex'
164
+ };
165
+ const objMasterCardIcon = {
166
+ icon: masterCardIcon,
167
+ testID: 'mastercard'
168
+ };
169
+ if (visaRange) selectedIcon = objVisaIcon;else if (amexRange) selectedIcon = objAmexIcon;else if (masterCardRange) selectedIcon = objMasterCardIcon;
167
170
  return /*#__PURE__*/_jsx(Icon, {
168
171
  icon: selectedIcon.icon,
169
172
  variant: {
@@ -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
@@ -11,8 +11,9 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.3.0",
14
- "@telus-uds/system-theme-tokens": "^2.45.0",
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.68.0"
77
+ "version": "1.70.0"
77
78
  }
@@ -168,7 +168,7 @@ const ExpandCollapsePanel = forwardRef(
168
168
  })
169
169
  }}
170
170
  >
171
- <View style={selectContainerStyles(themeTokens)}>{children}</View>
171
+ <View style={selectContainerStyles(themeTokens)}>{isExpanded ? children : null}</View>
172
172
  </View>
173
173
  </Animated.View>
174
174
  </View>
@@ -141,15 +141,23 @@ const selectButtonsContainerStyle = ({ buttonsPaddingRight }) => ({
141
141
  })
142
142
 
143
143
  const getIcon = (cardNumber = '', { defaultCreditIcon, amexIcon, visaIcon, masterCardIcon }) => {
144
- const cardType = {
145
- 1: { icon: visaIcon, testID: 'visa' },
146
- 2: { icon: amexIcon, testID: 'amex' },
147
- 4: { icon: masterCardIcon, testID: 'mastercard' }
148
- }
144
+ const sanitizedCardNumber = cardNumber.replace(/[^0-9]/g, '')
145
+ const firstFourDigits = sanitizedCardNumber.slice(0, 4)
146
+ const number = Number(firstFourDigits)
147
+
148
+ const visaRange = number >= 4000 && number <= 4999
149
+ const amexRange = (number >= 3400 && number <= 3499) || (number >= 3700 && number <= 3799)
150
+ const masterCardRange = (number >= 5100 && number <= 5599) || (number >= 2221 && number <= 2720)
151
+
152
+ let selectedIcon = { icon: defaultCreditIcon, testID: 'default' }
153
+ const objVisaIcon = { icon: visaIcon, testID: 'visa' }
154
+ const objAmexIcon = { icon: amexIcon, testID: 'amex' }
155
+ const objMasterCardIcon = { icon: masterCardIcon, testID: 'mastercard' }
156
+
157
+ if (visaRange) selectedIcon = objVisaIcon
158
+ else if (amexRange) selectedIcon = objAmexIcon
159
+ else if (masterCardRange) selectedIcon = objMasterCardIcon
149
160
 
150
- const firstDigit = cardNumber ? cardNumber[0] : ''
151
- const defaultIcon = { icon: defaultCreditIcon, testID: 'default' }
152
- const selectedIcon = cardNumber.length > 4 ? cardType[firstDigit] || defaultIcon : defaultIcon
153
161
  return <Icon icon={selectedIcon.icon} variant={{ size: 'large' }} testID={selectedIcon.testID} />
154
162
  }
155
163
 
@@ -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
  }