@ankhorage/zora 25.8.2 → 25.8.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.
Files changed (24) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts.map +1 -1
  4. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js +9 -2
  5. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js.map +1 -1
  6. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts +1 -0
  7. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts.map +1 -1
  8. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js.map +1 -1
  9. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts +3 -2
  10. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map +1 -1
  11. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js +17 -2
  12. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map +1 -1
  13. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts +4 -1
  14. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts.map +1 -1
  15. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js +2 -1
  16. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js.map +1 -1
  17. package/package.json +4 -4
  18. package/src/features/grid-interactions/adapters/inbound/GridInteractions.test.tsx +146 -0
  19. package/src/features/grid-interactions/adapters/inbound/GridInteractions.tsx +9 -1
  20. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx +1 -0
  21. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx +18 -1
  22. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.test.ts +15 -0
  23. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts +6 -0
  24. package/web-dist/components/grid-interactions/GridInteractions.js +36 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 25.8.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 427eb62: Update dependencies: `@ankhorage/ankh`, `@ankhorage/devtools`.
8
+
9
+ ## 25.8.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 6194188: Propagate the held Space modifier to web grid drag interactions for panning.
14
+
3
15
  ## 25.8.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/zora
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v25.8.2](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v25.8.4](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
7
7
 
8
8
  Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractions.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,qCAAqC,CAAC;AAM7C,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,GAAG,KAAK,EACT,EAAE,qBAAqB,qBA2DvB"}
1
+ {"version":3,"file":"GridInteractions.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,qCAAqC,CAAC;AAM7C,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,GAAG,KAAK,EACT,EAAE,qBAAqB,qBAmEvB"}
@@ -9,30 +9,37 @@ export function GridInteractions({ children, interactionPolicy, resizeHandle, te
9
9
  const controllerProps = React.useMemo(() => ({ ...props, interactionPolicy }), [interactionPolicy, props]);
10
10
  const [controller] = React.useState(() => createGridInteractionsController(controllerProps));
11
11
  const surfaceRef = React.useRef(null);
12
+ const spaceKeyRef = React.useRef(false);
12
13
  const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());
13
14
  React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);
14
15
  React.useLayoutEffect(() => {
15
16
  pointerQueue.activate();
16
17
  return () => {
18
+ spaceKeyRef.current = false;
17
19
  pointerQueue.dispose();
18
20
  controller.cancel();
19
21
  };
20
22
  }, [controller, pointerQueue]);
23
+ const setSpaceKey = React.useCallback((spaceKey) => {
24
+ spaceKeyRef.current = spaceKey;
25
+ }, []);
21
26
  const withPointer = (event, handlePointer) => {
22
27
  const surface = surfaceRef.current;
23
28
  if (!surface)
24
29
  return;
25
30
  const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };
26
- pointerQueue.enqueue(surface, (origin) => handlePointer(resolveGridInteractionPointer(eventSnapshot, origin)));
31
+ const modifierSnapshot = { spaceKey: spaceKeyRef.current };
32
+ pointerQueue.enqueue(surface, (origin) => handlePointer(resolveGridInteractionPointer(eventSnapshot, origin, modifierSnapshot)));
27
33
  };
28
34
  const enabled = interactionPolicy !== 'passive';
29
35
  return (<View accessible accessibilityLabel="Grid interaction surface" accessibilityRole="adjustable" ref={surfaceRef} testID={testID} onResponderGrant={(event) => withPointer(event, (pointer) => controller.begin(pointer, resizeHandle))} onResponderMove={(event) => withPointer(event, controller.move)} onResponderRelease={(event) => withPointer(event, controller.end)} onResponderTerminate={() => {
36
+ setSpaceKey(false);
30
37
  pointerQueue.cancel();
31
38
  controller.cancel();
32
39
  }} onStartShouldSetResponder={() => enabled}>
33
40
  <GridInteractionsKeyboardProxy enabled={enabled} onKeyDown={(key, keyPointer) => {
34
41
  return controller.keyDown(key, keyPointer);
35
- }}>
42
+ }} onSpaceKeyChange={setSpaceKey}>
36
43
  {children}
37
44
  </GridInteractionsKeyboardProxy>
38
45
  </View>);
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractions.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractions.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { View } from 'react-native';\n\nimport type {\n GridInteractionsController,\n GridInteractionsProps,\n} from '../../../../types/grid-interactions';\nimport { createGridInteractionsController } from '../../application/createGridInteractionsController';\nimport { createGridInteractionsPointerQueue } from './createGridInteractionsPointerQueue';\nimport { GridInteractionsKeyboardProxy } from './GridInteractionsKeyboardProxy';\nimport { resolveGridInteractionPointer } from './resolveGridInteractionPointer';\n\n/*** Adapts native responder and keyboard events to the controlled grid interaction boundary. */\nexport function GridInteractions({\n children,\n interactionPolicy,\n resizeHandle,\n testID,\n ...props\n}: GridInteractionsProps) {\n const controllerProps = React.useMemo(\n () => ({ ...props, interactionPolicy }),\n [interactionPolicy, props],\n );\n const [controller] = React.useState<GridInteractionsController>(() =>\n createGridInteractionsController(controllerProps),\n );\n const surfaceRef = React.useRef<View>(null);\n const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());\n React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);\n React.useLayoutEffect(() => {\n pointerQueue.activate();\n return () => {\n pointerQueue.dispose();\n controller.cancel();\n };\n }, [controller, pointerQueue]);\n const withPointer = (\n event: Parameters<typeof resolveGridInteractionPointer>[0],\n handlePointer: (pointer: ReturnType<typeof resolveGridInteractionPointer>) => void,\n ) => {\n const surface = surfaceRef.current;\n if (!surface) return;\n const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };\n pointerQueue.enqueue(surface, (origin) =>\n handlePointer(resolveGridInteractionPointer(eventSnapshot, origin)),\n );\n };\n const enabled = interactionPolicy !== 'passive';\n\n return (\n <View\n accessible\n accessibilityLabel=\"Grid interaction surface\"\n accessibilityRole=\"adjustable\"\n ref={surfaceRef}\n testID={testID}\n onResponderGrant={(event) =>\n withPointer(event, (pointer) => controller.begin(pointer, resizeHandle))\n }\n onResponderMove={(event) => withPointer(event, controller.move)}\n onResponderRelease={(event) => withPointer(event, controller.end)}\n onResponderTerminate={() => {\n pointerQueue.cancel();\n controller.cancel();\n }}\n onStartShouldSetResponder={() => enabled}\n >\n <GridInteractionsKeyboardProxy\n enabled={enabled}\n onKeyDown={(key, keyPointer) => {\n return controller.keyDown(key, keyPointer);\n }}\n >\n {children}\n </GridInteractionsKeyboardProxy>\n </View>\n );\n}\n"]}
1
+ {"version":3,"file":"GridInteractions.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAMpC,OAAO,EAAE,gCAAgC,EAAE,MAAM,oDAAoD,CAAC;AACtG,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAEhF,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,EAC/B,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,GAAG,KAAK,EACc;IACtB,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CACnC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,iBAAiB,EAAE,CAAC,EACvC,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAC3B,CAAC;IACF,MAAM,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAA6B,GAAG,EAAE,CACnE,gCAAgC,CAAC,eAAe,CAAC,CAClD,CAAC;IACF,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAO,IAAI,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,kCAAkC,EAAE,CAAC,CAAC;IAClF,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;IAC/F,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE;QACzB,YAAY,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,GAAG,EAAE;YACV,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,YAAY,CAAC,OAAO,EAAE,CAAC;YACvB,UAAU,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,QAAiB,EAAE,EAAE;QAC1D,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,MAAM,WAAW,GAAG,CAClB,KAA0D,EAC1D,aAAkF,EAClF,EAAE;QACF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,aAAa,GAAG,EAAE,WAAW,EAAE,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAChE,MAAM,gBAAgB,GAAG,EAAE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;QAC3D,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CACvC,aAAa,CAAC,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CACtF,CAAC;IACJ,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,iBAAiB,KAAK,SAAS,CAAC;IAEhD,OAAO,CACL,CAAC,IAAI,CACH,UAAU,CACV,kBAAkB,CAAC,0BAA0B,CAC7C,iBAAiB,CAAC,YAAY,CAC9B,GAAG,CAAC,CAAC,UAAU,CAAC,CAChB,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,gBAAgB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1B,WAAW,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,YAAY,CAAC,CACzE,CAAC,CACD,eAAe,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAChE,kBAAkB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAClE,oBAAoB,CAAC,CAAC,GAAG,EAAE;YACzB,WAAW,CAAC,KAAK,CAAC,CAAC;YACnB,YAAY,CAAC,MAAM,EAAE,CAAC;YACtB,UAAU,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC,CAAC,CACF,yBAAyB,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAEzC;MAAA,CAAC,6BAA6B,CAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;YAC7B,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC,CAAC,CACF,gBAAgB,CAAC,CAAC,WAAW,CAAC,CAE9B;QAAA,CAAC,QAAQ,CACX;MAAA,EAAE,6BAA6B,CACjC;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\nimport { View } from 'react-native';\n\nimport type {\n GridInteractionsController,\n GridInteractionsProps,\n} from '../../../../types/grid-interactions';\nimport { createGridInteractionsController } from '../../application/createGridInteractionsController';\nimport { createGridInteractionsPointerQueue } from './createGridInteractionsPointerQueue';\nimport { GridInteractionsKeyboardProxy } from './GridInteractionsKeyboardProxy';\nimport { resolveGridInteractionPointer } from './resolveGridInteractionPointer';\n\n/*** Adapts native responder and keyboard events to the controlled grid interaction boundary. */\nexport function GridInteractions({\n children,\n interactionPolicy,\n resizeHandle,\n testID,\n ...props\n}: GridInteractionsProps) {\n const controllerProps = React.useMemo(\n () => ({ ...props, interactionPolicy }),\n [interactionPolicy, props],\n );\n const [controller] = React.useState<GridInteractionsController>(() =>\n createGridInteractionsController(controllerProps),\n );\n const surfaceRef = React.useRef<View>(null);\n const spaceKeyRef = React.useRef(false);\n const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());\n React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);\n React.useLayoutEffect(() => {\n pointerQueue.activate();\n return () => {\n spaceKeyRef.current = false;\n pointerQueue.dispose();\n controller.cancel();\n };\n }, [controller, pointerQueue]);\n const setSpaceKey = React.useCallback((spaceKey: boolean) => {\n spaceKeyRef.current = spaceKey;\n }, []);\n const withPointer = (\n event: Parameters<typeof resolveGridInteractionPointer>[0],\n handlePointer: (pointer: ReturnType<typeof resolveGridInteractionPointer>) => void,\n ) => {\n const surface = surfaceRef.current;\n if (!surface) return;\n const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };\n const modifierSnapshot = { spaceKey: spaceKeyRef.current };\n pointerQueue.enqueue(surface, (origin) =>\n handlePointer(resolveGridInteractionPointer(eventSnapshot, origin, modifierSnapshot)),\n );\n };\n const enabled = interactionPolicy !== 'passive';\n\n return (\n <View\n accessible\n accessibilityLabel=\"Grid interaction surface\"\n accessibilityRole=\"adjustable\"\n ref={surfaceRef}\n testID={testID}\n onResponderGrant={(event) =>\n withPointer(event, (pointer) => controller.begin(pointer, resizeHandle))\n }\n onResponderMove={(event) => withPointer(event, controller.move)}\n onResponderRelease={(event) => withPointer(event, controller.end)}\n onResponderTerminate={() => {\n setSpaceKey(false);\n pointerQueue.cancel();\n controller.cancel();\n }}\n onStartShouldSetResponder={() => enabled}\n >\n <GridInteractionsKeyboardProxy\n enabled={enabled}\n onKeyDown={(key, keyPointer) => {\n return controller.keyDown(key, keyPointer);\n }}\n onSpaceKeyChange={setSpaceKey}\n >\n {children}\n </GridInteractionsKeyboardProxy>\n </View>\n );\n}\n"]}
@@ -6,6 +6,7 @@ interface GridInteractionsKeyboardProxyProps {
6
6
  readonly children: React.ReactNode;
7
7
  readonly enabled: boolean;
8
8
  readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
9
+ readonly onSpaceKeyChange: (spaceKey: boolean) => void;
9
10
  }
10
11
  export {};
11
12
  //# sourceMappingURL=GridInteractionsKeyboardProxy.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractionsKeyboardProxy.native.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,kGAAkG;AAClG,wBAAgB,6BAA6B,CAAC,EAAE,QAAQ,EAAE,EAAE,kCAAkC,qBAE7F;AAED,UAAU,kCAAkC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC;CAC/E"}
1
+ {"version":3,"file":"GridInteractionsKeyboardProxy.native.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,kGAAkG;AAClG,wBAAgB,6BAA6B,CAAC,EAAE,QAAQ,EAAE,EAAE,kCAAkC,qBAE7F;AAED,UAAU,kCAAkC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC;IAC9E,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractionsKeyboardProxy.native.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx"],"names":[],"mappings":"AAIA,kGAAkG;AAClG,MAAM,UAAU,6BAA6B,CAAC,EAAE,QAAQ,EAAsC;IAC5F,OAAO,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;AACzB,CAAC","sourcesContent":["import type React from 'react';\n\nimport type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Leaves native accessibility focus untouched where browser keyboard events are unavailable. */\nexport function GridInteractionsKeyboardProxy({ children }: GridInteractionsKeyboardProxyProps) {\n return <>{children}</>;\n}\n\ninterface GridInteractionsKeyboardProxyProps {\n readonly children: React.ReactNode;\n readonly enabled: boolean;\n readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;\n}\n"]}
1
+ {"version":3,"file":"GridInteractionsKeyboardProxy.native.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx"],"names":[],"mappings":"AAIA,kGAAkG;AAClG,MAAM,UAAU,6BAA6B,CAAC,EAAE,QAAQ,EAAsC;IAC5F,OAAO,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;AACzB,CAAC","sourcesContent":["import type React from 'react';\n\nimport type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Leaves native accessibility focus untouched where browser keyboard events are unavailable. */\nexport function GridInteractionsKeyboardProxy({ children }: GridInteractionsKeyboardProxyProps) {\n return <>{children}</>;\n}\n\ninterface GridInteractionsKeyboardProxyProps {\n readonly children: React.ReactNode;\n readonly enabled: boolean;\n readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;\n readonly onSpaceKeyChange: (spaceKey: boolean) => void;\n}\n"]}
@@ -1,11 +1,12 @@
1
- import type React from 'react';
1
+ import React from 'react';
2
2
  import type { GridInteractionPointer } from '../../../../types/grid-interactions';
3
3
  /*** Adapts web keyboard movement and resize commands without changing native responder behavior. */
4
- export declare function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, }: GridInteractionsKeyboardProxyProps): React.JSX.Element;
4
+ export declare function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, onSpaceKeyChange, }: GridInteractionsKeyboardProxyProps): React.JSX.Element;
5
5
  interface GridInteractionsKeyboardProxyProps {
6
6
  readonly children: React.ReactNode;
7
7
  readonly enabled: boolean;
8
8
  readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
9
+ readonly onSpaceKeyChange: (spaceKey: boolean) => void;
9
10
  }
10
11
  export {};
11
12
  //# sourceMappingURL=GridInteractionsKeyboardProxy.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractionsKeyboardProxy.web.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,oGAAoG;AACpG,wBAAgB,6BAA6B,CAAC,EAC5C,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,kCAAkC,qBAyBpC;AAED,UAAU,kCAAkC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC;CAC/E"}
1
+ {"version":3,"file":"GridInteractionsKeyboardProxy.web.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,oGAAoG;AACpG,wBAAgB,6BAA6B,CAAC,EAC5C,QAAQ,EACR,OAAO,EACP,SAAS,EACT,gBAAgB,GACjB,EAAE,kCAAkC,qBAmCpC;AAED,UAAU,kCAAkC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC;IAC9E,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACxD"}
@@ -1,8 +1,16 @@
1
+ import React from 'react';
1
2
  /*** Adapts web keyboard movement and resize commands without changing native responder behavior. */
2
- export function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, }) {
3
- return (<div aria-label="Grid interaction keyboard controls" onKeyDown={(event) => {
3
+ export function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, onSpaceKeyChange, }) {
4
+ React.useEffect(() => {
5
+ if (!enabled)
6
+ onSpaceKeyChange(false);
7
+ }, [enabled, onSpaceKeyChange]);
8
+ React.useEffect(() => () => onSpaceKeyChange(false), [onSpaceKeyChange]);
9
+ return (<div aria-label="Grid interaction keyboard controls" onBlur={() => onSpaceKeyChange(false)} onKeyDown={(event) => {
4
10
  if (!enabled)
5
11
  return;
12
+ if (isSpaceKey(event))
13
+ onSpaceKeyChange(true);
6
14
  if (onKeyDown(event.key, {
7
15
  altKey: event.altKey,
8
16
  ctrlKey: event.ctrlKey,
@@ -13,8 +21,15 @@ export function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, })
13
21
  })) {
14
22
  event.preventDefault();
15
23
  }
24
+ }} onKeyUp={(event) => {
25
+ if (isSpaceKey(event))
26
+ onSpaceKeyChange(false);
16
27
  }} style={{ display: 'contents' }} tabIndex={enabled ? 0 : -1}>
17
28
  {children}
18
29
  </div>);
19
30
  }
31
+ /*** Determines whether a web keyboard event represents the Space modifier. */
32
+ function isSpaceKey(event) {
33
+ return event.code === 'Space' || event.key === ' ';
34
+ }
20
35
  //# sourceMappingURL=GridInteractionsKeyboardProxy.web.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GridInteractionsKeyboardProxy.web.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx"],"names":[],"mappings":"AAIA,oGAAoG;AACpG,MAAM,UAAU,6BAA6B,CAAC,EAC5C,QAAQ,EACR,OAAO,EACP,SAAS,GAC0B;IACnC,OAAO,CACL,CAAC,GAAG,CACF,UAAU,CAAC,oCAAoC,CAC/C,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,IACE,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE;gBACnB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,CAAC,EAAE,CAAC;gBACJ,CAAC,EAAE,CAAC;aACL,CAAC,EACF,CAAC;gBACD,KAAK,CAAC,cAAc,EAAE,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CACF,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAC/B,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAE3B;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC","sourcesContent":["import type React from 'react';\n\nimport type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Adapts web keyboard movement and resize commands without changing native responder behavior. */\nexport function GridInteractionsKeyboardProxy({\n children,\n enabled,\n onKeyDown,\n}: GridInteractionsKeyboardProxyProps) {\n return (\n <div\n aria-label=\"Grid interaction keyboard controls\"\n onKeyDown={(event) => {\n if (!enabled) return;\n if (\n onKeyDown(event.key, {\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n x: 0,\n y: 0,\n })\n ) {\n event.preventDefault();\n }\n }}\n style={{ display: 'contents' }}\n tabIndex={enabled ? 0 : -1}\n >\n {children}\n </div>\n );\n}\n\ninterface GridInteractionsKeyboardProxyProps {\n readonly children: React.ReactNode;\n readonly enabled: boolean;\n readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;\n}\n"]}
1
+ {"version":3,"file":"GridInteractionsKeyboardProxy.web.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,oGAAoG;AACpG,MAAM,UAAU,6BAA6B,CAAC,EAC5C,QAAQ,EACR,OAAO,EACP,SAAS,EACT,gBAAgB,GACmB;IACnC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,OAAO;YAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEzE,OAAO,CACL,CAAC,GAAG,CACF,UAAU,CAAC,oCAAoC,CAC/C,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CACtC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,IAAI,UAAU,CAAC,KAAK,CAAC;gBAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC9C,IACE,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE;gBACnB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,CAAC,EAAE,CAAC;gBACJ,CAAC,EAAE,CAAC;aACL,CAAC,EACF,CAAC;gBACD,KAAK,CAAC,cAAc,EAAE,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CACF,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,UAAU,CAAC,KAAK,CAAC;gBAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC,CAAC,CACF,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAC/B,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAE3B;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC;AASD,8EAA8E;AAC9E,SAAS,UAAU,CAAC,KAA0C;IAC5D,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC;AACrD,CAAC","sourcesContent":["import React from 'react';\n\nimport type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Adapts web keyboard movement and resize commands without changing native responder behavior. */\nexport function GridInteractionsKeyboardProxy({\n children,\n enabled,\n onKeyDown,\n onSpaceKeyChange,\n}: GridInteractionsKeyboardProxyProps) {\n React.useEffect(() => {\n if (!enabled) onSpaceKeyChange(false);\n }, [enabled, onSpaceKeyChange]);\n React.useEffect(() => () => onSpaceKeyChange(false), [onSpaceKeyChange]);\n\n return (\n <div\n aria-label=\"Grid interaction keyboard controls\"\n onBlur={() => onSpaceKeyChange(false)}\n onKeyDown={(event) => {\n if (!enabled) return;\n if (isSpaceKey(event)) onSpaceKeyChange(true);\n if (\n onKeyDown(event.key, {\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n x: 0,\n y: 0,\n })\n ) {\n event.preventDefault();\n }\n }}\n onKeyUp={(event) => {\n if (isSpaceKey(event)) onSpaceKeyChange(false);\n }}\n style={{ display: 'contents' }}\n tabIndex={enabled ? 0 : -1}\n >\n {children}\n </div>\n );\n}\n\ninterface GridInteractionsKeyboardProxyProps {\n readonly children: React.ReactNode;\n readonly enabled: boolean;\n readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;\n readonly onSpaceKeyChange: (spaceKey: boolean) => void;\n}\n\n/*** Determines whether a web keyboard event represents the Space modifier. */\nfunction isSpaceKey(event: React.KeyboardEvent<HTMLDivElement>): boolean {\n return event.code === 'Space' || event.key === ' ';\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { GridInteractionPointer } from '../../../../types/grid-interactions';
2
2
  /*** Converts a responder event into coordinates local to the interaction surface. */
3
- export declare function resolveGridInteractionPointer(event: GridInteractionNativePointerEvent, surfaceOrigin: GridInteractionSurfaceOrigin): GridInteractionPointer;
3
+ export declare function resolveGridInteractionPointer(event: GridInteractionNativePointerEvent, surfaceOrigin: GridInteractionSurfaceOrigin, modifierState?: GridInteractionModifierState): GridInteractionPointer;
4
4
  interface GridInteractionNativePointerEvent {
5
5
  readonly nativeEvent: {
6
6
  readonly altKey?: boolean;
@@ -17,5 +17,8 @@ interface GridInteractionSurfaceOrigin {
17
17
  readonly x: number;
18
18
  readonly y: number;
19
19
  }
20
+ interface GridInteractionModifierState {
21
+ readonly spaceKey?: boolean;
22
+ }
20
23
  export {};
21
24
  //# sourceMappingURL=resolveGridInteractionPointer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveGridInteractionPointer.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,qFAAqF;AACrF,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,iCAAiC,EACxC,aAAa,EAAE,4BAA4B,GAC1C,sBAAsB,CAoBxB;AAED,UAAU,iCAAiC;IACzC,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;CACH;AAED,UAAU,4BAA4B;IACpC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"resolveGridInteractionPointer.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF,qFAAqF;AACrF,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,iCAAiC,EACxC,aAAa,EAAE,4BAA4B,EAC3C,aAAa,GAAE,4BAAiC,GAC/C,sBAAsB,CAqBxB;AAED,UAAU,iCAAiC;IACzC,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;CACH;AAED,UAAU,4BAA4B;IACpC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,4BAA4B;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B"}
@@ -1,5 +1,5 @@
1
1
  /*** Converts a responder event into coordinates local to the interaction surface. */
2
- export function resolveGridInteractionPointer(event, surfaceOrigin) {
2
+ export function resolveGridInteractionPointer(event, surfaceOrigin, modifierState = {}) {
3
3
  const { nativeEvent } = event;
4
4
  const { pageX, pageY } = nativeEvent;
5
5
  const x = typeof pageX === 'number' && Number.isFinite(pageX)
@@ -13,6 +13,7 @@ export function resolveGridInteractionPointer(event, surfaceOrigin) {
13
13
  ctrlKey: nativeEvent.ctrlKey,
14
14
  metaKey: nativeEvent.metaKey,
15
15
  shiftKey: nativeEvent.shiftKey,
16
+ spaceKey: modifierState.spaceKey,
16
17
  x,
17
18
  y,
18
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"resolveGridInteractionPointer.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,MAAM,UAAU,6BAA6B,CAC3C,KAAwC,EACxC,aAA2C;IAE3C,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC9B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC;IACrC,MAAM,CAAC,GACL,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjD,CAAC,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAC5B,MAAM,CAAC,GACL,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjD,CAAC,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAE5B,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,CAAC;QACD,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Converts a responder event into coordinates local to the interaction surface. */\nexport function resolveGridInteractionPointer(\n event: GridInteractionNativePointerEvent,\n surfaceOrigin: GridInteractionSurfaceOrigin,\n): GridInteractionPointer {\n const { nativeEvent } = event;\n const { pageX, pageY } = nativeEvent;\n const x =\n typeof pageX === 'number' && Number.isFinite(pageX)\n ? pageX - surfaceOrigin.x\n : nativeEvent.locationX;\n const y =\n typeof pageY === 'number' && Number.isFinite(pageY)\n ? pageY - surfaceOrigin.y\n : nativeEvent.locationY;\n\n return {\n altKey: nativeEvent.altKey,\n ctrlKey: nativeEvent.ctrlKey,\n metaKey: nativeEvent.metaKey,\n shiftKey: nativeEvent.shiftKey,\n x,\n y,\n };\n}\n\ninterface GridInteractionNativePointerEvent {\n readonly nativeEvent: {\n readonly altKey?: boolean;\n readonly ctrlKey?: boolean;\n readonly locationX: number;\n readonly locationY: number;\n readonly metaKey?: boolean;\n readonly pageX?: number;\n readonly pageY?: number;\n readonly shiftKey?: boolean;\n };\n}\n\ninterface GridInteractionSurfaceOrigin {\n readonly x: number;\n readonly y: number;\n}\n"]}
1
+ {"version":3,"file":"resolveGridInteractionPointer.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,MAAM,UAAU,6BAA6B,CAC3C,KAAwC,EACxC,aAA2C,EAC3C,gBAA8C,EAAE;IAEhD,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC9B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC;IACrC,MAAM,CAAC,GACL,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjD,CAAC,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAC5B,MAAM,CAAC,GACL,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjD,CAAC,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAE5B,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,QAAQ,EAAE,aAAa,CAAC,QAAQ;QAChC,CAAC;QACD,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { GridInteractionPointer } from '../../../../types/grid-interactions';\n\n/*** Converts a responder event into coordinates local to the interaction surface. */\nexport function resolveGridInteractionPointer(\n event: GridInteractionNativePointerEvent,\n surfaceOrigin: GridInteractionSurfaceOrigin,\n modifierState: GridInteractionModifierState = {},\n): GridInteractionPointer {\n const { nativeEvent } = event;\n const { pageX, pageY } = nativeEvent;\n const x =\n typeof pageX === 'number' && Number.isFinite(pageX)\n ? pageX - surfaceOrigin.x\n : nativeEvent.locationX;\n const y =\n typeof pageY === 'number' && Number.isFinite(pageY)\n ? pageY - surfaceOrigin.y\n : nativeEvent.locationY;\n\n return {\n altKey: nativeEvent.altKey,\n ctrlKey: nativeEvent.ctrlKey,\n metaKey: nativeEvent.metaKey,\n shiftKey: nativeEvent.shiftKey,\n spaceKey: modifierState.spaceKey,\n x,\n y,\n };\n}\n\ninterface GridInteractionNativePointerEvent {\n readonly nativeEvent: {\n readonly altKey?: boolean;\n readonly ctrlKey?: boolean;\n readonly locationX: number;\n readonly locationY: number;\n readonly metaKey?: boolean;\n readonly pageX?: number;\n readonly pageY?: number;\n readonly shiftKey?: boolean;\n };\n}\n\ninterface GridInteractionSurfaceOrigin {\n readonly x: number;\n readonly y: number;\n}\n\ninterface GridInteractionModifierState {\n readonly spaceKey?: boolean;\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "25.8.2",
4
+ "version": "25.8.4",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -41,7 +41,7 @@
41
41
  "react-native-worklets": "0.10.1"
42
42
  },
43
43
  "dependencies": {
44
- "@ankhorage/ankh": "^0.13.0",
44
+ "@ankhorage/ankh": "^0.13.1",
45
45
  "@ankhorage/capability": "^0.2.1",
46
46
  "@ankhorage/color-theory": "^0.3.1",
47
47
  "@ankhorage/contracts": "^25.2.0",
@@ -129,12 +129,12 @@
129
129
  "format": "ankhorage-prettier --write .",
130
130
  "format:check": "ankhorage-prettier --check .",
131
131
  "prepack": "bun run build",
132
- "test": "bun run build && bun test src examples/grid-workspaces/time",
132
+ "test": "bun run build && bun test src examples/grid-workspaces/time --isolate",
133
133
  "typecheck": "bun x tsc --noEmit -p tsconfig.json && bun x tsc --noEmit -p tsconfig.scripts.json",
134
134
  "version-packages": "ankhorage-changeset version"
135
135
  },
136
136
  "devDependencies": {
137
- "@ankhorage/devtools": "^2.3.13",
137
+ "@ankhorage/devtools": "^2.3.18",
138
138
  "@ankhorage/paradox": "^0.3.3",
139
139
  "@react-native-vector-icons/fontawesome": "^13.1.4",
140
140
  "@react-native-vector-icons/fontawesome5": "^13.1.4",
@@ -6,7 +6,9 @@ import { Window } from 'happy-dom';
6
6
  import React, { act } from 'react';
7
7
  import { createRoot } from 'react-dom/client';
8
8
 
9
+ import type { GridInteractionIntent } from '../../../../types/grid-interactions';
9
10
  import type { GridInteractions as GridInteractionsComponent } from './GridInteractions';
11
+ import { GridInteractionsKeyboardProxy } from './GridInteractionsKeyboardProxy.web';
10
12
 
11
13
  const webDistRoot = join(import.meta.dir, '../../../../../web-dist');
12
14
 
@@ -103,6 +105,110 @@ test('keeps a mounted RNW keyboard boundary current across parent rerenders', as
103
105
  }
104
106
  });
105
107
 
108
+ test('bridges a held Space key into web responder pan and clears it for the following marquee drag', async () => {
109
+ const browser = new Window();
110
+ const restore = installBrowserGlobals(browser);
111
+ const host = document.createElement('div');
112
+ document.body.append(host);
113
+ const root = createRoot(host);
114
+ const { GridInteractions } = await loadGridInteractions();
115
+ const intents: GridInteractionIntent[] = [];
116
+
117
+ try {
118
+ await renderGridInteractions(
119
+ root,
120
+ <GridInteractions
121
+ items={[]}
122
+ onIntent={(intent) => intents.push(intent)}
123
+ testID="grid-interactions"
124
+ viewport={{
125
+ height: 100,
126
+ offsetX: 0,
127
+ offsetY: 0,
128
+ pixelsPerUnitX: 2,
129
+ pixelsPerUnitY: 4,
130
+ width: 100,
131
+ }}
132
+ >
133
+ <span>nested target</span>
134
+ </GridInteractions>,
135
+ );
136
+ const keyboard = host.querySelector<HTMLElement>(
137
+ '[aria-label="Grid interaction keyboard controls"]',
138
+ );
139
+ const surface = host.querySelector<HTMLElement>('[data-testid="grid-interactions"]');
140
+ if (!keyboard || !surface) throw new Error('Missing web grid interaction boundary');
141
+
142
+ await dispatch(
143
+ keyboard,
144
+ new browser.KeyboardEvent('keydown', { bubbles: true, code: 'Space', key: ' ' }),
145
+ );
146
+ await drag(browser, surface, { x: 10, y: 20 }, { x: 30, y: 60 });
147
+ await dispatch(
148
+ keyboard,
149
+ new browser.KeyboardEvent('keyup', { bubbles: true, code: 'Space', key: ' ' }),
150
+ );
151
+ await drag(browser, surface, { x: 10, y: 20 }, { x: 30, y: 60 });
152
+ expect(intents.map((intent) => intent.type)).toEqual(['pan', 'marquee']);
153
+ } finally {
154
+ await act(() => Promise.resolve().then(() => root.unmount()));
155
+ host.remove();
156
+ browser.close();
157
+ restore();
158
+ }
159
+ });
160
+
161
+ test('clears the web Space modifier on blur, disable, and unmount', async () => {
162
+ const browser = new Window();
163
+ const restore = installBrowserGlobals(browser);
164
+ const host = document.createElement('div');
165
+ document.body.append(host);
166
+ const root = createRoot(host);
167
+ const spaceKeyChanges: boolean[] = [];
168
+ const onSpaceKeyChange = (spaceKey: boolean) => spaceKeyChanges.push(spaceKey);
169
+
170
+ try {
171
+ await renderGridInteractions(
172
+ root,
173
+ <GridInteractionsKeyboardProxy
174
+ enabled
175
+ onKeyDown={() => false}
176
+ onSpaceKeyChange={onSpaceKeyChange}
177
+ >
178
+ <span>nested target</span>
179
+ </GridInteractionsKeyboardProxy>,
180
+ );
181
+ const keyboard = host.querySelector<HTMLElement>(
182
+ '[aria-label="Grid interaction keyboard controls"]',
183
+ );
184
+ if (!keyboard) throw new Error('Missing keyboard interaction proxy');
185
+
186
+ await dispatch(
187
+ keyboard,
188
+ new browser.KeyboardEvent('keydown', { bubbles: true, code: 'Space', key: ' ' }),
189
+ );
190
+ await dispatch(keyboard, new browser.FocusEvent('focusout', { bubbles: true }));
191
+ await renderGridInteractions(
192
+ root,
193
+ <GridInteractionsKeyboardProxy
194
+ enabled={false}
195
+ onKeyDown={() => false}
196
+ onSpaceKeyChange={onSpaceKeyChange}
197
+ >
198
+ <span>nested target</span>
199
+ </GridInteractionsKeyboardProxy>,
200
+ );
201
+
202
+ expect(spaceKeyChanges).toEqual([true, false, false]);
203
+ } finally {
204
+ await act(() => Promise.resolve().then(() => root.unmount()));
205
+ expect(spaceKeyChanges.at(-1)).toBe(false);
206
+ host.remove();
207
+ browser.close();
208
+ restore();
209
+ }
210
+ });
211
+
106
212
  /*** Installs the DOM globals that the independently bundled RNW adapter requires. */
107
213
  function installBrowserGlobals(browser: Window): () => void {
108
214
  const keys = [
@@ -141,3 +247,43 @@ function renderGridInteractions(root: ReturnType<typeof createRoot>, component:
141
247
  function dispatch(target: HTMLElement, event: Event) {
142
248
  return act(() => Promise.resolve().then(() => target.dispatchEvent(event)));
143
249
  }
250
+
251
+ /*** Runs one browser touch drag through the RNW responder boundary. */
252
+ async function drag(
253
+ browser: Window,
254
+ target: HTMLElement,
255
+ start: { readonly x: number; readonly y: number },
256
+ end: { readonly x: number; readonly y: number },
257
+ ) {
258
+ await dispatch(target, createTouchEvent(browser, 'touchstart', target, start, [start]));
259
+ await dispatch(target, createTouchEvent(browser, 'touchmove', target, end, [end]));
260
+ await dispatch(target, createTouchEvent(browser, 'touchend', target, end, []));
261
+ }
262
+
263
+ /*** Creates a browser touch event consumed by the RNW responder implementation. */
264
+ function createTouchEvent(
265
+ browser: Window,
266
+ type: 'touchend' | 'touchmove' | 'touchstart',
267
+ target: HTMLElement,
268
+ changed: { readonly x: number; readonly y: number },
269
+ touches: readonly { readonly x: number; readonly y: number }[],
270
+ ) {
271
+ const event = new browser.Event(type, { bubbles: true });
272
+ Object.defineProperties(event, {
273
+ changedTouches: { value: [createTouch(target, changed)] },
274
+ touches: { value: touches.map((touch) => createTouch(target, touch)) },
275
+ });
276
+ return event;
277
+ }
278
+
279
+ /*** Creates one RNW-compatible touch point with page and client coordinates. */
280
+ function createTouch(target: HTMLElement, point: { readonly x: number; readonly y: number }) {
281
+ return {
282
+ clientX: point.x,
283
+ clientY: point.y,
284
+ identifier: 1,
285
+ pageX: point.x,
286
+ pageY: point.y,
287
+ target,
288
+ };
289
+ }
@@ -26,15 +26,20 @@ export function GridInteractions({
26
26
  createGridInteractionsController(controllerProps),
27
27
  );
28
28
  const surfaceRef = React.useRef<View>(null);
29
+ const spaceKeyRef = React.useRef(false);
29
30
  const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());
30
31
  React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);
31
32
  React.useLayoutEffect(() => {
32
33
  pointerQueue.activate();
33
34
  return () => {
35
+ spaceKeyRef.current = false;
34
36
  pointerQueue.dispose();
35
37
  controller.cancel();
36
38
  };
37
39
  }, [controller, pointerQueue]);
40
+ const setSpaceKey = React.useCallback((spaceKey: boolean) => {
41
+ spaceKeyRef.current = spaceKey;
42
+ }, []);
38
43
  const withPointer = (
39
44
  event: Parameters<typeof resolveGridInteractionPointer>[0],
40
45
  handlePointer: (pointer: ReturnType<typeof resolveGridInteractionPointer>) => void,
@@ -42,8 +47,9 @@ export function GridInteractions({
42
47
  const surface = surfaceRef.current;
43
48
  if (!surface) return;
44
49
  const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };
50
+ const modifierSnapshot = { spaceKey: spaceKeyRef.current };
45
51
  pointerQueue.enqueue(surface, (origin) =>
46
- handlePointer(resolveGridInteractionPointer(eventSnapshot, origin)),
52
+ handlePointer(resolveGridInteractionPointer(eventSnapshot, origin, modifierSnapshot)),
47
53
  );
48
54
  };
49
55
  const enabled = interactionPolicy !== 'passive';
@@ -61,6 +67,7 @@ export function GridInteractions({
61
67
  onResponderMove={(event) => withPointer(event, controller.move)}
62
68
  onResponderRelease={(event) => withPointer(event, controller.end)}
63
69
  onResponderTerminate={() => {
70
+ setSpaceKey(false);
64
71
  pointerQueue.cancel();
65
72
  controller.cancel();
66
73
  }}
@@ -71,6 +78,7 @@ export function GridInteractions({
71
78
  onKeyDown={(key, keyPointer) => {
72
79
  return controller.keyDown(key, keyPointer);
73
80
  }}
81
+ onSpaceKeyChange={setSpaceKey}
74
82
  >
75
83
  {children}
76
84
  </GridInteractionsKeyboardProxy>
@@ -11,4 +11,5 @@ interface GridInteractionsKeyboardProxyProps {
11
11
  readonly children: React.ReactNode;
12
12
  readonly enabled: boolean;
13
13
  readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
14
+ readonly onSpaceKeyChange: (spaceKey: boolean) => void;
14
15
  }
@@ -1,4 +1,4 @@
1
- import type React from 'react';
1
+ import React from 'react';
2
2
 
3
3
  import type { GridInteractionPointer } from '../../../../types/grid-interactions';
4
4
 
@@ -7,12 +7,20 @@ export function GridInteractionsKeyboardProxy({
7
7
  children,
8
8
  enabled,
9
9
  onKeyDown,
10
+ onSpaceKeyChange,
10
11
  }: GridInteractionsKeyboardProxyProps) {
12
+ React.useEffect(() => {
13
+ if (!enabled) onSpaceKeyChange(false);
14
+ }, [enabled, onSpaceKeyChange]);
15
+ React.useEffect(() => () => onSpaceKeyChange(false), [onSpaceKeyChange]);
16
+
11
17
  return (
12
18
  <div
13
19
  aria-label="Grid interaction keyboard controls"
20
+ onBlur={() => onSpaceKeyChange(false)}
14
21
  onKeyDown={(event) => {
15
22
  if (!enabled) return;
23
+ if (isSpaceKey(event)) onSpaceKeyChange(true);
16
24
  if (
17
25
  onKeyDown(event.key, {
18
26
  altKey: event.altKey,
@@ -26,6 +34,9 @@ export function GridInteractionsKeyboardProxy({
26
34
  event.preventDefault();
27
35
  }
28
36
  }}
37
+ onKeyUp={(event) => {
38
+ if (isSpaceKey(event)) onSpaceKeyChange(false);
39
+ }}
29
40
  style={{ display: 'contents' }}
30
41
  tabIndex={enabled ? 0 : -1}
31
42
  >
@@ -38,4 +49,10 @@ interface GridInteractionsKeyboardProxyProps {
38
49
  readonly children: React.ReactNode;
39
50
  readonly enabled: boolean;
40
51
  readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
52
+ readonly onSpaceKeyChange: (spaceKey: boolean) => void;
53
+ }
54
+
55
+ /*** Determines whether a web keyboard event represents the Space modifier. */
56
+ function isSpaceKey(event: React.KeyboardEvent<HTMLDivElement>): boolean {
57
+ return event.code === 'Space' || event.key === ' ';
41
58
  }
@@ -31,3 +31,18 @@ test('uses responder-local coordinates when page coordinates are unavailable on
31
31
  ),
32
32
  ).toEqual({ x: 4, y: 6 });
33
33
  });
34
+
35
+ test('carries a scoped web Space modifier snapshot without changing native event input', () => {
36
+ expect(
37
+ resolveGridInteractionPointer(
38
+ {
39
+ nativeEvent: {
40
+ locationX: 4,
41
+ locationY: 6,
42
+ },
43
+ },
44
+ { x: 120, y: 80 },
45
+ { spaceKey: true },
46
+ ),
47
+ ).toEqual({ spaceKey: true, x: 4, y: 6 });
48
+ });
@@ -4,6 +4,7 @@ import type { GridInteractionPointer } from '../../../../types/grid-interactions
4
4
  export function resolveGridInteractionPointer(
5
5
  event: GridInteractionNativePointerEvent,
6
6
  surfaceOrigin: GridInteractionSurfaceOrigin,
7
+ modifierState: GridInteractionModifierState = {},
7
8
  ): GridInteractionPointer {
8
9
  const { nativeEvent } = event;
9
10
  const { pageX, pageY } = nativeEvent;
@@ -21,6 +22,7 @@ export function resolveGridInteractionPointer(
21
22
  ctrlKey: nativeEvent.ctrlKey,
22
23
  metaKey: nativeEvent.metaKey,
23
24
  shiftKey: nativeEvent.shiftKey,
25
+ spaceKey: modifierState.spaceKey,
24
26
  x,
25
27
  y,
26
28
  };
@@ -43,3 +45,7 @@ interface GridInteractionSurfaceOrigin {
43
45
  readonly x: number;
44
46
  readonly y: number;
45
47
  }
48
+
49
+ interface GridInteractionModifierState {
50
+ readonly spaceKey?: boolean;
51
+ }
@@ -17,7 +17,7 @@ import {
17
17
 
18
18
  // src/features/grid-interactions/adapters/inbound/GridInteractions.tsx
19
19
  var import_react_native = __toESM(require_cjs(), 1);
20
- import React from "react";
20
+ import React2 from "react";
21
21
 
22
22
  // src/features/grid-interactions/application/createGridInteractionsController.ts
23
23
  function createGridInteractionsController(props) {
@@ -278,17 +278,27 @@ function measureSurfaceOrigin(surface, onOrigin) {
278
278
  }
279
279
 
280
280
  // src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx
281
+ import React from "react";
281
282
  import { jsx } from "react/jsx-runtime";
282
283
  function GridInteractionsKeyboardProxy({
283
284
  children,
284
285
  enabled,
285
- onKeyDown
286
+ onKeyDown,
287
+ onSpaceKeyChange
286
288
  }) {
289
+ React.useEffect(() => {
290
+ if (!enabled)
291
+ onSpaceKeyChange(false);
292
+ }, [enabled, onSpaceKeyChange]);
293
+ React.useEffect(() => () => onSpaceKeyChange(false), [onSpaceKeyChange]);
287
294
  return /* @__PURE__ */ jsx("div", {
288
295
  "aria-label": "Grid interaction keyboard controls",
296
+ onBlur: () => onSpaceKeyChange(false),
289
297
  onKeyDown: (event) => {
290
298
  if (!enabled)
291
299
  return;
300
+ if (isSpaceKey(event))
301
+ onSpaceKeyChange(true);
292
302
  if (onKeyDown(event.key, {
293
303
  altKey: event.altKey,
294
304
  ctrlKey: event.ctrlKey,
@@ -300,14 +310,21 @@ function GridInteractionsKeyboardProxy({
300
310
  event.preventDefault();
301
311
  }
302
312
  },
313
+ onKeyUp: (event) => {
314
+ if (isSpaceKey(event))
315
+ onSpaceKeyChange(false);
316
+ },
303
317
  style: { display: "contents" },
304
318
  tabIndex: enabled ? 0 : -1,
305
319
  children
306
320
  });
307
321
  }
322
+ function isSpaceKey(event) {
323
+ return event.code === "Space" || event.key === " ";
324
+ }
308
325
 
309
326
  // src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts
310
- function resolveGridInteractionPointer(event, surfaceOrigin) {
327
+ function resolveGridInteractionPointer(event, surfaceOrigin, modifierState = {}) {
311
328
  const { nativeEvent } = event;
312
329
  const { pageX, pageY } = nativeEvent;
313
330
  const x = typeof pageX === "number" && Number.isFinite(pageX) ? pageX - surfaceOrigin.x : nativeEvent.locationX;
@@ -317,6 +334,7 @@ function resolveGridInteractionPointer(event, surfaceOrigin) {
317
334
  ctrlKey: nativeEvent.ctrlKey,
318
335
  metaKey: nativeEvent.metaKey,
319
336
  shiftKey: nativeEvent.shiftKey,
337
+ spaceKey: modifierState.spaceKey,
320
338
  x,
321
339
  y
322
340
  };
@@ -331,24 +349,30 @@ function GridInteractions({
331
349
  testID,
332
350
  ...props
333
351
  }) {
334
- const controllerProps = React.useMemo(() => ({ ...props, interactionPolicy }), [interactionPolicy, props]);
335
- const [controller] = React.useState(() => createGridInteractionsController(controllerProps));
336
- const surfaceRef = React.useRef(null);
337
- const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());
338
- React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);
339
- React.useLayoutEffect(() => {
352
+ const controllerProps = React2.useMemo(() => ({ ...props, interactionPolicy }), [interactionPolicy, props]);
353
+ const [controller] = React2.useState(() => createGridInteractionsController(controllerProps));
354
+ const surfaceRef = React2.useRef(null);
355
+ const spaceKeyRef = React2.useRef(false);
356
+ const [pointerQueue] = React2.useState(() => createGridInteractionsPointerQueue());
357
+ React2.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);
358
+ React2.useLayoutEffect(() => {
340
359
  pointerQueue.activate();
341
360
  return () => {
361
+ spaceKeyRef.current = false;
342
362
  pointerQueue.dispose();
343
363
  controller.cancel();
344
364
  };
345
365
  }, [controller, pointerQueue]);
366
+ const setSpaceKey = React2.useCallback((spaceKey) => {
367
+ spaceKeyRef.current = spaceKey;
368
+ }, []);
346
369
  const withPointer = (event, handlePointer) => {
347
370
  const surface = surfaceRef.current;
348
371
  if (!surface)
349
372
  return;
350
373
  const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };
351
- pointerQueue.enqueue(surface, (origin) => handlePointer(resolveGridInteractionPointer(eventSnapshot, origin)));
374
+ const modifierSnapshot = { spaceKey: spaceKeyRef.current };
375
+ pointerQueue.enqueue(surface, (origin) => handlePointer(resolveGridInteractionPointer(eventSnapshot, origin, modifierSnapshot)));
352
376
  };
353
377
  const enabled = interactionPolicy !== "passive";
354
378
  return /* @__PURE__ */ jsx2(import_react_native.View, {
@@ -361,6 +385,7 @@ function GridInteractions({
361
385
  onResponderMove: (event) => withPointer(event, controller.move),
362
386
  onResponderRelease: (event) => withPointer(event, controller.end),
363
387
  onResponderTerminate: () => {
388
+ setSpaceKey(false);
364
389
  pointerQueue.cancel();
365
390
  controller.cancel();
366
391
  },
@@ -370,6 +395,7 @@ function GridInteractions({
370
395
  onKeyDown: (key, keyPointer) => {
371
396
  return controller.keyDown(key, keyPointer);
372
397
  },
398
+ onSpaceKeyChange: setSpaceKey,
373
399
  children
374
400
  })
375
401
  });