@ankhorage/zora 25.8.2 → 25.8.3
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.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js +9 -2
- package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts +1 -0
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts +3 -2
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js +17 -2
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts +4 -1
- package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js +2 -1
- package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js.map +1 -1
- package/package.json +2 -2
- package/src/features/grid-interactions/adapters/inbound/GridInteractions.test.tsx +146 -0
- package/src/features/grid-interactions/adapters/inbound/GridInteractions.tsx +9 -1
- package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx +1 -0
- package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx +18 -1
- package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.test.ts +15 -0
- package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts +6 -0
- package/web-dist/components/grid-interactions/GridInteractions.js +36 -10
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/zora
|
|
5
5
|
|
|
6
|
-
         
|
|
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,
|
|
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
|
-
|
|
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":"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,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,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,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,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CACvC,aAAa,CAAC,6BAA6B,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,
|
|
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 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"]}
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js.map
CHANGED
|
@@ -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"]}
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import
|
|
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
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GridInteractionsKeyboardProxy.web.js","sourceRoot":"","sources":["../../../../../src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx"],"names":[],"mappings":"
|
|
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
|
package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
};
|
package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js.map
CHANGED
|
@@ -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;
|
|
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.
|
|
4
|
+
"version": "25.8.3",
|
|
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": {
|
|
@@ -129,7 +129,7 @@
|
|
|
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
|
},
|
|
@@ -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>
|
package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
}
|
package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.test.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
335
|
-
const [controller] =
|
|
336
|
-
const surfaceRef =
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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
|
});
|