@tui-cruises/mein-schiff-web-react-component-library 2.2.2 → 2.2.3

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 CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [2.2.3](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/compare/v2.2.2...v2.2.3) (2025-07-21)
6
+
7
+
8
+ ### Features
9
+
10
+ * add responsive sizes to Icons, Pictograms and IconButtons. Removed numeric size properties ([49704b69](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/75ba4f03c80a43115e98c0d6c5cb0f913c824f1b))
11
+
5
12
  ### [2.2.2](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/compare/v2.2.1...v2.2.2) (2025-07-18)
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tui-cruises/mein-schiff-web-react-component-library",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "main": "./index.tsx",
5
5
  "types": "./index.tsx",
6
6
  "type": "module",
@@ -2,7 +2,12 @@
2
2
 
3
3
  import { dynamicComponents as dynamicComponentsIcons } from '../../../../icons/icons';
4
4
  import type { ComponentType, SVGProps } from 'react';
5
- import { twJoin } from 'tailwind-merge';
5
+ import { twJoin, twMerge } from 'tailwind-merge';
6
+ import {
7
+ ResponsiveValueClassMap,
8
+ getClassNamesFromMap,
9
+ ResponsiveValue,
10
+ } from '../../../helpers/responsive-property';
6
11
 
7
12
  // Type for local icons
8
13
  type DynamicComponentIconName = keyof typeof dynamicComponentsIcons;
@@ -10,6 +15,15 @@ type DynamicComponentIconName = keyof typeof dynamicComponentsIcons;
10
15
  // Union of both types to create a comprehensive IconName type
11
16
  export type IconName = DynamicComponentIconName;
12
17
 
18
+ /**
19
+ * The size of the icon.
20
+ * 'xs' (16x16), 'sm' (20x20),
21
+ * 'md' (24x24), 'lg' (32x32).
22
+ *
23
+ * @default 'md'
24
+ */
25
+ type IconSize = 'xs' | 'sm' | 'md' | 'lg';
26
+
13
27
  export type IconProps = {
14
28
  /**
15
29
  * Additional TailwindCSS utilities can be added with this prop.
@@ -27,56 +41,90 @@ export type IconProps = {
27
41
  *
28
42
  * @default 'md'
29
43
  */
30
- size?: number | 'xs' | 'sm' | 'md' | 'lg';
44
+ size?: ResponsiveValue<IconSize>;
31
45
  };
32
46
 
33
47
  /**
34
- * Component to display an icon, based on the [local icon library](/docs/foundation-icons-pictograms--docs).
48
+ * Component to display an icon, based on the [Iconoir icon library](https://iconoir.com/)
49
+ * or a custom local icon library.
35
50
  *
36
51
  * @component
37
52
  * @param {IconProps} props - The props for the Icon component.
38
53
  * @returns {JSX.Element | null} The rendered icon component or null if the icon is not found.
39
54
  */
40
55
  const Icon = (props: IconProps) => {
41
- const { className, name, size = 'md' } = props;
56
+ const { name, size } = props;
42
57
 
43
58
  const IconComponent = getIconComponent(name);
44
59
  if (!IconComponent) {
45
60
  return null;
46
61
  }
47
62
 
48
- const iconSize = getIconSize(size);
63
+ let classNameContainsSize = ['size-', 'h-', 'w-'].some(prefix =>
64
+ props.className?.includes(prefix),
65
+ );
49
66
 
50
67
  return (
51
- <IconComponent
52
- className={twJoin('pointer-events-none', className)}
53
- width={iconSize}
54
- height={iconSize}
55
- />
68
+ <span
69
+ className={twMerge(
70
+ 'block',
71
+ !classNameContainsSize && getClassNamesFromMap(sizeCssMap, size, 'md'),
72
+ props.className,
73
+ )}
74
+ >
75
+ <IconComponent
76
+ className={twJoin(
77
+ 'pointer-events-none block size-full',
78
+ name === 'loading' && 'animate-spin',
79
+ )}
80
+ />
81
+ </span>
56
82
  );
57
83
  };
58
84
 
59
- /**
60
- * Helper function to get the icon size in pixels.
61
- *
62
- * @param {NonNullable<IconProps['size']>} size - The size prop, either a number or a predefined size string.
63
- * @returns {number} The size in pixels.
64
- */
65
- const getIconSize = (size: NonNullable<IconProps['size']>) => {
66
- if (typeof size === 'number') {
67
- return size;
68
- }
69
-
70
- switch (size) {
71
- case 'xs':
72
- return 16;
73
- case 'sm':
74
- return 20;
75
- case 'lg':
76
- return 32;
77
- default:
78
- return 24;
79
- }
85
+ const sizeCssMap: ResponsiveValueClassMap<IconSize> = {
86
+ initial: {
87
+ xs: 'size-[16px]',
88
+ sm: 'size-[20px]',
89
+ md: 'size-[24px]',
90
+ lg: 'size-[32px]',
91
+ },
92
+ xs: {
93
+ xs: 'xs:size-[16px]',
94
+ sm: 'xs:size-[20px]',
95
+ md: 'xs:size-[24px]',
96
+ lg: 'xs:size-[32px]',
97
+ },
98
+ sm: {
99
+ xs: 'sm:size-[16px]',
100
+ sm: 'sm:size-[20px]',
101
+ md: 'sm:size-[24px]',
102
+ lg: 'sm:size-[32px]',
103
+ },
104
+ md: {
105
+ xs: 'md:size-[16px]',
106
+ sm: 'md:size-[20px]',
107
+ md: 'md:size-[24px]',
108
+ lg: 'md:size-[32px]',
109
+ },
110
+ lg: {
111
+ xs: 'lg:size-[16px]',
112
+ sm: 'lg:size-[20px]',
113
+ md: 'lg:size-[24px]',
114
+ lg: 'lg:size-[32px]',
115
+ },
116
+ xl: {
117
+ xs: 'xl:size-[16px]',
118
+ sm: 'xl:size-[20px]',
119
+ md: 'xl:size-[24px]',
120
+ lg: 'xl:size-[32px]',
121
+ },
122
+ xxl: {
123
+ xs: '2xl:size-[16px]',
124
+ sm: '2xl:size-[20px]',
125
+ md: '2xl:size-[24px]',
126
+ lg: '2xl:size-[32px]',
127
+ },
80
128
  };
81
129
 
82
130
  /**
@@ -9,7 +9,11 @@ import {
9
9
  import { twJoin, twMerge } from 'tailwind-merge';
10
10
  import { Slot, Slottable } from '@radix-ui/react-slot';
11
11
  import { wrapSlottableChildren } from '../../../helpers/slot';
12
- import { useScreenNameAtLeast } from '../../../hooks/use-screen-name-at-least';
12
+ import {
13
+ getClassNamesFromMap,
14
+ ResponsiveValue,
15
+ ResponsiveValueClassMap,
16
+ } from '../../../helpers/responsive-property';
13
17
 
14
18
  type CommonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
15
19
  asChild?: boolean;
@@ -18,14 +22,18 @@ type CommonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
18
22
  on?: 'white' | 'gray' | 'ocean' | 'image';
19
23
  };
20
24
 
25
+ type RegularSize = 'sm' | 'md' | 'lg';
26
+ type GhostButtonSize = 'xs' | 'sm' | 'md' | 'lg';
27
+
21
28
  export type IconButtonPrimaryAndSecondaryProps = CommonProps & {
22
29
  variant?: 'primary' | 'secondary';
23
- size?: 'sm' | 'md' | 'lg';
30
+ /** The button's size. Defaults to `sm`. */
31
+ size?: ResponsiveValue<RegularSize>;
24
32
  };
25
33
 
26
34
  export type IconButtonGhostProps = CommonProps & {
27
35
  variant: 'ghost';
28
- size?: 'xs' | 'sm' | 'md' | 'lg';
36
+ size?: ResponsiveValue<GhostButtonSize>;
29
37
  };
30
38
 
31
39
  export type IconButtonProps =
@@ -37,7 +45,7 @@ const IconButtonRenderFunction: ForwardRefRenderFunction<
37
45
  IconButtonProps
38
46
  > = (props, ref) => {
39
47
  const {
40
- size = 'md',
48
+ size,
41
49
  variant = 'primary',
42
50
  on = 'white',
43
51
  iconName,
@@ -47,20 +55,10 @@ const IconButtonRenderFunction: ForwardRefRenderFunction<
47
55
  ...rest
48
56
  } = props;
49
57
 
50
- // Assuming `useScreenNameAtLeast` is returning a screen size string
51
- const isMdOrAbove = useScreenNameAtLeast('md', 'md');
52
-
53
- let resolvedIconSize = size;
54
- if (size === 'xs' && isMdOrAbove) {
55
- resolvedIconSize = 'md';
56
- }
57
-
58
58
  const buttonClassNames = twMerge(
59
59
  'inline-flex rounded-full transition-all',
60
60
 
61
- size === 'xs' && 'p-0.5', // 20px
62
- size === 'sm' && 'p-3 md:p-2', // 36px
63
- size === 'md' && 'p-3.5 md:p-3', // 52px md:48px
61
+ getClassNamesFromMap(iconCssMap, size, 'md'),
64
62
 
65
63
  // Variants
66
64
  variant === 'primary' && [
@@ -130,20 +128,17 @@ const IconButtonRenderFunction: ForwardRefRenderFunction<
130
128
  'focus:outline-none',
131
129
  'disabled:!pointer-events-none disabled:text-secondary-marine-48',
132
130
 
133
- size === 'md' ? 'text-base' : 'text-sm',
131
+ getClassNamesFromMap(buttonCssMap, size, 'md'),
134
132
  variant !== 'ghost' && ['gap-2'],
135
- variant === 'ghost' && [
136
- size === 'xs' && 'gap-0.5',
137
- size === 'sm' && 'gap-1',
138
- size === 'md' && 'gap-1',
139
- ],
133
+ variant === 'ghost' &&
134
+ getClassNamesFromMap(ghostButtonCssMap, size, 'md'),
140
135
  iconPosition === 'right' && 'flex-row-reverse',
141
136
 
142
137
  rest.className,
143
138
  )}
144
139
  >
145
140
  <div className={buttonClassNames}>
146
- <Icon name={iconName} size={resolvedIconSize} />
141
+ <Icon name={iconName} size={size} />
147
142
  </div>
148
143
 
149
144
  <Slottable>
@@ -159,6 +154,80 @@ const IconButtonRenderFunction: ForwardRefRenderFunction<
159
154
  );
160
155
  };
161
156
 
157
+ /**
158
+ * The css map for the responsive value css used in icon.
159
+ */
160
+ const iconCssMap: ResponsiveValueClassMap<GhostButtonSize> = {
161
+ initial: {
162
+ xs: 'p-0.5',
163
+ sm: 'p-2',
164
+ md: 'p-3',
165
+ lg: undefined,
166
+ },
167
+ xs: {
168
+ xs: 'xs:p-0.5',
169
+ sm: 'xs:p-2',
170
+ md: 'xs:p-3',
171
+ lg: undefined,
172
+ },
173
+ sm: {
174
+ xs: 'sm:p-0.5',
175
+ sm: 'sm:p-2',
176
+ md: 'sm:p-3',
177
+ lg: undefined,
178
+ },
179
+ md: {
180
+ xs: 'md:p-0.5',
181
+ sm: 'md:p-2',
182
+ md: 'md:p-3',
183
+ lg: undefined,
184
+ },
185
+ lg: {
186
+ xs: 'lg:p-0.5',
187
+ sm: 'lg:p-2',
188
+ md: 'lg:p-3',
189
+ lg: undefined,
190
+ },
191
+ xl: {
192
+ xs: 'xl:p-0.5',
193
+ sm: 'xl:p-2',
194
+ md: 'xl:p-3',
195
+ lg: undefined,
196
+ },
197
+ xxl: {
198
+ xs: '2xl:p-0.5',
199
+ sm: '2xl:p-2',
200
+ md: '2xl:p-3',
201
+ lg: undefined,
202
+ },
203
+ };
204
+
205
+ /**
206
+ * The css map for the responsive value css used in button.
207
+ */
208
+ const buttonCssMap: ResponsiveValueClassMap<GhostButtonSize> = {
209
+ initial: size => (size === 'md' ? 'text-base' : 'text-sm'),
210
+ xs: size => (size === 'md' ? 'xs:text-base' : 'xs:text-sm'),
211
+ sm: size => (size === 'md' ? 'sm:text-base' : 'sm:text-sm'),
212
+ md: size => (size === 'md' ? 'md:text-base' : 'md:text-sm'),
213
+ lg: size => (size === 'md' ? 'lg:text-base' : 'lg:text-sm'),
214
+ xl: size => (size === 'md' ? 'xl:text-base' : 'xl:text-sm'),
215
+ xxl: size => (size === 'md' ? '2xl:text-base' : '2xl:text-sm'),
216
+ };
217
+
218
+ /**
219
+ * The css map for the responsive value css used in the ghost button variant.
220
+ */
221
+ const ghostButtonCssMap: ResponsiveValueClassMap<GhostButtonSize> = {
222
+ initial: { xs: 'gap-0.5', sm: 'gap-1', md: 'gap-1', lg: undefined },
223
+ xs: { xs: 'xs:gap-0.5', sm: 'xs:gap-1', md: 'xs:gap-1', lg: undefined },
224
+ sm: { xs: 'sm:gap-0.5', sm: 'sm:gap-1', md: 'sm:gap-1', lg: undefined },
225
+ md: { xs: 'md:gap-0.5', sm: 'md:gap-1', md: 'md:gap-1', lg: undefined },
226
+ lg: { xs: 'lg:gap-0.5', sm: 'lg:gap-1', md: 'lg:gap-1', lg: undefined },
227
+ xl: { xs: 'xl:gap-0.5', sm: 'xl:gap-1', md: 'xl:gap-1', lg: undefined },
228
+ xxl: { xs: '2xl:gap-0.5', sm: '2xl:gap-1', md: '2xl:gap-1', lg: undefined },
229
+ };
230
+
162
231
  /**
163
232
  * IconButton is a simple round button component, displaying one icon with an optional label.
164
233
  *
@@ -1,6 +1,11 @@
1
1
  import type { FC } from 'react';
2
2
  import { dynamicComponents as dynamicComponentsPictograms } from '../../../../icons/pictograms';
3
3
  import { twMerge } from 'tailwind-merge';
4
+ import {
5
+ getClassNamesFromMap,
6
+ ResponsiveValue,
7
+ ResponsiveValueClassMap,
8
+ } from '../../../helpers/responsive-property';
4
9
 
5
10
  type CollectionPictograms = typeof dynamicComponentsPictograms;
6
11
 
@@ -8,58 +13,87 @@ export type PictogramName = keyof CollectionPictograms;
8
13
 
9
14
  type IconComponent = CollectionPictograms[PictogramName];
10
15
 
11
- export type PictogramProps = {
12
- className?: string;
13
- name: PictogramName;
14
- size?: number | 'sm' | 'md';
15
- };
16
-
17
16
  /**
18
- * Determines and returns width and height attributes based on the size parameter.
17
+ * The size of the icon.
18
+ * 'sm' (40x40),
19
+ * 'md' (60x60)
20
+ *
21
+ * @default 'sm'
19
22
  */
20
- const getSizeAttributes = (size: PictogramProps['size']) => {
21
- switch (size) {
22
- case 'sm':
23
- return { width: '40', height: '40' };
24
- case 'md':
25
- return { width: '60', height: '60' };
26
- default:
27
- return {
28
- width: size?.toString() || '40',
29
- height: size?.toString() || '40',
30
- };
31
- }
23
+ type PictogramSize = 'sm' | 'md';
24
+
25
+ export type PictogramProps = {
26
+ className?: string;
27
+ name: PictogramName;
28
+ size?: ResponsiveValue<PictogramSize>;
32
29
  };
33
30
 
34
31
  /**
35
32
  * Renders the icon.
36
33
  */
37
34
  const renderIcon = (
38
- Element: IconComponent,
39
- wrapperAttrs: Record<string, string>,
40
- svgAttrs?: Record<string, string>,
35
+ Element: IconComponent,
36
+ size: PictogramProps['size'],
37
+ wrapperAttrs: Record<string, string>,
41
38
  ) => {
42
- const className = twMerge('inline-block', wrapperAttrs.className);
39
+ let classNameContainsSize = ['size-', 'h-', 'w-'].some(prefix =>
40
+ wrapperAttrs.className?.includes(prefix),
41
+ );
42
+
43
+ const className = twMerge(
44
+ 'inline-block',
45
+ !classNameContainsSize && getClassNamesFromMap(sizeCssMap, size, 'sm'),
46
+ wrapperAttrs.className,
47
+ );
43
48
 
44
- return (
45
- <div {...wrapperAttrs} className={className}>
46
- <Element {...svgAttrs} />
47
- </div>
48
- );
49
+ return (
50
+ <div {...wrapperAttrs} className={className}>
51
+ <Element className="block size-full" viewBox="0 0 40 40" />
52
+ </div>
53
+ );
49
54
  };
50
55
 
51
56
  /**
52
- * Component to display an pictogram, based on the [local pictogram library](/docs/foundation-icons-pictograms--docs).
57
+ * Renders a pictogram with the specified size and icon.
53
58
  */
54
- const Pictogram: FC<PictogramProps> = ({ size = 'sm', name, ...rest }) => {
55
- const Element = dynamicComponentsPictograms[name];
56
- if (!Element) {
57
- return null;
58
- }
59
+ const Pictogram: FC<PictogramProps> = ({ size, name, ...rest }) => {
60
+ const Element = dynamicComponentsPictograms[name];
61
+ if (!Element) {
62
+ return null;
63
+ }
59
64
 
60
- const { width, height } = getSizeAttributes(size);
65
+ return renderIcon(Element, size, rest);
66
+ };
61
67
 
62
- return renderIcon(Element, rest, { width, height, viewBox: '0 0 40 40' });
68
+ const sizeCssMap: ResponsiveValueClassMap<PictogramSize> = {
69
+ initial: {
70
+ sm: 'size-[40px]',
71
+ md: 'size-[60px]',
72
+ },
73
+ xs: {
74
+ sm: 'xs:size-[40px]',
75
+ md: 'xs:size-[60px]',
76
+ },
77
+ sm: {
78
+ sm: 'sm:size-[40px]',
79
+ md: 'sm:size-[60px]',
80
+ },
81
+ md: {
82
+ sm: 'md:size-[40px]',
83
+ md: 'md:size-[60px]',
84
+ },
85
+ lg: {
86
+ sm: 'lg:size-[40px]',
87
+ md: 'lg:size-[60px]',
88
+ },
89
+ xl: {
90
+ sm: 'xl:size-[40px]',
91
+ md: 'xl:size-[60px]',
92
+ },
93
+ xxl: {
94
+ sm: '2xl:size-[40px]',
95
+ md: '2xl:size-[60px]',
96
+ },
63
97
  };
64
98
 
65
99
  Pictogram.displayName = 'Pictogram';
@@ -4,21 +4,42 @@ import { ScreenName } from './screen';
4
4
 
5
5
  export type ResponsiveValue<T> = T | [T, ScreenNameIndexedValues<T>?];
6
6
 
7
+ type ScreenNameWithInitial = ScreenName | 'initial';
8
+
7
9
  type ScreenNameIndexedValues<T> = {
8
10
  [screenName in ScreenName]?: T;
9
11
  };
10
12
 
13
+ type OptionalScreenNameIndexedValuesWithInitial<T> = {
14
+ [screenName in ScreenNameWithInitial]: T | undefined;
15
+ };
16
+
11
17
  type ScreenNameIndexedValuesWithInitial<T> = {
12
- [screenName in ScreenName | 'initial']?: T;
18
+ [screenName in ScreenNameWithInitial]: T;
13
19
  };
14
20
 
21
+ // export type ResponsiveValueClassMap<T extends string | number> = {
22
+ // [key in ScreenNameWithInitial]?: Record<T, string | undefined> | ((value: T) => string | undefined)
23
+ // }
24
+
25
+ /**
26
+ * Responsive value class map.
27
+ * Provides a mapping for each screen name to a responsive value or a function that resolves the value.
28
+ * You can use this to create responsive styles based on the screen size when you want to avoid any hook based solutions.
29
+ * This is a more complex approach than using a simple responsive value, but it enables server side rendering and avoids any layout shifts based on initial screen name resolution.
30
+ */
31
+ export type ResponsiveValueClassMap<T extends string | number> = Record<
32
+ ScreenNameWithInitial,
33
+ Record<T, string | undefined> | ((value: T) => string | undefined)
34
+ >;
35
+
15
36
  /**
16
37
  * Resolves given responsive property value to screen name indexed values including an initial key for the initial screen.
17
38
  */
18
39
  export const getScreenIndexedResponsiveValue = <T extends any = any>(
19
40
  propValue: ResponsiveValue<T> | undefined,
20
41
  defaultValue: T,
21
- ): ScreenNameIndexedValuesWithInitial<T> => {
42
+ ): OptionalScreenNameIndexedValuesWithInitial<T> => {
22
43
  if (typeof propValue === 'undefined' || !Array.isArray(propValue)) {
23
44
  return {
24
45
  initial: propValue ?? defaultValue,
@@ -64,29 +85,33 @@ export const getScreenIndexedResponsiveValueForEachScreen = <
64
85
  >(
65
86
  propValue: ResponsiveValue<T> | undefined,
66
87
  defaultValue: T,
67
- ): ScreenNameIndexedValues<T> => {
88
+ ): ScreenNameIndexedValuesWithInitial<T> => {
68
89
  const screenNameIndexedValues = getScreenIndexedResponsiveValue(
69
90
  propValue,
70
91
  defaultValue,
71
92
  );
72
93
 
73
- const screens: Record<ScreenName, T | undefined> = {
74
- xs: screenNameIndexedValues.xs,
75
- sm: screenNameIndexedValues.sm,
76
- md: screenNameIndexedValues.md,
77
- lg: screenNameIndexedValues.lg,
78
- xl: screenNameIndexedValues.xl,
79
- xxl: screenNameIndexedValues.xxl,
94
+ const screens: Record<ScreenNameWithInitial, T | undefined> = {
95
+ ...screenNameIndexedValues,
80
96
  };
81
97
 
82
- screens.xs = screens.xs ?? screenNameIndexedValues.initial;
98
+ screens.initial = screens.initial ?? defaultValue;
99
+ screens.xs = screens.xs ?? screens.initial;
83
100
  screens.sm = screens.sm ?? screens.xs;
84
101
  screens.md = screens.md ?? screens.sm;
85
102
  screens.lg = screens.lg ?? screens.md;
86
103
  screens.xl = screens.xl ?? screens.lg;
87
104
  screens.xxl = screens.xxl ?? screens.xl;
88
105
 
89
- return screens;
106
+ return {
107
+ initial: screens.initial ?? defaultValue,
108
+ xs: screens.xs ?? screens.initial,
109
+ sm: screens.sm ?? screens.xs,
110
+ md: screens.md ?? screens.sm,
111
+ lg: screens.lg ?? screens.md,
112
+ xl: screens.xl ?? screens.lg,
113
+ xxl: screens.xxl ?? screens.xl,
114
+ };
90
115
  };
91
116
 
92
117
  /**
@@ -116,3 +141,44 @@ export const getResponsiveValue = <T extends any = any>(
116
141
 
117
142
  return propValue[0];
118
143
  };
144
+
145
+ /**
146
+ * Generates css for each responsive value based on the rules from the given screen/value map.
147
+ * See the `Icon` component for a simple example of how to use this or the `IconButton` component for a more complex example.
148
+ */
149
+ export const getClassNamesFromMap = <T extends string | number = string>(
150
+ map: ResponsiveValueClassMap<T>,
151
+ value: ResponsiveValue<T> | undefined,
152
+ defaultValue: T,
153
+ ): string => {
154
+ let screenIndexed = getScreenIndexedResponsiveValue<T>(value, defaultValue);
155
+
156
+ let classList = Object.entries(screenIndexed).reduce<string[]>(
157
+ (list, [screenArg, value]) => {
158
+ if (typeof value === 'undefined') {
159
+ return list;
160
+ }
161
+
162
+ let screen = screenArg as keyof typeof screenIndexed;
163
+ let resolvedScreenIndexedValues = map[screen];
164
+
165
+ if (typeof resolvedScreenIndexedValues === 'undefined') {
166
+ return list;
167
+ }
168
+
169
+ let screenValue =
170
+ typeof resolvedScreenIndexedValues === 'function'
171
+ ? resolvedScreenIndexedValues(value)
172
+ : resolvedScreenIndexedValues?.[value];
173
+
174
+ if (typeof screenValue === 'undefined') {
175
+ return list;
176
+ }
177
+
178
+ return [...list, screenValue];
179
+ },
180
+ [],
181
+ );
182
+
183
+ return classList.join(' ');
184
+ };