@idealyst/components 1.1.7 → 1.1.8

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 (105) hide show
  1. package/package.json +3 -3
  2. package/src/Accordion/Accordion.native.tsx +8 -6
  3. package/src/Accordion/Accordion.styles.old.tsx +298 -0
  4. package/src/Accordion/Accordion.styles.tsx +102 -236
  5. package/src/ActivityIndicator/ActivityIndicator.styles.old.tsx +94 -0
  6. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +44 -74
  7. package/src/Alert/Alert.native.tsx +16 -6
  8. package/src/Alert/Alert.styles.old.tsx +209 -0
  9. package/src/Alert/Alert.styles.tsx +67 -149
  10. package/src/Avatar/Avatar.styles.old.tsx +99 -0
  11. package/src/Avatar/Avatar.styles.tsx +35 -80
  12. package/src/Badge/Badge.styles.old.tsx +157 -0
  13. package/src/Badge/Badge.styles.tsx +61 -121
  14. package/src/Breadcrumb/Breadcrumb.styles.old.tsx +231 -0
  15. package/src/Breadcrumb/Breadcrumb.styles.tsx +83 -200
  16. package/src/Breadcrumb/Breadcrumb.web.tsx +28 -23
  17. package/src/Button/Button.styles.tsx +89 -141
  18. package/src/Card/Card.native.tsx +7 -11
  19. package/src/Card/Card.styles.old.tsx +160 -0
  20. package/src/Card/Card.styles.tsx +105 -142
  21. package/src/Card/Card.web.tsx +5 -4
  22. package/src/Checkbox/Checkbox.native.tsx +9 -5
  23. package/src/Checkbox/Checkbox.styles.old.tsx +271 -0
  24. package/src/Checkbox/Checkbox.styles.tsx +104 -216
  25. package/src/Checkbox/Checkbox.web.tsx +6 -6
  26. package/src/Chip/Chip.styles.old.tsx +184 -0
  27. package/src/Chip/Chip.styles.tsx +34 -72
  28. package/src/Dialog/Dialog.native.tsx +7 -4
  29. package/src/Dialog/Dialog.styles.old.tsx +202 -0
  30. package/src/Dialog/Dialog.styles.tsx +69 -133
  31. package/src/Divider/Divider.styles.old.tsx +172 -0
  32. package/src/Divider/Divider.styles.tsx +62 -84
  33. package/src/Icon/Icon.native.tsx +8 -8
  34. package/src/Icon/Icon.styles.old.tsx +81 -0
  35. package/src/Icon/Icon.styles.tsx +52 -66
  36. package/src/Icon/Icon.web.tsx +43 -7
  37. package/src/Icon/IconSvg/IconSvg.web.tsx +2 -0
  38. package/src/Image/Image.styles.old.tsx +69 -0
  39. package/src/Image/Image.styles.tsx +46 -60
  40. package/src/Input/Input.native.tsx +138 -53
  41. package/src/Input/Input.styles.old.tsx +289 -0
  42. package/src/Input/Input.styles.tsx +127 -198
  43. package/src/List/List.native.tsx +5 -2
  44. package/src/List/List.styles.old.tsx +242 -0
  45. package/src/List/List.styles.tsx +179 -215
  46. package/src/List/ListItem.native.tsx +12 -6
  47. package/src/List/ListItem.web.tsx +23 -13
  48. package/src/Menu/Menu.styles.old.tsx +197 -0
  49. package/src/Menu/Menu.styles.tsx +68 -150
  50. package/src/Menu/MenuItem.native.tsx +5 -3
  51. package/src/Menu/MenuItem.styles.old.tsx +114 -0
  52. package/src/Menu/MenuItem.styles.tsx +57 -89
  53. package/src/Menu/MenuItem.web.tsx +8 -3
  54. package/src/Popover/Popover.native.tsx +10 -4
  55. package/src/Popover/Popover.styles.old.tsx +135 -0
  56. package/src/Popover/Popover.styles.tsx +51 -112
  57. package/src/Pressable/Pressable.styles.old.tsx +27 -0
  58. package/src/Pressable/Pressable.styles.tsx +35 -27
  59. package/src/Progress/Progress.styles.old.tsx +200 -0
  60. package/src/Progress/Progress.styles.tsx +75 -164
  61. package/src/RadioButton/RadioButton.native.tsx +4 -3
  62. package/src/RadioButton/RadioButton.styles.old.tsx +175 -0
  63. package/src/RadioButton/RadioButton.styles.tsx +83 -154
  64. package/src/RadioButton/RadioButton.web.tsx +2 -2
  65. package/src/SVGImage/SVGImage.styles.old.tsx +86 -0
  66. package/src/SVGImage/SVGImage.styles.tsx +35 -78
  67. package/src/Screen/Screen.native.tsx +18 -25
  68. package/src/Screen/Screen.styles.old.tsx +87 -0
  69. package/src/Screen/Screen.styles.tsx +105 -67
  70. package/src/Screen/Screen.web.tsx +1 -1
  71. package/src/Select/Select.native.tsx +42 -33
  72. package/src/Select/Select.styles.old.tsx +353 -0
  73. package/src/Select/Select.styles.tsx +223 -292
  74. package/src/Skeleton/Skeleton.styles.old.tsx +67 -0
  75. package/src/Skeleton/Skeleton.styles.tsx +29 -53
  76. package/src/Slider/Slider.styles.old.tsx +259 -0
  77. package/src/Slider/Slider.styles.tsx +153 -234
  78. package/src/Switch/Switch.native.tsx +7 -5
  79. package/src/Switch/Switch.styles.old.tsx +203 -0
  80. package/src/Switch/Switch.styles.tsx +101 -174
  81. package/src/Switch/Switch.web.tsx +5 -5
  82. package/src/TabBar/TabBar.native.tsx +3 -2
  83. package/src/TabBar/TabBar.styles.old.tsx +343 -0
  84. package/src/TabBar/TabBar.styles.tsx +145 -279
  85. package/src/Table/Table.native.tsx +18 -9
  86. package/src/Table/Table.styles.old.tsx +311 -0
  87. package/src/Table/Table.styles.tsx +152 -286
  88. package/src/Text/Text.native.tsx +1 -3
  89. package/src/Text/Text.style.demo.tsx +16 -0
  90. package/src/Text/Text.styles.old.tsx +219 -0
  91. package/src/Text/Text.styles.tsx +94 -84
  92. package/src/Text/Text.web.tsx +2 -2
  93. package/src/Text/index.ts +1 -0
  94. package/src/TextArea/TextArea.styles.old.tsx +213 -0
  95. package/src/TextArea/TextArea.styles.tsx +93 -181
  96. package/src/Tooltip/Tooltip.styles.old.tsx +82 -0
  97. package/src/Tooltip/Tooltip.styles.tsx +32 -56
  98. package/src/Video/Video.styles.old.tsx +51 -0
  99. package/src/Video/Video.styles.tsx +32 -44
  100. package/src/View/View.native.tsx +12 -14
  101. package/src/View/View.styles.old.tsx +125 -0
  102. package/src/View/View.styles.tsx +76 -106
  103. package/src/View/View.web.tsx +1 -0
  104. package/src/examples/CardExamples.tsx +0 -6
  105. package/src/extensions/extendComponent.ts +61 -0
@@ -1,179 +1,108 @@
1
+ /**
2
+ * RadioButton styles using defineStyle with dynamic props.
3
+ */
1
4
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles, Intent, Size} from '@idealyst/theme';
3
- import { buildSizeVariants } from '../utils/buildSizeVariants';
4
- import {
5
- buildMarginVariants,
6
- buildMarginVerticalVariants,
7
- buildMarginHorizontalVariants,
8
- } from '../utils/buildViewStyleVariants';
9
- import { applyExtensions } from '../extensions/applyExtension';
5
+ import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
7
+ import { ViewStyleSize } from '../utils/viewStyleProps';
10
8
 
11
- type RadioButtonSize = Size;
12
- type RadioButtonIntent = Intent;
13
- type RadioGroupOrientation = 'horizontal' | 'vertical';
14
-
15
- type RadioButtonVariants = {
16
- size: RadioButtonSize;
17
- intent: RadioButtonIntent;
18
- checked: boolean;
19
- disabled: boolean;
20
- }
9
+ // Required: Unistyles must see StyleSheet usage in original source to process this file
10
+ void StyleSheet;
21
11
 
22
- type RadioGroupVariants = {
23
- orientation: RadioGroupOrientation;
24
- }
12
+ // Wrap theme for $iterator support
13
+ type Theme = ThemeStyleWrapper<BaseTheme>;
25
14
 
26
- export type ExpandedRadioButtonStyles = StylesheetStyles<keyof RadioButtonVariants>;
27
- export type ExpandedRadioGroupStyles = StylesheetStyles<keyof RadioGroupVariants>;
28
-
29
- export type RadioButtonStylesheet = {
30
- container: ExpandedRadioButtonStyles;
31
- radio: ExpandedRadioButtonStyles;
32
- radioDot: ExpandedRadioButtonStyles;
33
- label: ExpandedRadioButtonStyles;
34
- groupContainer: ExpandedRadioGroupStyles;
35
- }
15
+ type RadioGroupOrientation = 'horizontal' | 'vertical';
36
16
 
37
- function createRadioSizeVariants(theme: Theme) {
38
- return buildSizeVariants(theme, 'radioButton', (size) => ({
39
- width: size.radioSize,
40
- height: size.radioSize,
41
- }));
42
- }
17
+ export type RadioButtonDynamicProps = {
18
+ size?: Size;
19
+ intent?: Intent;
20
+ checked?: boolean;
21
+ disabled?: boolean;
22
+ orientation?: RadioGroupOrientation;
23
+ margin?: ViewStyleSize;
24
+ marginVertical?: ViewStyleSize;
25
+ marginHorizontal?: ViewStyleSize;
26
+ };
43
27
 
44
- function createCheckedVariants(theme: Theme, intent: RadioButtonIntent) {
45
- const intentValue = theme.intents[intent];
46
- return {
47
- true: {
48
- borderColor: intentValue.primary,
49
- },
50
- false: {
51
- borderColor: theme.colors.border.primary,
28
+ /**
29
+ * RadioButton styles with intent/checked/disabled handling.
30
+ */
31
+ export const radioButtonStyles = defineStyle('RadioButton', (theme: Theme) => ({
32
+ container: (_props: RadioButtonDynamicProps) => ({
33
+ flexDirection: 'row' as const,
34
+ alignItems: 'center' as const,
35
+ paddingVertical: 4,
36
+ variants: {
37
+ size: {
38
+ gap: theme.sizes.$radioButton.gap,
39
+ },
40
+ margin: {
41
+ margin: theme.sizes.$view.padding,
42
+ },
43
+ marginVertical: {
44
+ marginVertical: theme.sizes.$view.padding,
45
+ },
46
+ marginHorizontal: {
47
+ marginHorizontal: theme.sizes.$view.padding,
48
+ },
52
49
  },
53
- } as const;
54
- }
55
-
56
- function createRadioDotSizeVariants(theme: Theme) {
57
- return buildSizeVariants(theme, 'radioButton', (size) => ({
58
- width: size.radioDotSize,
59
- height: size.radioDotSize,
60
- }));
61
- }
50
+ }),
62
51
 
63
- function createRadioDotIntentColor(theme: Theme, intent: RadioButtonIntent) {
64
- return theme.intents[intent].primary;
65
- }
52
+ radio: ({ intent = 'primary', checked = false, disabled = false }: RadioButtonDynamicProps) => {
53
+ const intentValue = theme.intents[intent];
66
54
 
67
- function createRadioStyles(theme: Theme) {
68
- return ({ intent }: Partial<RadioButtonVariants>) => {
69
55
  return {
70
56
  borderRadius: 9999,
71
57
  borderWidth: 1.5,
72
- borderStyle: 'solid',
73
- alignItems: 'center',
74
- justifyContent: 'center',
75
- backgroundColor: theme.colors.surface.primary,
58
+ borderStyle: 'solid' as const,
59
+ alignItems: 'center' as const,
60
+ justifyContent: 'center' as const,
61
+ backgroundColor: disabled
62
+ ? theme.colors.surface.tertiary
63
+ : theme.colors.surface.primary,
64
+ borderColor: checked ? intentValue.primary : theme.colors.border.primary,
65
+ opacity: disabled ? 0.5 : 1,
76
66
  variants: {
77
- size: createRadioSizeVariants(theme),
78
- checked: createCheckedVariants(theme, intent),
79
- disabled: {
80
- true: {
81
- opacity: 0.5,
82
- backgroundColor: theme.colors.surface.tertiary,
83
- _web: {
84
- cursor: 'not-allowed',
85
- },
86
- },
87
- false: {
88
- opacity: 1,
89
- backgroundColor: theme.colors.surface.primary,
90
- _web: {
91
- cursor: 'pointer',
92
- _hover: {
93
- opacity: 0.8,
94
- },
95
- _active: {
96
- opacity: 0.6,
97
- },
98
- },
99
- },
67
+ size: {
68
+ width: theme.sizes.$radioButton.radioSize,
69
+ height: theme.sizes.$radioButton.radioSize,
100
70
  },
101
71
  },
102
72
  _web: {
103
73
  transition: 'all 0.2s ease',
74
+ cursor: disabled ? 'not-allowed' : 'pointer',
75
+ _hover: disabled ? {} : { opacity: 0.8 },
76
+ _active: disabled ? {} : { opacity: 0.6 },
104
77
  },
105
78
  } as const;
106
- }
107
- }
108
-
109
- function createRadioDotStyles(theme: Theme) {
110
- return ({ intent }: Partial<RadioButtonVariants>) => {
111
- return {
112
- borderRadius: 9999,
113
- backgroundColor: createRadioDotIntentColor(theme, intent),
114
- variants: {
115
- size: createRadioDotSizeVariants(theme),
116
- },
117
- } as const;
118
- }
119
- }
79
+ },
120
80
 
121
- // Container style creator for extension support
122
- function createContainerStyles(theme: Theme) {
123
- return () => ({
124
- flexDirection: 'row' as const,
125
- alignItems: 'center' as const,
126
- paddingVertical: 4,
81
+ radioDot: ({ intent = 'primary' }: RadioButtonDynamicProps) => ({
82
+ borderRadius: 9999,
83
+ backgroundColor: theme.intents[intent].primary,
127
84
  variants: {
128
- size: buildSizeVariants(theme, 'radioButton', (size) => ({
129
- gap: size.gap,
130
- })),
131
- margin: buildMarginVariants(theme),
132
- marginVertical: buildMarginVerticalVariants(theme),
133
- marginHorizontal: buildMarginHorizontalVariants(theme),
134
- } as const,
135
- });
136
- }
137
-
138
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
139
- // transform on native cannot resolve function calls to extract variant structures.
140
- export const radioButtonStyles = StyleSheet.create((theme: Theme) => {
141
- // Apply extensions to main visual elements
142
- const extended = applyExtensions('RadioButton', theme, {
143
- container: createContainerStyles(theme),
144
- radio: createRadioStyles(theme),
145
- radioDot: createRadioDotStyles(theme),
146
- });
147
-
148
- return {
149
- ...extended,
150
- // Minor utility styles
151
- label: {
152
- color: theme.colors.text.primary,
153
- variants: {
154
- size: buildSizeVariants(theme, 'radioButton', (size) => ({
155
- fontSize: size.fontSize,
156
- })),
157
- disabled: {
158
- true: { opacity: 0.5 },
159
- false: { opacity: 1 },
160
- },
85
+ size: {
86
+ width: theme.sizes.$radioButton.radioDotSize,
87
+ height: theme.sizes.$radioButton.radioDotSize,
161
88
  },
162
89
  },
163
- groupContainer: {
164
- gap: 4,
165
- variants: {
166
- orientation: {
167
- horizontal: {
168
- flexDirection: 'row',
169
- flexWrap: 'wrap',
170
- gap: 16,
171
- },
172
- vertical: {
173
- flexDirection: 'column',
174
- },
175
- },
90
+ }),
91
+
92
+ label: ({ disabled = false }: RadioButtonDynamicProps) => ({
93
+ color: theme.colors.text.primary,
94
+ opacity: disabled ? 0.5 : 1,
95
+ variants: {
96
+ size: {
97
+ fontSize: theme.sizes.$radioButton.fontSize,
176
98
  },
177
99
  },
178
- };
179
- });
100
+ }),
101
+
102
+ groupContainer: ({ orientation = 'vertical' }: RadioButtonDynamicProps) => ({
103
+ gap: 4,
104
+ flexDirection: orientation === 'horizontal' ? ('row' as const) : ('column' as const),
105
+ flexWrap: orientation === 'horizontal' ? ('wrap' as const) : undefined,
106
+ ...(orientation === 'horizontal' ? { gap: 16 } : {}),
107
+ }),
108
+ }));
@@ -88,9 +88,9 @@ const RadioButton: React.FC<RadioButtonProps> = ({
88
88
  });
89
89
 
90
90
  const containerProps = getWebProps([(radioButtonStyles.container as any)({}), style]);
91
- const radioProps = getWebProps([(radioButtonStyles.radio as any)({ intent })]);
91
+ const radioProps = getWebProps([(radioButtonStyles.radio as any)({ intent, checked, disabled })]);
92
92
  const dotProps = getWebProps([(radioButtonStyles.radioDot as any)({ intent })]);
93
- const labelProps = getWebProps([radioButtonStyles.label]);
93
+ const labelProps = getWebProps([(radioButtonStyles.label as any)({ disabled })]);
94
94
 
95
95
  return (
96
96
  <button
@@ -0,0 +1,86 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Intent} from '@idealyst/theme';
3
+ import { applyExtensions } from '../extensions/applyExtension';
4
+
5
+ type SVGImageIntent = Intent;
6
+
7
+ type SVGImageVariants = {
8
+ intent: SVGImageIntent;
9
+ }
10
+
11
+ export type ExpandedSVGImageStyles = StylesheetStyles<keyof SVGImageVariants>;
12
+
13
+ export type SVGImageStylesheet = {
14
+ container: ExpandedSVGImageStyles;
15
+ image: ExpandedSVGImageStyles;
16
+ }
17
+
18
+ function createContainerIntentVariants(theme: Theme) {
19
+ return {
20
+ primary: {
21
+ filter: `brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%)`,
22
+ },
23
+ success: {
24
+ filter: `brightness(0) saturate(100%) invert(64%) sepia(88%) saturate(3323%) hue-rotate(84deg) brightness(119%) contrast(119%)`,
25
+ },
26
+ error: {
27
+ filter: `brightness(0) saturate(100%) invert(23%) sepia(89%) saturate(7395%) hue-rotate(4deg) brightness(102%) contrast(118%)`,
28
+ },
29
+ warning: {
30
+ filter: `brightness(0) saturate(100%) invert(54%) sepia(98%) saturate(4341%) hue-rotate(21deg) brightness(101%) contrast(101%)`,
31
+ },
32
+ neutral: {
33
+ filter: `brightness(0) saturate(100%) invert(52%) sepia(23%) saturate(3207%) hue-rotate(314deg) brightness(99%) contrast(96%)`,
34
+ },
35
+ info: {
36
+ filter: `brightness(0) saturate(100%) invert(58%) sepia(96%) saturate(2582%) hue-rotate(165deg) brightness(99%) contrast(91%)`,
37
+ },
38
+ };
39
+ }
40
+
41
+ function createContainerNativeIntentVariants(theme: Theme) {
42
+ const variants: Record<SVGImageIntent, any> = {} as any;
43
+ for (const intent in theme.intents) {
44
+ variants[intent as SVGImageIntent] = {
45
+ tintColor: theme.intents[intent as SVGImageIntent].primary,
46
+ };
47
+ }
48
+ return variants;
49
+ }
50
+
51
+ // Style creators for extension support
52
+ function createContainerStyles(theme: Theme) {
53
+ return () => ({
54
+ alignItems: 'center' as const,
55
+ justifyContent: 'center' as const,
56
+ variants: {
57
+ intent: createContainerIntentVariants(theme),
58
+ },
59
+ _web: {
60
+ userSelect: 'none',
61
+ },
62
+ _native: {
63
+ variants: {
64
+ intent: createContainerNativeIntentVariants(theme),
65
+ },
66
+ },
67
+ });
68
+ }
69
+
70
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
71
+ // transform on native cannot resolve function calls to extract variant structures.
72
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
73
+ export const svgImageStyles = StyleSheet.create((theme: Theme) => {
74
+ // Apply extensions to main visual elements
75
+
76
+ return applyExtensions('SVGImage', theme, {container: createContainerStyles(theme),
77
+ // Additional styles (merged from return)
78
+ // Minor utility styles (not extended)
79
+ image: {
80
+ _web: {
81
+ display: 'block',
82
+ maxWidth: '100%',
83
+ height: 'auto',
84
+ },
85
+ }});
86
+ });
@@ -1,90 +1,47 @@
1
+ /**
2
+ * SVGImage styles using defineStyle with dynamic intent handling.
3
+ */
1
4
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles, Intent} from '@idealyst/theme';
3
- import { applyExtensions } from '../extensions/applyExtension';
5
+ import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent } from '@idealyst/theme';
4
7
 
5
- type SVGImageIntent = Intent;
8
+ // Required: Unistyles must see StyleSheet usage in original source to process this file
9
+ void StyleSheet;
6
10
 
7
- type SVGImageVariants = {
8
- intent: SVGImageIntent;
9
- }
11
+ // Wrap theme for $iterator support
12
+ type Theme = ThemeStyleWrapper<BaseTheme>;
10
13
 
11
- export type ExpandedSVGImageStyles = StylesheetStyles<keyof SVGImageVariants>;
14
+ export type SVGImageDynamicProps = {
15
+ intent?: Intent;
16
+ };
12
17
 
13
- export type SVGImageStylesheet = {
14
- container: ExpandedSVGImageStyles;
15
- image: ExpandedSVGImageStyles;
16
- }
17
-
18
- function createContainerIntentVariants(theme: Theme) {
19
- return {
20
- primary: {
21
- filter: `brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%)`,
22
- },
23
- success: {
24
- filter: `brightness(0) saturate(100%) invert(64%) sepia(88%) saturate(3323%) hue-rotate(84deg) brightness(119%) contrast(119%)`,
25
- },
26
- error: {
27
- filter: `brightness(0) saturate(100%) invert(23%) sepia(89%) saturate(7395%) hue-rotate(4deg) brightness(102%) contrast(118%)`,
28
- },
29
- warning: {
30
- filter: `brightness(0) saturate(100%) invert(54%) sepia(98%) saturate(4341%) hue-rotate(21deg) brightness(101%) contrast(101%)`,
31
- },
32
- neutral: {
33
- filter: `brightness(0) saturate(100%) invert(52%) sepia(23%) saturate(3207%) hue-rotate(314deg) brightness(99%) contrast(96%)`,
34
- },
35
- info: {
36
- filter: `brightness(0) saturate(100%) invert(58%) sepia(96%) saturate(2582%) hue-rotate(165deg) brightness(99%) contrast(91%)`,
37
- },
38
- };
39
- }
40
-
41
- function createContainerNativeIntentVariants(theme: Theme) {
42
- const variants: Record<SVGImageIntent, any> = {} as any;
43
- for (const intent in theme.intents) {
44
- variants[intent as SVGImageIntent] = {
45
- tintColor: theme.intents[intent as SVGImageIntent].primary,
46
- };
47
- }
48
- return variants;
49
- }
50
-
51
- // Style creators for extension support
52
- function createContainerStyles(theme: Theme) {
53
- return () => ({
18
+ /**
19
+ * SVGImage styles with intent-based coloring.
20
+ * Uses CSS filters on web and tintColor on native.
21
+ */
22
+ export const svgImageStyles = defineStyle('SVGImage', (theme: Theme) => ({
23
+ container: ({ intent }: SVGImageDynamicProps) => ({
54
24
  alignItems: 'center' as const,
55
25
  justifyContent: 'center' as const,
56
- variants: {
57
- intent: createContainerIntentVariants(theme),
58
- },
26
+ // Native: use tintColor for intent
27
+ ...(intent ? { tintColor: theme.intents[intent].primary } : {}),
59
28
  _web: {
60
29
  userSelect: 'none',
30
+ // Web: use CSS filters for intent coloring
31
+ ...(intent === 'primary' ? { filter: 'brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%)' } : {}),
32
+ ...(intent === 'success' ? { filter: 'brightness(0) saturate(100%) invert(64%) sepia(88%) saturate(3323%) hue-rotate(84deg) brightness(119%) contrast(119%)' } : {}),
33
+ ...(intent === 'error' ? { filter: 'brightness(0) saturate(100%) invert(23%) sepia(89%) saturate(7395%) hue-rotate(4deg) brightness(102%) contrast(118%)' } : {}),
34
+ ...(intent === 'warning' ? { filter: 'brightness(0) saturate(100%) invert(54%) sepia(98%) saturate(4341%) hue-rotate(21deg) brightness(101%) contrast(101%)' } : {}),
35
+ ...(intent === 'neutral' ? { filter: 'brightness(0) saturate(100%) invert(52%) sepia(23%) saturate(3207%) hue-rotate(314deg) brightness(99%) contrast(96%)' } : {}),
36
+ ...(intent === 'info' ? { filter: 'brightness(0) saturate(100%) invert(58%) sepia(96%) saturate(2582%) hue-rotate(165deg) brightness(99%) contrast(91%)' } : {}),
61
37
  },
62
- _native: {
63
- variants: {
64
- intent: createContainerNativeIntentVariants(theme),
65
- },
66
- },
67
- });
68
- }
69
-
70
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
71
- // transform on native cannot resolve function calls to extract variant structures.
72
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
73
- export const svgImageStyles = StyleSheet.create((theme: Theme) => {
74
- // Apply extensions to main visual elements
75
- const extended = applyExtensions('SVGImage', theme, {
76
- container: createContainerStyles(theme),
77
- });
38
+ }),
78
39
 
79
- return {
80
- ...extended,
81
- // Minor utility styles (not extended)
82
- image: {
83
- _web: {
84
- display: 'block',
85
- maxWidth: '100%',
86
- height: 'auto',
87
- },
40
+ image: (_props: SVGImageDynamicProps) => ({
41
+ _web: {
42
+ display: 'block',
43
+ maxWidth: '100%',
44
+ height: 'auto',
88
45
  },
89
- };
90
- });
46
+ }),
47
+ }));
@@ -24,6 +24,7 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
24
24
  }, ref) => {
25
25
  const insets = useSafeAreaInsets();
26
26
 
27
+ // Set active variants for this render
27
28
  screenStyles.useVariants({
28
29
  background,
29
30
  safeArea,
@@ -36,6 +37,9 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
36
37
  marginHorizontal,
37
38
  });
38
39
 
40
+ // Call styles as functions to get theme-reactive styles
41
+ const screenStyle = (screenStyles.screen as any)({});
42
+
39
43
  // Calculate safe area padding
40
44
  const safeAreaStyle = safeArea ? {
41
45
  paddingTop: insets.top,
@@ -45,43 +49,32 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
45
49
  } : undefined;
46
50
 
47
51
  if (scrollable) {
48
- // For ScrollView, flex: 1 goes on the ScrollView style
49
- // Background and padding go on contentContainerStyle (without flex: 1)
50
- const scrollViewStyle = [{ flex: 1 }, style];
51
-
52
- const contentContainerStyleArray = [
53
- screenStyles.screenContent,
54
- safeAreaStyle,
55
- contentInset ? {
56
- paddingTop: (safeArea ? insets.top : 0) + (contentInset.top ?? 0),
57
- paddingBottom: (safeArea ? insets.bottom : 0) + (contentInset.bottom ?? 0),
58
- paddingLeft: (safeArea ? insets.left : 0) + (contentInset.left ?? 0),
59
- paddingRight: (safeArea ? insets.right : 0) + (contentInset.right ?? 0),
60
- } : undefined,
61
- ];
52
+ // Content styles applied via View wrapper for Unistyles reactivity
53
+ // (contentContainerStyle isn't reactive, only style prop is)
54
+ const contentInsetStyle = contentInset ? {
55
+ paddingTop: (safeArea ? insets.top : 0) + (contentInset.top ?? 0),
56
+ paddingBottom: (safeArea ? insets.bottom : 0) + (contentInset.bottom ?? 0),
57
+ paddingLeft: (safeArea ? insets.left : 0) + (contentInset.left ?? 0),
58
+ paddingRight: (safeArea ? insets.right : 0) + (contentInset.right ?? 0),
59
+ } : safeAreaStyle;
62
60
 
63
61
  return (
64
62
  <RNScrollView
65
63
  ref={ref as any}
66
64
  nativeID={id}
67
- style={scrollViewStyle}
68
- contentContainerStyle={contentContainerStyleArray}
65
+ style={[screenStyle, style]}
66
+ contentContainerStyle={{ flexGrow: 1 }}
69
67
  testID={testID}
70
68
  >
71
- {children}
69
+ <RNView style={[contentInsetStyle, { flex: 1 }]}>
70
+ {children}
71
+ </RNView>
72
72
  </RNScrollView>
73
73
  );
74
74
  }
75
75
 
76
- const screenStyle = (screenStyles.screen as any)({});
77
- const containerStyle = [
78
- screenStyle,
79
- safeAreaStyle,
80
- style,
81
- ];
82
-
83
76
  return (
84
- <RNView ref={ref as any} nativeID={id} style={containerStyle} testID={testID}>
77
+ <RNView ref={ref as any} nativeID={id} style={[screenStyle, safeAreaStyle, style]} testID={testID}>
85
78
  {children}
86
79
  </RNView>
87
80
  );
@@ -0,0 +1,87 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme } from '@idealyst/theme';
3
+ import {
4
+ buildGapVariants,
5
+ buildPaddingVariants,
6
+ buildPaddingVerticalVariants,
7
+ buildPaddingHorizontalVariants,
8
+ buildMarginVariants,
9
+ buildMarginVerticalVariants,
10
+ buildMarginHorizontalVariants,
11
+ } from '../utils/buildViewStyleVariants';
12
+ import { applyExtensions } from '../extensions/applyExtension';
13
+
14
+ function generateBackgroundVariants(theme: Theme) {
15
+ return {
16
+ primary: { backgroundColor: theme.colors.surface.primary },
17
+ secondary: { backgroundColor: theme.colors.surface.secondary },
18
+ tertiary: { backgroundColor: theme.colors.surface.tertiary },
19
+ inverse: { backgroundColor: theme.colors.surface.inverse },
20
+ 'inverse-secondary': { backgroundColor: theme.colors.surface['inverse-secondary'] },
21
+ 'inverse-tertiary': { backgroundColor: theme.colors.surface['inverse-tertiary'] },
22
+ transparent: { backgroundColor: 'transparent' },
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Create dynamic screen styles.
28
+ * Returns a function to ensure Unistyles can track theme changes.
29
+ */
30
+ function createScreenStyles(theme: Theme) {
31
+ return (_props?: {}) => ({
32
+ flex: 1,
33
+ variants: {
34
+ background: generateBackgroundVariants(theme),
35
+ safeArea: {
36
+ true: {},
37
+ false: {},
38
+ },
39
+ gap: buildGapVariants(theme),
40
+ padding: buildPaddingVariants(theme),
41
+ paddingVertical: buildPaddingVerticalVariants(theme),
42
+ paddingHorizontal: buildPaddingHorizontalVariants(theme),
43
+ margin: buildMarginVariants(theme),
44
+ marginVertical: buildMarginVerticalVariants(theme),
45
+ marginHorizontal: buildMarginHorizontalVariants(theme),
46
+ },
47
+ _web: {
48
+ overflow: 'auto',
49
+ display: 'flex',
50
+ flexDirection: 'column',
51
+ minHeight: '100%',
52
+ boxSizing: 'border-box',
53
+ },
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Create dynamic screen content styles for ScrollView.
59
+ * No flex: 1 so content can grow beyond screen height.
60
+ */
61
+ function createScreenContentStyles(theme: Theme) {
62
+ return (_props?: {}) => ({
63
+ variants: {
64
+ background: generateBackgroundVariants(theme),
65
+ safeArea: {
66
+ true: {},
67
+ false: {},
68
+ },
69
+ gap: buildGapVariants(theme),
70
+ padding: buildPaddingVariants(theme),
71
+ paddingVertical: buildPaddingVerticalVariants(theme),
72
+ paddingHorizontal: buildPaddingHorizontalVariants(theme),
73
+ margin: buildMarginVariants(theme),
74
+ marginVertical: buildMarginVerticalVariants(theme),
75
+ marginHorizontal: buildMarginHorizontalVariants(theme),
76
+ },
77
+ });
78
+ }
79
+
80
+ // Styles use applyExtensions to enable theme extensions and ensure proper
81
+ // reactivity with Unistyles' native Shadow Tree updates.
82
+ export const screenStyles = StyleSheet.create((theme: Theme) => {
83
+ return applyExtensions('Screen', theme, {
84
+ screen: createScreenStyles(theme),
85
+ screenContent: createScreenContentStyles(theme),
86
+ });
87
+ });