@rafaelaugusto/jolt 0.1.0-beta.16 → 0.1.0-beta.17

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 (84) hide show
  1. package/README.md +7 -2
  2. package/dist/bottom-sheet/geometry.d.ts +18 -0
  3. package/dist/bottom-sheet/geometry.d.ts.map +1 -0
  4. package/dist/bottom-sheet/geometry.js +51 -0
  5. package/dist/bottom-sheet/geometry.js.map +1 -0
  6. package/dist/bottom-sheet/index.d.ts +56 -0
  7. package/dist/bottom-sheet/index.d.ts.map +1 -0
  8. package/dist/bottom-sheet/index.js +462 -0
  9. package/dist/bottom-sheet/index.js.map +1 -0
  10. package/dist/bottom-sheet/native.d.ts +11 -0
  11. package/dist/bottom-sheet/native.d.ts.map +1 -0
  12. package/dist/bottom-sheet/native.js +173 -0
  13. package/dist/bottom-sheet/native.js.map +1 -0
  14. package/dist/gesture-native.d.ts +1 -0
  15. package/dist/gesture-native.d.ts.map +1 -1
  16. package/dist/gesture-native.js +6 -0
  17. package/dist/gesture-native.js.map +1 -1
  18. package/dist/gestures/index.d.ts +4 -0
  19. package/dist/gestures/index.d.ts.map +1 -1
  20. package/dist/gestures/index.js +12 -1
  21. package/dist/gestures/index.js.map +1 -1
  22. package/dist/native/index.d.ts +2 -0
  23. package/dist/native/index.d.ts.map +1 -1
  24. package/dist/native/index.js +1 -0
  25. package/dist/native/index.js.map +1 -1
  26. package/dist/native-property.d.ts +2 -0
  27. package/dist/native-property.d.ts.map +1 -1
  28. package/dist/native-property.js +12 -4
  29. package/dist/native-property.js.map +1 -1
  30. package/dist/reorder/handle.d.ts +6 -0
  31. package/dist/reorder/handle.d.ts.map +1 -0
  32. package/dist/reorder/handle.js +10 -0
  33. package/dist/reorder/handle.js.map +1 -0
  34. package/dist/reorder/index.d.ts +10 -0
  35. package/dist/reorder/index.d.ts.map +1 -0
  36. package/dist/reorder/index.js +233 -0
  37. package/dist/reorder/index.js.map +1 -0
  38. package/dist/reorder/list-view.d.ts +5 -0
  39. package/dist/reorder/list-view.d.ts.map +1 -0
  40. package/dist/reorder/list-view.js +238 -0
  41. package/dist/reorder/list-view.js.map +1 -0
  42. package/dist/reorder/model.d.ts +6 -0
  43. package/dist/reorder/model.d.ts.map +1 -0
  44. package/dist/reorder/model.js +30 -0
  45. package/dist/reorder/model.js.map +1 -0
  46. package/dist/reorder/surface.d.ts +6 -0
  47. package/dist/reorder/surface.d.ts.map +1 -0
  48. package/dist/reorder/surface.js +10 -0
  49. package/dist/reorder/surface.js.map +1 -0
  50. package/dist/reorder/types.d.ts +92 -0
  51. package/dist/reorder/types.d.ts.map +1 -0
  52. package/dist/reorder/types.js +2 -0
  53. package/dist/reorder/types.js.map +1 -0
  54. package/dist/scene-shader.d.ts +18 -0
  55. package/dist/scene-shader.d.ts.map +1 -0
  56. package/dist/scene-shader.js +76 -0
  57. package/dist/scene-shader.js.map +1 -0
  58. package/package.json +9 -1
  59. package/platforms/android/java/org/rafaelaugusto/jolt/GestureSession.java +7 -2
  60. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderHandle.java +25 -0
  61. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSession.java +213 -0
  62. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSurface.java +27 -0
  63. package/platforms/android/proguard-rules.pro +5 -0
  64. package/platforms/ios/src/JoltGestureSession.h +1 -0
  65. package/platforms/ios/src/JoltGestureSession.m +7 -2
  66. package/platforms/ios/src/JoltReorderSession.h +15 -0
  67. package/platforms/ios/src/JoltReorderSession.m +205 -0
  68. package/platforms/ios/src/module.modulemap +1 -0
  69. package/src/android-plugin.d.ts +34 -0
  70. package/src/bottom-sheet/geometry.ts +84 -0
  71. package/src/bottom-sheet/index.ts +536 -0
  72. package/src/bottom-sheet/native.ts +214 -0
  73. package/src/gesture-native.ts +15 -0
  74. package/src/gestures/index.ts +16 -1
  75. package/src/ios-plugin.d.ts +32 -0
  76. package/src/native/index.ts +6 -0
  77. package/src/native-property.ts +17 -4
  78. package/src/reorder/handle.ts +9 -0
  79. package/src/reorder/index.ts +243 -0
  80. package/src/reorder/list-view.ts +262 -0
  81. package/src/reorder/model.ts +47 -0
  82. package/src/reorder/surface.ts +10 -0
  83. package/src/reorder/types.ts +87 -0
  84. package/src/scene-shader.ts +139 -0
@@ -0,0 +1,536 @@
1
+ import {
2
+ Application,
3
+ GridLayout,
4
+ ScrollView,
5
+ Trace,
6
+ View,
7
+ isAndroid,
8
+ } from '@nativescript/core'
9
+ import { bind, type SpringValueOptions } from '../motion-value.js'
10
+ import { GestureSurface, gestures, type Gestures } from '../gestures/index.js'
11
+ import { currentKeyboard, onKeyboard } from '../keyboard.js'
12
+ import {
13
+ releaseStop,
14
+ sheetGeometry,
15
+ validateSnaps,
16
+ type SheetGeometry,
17
+ type SheetSnap,
18
+ } from './geometry.js'
19
+ import {
20
+ captureFocus,
21
+ dismissKeyboard,
22
+ revealFocusedField,
23
+ sheetActions,
24
+ windowHeight,
25
+ } from './native.js'
26
+
27
+ export { GestureSurface }
28
+ export type { SheetSnap }
29
+
30
+ export interface BottomSheetOptions {
31
+ /** A separate, focusable drag handle inside the panel; receives native accessibility actions. */
32
+ handle: View
33
+ snap: readonly SheetSnap[]
34
+ /** A descendant vertical ScrollView. Native list plugins need their own integration. */
35
+ scrollView?: ScrollView
36
+ /** Measured content for the content snap; defaults to scrollView.content. */
37
+ content?: View
38
+ /** A sibling behind the panel, filling the same one-cell GridLayout. */
39
+ backdrop?: View
40
+ backdropOpacity?: number
41
+ /** Follow temporarily expands above the keyboard, then restores the selected snap. */
42
+ keyboard?: 'follow' | 'none'
43
+ /** Whether dragging, the backdrop, back and accessibility can dismiss. Defaults to true. */
44
+ dismissible?: boolean
45
+ safeArea?: { top?: number; bottom?: number }
46
+ returnFocus?: View
47
+ spring?: Pick<SpringValueOptions, 'stiffness' | 'damping' | 'mass'>
48
+ accessibility?: { label?: string; expand?: string; collapse?: string; dismiss?: string }
49
+ onChange?: (point: SheetSnap | null) => void
50
+ /** Called after a close settles; interrupted closes do not dismiss. */
51
+ onDismiss?: () => void
52
+ }
53
+
54
+ export interface BottomSheet {
55
+ readonly isOpen: boolean
56
+ readonly snapPoint: SheetSnap | null
57
+ readonly visibleHeight: number
58
+ readonly paused: boolean
59
+ readonly disposed: boolean
60
+ open(point?: SheetSnap): void
61
+ snapTo(point: SheetSnap): void
62
+ expand(): void
63
+ collapse(): void
64
+ dismiss(): void
65
+ remeasure(): void
66
+ pause(): void
67
+ play(): void
68
+ dispose(): void
69
+ }
70
+
71
+ const owners = new WeakMap<GestureSurface, BottomSheet>()
72
+ const descendant = (child: View, parent: View) => {
73
+ for (let current = child.parent; current; current = current.parent)
74
+ if (current === parent) return true
75
+ return false
76
+ }
77
+ const report = (callback: (() => void) | undefined) => {
78
+ try {
79
+ callback?.()
80
+ } catch (error) {
81
+ Trace.write(
82
+ `Jolt bottom sheet callback failed: ${String(error)}`,
83
+ Trace.categories.Error,
84
+ Trace.messageType.error,
85
+ )
86
+ }
87
+ }
88
+
89
+ /** An in-page sheet, built from native gestures and live spring bindings. */
90
+ export function bottomSheet(
91
+ panel: GestureSurface,
92
+ options: BottomSheetOptions,
93
+ ): BottomSheet {
94
+ if (!(panel instanceof GestureSurface) || !(panel.parent instanceof GridLayout))
95
+ throw new TypeError(
96
+ 'bottomSheet() needs a GestureSurface inside an overlay GridLayout.',
97
+ )
98
+ if (!options || typeof options !== 'object')
99
+ throw new TypeError('Sheet options are required.')
100
+ validateSnaps(options.snap)
101
+ const points = [...options.snap],
102
+ host = panel.parent
103
+ if (!(options.handle instanceof View) || !descendant(options.handle, panel))
104
+ throw new TypeError('Provide a separate accessibility handle inside the sheet.')
105
+ const handle = options.handle,
106
+ scroll = options.scrollView
107
+ if (
108
+ scroll &&
109
+ (!(scroll instanceof ScrollView) ||
110
+ scroll.orientation !== 'vertical' ||
111
+ !descendant(scroll, panel))
112
+ )
113
+ throw new TypeError('Sheet scrolling needs a descendant vertical ScrollView.')
114
+ const content = options.content ?? scroll?.content
115
+ if (content && (!(content instanceof View) || !descendant(content, panel)))
116
+ throw new TypeError('Sheet content must be a descendant of its panel.')
117
+ if (points.includes('content') && !content)
118
+ throw new TypeError(
119
+ 'A content snap needs scrollView.content or an explicit content view.',
120
+ )
121
+ const backdrop = options.backdrop
122
+ if (
123
+ backdrop &&
124
+ (!(backdrop instanceof View) || backdrop.parent !== host || backdrop === panel)
125
+ )
126
+ throw new TypeError('The backdrop must be a sibling behind the sheet.')
127
+ const opacity = options.backdropOpacity ?? 0.45
128
+ if (!Number.isFinite(opacity) || opacity < 0 || opacity > 1)
129
+ throw new RangeError('Backdrop opacity must be between zero and one.')
130
+ if (options.keyboard !== undefined && !['follow', 'none'].includes(options.keyboard))
131
+ throw new TypeError('Sheet keyboard behavior must be follow or none.')
132
+ for (const inset of Object.values(options.safeArea ?? {}))
133
+ if (!Number.isFinite(inset) || inset < 0)
134
+ throw new RangeError('Safe-area insets must be finite and nonnegative.')
135
+ if (options.dismissible !== undefined && typeof options.dismissible !== 'boolean')
136
+ throw new TypeError('dismissible must be boolean.')
137
+ for (const callback of [options.onChange, options.onDismiss])
138
+ if (callback !== undefined && typeof callback !== 'function')
139
+ throw new TypeError('Sheet callbacks must be functions.')
140
+
141
+ owners.get(panel)?.dispose()
142
+ const original = {
143
+ contentAlignment: content?.verticalAlignment,
144
+ height: panel.height,
145
+ marginTop: panel.marginTop,
146
+ marginBottom: panel.marginBottom,
147
+ alignment: panel.verticalAlignment,
148
+ visibility: panel.visibility,
149
+ y: panel.translateY,
150
+ clipping: host.clipToBounds,
151
+ backdropOpacity: backdrop?.opacity,
152
+ backdropVisibility: backdrop?.visibility,
153
+ accessible: handle.accessible,
154
+ label: handle.accessibilityLabel,
155
+ value: handle.accessibilityValue,
156
+ }
157
+ let geometry: SheetGeometry | undefined, input: Gestures | undefined
158
+ let opened = false,
159
+ point = points[0]!,
160
+ ended = false,
161
+ held = false,
162
+ changing = false,
163
+ pendingDismiss = false,
164
+ contactSettled = false,
165
+ focusCaptured = false
166
+ let keyboard = currentKeyboard(),
167
+ timer: ReturnType<typeof setTimeout> | undefined
168
+ let releasePanel = () => {},
169
+ releaseBackdrop = () => {},
170
+ releaseActions = () => {},
171
+ releaseKeyboard = () => {}
172
+ let releaseClose = () => {},
173
+ restoreFocus = () => {}
174
+ const dismissible = options.dismissible !== false
175
+ const followKeyboard = options.keyboard !== 'none'
176
+ const keyboardOpen = () => followKeyboard && keyboard.visible
177
+ const targetY = () =>
178
+ !opened
179
+ ? geometry!.closedY
180
+ : keyboardOpen()
181
+ ? 0
182
+ : geometry!.stops.find((stop) => stop.point === point)!.y
183
+ const minimum = () =>
184
+ keyboardOpen() ? 0 : Math.min(...geometry!.stops.map((stop) => stop.y))
185
+
186
+ function finishClose() {
187
+ releaseClose()
188
+ releaseClose = () => {}
189
+ if (ended || opened) return
190
+ panel.visibility = 'hidden'
191
+ if (backdrop) backdrop.visibility = 'hidden'
192
+ input?.pause()
193
+ if (pendingDismiss) {
194
+ pendingDismiss = false
195
+ const restore = restoreFocus
196
+ restoreFocus = () => {}
197
+ focusCaptured = false
198
+ restore()
199
+ report(options.onDismiss)
200
+ }
201
+ }
202
+ function move() {
203
+ if (!input || !geometry || held || ended) return
204
+ changing = true
205
+ try {
206
+ input.updateBounds({
207
+ top: minimum(),
208
+ bottom:
209
+ !opened || dismissible
210
+ ? geometry.closedY
211
+ : Math.max(...geometry.stops.map((stop) => stop.y)),
212
+ })
213
+ input.settleTo({ y: targetY() })
214
+ } finally {
215
+ changing = false
216
+ }
217
+ if (!opened) {
218
+ releaseClose()
219
+ const check = () => {
220
+ if (
221
+ !opened &&
222
+ geometry &&
223
+ input &&
224
+ Math.abs(input.y.get() - geometry.closedY) < 0.01
225
+ )
226
+ finishClose()
227
+ }
228
+ releaseClose = input.y.subscribe(check, { immediate: false })
229
+ check()
230
+ }
231
+ }
232
+ function setTarget(next: SheetSnap | null) {
233
+ if (ended) return
234
+ if (next !== null && !points.includes(next))
235
+ throw new RangeError('Choose one of the configured sheet snap points.')
236
+ const wasOpen = opened,
237
+ changed = (next !== null) !== opened || (next !== null && next !== point)
238
+ releaseClose()
239
+ releaseClose = () => {}
240
+ if (next !== null) {
241
+ if (!opened && !pendingDismiss && panel.isLoaded) {
242
+ restoreFocus = captureFocus(panel, options.returnFocus)
243
+ focusCaptured = true
244
+ }
245
+ opened = true
246
+ pendingDismiss = false
247
+ point = next
248
+ panel.visibility = 'visible'
249
+ if (backdrop) backdrop.visibility = 'visible'
250
+ if (!held) input?.play()
251
+ } else {
252
+ opened = false
253
+ pendingDismiss ||= wasOpen
254
+ dismissKeyboard(panel)
255
+ }
256
+ handle.accessibilityValue = opened ? String(point) : 'Closed'
257
+ move()
258
+ if (changed) report(() => options.onChange?.(next))
259
+ }
260
+ function adjacent(expand: boolean) {
261
+ if (ended) return
262
+ if (!opened) {
263
+ if (expand) setTarget(point)
264
+ return
265
+ }
266
+ if (!geometry) return
267
+ const current = geometry.stops.find((stop) => stop.point === point)!
268
+ const candidates = geometry.stops
269
+ .filter((stop) =>
270
+ expand
271
+ ? stop.height > current.height + 0.01
272
+ : stop.height < current.height - 0.01,
273
+ )
274
+ .sort((a, b) => (expand ? a.height - b.height : b.height - a.height))
275
+ if (candidates[0]) setTarget(candidates[0].point)
276
+ else if (!expand && dismissible) setTarget(null)
277
+ }
278
+ function remeasure() {
279
+ if (ended || !panel.isLoaded || !handle.isLoaded || (scroll && !scroll.isLoaded))
280
+ return
281
+ const size = host.getActualSize()
282
+ if (!(size.height > 0)) return
283
+ const safe = host.getSafeAreaInsets()
284
+ const top = options.safeArea?.top ?? safe.top
285
+ const bottom = options.safeArea?.bottom ?? safe.bottom
286
+ const chrome = scroll
287
+ ? Math.max(0, panel.getActualSize().height - scroll.getActualSize().height)
288
+ : 0
289
+ const contentHeight = (content?.getActualSize().height ?? 0) + chrome
290
+ const next = sheetGeometry(
291
+ points,
292
+ size.height,
293
+ (host.getLocationInWindow()?.y ?? 0) + size.height,
294
+ windowHeight(host),
295
+ followKeyboard ? keyboard.height : 0,
296
+ top,
297
+ bottom,
298
+ contentHeight,
299
+ )
300
+ const previous = geometry
301
+ geometry = next
302
+ const panelHeight = keyboardOpen()
303
+ ? next.height
304
+ : Math.max(...next.stops.map((stop) => stop.height))
305
+ if (panel.height !== panelHeight) panel.height = panelHeight
306
+ if (panel.marginTop !== next.top) panel.marginTop = next.top
307
+ if (!input) {
308
+ input = gestures(panel, {
309
+ pan: { axis: 'y', bounds: { top: minimum(), bottom: next.closedY } },
310
+ initial: { y: next.closedY },
311
+ scrollView: scroll,
312
+ spring: options.spring,
313
+ onContactStart() {
314
+ const wasOpen = opened
315
+ contactSettled = false
316
+ releaseClose()
317
+ releaseClose = () => {}
318
+ opened = true
319
+ pendingDismiss = false
320
+ handle.accessibilityValue = String(point)
321
+ if (!wasOpen) report(() => options.onChange?.(point))
322
+ },
323
+ onContactEnd(event) {
324
+ if (contactSettled || changing || held || ended || !geometry) return
325
+ const stop = releaseStop(geometry, event.y, 0, dismissible)
326
+ setTarget(stop?.point ?? null)
327
+ },
328
+ onStart() {
329
+ releaseClose()
330
+ releaseClose = () => {}
331
+ opened = true
332
+ pendingDismiss = false
333
+ },
334
+ onEnd(event) {
335
+ contactSettled = true
336
+ if (!geometry) return
337
+ const stop = releaseStop(geometry, event.y, event.velocityY, dismissible)
338
+ setTarget(stop?.point ?? null)
339
+ },
340
+ onCancel() {
341
+ contactSettled = true
342
+ if (!changing && !held && !ended) move()
343
+ },
344
+ })
345
+ releasePanel = bind(panel, { y: input.y })
346
+ if (held || !opened) input.pause()
347
+ }
348
+ if (
349
+ !previous ||
350
+ previous.closedY !== next.closedY ||
351
+ previous.height !== next.height ||
352
+ previous.stops.some((stop, i) => stop.y !== next.stops[i]!.y)
353
+ ) {
354
+ releaseBackdrop()
355
+ releaseBackdrop = backdrop
356
+ ? bind(backdrop, { opacity: input.y.to([minimum(), next.closedY], [opacity, 0]) })
357
+ : () => {}
358
+ if (!input.active) move()
359
+ else
360
+ input.updateBounds({
361
+ top: minimum(),
362
+ bottom: dismissible
363
+ ? next.closedY
364
+ : Math.max(...next.stops.map((stop) => stop.y)),
365
+ })
366
+ }
367
+ if (opened && keyboardOpen()) revealFocusedField(panel, scroll)
368
+ }
369
+ function queueMeasure() {
370
+ if (ended || timer !== undefined) return
371
+ timer = setTimeout(() => {
372
+ timer = undefined
373
+ remeasure()
374
+ }, 0)
375
+ }
376
+ function back(event: { cancel: boolean }) {
377
+ if (opened && panel.isLoaded && dismissible) {
378
+ event.cancel = true
379
+ setTarget(null)
380
+ }
381
+ }
382
+ function backdropTap() {
383
+ if (opened && dismissible) setTarget(null)
384
+ }
385
+ function attach() {
386
+ if (ended || !panel.isLoaded || !handle.isLoaded || (scroll && !scroll.isLoaded))
387
+ return
388
+ if (opened) {
389
+ panel.visibility = 'visible'
390
+ if (backdrop) backdrop.visibility = 'visible'
391
+ }
392
+ if (opened && !focusCaptured) {
393
+ restoreFocus = captureFocus(panel, options.returnFocus)
394
+ focusCaptured = true
395
+ }
396
+ releaseActions()
397
+ const labels = options.accessibility
398
+ releaseActions = sheetActions(handle, [
399
+ { label: labels?.expand ?? 'Expand', run: () => adjacent(true) },
400
+ { label: labels?.collapse ?? 'Collapse', run: () => adjacent(false) },
401
+ ...(dismissible
402
+ ? [{ label: labels?.dismiss ?? 'Dismiss', run: () => setTarget(null) }]
403
+ : []),
404
+ ])
405
+ releaseKeyboard()
406
+ releaseKeyboard = onKeyboard((state) => {
407
+ keyboard = state
408
+ remeasure()
409
+ if (opened) move()
410
+ queueMeasure()
411
+ })
412
+ keyboard = currentKeyboard()
413
+ remeasure()
414
+ queueMeasure()
415
+ }
416
+ function detach() {
417
+ if (timer !== undefined) clearTimeout(timer)
418
+ timer = undefined
419
+ releaseClose()
420
+ releaseClose = () => {}
421
+ if (backdrop) backdrop.visibility = 'hidden'
422
+ releaseActions()
423
+ releaseActions = () => {}
424
+ releaseKeyboard()
425
+ releaseKeyboard = () => {}
426
+ releasePanel()
427
+ releasePanel = () => {}
428
+ releaseBackdrop()
429
+ releaseBackdrop = () => {}
430
+ changing = true
431
+ input?.dispose()
432
+ input = undefined
433
+ geometry = undefined
434
+ changing = false
435
+ }
436
+ const control: BottomSheet = {
437
+ get isOpen() {
438
+ return opened
439
+ },
440
+ get snapPoint() {
441
+ return opened ? point : null
442
+ },
443
+ get visibleHeight() {
444
+ return geometry && input
445
+ ? Math.max(0, Math.min(geometry.height, geometry.height - input.y.get()))
446
+ : 0
447
+ },
448
+ get paused() {
449
+ return held
450
+ },
451
+ get disposed() {
452
+ return ended
453
+ },
454
+ open: (next = point) => setTarget(next),
455
+ snapTo: (next) => setTarget(next),
456
+ expand: () => adjacent(true),
457
+ collapse: () => adjacent(false),
458
+ dismiss: () => setTarget(null),
459
+ remeasure,
460
+ pause() {
461
+ held = true
462
+ input?.pause()
463
+ },
464
+ play() {
465
+ if (ended) return
466
+ held = false
467
+ input?.play()
468
+ move()
469
+ },
470
+ dispose() {
471
+ if (ended) return
472
+ ended = true
473
+ detach()
474
+ for (const view of [panel, handle, ...(scroll ? [scroll] : [])])
475
+ view.off('loaded', attach)
476
+ panel.off('unloaded', detach)
477
+ for (const view of new Set([
478
+ host,
479
+ panel,
480
+ ...(content ? [content] : []),
481
+ ...(scroll ? [scroll] : []),
482
+ ]))
483
+ view.off('layoutChanged', queueMeasure)
484
+ backdrop?.off('tap', backdropTap)
485
+ if (isAndroid)
486
+ Application.android.off(Application.android.activityBackPressedEvent, back)
487
+ if (content) content.verticalAlignment = original.contentAlignment!
488
+ panel.height = original.height
489
+ panel.marginTop = original.marginTop
490
+ panel.marginBottom = original.marginBottom
491
+ panel.verticalAlignment = original.alignment
492
+ panel.visibility = original.visibility
493
+ panel.translateY = original.y
494
+ host.clipToBounds = original.clipping
495
+ handle.accessible = original.accessible
496
+ handle.accessibilityLabel = original.label
497
+ handle.accessibilityValue = original.value
498
+ if (backdrop) {
499
+ backdrop.opacity = original.backdropOpacity!
500
+ backdrop.visibility = original.backdropVisibility!
501
+ }
502
+ if (owners.get(panel) === control) owners.delete(panel)
503
+ },
504
+ }
505
+ owners.set(panel, control)
506
+ if (content && points.includes('content')) content.verticalAlignment = 'top'
507
+ panel.verticalAlignment = 'top'
508
+ panel.marginBottom = 0
509
+ panel.visibility = 'hidden'
510
+ host.clipToBounds = true
511
+ handle.accessible = true
512
+ handle.accessibilityLabel =
513
+ options.accessibility?.label ?? (handle.accessibilityLabel || 'Bottom sheet')
514
+ handle.accessibilityValue = 'Closed'
515
+ if (backdrop) backdrop.visibility = 'hidden'
516
+ for (const view of [panel, handle, ...(scroll ? [scroll] : [])])
517
+ view.on('loaded', attach)
518
+ panel.on('unloaded', detach)
519
+ for (const view of new Set([
520
+ host,
521
+ panel,
522
+ ...(content ? [content] : []),
523
+ ...(scroll ? [scroll] : []),
524
+ ]))
525
+ view.on('layoutChanged', queueMeasure)
526
+ backdrop?.on('tap', backdropTap)
527
+ if (isAndroid)
528
+ Application.android.on(Application.android.activityBackPressedEvent, back)
529
+ try {
530
+ attach()
531
+ } catch (error) {
532
+ control.dispose()
533
+ throw error
534
+ }
535
+ return control
536
+ }