@blario/react-native 0.1.2 → 0.1.4

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.
@@ -5,6 +5,7 @@ import { classify } from './classify.ts';
5
5
  import type { ElementKind } from './kinds.ts';
6
6
  import { isFocusedScreen, type ScreenRef, screenOf } from './navigation.ts';
7
7
  import { roleOf } from './properties.ts';
8
+ import { isWheel } from './scroll.ts';
8
9
 
9
10
  export const AGENT_OVERLAY_TEST_ID = 'blar-agent-overlay';
10
11
 
@@ -14,10 +15,12 @@ export interface Candidate {
14
15
  host: Fiber;
15
16
  ancestors: Fiber[];
16
17
  scrollContainers: Fiber[];
18
+ wheelOption?: boolean;
17
19
  }
18
20
 
19
21
  const MODAL_COMPONENT_NAME = 'Modal';
20
22
  const TEXT_HOST_TYPE = 'RCTText';
23
+ const WHEEL_OPTION_LIMIT = 24;
21
24
  const TEXT_BLOCK_LIMIT = 800;
22
25
  const HEADING_ROLES: readonly string[] = ['header', 'heading'];
23
26
 
@@ -75,6 +78,9 @@ export function collect(root: Fiber): Collected {
75
78
  const block = readTextBlock(fiber, ancestors, scrollContainers);
76
79
  if (block !== null) {
77
80
  textBlocks.push(block);
81
+ if (isWheelOption(block)) {
82
+ candidates.push({ kind: 'button', fiber, host: fiber, ancestors: block.ancestors, scrollContainers: block.scrollContainers, wheelOption: true });
83
+ }
78
84
  }
79
85
  ancestors.push(fiber);
80
86
  if (kind === 'scroll') {
@@ -118,6 +124,14 @@ function readTextBlock(fiber: Fiber, ancestors: Fiber[], scrollContainers: Fiber
118
124
  };
119
125
  }
120
126
 
127
+ function isWheelOption(block: TextBlock): boolean {
128
+ const wheel = block.scrollContainers[block.scrollContainers.length - 1];
129
+ if (wheel === undefined || !isWheel(wheel) || block.text.length > WHEEL_OPTION_LIMIT) {
130
+ return false;
131
+ }
132
+ return !block.ancestors.slice(block.ancestors.lastIndexOf(wheel)).some((ancestor) => classify(ancestor) === 'button');
133
+ }
134
+
121
135
  export function isOpenModal(fiber: Fiber): boolean {
122
136
  return componentName(fiber) === MODAL_COMPONENT_NAME && fiber.child !== null && propsOf(fiber).visible !== false;
123
137
  }
@@ -2,13 +2,9 @@ import { childrenOf, propsOf } from '../fiber/reader.ts';
2
2
  import type { Fiber } from '../fiber/types.ts';
3
3
  import { AGENT_OVERLAY_TEST_ID, isOpenModal } from './collect.ts';
4
4
 
5
- const NATIVE_SCREEN = 'RNSScreen';
6
-
7
- // A native-stack screen presented as a modal or sheet is a UIKit presentation too, which covers the agent layer.
8
- function isPresentedScreen(fiber: Fiber): boolean {
9
- const presentation = propsOf(fiber).stackPresentation;
10
- return fiber.type === NATIVE_SCREEN && typeof presentation === 'string' && presentation !== 'push';
11
- }
5
+ // react-native-screens renders an iOS screen presented as a modal or sheet as RNSModalScreen: a UIKit presentation
6
+ // too, which covers the agent layer.
7
+ const PRESENTED_SCREEN = 'RNSModalScreen';
12
8
 
13
9
  export function countOpenModals(fiber: Fiber): number {
14
10
  const pending = [fiber];
@@ -18,7 +14,7 @@ export function countOpenModals(fiber: Fiber): number {
18
14
  if (current === undefined || propsOf(current).testID === AGENT_OVERLAY_TEST_ID) {
19
15
  continue;
20
16
  }
21
- if (isOpenModal(current) || isPresentedScreen(current)) {
17
+ if (isOpenModal(current) || current.type === PRESENTED_SCREEN) {
22
18
  count += 1;
23
19
  }
24
20
  for (const child of childrenOf(current)) {
@@ -43,6 +43,12 @@ export function isHorizontal(fiber: Fiber): boolean {
43
43
  return propsOf(fiber).horizontal === true;
44
44
  }
45
45
 
46
+ // A wheel picker: a snapping list whose rows are plain text, where scrolling a row into the center is what selects it.
47
+ export function isWheel(fiber: Fiber): boolean {
48
+ const { snapToInterval, snapToOffsets } = propsOf(fiber);
49
+ return typeof snapToInterval === 'number' || Array.isArray(snapToOffsets);
50
+ }
51
+
46
52
  export async function measureScrollGeometry(scrollFiber: Fiber): Promise<ScrollGeometry | null> {
47
53
  const scrollHost = findScrollHost(scrollFiber);
48
54
  if (scrollHost === null) {
@@ -73,6 +79,12 @@ export function viewportExtentOf(geometry: ScrollGeometry): number {
73
79
  return geometry.horizontal ? geometry.viewport.width : geometry.viewport.height;
74
80
  }
75
81
 
82
+ export function offsetCentering(rect: Rect, geometry: ScrollGeometry): number {
83
+ const { viewport, horizontal } = geometry;
84
+ const delta = horizontal ? rect.x + rect.width / 2 - (viewport.x + viewport.width / 2) : rect.y + rect.height / 2 - (viewport.y + viewport.height / 2);
85
+ return Math.min(Math.max(scrollOffsetOf(geometry) + delta, 0), maxScrollOffsetOf(geometry));
86
+ }
87
+
76
88
  export function scrollDirectionsOf(geometry: ScrollGeometry): ScrollDirection[] {
77
89
  const offset = scrollOffsetOf(geometry);
78
90
  const canBackward = offset > SCROLL_TOLERANCE;
@@ -60,7 +60,8 @@ export function dropWrapperDuplicates(measured: MeasuredCandidate[]): MeasuredCa
60
60
  const OFFSCREEN_LIMIT = 30;
61
61
 
62
62
  export function limitOffscreen(measured: MeasuredCandidate[]): MeasuredCandidate[] {
63
- const hidden = measured.filter((entry) => entry.offscreen !== null && entry.candidate.kind !== 'scroll');
63
+ // Every option of a wheel stays listed: one dropped here could never be selected.
64
+ const hidden = measured.filter((entry) => entry.offscreen !== null && entry.candidate.kind !== 'scroll' && entry.candidate.wheelOption !== true);
64
65
  if (hidden.length <= OFFSCREEN_LIMIT) {
65
66
  return measured;
66
67
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '0.1.2';
1
+ export const SDK_VERSION = '0.1.4';