@niibase/uniwind 1.5.0 → 1.5.1

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 (99) hide show
  1. package/CHANGELOG.md +1173 -0
  2. package/dist/common/components/web/ActivityIndicator.js +3 -1
  3. package/dist/common/components/web/Button.js +3 -1
  4. package/dist/common/components/web/FlatList.js +3 -1
  5. package/dist/common/components/web/Image.js +3 -1
  6. package/dist/common/components/web/ImageBackground.js +3 -1
  7. package/dist/common/components/web/KeyboardAvoidingView.js +3 -1
  8. package/dist/common/components/web/Modal.js +3 -1
  9. package/dist/common/components/web/Pressable.js +3 -1
  10. package/dist/common/components/web/RefreshControl.js +3 -1
  11. package/dist/common/components/web/SafeAreaView.js +3 -1
  12. package/dist/common/components/web/ScrollView.js +3 -1
  13. package/dist/common/components/web/SectionList.js +3 -1
  14. package/dist/common/components/web/Switch.js +3 -1
  15. package/dist/common/components/web/Text.js +3 -1
  16. package/dist/common/components/web/TextInput.js +3 -1
  17. package/dist/common/components/web/TouchableHighlight.js +3 -1
  18. package/dist/common/components/web/TouchableOpacity.js +3 -1
  19. package/dist/common/components/web/TouchableWithoutFeedback.js +3 -1
  20. package/dist/common/components/web/View.js +2 -0
  21. package/dist/common/components/web/VirtualizedList.js +3 -1
  22. package/dist/common/components/web/generateDataSet.js +18 -0
  23. package/dist/common/core/web/cssListener.js +58 -6
  24. package/dist/common/core/web/getWebStyles.js +20 -18
  25. package/dist/common/css/extraUtilities.js +19 -0
  26. package/dist/common/css/index.js +2 -3
  27. package/dist/common/css/insets.js +2 -2
  28. package/dist/common/css/overwrite.js +2 -2
  29. package/dist/common/css/variants.js +2 -2
  30. package/dist/metro/metro-transformer.cjs +1 -1
  31. package/dist/metro/metro-transformer.mjs +1 -1
  32. package/dist/module/components/web/ActivityIndicator.js +3 -1
  33. package/dist/module/components/web/Button.js +3 -1
  34. package/dist/module/components/web/FlatList.js +3 -1
  35. package/dist/module/components/web/Image.js +3 -1
  36. package/dist/module/components/web/ImageBackground.js +3 -1
  37. package/dist/module/components/web/KeyboardAvoidingView.js +3 -1
  38. package/dist/module/components/web/Modal.js +3 -1
  39. package/dist/module/components/web/Pressable.js +3 -1
  40. package/dist/module/components/web/RefreshControl.js +3 -1
  41. package/dist/module/components/web/SafeAreaView.js +3 -1
  42. package/dist/module/components/web/ScrollView.js +3 -1
  43. package/dist/module/components/web/SectionList.js +3 -1
  44. package/dist/module/components/web/Switch.js +3 -1
  45. package/dist/module/components/web/Text.js +3 -1
  46. package/dist/module/components/web/TextInput.js +3 -1
  47. package/dist/module/components/web/TouchableHighlight.js +3 -1
  48. package/dist/module/components/web/TouchableOpacity.js +3 -1
  49. package/dist/module/components/web/TouchableWithoutFeedback.js +3 -1
  50. package/dist/module/components/web/View.js +2 -0
  51. package/dist/module/components/web/VirtualizedList.js +3 -1
  52. package/dist/module/components/web/generateDataSet.d.ts +32 -0
  53. package/dist/module/components/web/generateDataSet.js +9 -0
  54. package/dist/module/core/web/cssListener.d.ts +6 -1
  55. package/dist/module/core/web/cssListener.js +58 -6
  56. package/dist/module/core/web/getWebStyles.js +21 -18
  57. package/dist/module/css/extraUtilities.d.ts +1 -0
  58. package/dist/module/css/extraUtilities.js +20 -0
  59. package/dist/module/css/index.js +8 -8
  60. package/dist/module/css/insets.d.ts +1 -1
  61. package/dist/module/css/insets.js +2 -1
  62. package/dist/module/css/overwrite.d.ts +1 -1
  63. package/dist/module/css/overwrite.js +1 -1
  64. package/dist/module/css/variants.d.ts +1 -1
  65. package/dist/module/css/variants.js +2 -1
  66. package/dist/shared/{uniwind.DTMo4epG.cjs → uniwind.BZyFsest.cjs} +28 -6
  67. package/dist/shared/{uniwind.BWb5KNML.mjs → uniwind.C-rHhHOg.mjs} +28 -6
  68. package/dist/vite/index.cjs +1 -1
  69. package/dist/vite/index.mjs +1 -1
  70. package/package.json +3 -1
  71. package/src/components/web/ActivityIndicator.tsx +2 -0
  72. package/src/components/web/Button.tsx +2 -0
  73. package/src/components/web/FlatList.tsx +2 -0
  74. package/src/components/web/Image.tsx +2 -0
  75. package/src/components/web/ImageBackground.tsx +2 -0
  76. package/src/components/web/KeyboardAvoidingView.tsx +2 -0
  77. package/src/components/web/Modal.tsx +2 -0
  78. package/src/components/web/Pressable.tsx +2 -0
  79. package/src/components/web/RefreshControl.tsx +2 -0
  80. package/src/components/web/SafeAreaView.tsx +2 -0
  81. package/src/components/web/ScrollView.tsx +2 -0
  82. package/src/components/web/SectionList.tsx +2 -0
  83. package/src/components/web/Switch.tsx +2 -0
  84. package/src/components/web/Text.tsx +2 -0
  85. package/src/components/web/TextInput.tsx +2 -0
  86. package/src/components/web/TouchableHighlight.tsx +2 -0
  87. package/src/components/web/TouchableOpacity.tsx +2 -0
  88. package/src/components/web/TouchableWithoutFeedback.tsx +2 -0
  89. package/src/components/web/View.tsx +2 -0
  90. package/src/components/web/VirtualizedList.tsx +2 -0
  91. package/src/components/web/generateDataSet.ts +52 -0
  92. package/src/core/web/cssListener.ts +73 -6
  93. package/src/core/web/getWebStyles.ts +26 -25
  94. package/src/css/extraUtilities.ts +26 -0
  95. package/src/css/index.ts +8 -8
  96. package/src/css/insets.ts +3 -1
  97. package/src/css/overwrite.ts +1 -1
  98. package/src/css/variants.ts +3 -1
  99. package/uniwind.css +8 -0
@@ -2,9 +2,10 @@ import { StyleDependency } from '../../types'
2
2
  import { UniwindListener } from '../listener'
3
3
 
4
4
  class CSSListenerBuilder {
5
+ activeRules = new Set<CSSStyleRule>()
5
6
  private classNameMediaQueryListeners = new Map<string, MediaQueryList>()
6
7
  private listeners = new Map<MediaQueryList, Set<VoidFunction>>()
7
- private registeredRules = new Map<string, MediaQueryList>()
8
+ private registeredRulesMediaQueries = new Map<string, MediaQueryList>()
8
9
  private processedStyleSheets = new WeakSet<CSSStyleSheet>()
9
10
  private pendingInitialization: number | undefined = undefined
10
11
 
@@ -15,6 +16,22 @@ class CSSListenerBuilder {
15
16
 
16
17
  const observer = new MutationObserver(mutations => {
17
18
  for (const mutation of mutations) {
19
+ if (mutation.type === 'attributes') {
20
+ const el = mutation.target as HTMLLinkElement | HTMLStyleElement
21
+
22
+ if (!('sheet' in el)) {
23
+ continue
24
+ }
25
+
26
+ const sheet = el.sheet
27
+
28
+ if (sheet) {
29
+ this.processedStyleSheets.delete(sheet)
30
+ }
31
+
32
+ this.scheduleInitialization()
33
+ }
34
+
18
35
  if (mutation.type === 'childList') {
19
36
  this.scheduleInitialization()
20
37
  }
@@ -83,8 +100,19 @@ class CSSListenerBuilder {
83
100
  }
84
101
  }
85
102
 
103
+ private pruneStaleRules() {
104
+ const activeSheets = new Set(Array.from(document.styleSheets))
105
+
106
+ for (const rule of this.activeRules) {
107
+ if (!rule.parentStyleSheet || !activeSheets.has(rule.parentStyleSheet)) {
108
+ this.activeRules.delete(rule)
109
+ }
110
+ }
111
+ }
112
+
86
113
  private initialize() {
87
114
  this.pendingInitialization = undefined
115
+ this.pruneStaleRules()
88
116
 
89
117
  for (const sheet of Array.from(document.styleSheets)) {
90
118
  // Skip already processed stylesheets
@@ -121,6 +149,10 @@ class CSSListenerBuilder {
121
149
  return rule.constructor.name === 'CSSMediaRule'
122
150
  }
123
151
 
152
+ private isSupportsRule(rule: CSSRule): rule is CSSSupportsRule {
153
+ return rule.constructor.name === 'CSSSupportsRule'
154
+ }
155
+
124
156
  private collectParentMediaQueries(rule: CSSRule, acc = [] as Array<CSSMediaRule>): Array<CSSMediaRule> {
125
157
  const { parentRule } = rule
126
158
 
@@ -144,13 +176,25 @@ class CSSListenerBuilder {
144
176
  if (this.isStyleRule(rule)) {
145
177
  const mediaQueries = this.collectParentMediaQueries(rule)
146
178
 
179
+ this.activeRules.add(rule)
180
+
147
181
  if (mediaQueries.length > 0) {
148
- this.addMediaQuery(mediaQueries, rule.selectorText)
182
+ this.addMediaQuery(mediaQueries, rule)
149
183
  }
150
184
 
151
185
  continue
152
186
  }
153
187
 
188
+ if (this.isSupportsRule(rule)) {
189
+ if (!CSS.supports(rule.conditionText)) {
190
+ continue
191
+ }
192
+
193
+ this.addMediaQueriesDeep(rule.cssRules)
194
+
195
+ continue
196
+ }
197
+
154
198
  if ('cssRules' in rule && rule.cssRules instanceof CSSRuleList) {
155
199
  this.addMediaQueriesDeep(rule.cssRules)
156
200
 
@@ -159,27 +203,50 @@ class CSSListenerBuilder {
159
203
  }
160
204
  }
161
205
 
162
- private addMediaQuery(mediaQueries: Array<CSSMediaRule>, className: string) {
206
+ private addMediaQuery(mediaQueries: Array<CSSMediaRule>, rule: CSSStyleRule) {
207
+ const className = rule.selectorText
163
208
  const rules = mediaQueries.map(mediaQuery => mediaQuery.conditionText).sort().join(' and ')
164
209
  const parsedClassName = className.replace('.', '').replace('\\', '')
165
- const cachedMediaQueryList = this.registeredRules.get(rules)
210
+ const cachedMediaQueryList = this.registeredRulesMediaQueries.get(rules)
166
211
 
167
212
  if (cachedMediaQueryList) {
168
213
  this.classNameMediaQueryListeners.set(parsedClassName, cachedMediaQueryList)
214
+ this.toggleRule(cachedMediaQueryList, rule)
215
+
216
+ cachedMediaQueryList.addEventListener('change', () => {
217
+ this.toggleRule(cachedMediaQueryList, rule)
218
+ })
169
219
 
170
220
  return
171
221
  }
172
222
 
173
223
  const mediaQueryList = window.matchMedia(rules)
174
224
 
175
- this.registeredRules.set(rules, mediaQueryList)
225
+ this.toggleRule(mediaQueryList, rule)
226
+ this.registeredRulesMediaQueries.set(rules, mediaQueryList)
176
227
  this.listeners.set(mediaQueryList, new Set())
177
228
  this.classNameMediaQueryListeners.set(parsedClassName, mediaQueryList)
178
229
 
179
230
  mediaQueryList.addEventListener('change', () => {
180
- this.listeners.get(mediaQueryList)!.forEach(listener => listener())
231
+ this.listeners.get(mediaQueryList)!.forEach(listener => {
232
+ listener()
233
+ })
234
+ this.toggleRule(mediaQueryList, rule)
181
235
  })
182
236
  }
237
+
238
+ private isRuleLive(rule: CSSStyleRule) {
239
+ const sheet = rule.parentStyleSheet
240
+ return sheet !== null && Array.from(document.styleSheets).includes(sheet)
241
+ }
242
+
243
+ private toggleRule(mqList: MediaQueryList, rule: CSSStyleRule) {
244
+ if (mqList.matches && this.isRuleLive(rule)) {
245
+ this.activeRules.add(rule)
246
+ } else {
247
+ this.activeRules.delete(rule)
248
+ }
249
+ }
183
250
  }
184
251
 
185
252
  export const CSSListener = new CSSListenerBuilder()
@@ -1,4 +1,5 @@
1
1
  import { RNStyle, UniwindContextType } from '../types'
2
+ import { CSSListener } from './cssListener'
2
3
  import { parseCSSValue } from './parseCSSValue'
3
4
 
4
5
  const dummyParent = typeof document !== 'undefined'
@@ -15,40 +16,40 @@ if (dummyParent && dummy) {
15
16
  dummyParent.appendChild(dummy)
16
17
  }
17
18
 
18
- const getComputedStyles = () => {
19
+ const getActiveStylesForClass = (className: string) => {
20
+ const extractedStyles = {} as Record<string, string>
21
+
19
22
  if (!dummy) {
20
- return {} as CSSStyleDeclaration
23
+ return extractedStyles
21
24
  }
22
25
 
26
+ const classNames = className.split(/\s+/).filter(Boolean)
23
27
  const computedStyles = window.getComputedStyle(dummy)
24
- const styles = {} as CSSStyleDeclaration
25
-
26
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
27
- for (let i = 0; i < computedStyles.length; i++) {
28
- // Typescript is unable to infer it properly
29
- const prop = computedStyles[i] as any
30
28
 
31
- styles[prop] = computedStyles.getPropertyValue(prop)
32
- }
29
+ CSSListener.activeRules.forEach(rule => {
30
+ const selector = rule.selectorText
31
+ const mightMatch = classNames.some((cls) => selector.includes(`.${CSS.escape(cls)}`))
33
32
 
34
- return styles
35
- }
36
-
37
- const initialStyles = typeof document !== 'undefined'
38
- ? getComputedStyles()
39
- : {} as CSSStyleDeclaration
40
-
41
- const getObjectDifference = <T extends object>(obj1: T, obj2: T): T => {
42
- const diff = {} as T
43
- const keys = Object.keys(obj2) as Array<keyof T>
33
+ if (!mightMatch) {
34
+ return
35
+ }
44
36
 
45
- keys.forEach(key => {
46
- if (obj2[key] !== obj1[key]) {
47
- diff[key] = obj2[key]
37
+ // element.matches() throws errors if it sees pseudo-elements like ::before
38
+ // So we strip them out safely just for the matching test
39
+ const safeSelector = selector.replace(/::[a-z-]+/gi, '')
40
+
41
+ try {
42
+ if (safeSelector !== '' && dummy.matches(safeSelector)) {
43
+ for (const propertyName of rule.style) {
44
+ extractedStyles[propertyName] = computedStyles.getPropertyValue(propertyName)
45
+ }
46
+ }
47
+ } catch {
48
+ // Failsafe for unparseable selectors
48
49
  }
49
50
  })
50
51
 
51
- return diff
52
+ return extractedStyles
52
53
  }
53
54
 
54
55
  export const getWebStyles = (className: string | undefined, uniwindContext: UniwindContextType): RNStyle => {
@@ -68,7 +69,7 @@ export const getWebStyles = (className: string | undefined, uniwindContext: Uniw
68
69
 
69
70
  dummy.className = className
70
71
 
71
- const computedStyles = getObjectDifference(initialStyles, getComputedStyles())
72
+ const computedStyles = getActiveStylesForClass(className)
72
73
 
73
74
  return Object.fromEntries(
74
75
  Object.entries(computedStyles)
@@ -0,0 +1,26 @@
1
+ import { RNStyle } from '../core/types'
2
+
3
+ const toKebabCase = (str: string) => str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)
4
+
5
+ const generateExtraUtilities = (map: Record<string, RNStyle>) => {
6
+ return Object.entries(map)
7
+ .map(([name, style]) =>
8
+ [
9
+ `@utility ${name} {`,
10
+ ...Object.entries(style).map(([key, value]) => ` ${toKebabCase(key)}: ${value};`),
11
+ `}`,
12
+ '',
13
+ ].join('\n')
14
+ ).join('\n')
15
+ }
16
+
17
+ const EXTRA_UTILITIES_MAP = {
18
+ 'border-continuous': {
19
+ borderCurve: 'continuous',
20
+ },
21
+ 'border-circular': {
22
+ borderCurve: 'circular',
23
+ },
24
+ } satisfies Record<string, RNStyle>
25
+
26
+ export const EXTRA_UTILITIES_CSS = generateExtraUtilities(EXTRA_UTILITIES_MAP)
package/src/css/index.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  import fs from 'fs'
2
2
  import path from 'path'
3
- import { generateCSSForInsets } from './insets'
4
- import { overwrite } from './overwrite'
3
+ import { EXTRA_UTILITIES_CSS } from './extraUtilities'
4
+ import { INSETS_CSS } from './insets'
5
+ import { OVERWRITE_CSS } from './overwrite'
5
6
  import { generateCSSForThemes } from './themes'
6
- import { generateCSSForVariants } from './variants'
7
+ import { VARIANTS_CSS } from './variants'
7
8
 
8
9
  const dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname
9
10
 
10
11
  export const buildCSS = async (themes: Array<string>, input: string) => {
11
- const variants = generateCSSForVariants()
12
- const insets = generateCSSForInsets()
13
12
  const themesCSS = await generateCSSForThemes(themes, input)
14
13
  const cssFilePath = path.join(dirname, '../../uniwind.css')
15
14
  const oldCSSFile = fs.existsSync(cssFilePath)
@@ -17,9 +16,10 @@ export const buildCSS = async (themes: Array<string>, input: string) => {
17
16
  : ''
18
17
 
19
18
  const newCssFile = [
20
- variants,
21
- insets,
22
- overwrite,
19
+ VARIANTS_CSS,
20
+ INSETS_CSS,
21
+ OVERWRITE_CSS,
22
+ EXTRA_UTILITIES_CSS,
23
23
  themesCSS,
24
24
  ].join('\n')
25
25
 
package/src/css/insets.ts CHANGED
@@ -9,7 +9,7 @@ type TypeName = (typeof types)[number]
9
9
  type SafeAreaType = (typeof safeAreaTypes)[number]
10
10
  type Inset = 'top' | 'bottom' | 'left' | 'right'
11
11
 
12
- export const generateCSSForInsets = () => {
12
+ const generateCSSForInsets = () => {
13
13
  let css = `@utility h-screen-safe {
14
14
  height: calc(100vh - (env(safe-area-inset-top) + env(safe-area-inset-bottom)));
15
15
  }\n\n`
@@ -104,3 +104,5 @@ export const generateCSSForInsets = () => {
104
104
  // Remove the last newline character
105
105
  return css.slice(0, -1)
106
106
  }
107
+
108
+ export const INSETS_CSS = generateCSSForInsets()
@@ -13,4 +13,4 @@ const overwriteDisabled = `@custom-variant disabled {
13
13
  }
14
14
  `
15
15
 
16
- export const overwrite = overwriteDisabled
16
+ export const OVERWRITE_CSS = overwriteDisabled
@@ -1,6 +1,6 @@
1
1
  const variants = ['ios', 'android', 'web', 'native', 'tv', 'android-tv', 'apple-tv']
2
2
 
3
- export const generateCSSForVariants = () => {
3
+ const generateCSSForVariants = () => {
4
4
  let css = ''
5
5
 
6
6
  variants.forEach(variant => {
@@ -9,3 +9,5 @@ export const generateCSSForVariants = () => {
9
9
 
10
10
  return css
11
11
  }
12
+
13
+ export const VARIANTS_CSS = generateCSSForVariants()
package/uniwind.css CHANGED
@@ -393,6 +393,14 @@
393
393
  }
394
394
  }
395
395
 
396
+ @utility border-continuous {
397
+ border-curve: continuous;
398
+ }
399
+
400
+ @utility border-circular {
401
+ border-curve: circular;
402
+ }
403
+
396
404
  @custom-variant light {
397
405
  &:where(.light, .light *) {
398
406
  @slot;