@tamagui/core 1.129.5 → 1.129.6-1751237024118

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 (61) hide show
  1. package/dist/cjs/index.cjs +15 -9
  2. package/dist/cjs/index.js +11 -7
  3. package/dist/cjs/index.js.map +2 -2
  4. package/dist/cjs/index.native.js +15 -7
  5. package/dist/cjs/index.native.js.map +2 -2
  6. package/dist/esm/index.js +15 -5
  7. package/dist/esm/index.js.map +1 -1
  8. package/dist/esm/index.mjs +11 -6
  9. package/dist/esm/index.mjs.map +1 -1
  10. package/dist/esm/index.native.js +13 -6
  11. package/dist/esm/index.native.js.map +1 -1
  12. package/dist/native.js +427 -356
  13. package/dist/native.js.map +3 -3
  14. package/dist/test.native.js +407 -335
  15. package/dist/test.native.js.map +3 -3
  16. package/package.json +9 -8
  17. package/src/index.tsx +19 -3
  18. package/types/index.d.ts +3 -2
  19. package/types/index.d.ts.map +1 -1
  20. package/dist/cjs/helpers/getBoundingClientRect.cjs +0 -41
  21. package/dist/cjs/helpers/getBoundingClientRect.js +0 -39
  22. package/dist/cjs/helpers/getBoundingClientRect.js.map +0 -6
  23. package/dist/cjs/helpers/getBoundingClientRect.native.js +0 -48
  24. package/dist/cjs/helpers/getBoundingClientRect.native.js.map +0 -6
  25. package/dist/cjs/helpers/getRect.cjs +0 -44
  26. package/dist/cjs/helpers/getRect.js +0 -27
  27. package/dist/cjs/helpers/getRect.js.map +0 -6
  28. package/dist/cjs/helpers/getRect.native.js +0 -39
  29. package/dist/cjs/helpers/getRect.native.js.map +0 -6
  30. package/dist/cjs/hooks/useElementLayout.cjs +0 -203
  31. package/dist/cjs/hooks/useElementLayout.js +0 -152
  32. package/dist/cjs/hooks/useElementLayout.js.map +0 -6
  33. package/dist/cjs/hooks/useElementLayout.native.js +0 -185
  34. package/dist/cjs/hooks/useElementLayout.native.js.map +0 -6
  35. package/dist/esm/helpers/getBoundingClientRect.js +0 -23
  36. package/dist/esm/helpers/getBoundingClientRect.js.map +0 -6
  37. package/dist/esm/helpers/getBoundingClientRect.mjs +0 -17
  38. package/dist/esm/helpers/getBoundingClientRect.mjs.map +0 -1
  39. package/dist/esm/helpers/getBoundingClientRect.native.js +0 -24
  40. package/dist/esm/helpers/getBoundingClientRect.native.js.map +0 -1
  41. package/dist/esm/helpers/getRect.js +0 -11
  42. package/dist/esm/helpers/getRect.js.map +0 -6
  43. package/dist/esm/helpers/getRect.mjs +0 -21
  44. package/dist/esm/helpers/getRect.mjs.map +0 -1
  45. package/dist/esm/helpers/getRect.native.js +0 -22
  46. package/dist/esm/helpers/getRect.native.js.map +0 -1
  47. package/dist/esm/hooks/useElementLayout.js +0 -140
  48. package/dist/esm/hooks/useElementLayout.js.map +0 -6
  49. package/dist/esm/hooks/useElementLayout.mjs +0 -175
  50. package/dist/esm/hooks/useElementLayout.mjs.map +0 -1
  51. package/dist/esm/hooks/useElementLayout.native.js +0 -189
  52. package/dist/esm/hooks/useElementLayout.native.js.map +0 -1
  53. package/src/helpers/getBoundingClientRect.tsx +0 -30
  54. package/src/helpers/getRect.tsx +0 -8
  55. package/src/hooks/useElementLayout.tsx +0 -292
  56. package/types/helpers/getBoundingClientRect.d.ts +0 -3
  57. package/types/helpers/getBoundingClientRect.d.ts.map +0 -1
  58. package/types/helpers/getRect.d.ts +0 -9
  59. package/types/helpers/getRect.d.ts.map +0 -1
  60. package/types/hooks/useElementLayout.d.ts +0 -26
  61. package/types/hooks/useElementLayout.d.ts.map +0 -1
@@ -1,292 +0,0 @@
1
- import { isClient, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import {
3
- isEqualShallow,
4
- type TamaguiComponentStateRef,
5
- ___onDidFinishClientRender,
6
- } from '@tamagui/web'
7
- import type { RefObject } from 'react'
8
-
9
- const LayoutHandlers = new WeakMap<HTMLElement, Function>()
10
- const Nodes = new Set<HTMLElement>()
11
-
12
- type LayoutMeasurementStrategy = 'off' | 'sync' | 'async'
13
-
14
- let strategy: LayoutMeasurementStrategy = 'async'
15
-
16
- export function setOnLayoutStrategy(state: LayoutMeasurementStrategy) {
17
- strategy = state
18
- }
19
-
20
- export type LayoutValue = {
21
- x: number
22
- y: number
23
- width: number
24
- height: number
25
- left: number
26
- top: number
27
- }
28
-
29
- export type LayoutEvent = {
30
- nativeEvent: {
31
- layout: LayoutValue
32
- target: any
33
- }
34
- timeStamp: number
35
- }
36
-
37
- const NodeRectCache = new WeakMap<HTMLElement, DOMRect>()
38
- const ParentRectCache = new WeakMap<HTMLElement, DOMRect>()
39
- const DebounceTimers = new WeakMap<HTMLElement, NodeJS.Timeout>()
40
- const LastChangeTime = new WeakMap<HTMLElement, number>()
41
-
42
- const rAF = typeof window !== 'undefined' ? window.requestAnimationFrame : undefined
43
- const DEBOUNCE_DELAY = 32 // 32ms debounce (2 frames at 60fps)
44
-
45
- if (isClient) {
46
- if (rAF) {
47
- // prevent thrashing during first hydration (somewhat, streaming gets trickier)
48
- let avoidUpdates = true
49
- const queuedUpdates = new Map<HTMLElement, Function>()
50
-
51
- // track frame timing to detect sync work and avoid updates during heavy periods
52
- let lastFrameAt = Date.now()
53
- const numDroppedFramesUntilPause = 2 // adjust sensitivity
54
-
55
- ___onDidFinishClientRender(() => {
56
- avoidUpdates = false
57
- if (queuedUpdates) {
58
- queuedUpdates.forEach((cb) => cb())
59
- queuedUpdates.clear()
60
- }
61
- })
62
-
63
- async function updateLayoutIfChanged(node: HTMLElement) {
64
- const nodeRect = node.getBoundingClientRect()
65
- const parentNode = node.parentElement
66
- const parentRect = parentNode?.getBoundingClientRect()
67
-
68
- const onLayout = LayoutHandlers.get(node)
69
- if (typeof onLayout !== 'function') return
70
-
71
- const cachedRect = NodeRectCache.get(node)
72
- const cachedParentRect = parentNode ? NodeRectCache.get(parentNode) : null
73
-
74
- if (
75
- !cachedRect ||
76
- // has changed one rect
77
- (!isEqualShallow(cachedRect, nodeRect) &&
78
- (!cachedParentRect || !isEqualShallow(cachedParentRect, parentRect)))
79
- ) {
80
- NodeRectCache.set(node, nodeRect)
81
- if (parentRect && parentNode) {
82
- ParentRectCache.set(parentNode, parentRect)
83
- }
84
-
85
- if (avoidUpdates) {
86
- // Use sync version for queued updates to avoid promise complications
87
- const event = getElementLayoutEvent(node)
88
- queuedUpdates.set(node, () => onLayout(event))
89
- } else if (strategy === 'async') {
90
- // For async strategy, debounce the layout update
91
- const now = Date.now()
92
- LastChangeTime.set(node, now)
93
-
94
- // Clear existing debounce timer
95
- const existingTimer = DebounceTimers.get(node)
96
- if (existingTimer) {
97
- clearTimeout(existingTimer)
98
- }
99
-
100
- // Set new debounce timer
101
- const timer = setTimeout(async () => {
102
- const lastChange = LastChangeTime.get(node) || 0
103
- const timeSinceChange = Date.now() - lastChange
104
-
105
- // Only fire if at least DEBOUNCE_DELAY has passed since last change
106
- if (timeSinceChange >= DEBOUNCE_DELAY) {
107
- const event = await getElementLayoutEventAsync(node)
108
- onLayout(event)
109
- DebounceTimers.delete(node)
110
- } else {
111
- // Reschedule if not enough time has passed
112
- const remainingDelay = DEBOUNCE_DELAY - timeSinceChange
113
- const newTimer = setTimeout(async () => {
114
- const event = await getElementLayoutEventAsync(node)
115
- onLayout(event)
116
- DebounceTimers.delete(node)
117
- }, remainingDelay)
118
- DebounceTimers.set(node, newTimer)
119
- }
120
- }, DEBOUNCE_DELAY)
121
-
122
- DebounceTimers.set(node, timer)
123
- } else {
124
- // Sync strategy - use sync version
125
- const event = getElementLayoutEvent(node)
126
- onLayout(event)
127
- }
128
- }
129
- }
130
-
131
- // note that getBoundingClientRect() does not thrash layout if its after an animation frame
132
- rAF!(layoutOnAnimationFrame)
133
- function layoutOnAnimationFrame() {
134
- const now = Date.now()
135
- const timeSinceLastFrame = now - lastFrameAt
136
- lastFrameAt = now
137
-
138
- if (strategy !== 'off') {
139
- // avoid updates if we've been dropping frames (indicates sync work happening)
140
- const expectedFrameTime = 16.67 // ~60fps
141
- const hasRecentSyncWork =
142
- timeSinceLastFrame > expectedFrameTime * numDroppedFramesUntilPause
143
-
144
- if (!hasRecentSyncWork) {
145
- Nodes.forEach(updateLayoutIfChanged)
146
- }
147
- }
148
- rAF!(layoutOnAnimationFrame)
149
- }
150
- } else {
151
- if (process.env.NODE_ENV === 'development') {
152
- console.warn(
153
- `No requestAnimationFrame - please polyfill for onLayout to work correctly`
154
- )
155
- }
156
- }
157
- }
158
-
159
- // Sync versions
160
- export const getElementLayoutEvent = (target: HTMLElement): LayoutEvent => {
161
- let res: LayoutEvent | null = null
162
- measureLayout(target, null, (x, y, width, height, left, top) => {
163
- res = {
164
- nativeEvent: {
165
- layout: { x, y, width, height, left, top },
166
- target,
167
- },
168
- timeStamp: Date.now(),
169
- }
170
- })
171
- if (!res) {
172
- throw new Error(`‼️`) // impossible
173
- }
174
- return res
175
- }
176
-
177
- export const measureLayout = (
178
- node: HTMLElement,
179
- relativeTo: HTMLElement | null,
180
- callback: (
181
- x: number,
182
- y: number,
183
- width: number,
184
- height: number,
185
- left: number,
186
- top: number
187
- ) => void
188
- ) => {
189
- const relativeNode = relativeTo || node?.parentElement
190
- if (relativeNode instanceof HTMLElement) {
191
- const nodeDim = node.getBoundingClientRect()
192
- const relativeNodeDim = relativeNode.getBoundingClientRect()
193
-
194
- if (relativeNodeDim && nodeDim) {
195
- const { x, y, width, height, left, top } = getRelativeDimensions(
196
- nodeDim,
197
- relativeNodeDim
198
- )
199
- callback(x, y, width, height, left, top)
200
- }
201
- }
202
- }
203
-
204
- export const getElementLayoutEventAsync = async (
205
- target: HTMLElement
206
- ): Promise<LayoutEvent> => {
207
- let res: LayoutEvent | null = null
208
- await measureLayoutAsync(target, null, (x, y, width, height, left, top) => {
209
- res = {
210
- nativeEvent: {
211
- layout: { x, y, width, height, left, top },
212
- target,
213
- },
214
- timeStamp: Date.now(),
215
- }
216
- })
217
- if (!res) {
218
- throw new Error(`‼️`) // impossible
219
- }
220
- return res
221
- }
222
-
223
- export const measureLayoutAsync = async (
224
- node: HTMLElement,
225
- relativeTo: HTMLElement | null,
226
- callback: (
227
- x: number,
228
- y: number,
229
- width: number,
230
- height: number,
231
- left: number,
232
- top: number
233
- ) => void
234
- ) => {
235
- const relativeNode = relativeTo || node?.parentElement
236
- if (relativeNode instanceof HTMLElement) {
237
- const [nodeDim, relativeNodeDim] = await Promise.all([
238
- node.getBoundingClientRect(),
239
- relativeNode.getBoundingClientRect(),
240
- ])
241
-
242
- if (relativeNodeDim && nodeDim) {
243
- const { x, y, width, height, left, top } = getRelativeDimensions(
244
- nodeDim,
245
- relativeNodeDim
246
- )
247
- callback(x, y, width, height, left, top)
248
- }
249
- }
250
- }
251
-
252
- const getRelativeDimensions = (a: DOMRectReadOnly, b: DOMRectReadOnly) => {
253
- const { height, left, top, width } = a
254
- const x = left - b.left
255
- const y = top - b.top
256
- return { x, y, width, height, left, top }
257
- }
258
-
259
- export function useElementLayout(
260
- ref: RefObject<TamaguiComponentStateRef>,
261
- onLayout?: ((e: LayoutEvent) => void) | null
262
- ) {
263
- // ensure always up to date so we can avoid re-running effect
264
- const node = ref.current?.host as HTMLElement
265
- if (node && onLayout) {
266
- LayoutHandlers.set(node, onLayout)
267
- }
268
-
269
- useIsomorphicLayoutEffect(() => {
270
- if (!onLayout) return
271
- const node = ref.current?.host as HTMLElement
272
- if (!node) return
273
-
274
- LayoutHandlers.set(node, onLayout)
275
- Nodes.add(node)
276
- onLayout(getElementLayoutEvent(node))
277
-
278
- return () => {
279
- Nodes.delete(node)
280
- LayoutHandlers.delete(node)
281
- NodeRectCache.delete(node)
282
-
283
- // Clean up debounce timer and tracking
284
- const timer = DebounceTimers.get(node)
285
- if (timer) {
286
- clearTimeout(timer)
287
- DebounceTimers.delete(node)
288
- }
289
- LastChangeTime.delete(node)
290
- }
291
- }, [ref, !!onLayout])
292
- }
@@ -1,3 +0,0 @@
1
- export declare const getBoundingClientRect: (node: HTMLElement | null) => undefined | DOMRect;
2
- export declare const getBoundingClientRectAsync: (element: HTMLElement) => Promise<DOMRectReadOnly | undefined>;
3
- //# sourceMappingURL=getBoundingClientRect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getBoundingClientRect.d.ts","sourceRoot":"","sources":["../../src/helpers/getBoundingClientRect.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,GAAI,MAAM,WAAW,GAAG,IAAI,KAAG,SAAS,GAAG,OAG5E,CAAA;AAED,eAAO,MAAM,0BAA0B,GACrC,SAAS,WAAW,KACnB,OAAO,CAAC,eAAe,GAAG,SAAS,CAsBrC,CAAA"}
@@ -1,9 +0,0 @@
1
- export declare const getRect: (node: HTMLElement) => {
2
- x: number;
3
- y: number;
4
- width: number;
5
- height: number;
6
- top: number;
7
- left: number;
8
- } | undefined;
9
- //# sourceMappingURL=getRect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getRect.d.ts","sourceRoot":"","sources":["../../src/helpers/getRect.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,GAAI,MAAM,WAAW;;;;;;;aAKxC,CAAA"}
@@ -1,26 +0,0 @@
1
- import { type TamaguiComponentStateRef } from '@tamagui/web';
2
- import type { RefObject } from 'react';
3
- type LayoutMeasurementStrategy = 'off' | 'sync' | 'async';
4
- export declare function setOnLayoutStrategy(state: LayoutMeasurementStrategy): void;
5
- export type LayoutValue = {
6
- x: number;
7
- y: number;
8
- width: number;
9
- height: number;
10
- left: number;
11
- top: number;
12
- };
13
- export type LayoutEvent = {
14
- nativeEvent: {
15
- layout: LayoutValue;
16
- target: any;
17
- };
18
- timeStamp: number;
19
- };
20
- export declare const getElementLayoutEvent: (target: HTMLElement) => LayoutEvent;
21
- export declare const measureLayout: (node: HTMLElement, relativeTo: HTMLElement | null, callback: (x: number, y: number, width: number, height: number, left: number, top: number) => void) => void;
22
- export declare const getElementLayoutEventAsync: (target: HTMLElement) => Promise<LayoutEvent>;
23
- export declare const measureLayoutAsync: (node: HTMLElement, relativeTo: HTMLElement | null, callback: (x: number, y: number, width: number, height: number, left: number, top: number) => void) => Promise<void>;
24
- export declare function useElementLayout(ref: RefObject<TamaguiComponentStateRef>, onLayout?: ((e: LayoutEvent) => void) | null): void;
25
- export {};
26
- //# sourceMappingURL=useElementLayout.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useElementLayout.d.ts","sourceRoot":"","sources":["../../src/hooks/useElementLayout.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,KAAK,yBAAyB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;AAIzD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,yBAAyB,QAEnE;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE;QACX,MAAM,EAAE,WAAW,CAAA;QACnB,MAAM,EAAE,GAAG,CAAA;KACZ,CAAA;IACD,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AA6HD,eAAO,MAAM,qBAAqB,GAAI,QAAQ,WAAW,KAAG,WAe3D,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,MAAM,WAAW,EACjB,YAAY,WAAW,GAAG,IAAI,EAC9B,UAAU,CACR,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,KACR,IAAI,SAeV,CAAA;AAED,eAAO,MAAM,0BAA0B,GACrC,QAAQ,WAAW,KAClB,OAAO,CAAC,WAAW,CAerB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,MAAM,WAAW,EACjB,YAAY,WAAW,GAAG,IAAI,EAC9B,UAAU,CACR,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,KACR,IAAI,kBAiBV,CAAA;AASD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,CAAC,wBAAwB,CAAC,EACxC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,IAAI,QA+B7C"}