@ankhorage/zora 25.8.12 → 25.8.14
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 +12 -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 +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map +1 -1
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js +9 -2
- package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map +1 -1
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts.map +1 -1
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js +11 -8
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js.map +1 -1
- package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts +1 -1
- package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts.map +1 -1
- package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js +25 -5
- package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js.map +1 -1
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts +1 -1
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts.map +1 -1
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js +5 -2
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js.map +1 -1
- package/dist/types/matrix-grid.d.ts +2 -0
- package/dist/types/matrix-grid.d.ts.map +1 -1
- package/dist/types/matrix-grid.js.map +1 -1
- package/package.json +2 -2
- package/src/features/grid-interactions/adapters/inbound/GridInteractions.test.tsx +6 -1
- package/src/features/grid-interactions/adapters/inbound/GridInteractions.tsx +0 -1
- package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx +10 -1
- package/src/features/matrix-grid/adapters/inbound/MatrixGrid.protected-focus.test.tsx +252 -0
- package/src/features/matrix-grid/adapters/inbound/MatrixGrid.test.tsx +79 -0
- package/src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx +16 -5
- package/src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts +31 -4
- package/src/features/matrix-grid/application/resolveMatrixGridSelection.test.ts +20 -13
- package/src/features/matrix-grid/application/resolveMatrixGridSelection.ts +3 -0
- package/src/types/matrix-grid.ts +2 -0
- package/web-dist/components/grid-interactions/GridInteractions.js +9 -2
- package/web-dist/components/matrix-grid/MatrixGrid.js +41 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 25.8.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2ba10d7: Prevent protected MatrixGrid cells from receiving focus or changing selection.
|
|
8
|
+
|
|
9
|
+
## 25.8.13
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- df79c6e: Fix the web grid interaction keyboard boundary so it is reachable through normal tab navigation in browsers.
|
|
14
|
+
|
|
3
15
|
## 25.8.12
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
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;AAO7C,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;AAO7C,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,GAAG,KAAK,EACT,EAAE,qBAAqB,qBAyEvB"}
|
|
@@ -34,7 +34,7 @@ export function GridInteractions({ children, interactionPolicy, resizeHandle, te
|
|
|
34
34
|
pointerQueue.enqueue(surface, (origin) => handlePointer(resolveGridInteractionPointer(eventSnapshot, origin, modifierSnapshot)));
|
|
35
35
|
};
|
|
36
36
|
const enabled = interactionPolicy !== 'passive';
|
|
37
|
-
return (<View accessible accessibilityLabel="Grid interaction surface"
|
|
37
|
+
return (<View accessible accessibilityLabel="Grid interaction surface" ref={surfaceRef} testID={testID} onResponderGrant={(event) => {
|
|
38
38
|
scrollLock.acquire();
|
|
39
39
|
withPointer(event, (pointer) => controller.begin(pointer, resizeHandle));
|
|
40
40
|
}} onResponderMove={(event) => withPointer(event, controller.move)} onResponderRelease={(event) => {
|
|
@@ -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;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,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,MAAM,UAAU,GAAG,4BAA4B,EAAE,CAAC;IAClD,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,
|
|
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;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,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,MAAM,UAAU,GAAG,4BAA4B,EAAE,CAAC;IAClD,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,GAAG,CAAC,CAAC,UAAU,CAAC,CAChB,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,gBAAgB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE;YAC1B,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,WAAW,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;QAC3E,CAAC,CAAC,CACF,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;YAC5B,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC,CAAC,CACF,oBAAoB,CAAC,CAAC,GAAG,EAAE;YACzB,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,WAAW,CAAC,KAAK,CAAC,CAAC;YACnB,YAAY,CAAC,MAAM,EAAE,CAAC;YACtB,UAAU,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC,CAAC,CACF,6BAA6B,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAC3C,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';\nimport { useGridInteractionScrollLock } from './useGridInteractionScrollLock';\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 const scrollLock = useGridInteractionScrollLock();\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 ref={surfaceRef}\n testID={testID}\n onResponderGrant={(event) => {\n scrollLock.acquire();\n withPointer(event, (pointer) => controller.begin(pointer, resizeHandle));\n }}\n onResponderMove={(event) => withPointer(event, controller.move)}\n onResponderRelease={(event) => {\n scrollLock.release();\n withPointer(event, controller.end);\n }}\n onResponderTerminate={() => {\n scrollLock.release();\n setSpaceKey(false);\n pointerQueue.cancel();\n controller.cancel();\n }}\n onResponderTerminationRequest={() => false}\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.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,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,
|
|
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,qBAoCpC;AAUD,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
|
@@ -6,7 +6,7 @@ export function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, on
|
|
|
6
6
|
onSpaceKeyChange(false);
|
|
7
7
|
}, [enabled, onSpaceKeyChange]);
|
|
8
8
|
React.useEffect(() => () => onSpaceKeyChange(false), [onSpaceKeyChange]);
|
|
9
|
-
return (<div aria-label="Grid interaction keyboard controls" onBlur={() => onSpaceKeyChange(false)} onKeyDown={(event) => {
|
|
9
|
+
return (<div aria-label="Grid interaction keyboard controls" role="group" onBlur={() => onSpaceKeyChange(false)} onKeyDown={(event) => {
|
|
10
10
|
if (!enabled)
|
|
11
11
|
return;
|
|
12
12
|
if (isSpaceKey(event))
|
|
@@ -24,10 +24,17 @@ export function GridInteractionsKeyboardProxy({ children, enabled, onKeyDown, on
|
|
|
24
24
|
}} onKeyUp={(event) => {
|
|
25
25
|
if (isSpaceKey(event))
|
|
26
26
|
onSpaceKeyChange(false);
|
|
27
|
-
}} style={
|
|
27
|
+
}} style={keyboardBoundaryStyle} tabIndex={enabled ? 0 : -1}>
|
|
28
28
|
{children}
|
|
29
29
|
</div>);
|
|
30
30
|
}
|
|
31
|
+
const keyboardBoundaryStyle = {
|
|
32
|
+
alignSelf: 'stretch',
|
|
33
|
+
display: 'flex',
|
|
34
|
+
flex: 1,
|
|
35
|
+
minHeight: 0,
|
|
36
|
+
minWidth: 0,
|
|
37
|
+
};
|
|
31
38
|
/*** Determines whether a web keyboard event represents the Space modifier. */
|
|
32
39
|
function isSpaceKey(event) {
|
|
33
40
|
return event.code === 'Space' || event.key === ' ';
|
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":"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,
|
|
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,IAAI,CAAC,OAAO,CACZ,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,qBAAqB,CAAC,CAC7B,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;AAED,MAAM,qBAAqB,GAAG;IAC5B,SAAS,EAAE,SAAS;IACpB,OAAO,EAAE,MAAM;IACf,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,CAAC;IACZ,QAAQ,EAAE,CAAC;CACH,CAAC;AASX,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 role=\"group\"\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={keyboardBoundaryStyle}\n tabIndex={enabled ? 0 : -1}\n >\n {children}\n </div>\n );\n}\n\nconst keyboardBoundaryStyle = {\n alignSelf: 'stretch',\n display: 'flex',\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n} as const;\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MatrixGrid.d.ts","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"MatrixGrid.d.ts","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAuOrE,gGAAgG;AAChG,eAAO,MAAM,UAAU,uDAAsC,CAAC"}
|
|
@@ -12,7 +12,7 @@ import { resolveMatrixGridSelection } from '../../application/resolveMatrixGridS
|
|
|
12
12
|
import { MatrixGridKeyboardProxy } from './MatrixGridKeyboardProxy';
|
|
13
13
|
import { useMatrixGridKeyboardFocus } from './useMatrixGridKeyboardFocus';
|
|
14
14
|
/*** Compose the canonical GridView around released sparse matrix placement and culling geometry. */
|
|
15
|
-
function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, interactionPolicy, layout, onSelectionChange, onViewportChange, overscanPixels = 160, readOnly = false, renderCell, selectedCellIds = [], selectionMode = 'single', testID, viewport: controlledViewport, width, zoomX = 1, zoomY = 1, }) {
|
|
15
|
+
function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, interactionPolicy, layout, onSelectionChange, onViewportChange, overscanPixels = 160, protectedCellIds = [], readOnly = false, renderCell, selectedCellIds = [], selectionMode = 'single', testID, viewport: controlledViewport, width, zoomX = 1, zoomY = 1, }) {
|
|
16
16
|
const { theme } = useZoraTheme();
|
|
17
17
|
const contentSize = React.useMemo(() => resolveMatrixGridContentSize(layout), [layout]);
|
|
18
18
|
const initialViewport = React.useMemo(() => ({ height, offsetX: 0, offsetY: 0, pixelsPerUnitX: zoomX, pixelsPerUnitY: zoomY, width }), [height, width, zoomX, zoomY]);
|
|
@@ -22,18 +22,20 @@ function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, in
|
|
|
22
22
|
const cellPlacements = React.useMemo(() => getVisibleMatrixCells(layout, cells, viewport, overscanPixels), [cells, layout, overscanPixels, viewport]);
|
|
23
23
|
const [visibleIds, setVisibleIds] = React.useState([]);
|
|
24
24
|
const anchorId = React.useRef(null);
|
|
25
|
+
const protectedCellIdSet = React.useMemo(() => new Set(protectedCellIds), [protectedCellIds]);
|
|
26
|
+
const isNavigable = React.useCallback((id) => !protectedCellIdSet.has(id), [protectedCellIdSet]);
|
|
25
27
|
const handleViewportChange = React.useCallback((proposal) => {
|
|
26
28
|
if (controlledViewport === undefined)
|
|
27
29
|
setUncontrolledViewport(proposal);
|
|
28
30
|
onViewportChange?.(proposal);
|
|
29
31
|
}, [controlledViewport, onViewportChange]);
|
|
30
32
|
const activateCell = React.useCallback((cell, event) => {
|
|
31
|
-
if (interactionPolicy === 'passive' || readOnly)
|
|
33
|
+
if (interactionPolicy === 'passive' || readOnly || protectedCellIdSet.has(cell.id))
|
|
32
34
|
return;
|
|
33
35
|
const intent = isRangeSelectionEvent(event)
|
|
34
36
|
? 'range'
|
|
35
37
|
: resolveSelectionEventIntent(event, 'pointer');
|
|
36
|
-
onSelectionChange?.(resolveMatrixGridSelection(sparseIndex.entries, selectedCellIds, cell.id, anchorId.current, intent, selectionMode));
|
|
38
|
+
onSelectionChange?.(resolveMatrixGridSelection(sparseIndex.entries, selectedCellIds, cell.id, anchorId.current, intent, selectionMode, protectedCellIdSet));
|
|
37
39
|
if (intent !== 'range')
|
|
38
40
|
anchorId.current = cell.id;
|
|
39
41
|
}, [
|
|
@@ -43,6 +45,7 @@ function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, in
|
|
|
43
45
|
selectedCellIds,
|
|
44
46
|
selectionMode,
|
|
45
47
|
sparseIndex.entries,
|
|
48
|
+
protectedCellIdSet,
|
|
46
49
|
]);
|
|
47
50
|
const handleKeyboardNavigation = React.useCallback((targetId, originId, shiftKey) => {
|
|
48
51
|
anchorId.current ??= originId;
|
|
@@ -52,7 +55,7 @@ function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, in
|
|
|
52
55
|
if (cell)
|
|
53
56
|
activateCell(getMatrixCellPlacement(layout, cell), { shiftKey: true });
|
|
54
57
|
}, [activateCell, layout, sparseIndex]);
|
|
55
|
-
const keyboard = useMatrixGridKeyboardFocus(sparseIndex, interactionPolicy === 'passive', visibleIds, handleKeyboardNavigation);
|
|
58
|
+
const keyboard = useMatrixGridKeyboardFocus(sparseIndex, interactionPolicy === 'passive', visibleIds, isNavigable, handleKeyboardNavigation);
|
|
56
59
|
const focusedPlacement = React.useMemo(() => {
|
|
57
60
|
const focusedCell = keyboard.focusedId === null ? undefined : sparseIndex.cellById.get(keyboard.focusedId);
|
|
58
61
|
return focusedCell ? getMatrixCellPlacement(layout, focusedCell) : undefined;
|
|
@@ -66,12 +69,12 @@ function MatrixGridInner({ accessibilityLabel = 'Matrix grid', cells, height, in
|
|
|
66
69
|
const cell = cellById.get(item.id);
|
|
67
70
|
if (!cell)
|
|
68
71
|
return null;
|
|
69
|
-
return renderMatrixCell(cell, selectedCellIds, theme.semantics.neutral.divider, theme.colors.primary, renderCell, activateCell, keyboard, interactionPolicy === 'passive', readOnly, testID);
|
|
72
|
+
return renderMatrixCell(cell, selectedCellIds, theme.semantics.neutral.divider, theme.colors.primary, renderCell, activateCell, keyboard, interactionPolicy === 'passive', protectedCellIdSet.has(cell.id), readOnly, testID);
|
|
70
73
|
}} testID={testID} viewport={viewport} width={width} onViewportChange={handleViewportChange}/>
|
|
71
74
|
</View>);
|
|
72
75
|
}
|
|
73
76
|
/*** Render one positioned sparse cell with native/web accessible selection semantics. */
|
|
74
|
-
function renderMatrixCell(cell, selectedCellIds, dividerColor, selectedColor, renderCell, activateCell, keyboard, passive, readOnly, testID) {
|
|
77
|
+
function renderMatrixCell(cell, selectedCellIds, dividerColor, selectedColor, renderCell, activateCell, keyboard, passive, protectedCell, readOnly, testID) {
|
|
75
78
|
const selected = selectedCellIds.includes(cell.id);
|
|
76
79
|
const canInteract = keyboard.tabStopId === cell.id;
|
|
77
80
|
return (<MatrixGridKeyboardProxy onKeyDown={(event) => {
|
|
@@ -79,12 +82,12 @@ function renderMatrixCell(cell, selectedCellIds, dividerColor, selectedColor, re
|
|
|
79
82
|
return true;
|
|
80
83
|
if (!['Enter', ' '].includes(event.key))
|
|
81
84
|
return false;
|
|
82
|
-
if (passive || readOnly)
|
|
85
|
+
if (passive || protectedCell || readOnly)
|
|
83
86
|
return false;
|
|
84
87
|
activateCell(cell, event);
|
|
85
88
|
return true;
|
|
86
89
|
}}>
|
|
87
|
-
<NativePressable key={cell.id} accessibilityActions={[{ name: 'activate' }]} accessibilityLabel={`Cell ${cell.id}`} accessibilityRole="button" accessibilityState={{ disabled: passive, selected }} disabled={passive} focusable={Platform.OS !== 'web' || canInteract} style={[
|
|
90
|
+
<NativePressable key={cell.id} accessibilityActions={[{ name: 'activate' }]} accessibilityLabel={`Cell ${cell.id}`} accessibilityRole="button" accessibilityState={{ disabled: passive || protectedCell, selected }} disabled={passive || protectedCell} focusable={!protectedCell && (Platform.OS !== 'web' || canInteract)} style={[
|
|
88
91
|
styles.cell,
|
|
89
92
|
{
|
|
90
93
|
borderColor: selected || keyboard.focusedId === cell.id ? selectedColor : dividerColor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MatrixGrid.js","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getMatrixCellPlacement,\n getVisibleMatrixCells,\n type GridMatrixCellPlacement,\n type GridViewport,\n} from '@ankhorage/grid-view';\nimport { isRecord } from '@ankhorage/utility/object';\nimport React from 'react';\nimport { Platform, Pressable as NativePressable, StyleSheet, View } from 'react-native';\n\nimport type { MatrixGridProps } from '../../../../types/matrix-grid';\nimport { GridView } from '../../../grid-view/public';\nimport { resolveSelectionEventIntent } from '../../../selection/public';\nimport { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope';\nimport { useZoraTheme } from '../../../theme/composition/useZoraTheme';\nimport { createMatrixGridSparseIndex } from '../../application/createMatrixGridSparseIndex';\nimport { resolveMatrixGridContentSize } from '../../application/resolveMatrixGridContentSize';\nimport { resolveMatrixGridSelection } from '../../application/resolveMatrixGridSelection';\nimport { MatrixGridKeyboardProxy } from './MatrixGridKeyboardProxy';\nimport { useMatrixGridKeyboardFocus } from './useMatrixGridKeyboardFocus';\n\n/*** Compose the canonical GridView around released sparse matrix placement and culling geometry. */\nfunction MatrixGridInner({\n accessibilityLabel = 'Matrix grid',\n cells,\n height,\n interactionPolicy,\n layout,\n onSelectionChange,\n onViewportChange,\n overscanPixels = 160,\n readOnly = false,\n renderCell,\n selectedCellIds = [],\n selectionMode = 'single',\n testID,\n viewport: controlledViewport,\n width,\n zoomX = 1,\n zoomY = 1,\n}: MatrixGridProps) {\n const { theme } = useZoraTheme();\n const contentSize = React.useMemo(() => resolveMatrixGridContentSize(layout), [layout]);\n const initialViewport = React.useMemo<GridViewport>(\n () => ({ height, offsetX: 0, offsetY: 0, pixelsPerUnitX: zoomX, pixelsPerUnitY: zoomY, width }),\n [height, width, zoomX, zoomY],\n );\n const [uncontrolledViewport, setUncontrolledViewport] = React.useState(initialViewport);\n const viewport = controlledViewport ?? uncontrolledViewport;\n const sparseIndex = React.useMemo(\n () => createMatrixGridSparseIndex(layout, cells),\n [cells, layout],\n );\n const cellPlacements = React.useMemo(\n () => getVisibleMatrixCells(layout, cells, viewport, overscanPixels),\n [cells, layout, overscanPixels, viewport],\n );\n const [visibleIds, setVisibleIds] = React.useState<readonly string[]>([]);\n const anchorId = React.useRef<string | null>(null);\n\n const handleViewportChange = React.useCallback(\n (proposal: GridViewport) => {\n if (controlledViewport === undefined) setUncontrolledViewport(proposal);\n onViewportChange?.(proposal);\n },\n [controlledViewport, onViewportChange],\n );\n\n const activateCell = React.useCallback(\n (cell: GridMatrixCellPlacement, event: unknown) => {\n if (interactionPolicy === 'passive' || readOnly) return;\n const intent = isRangeSelectionEvent(event)\n ? 'range'\n : resolveSelectionEventIntent(event, 'pointer');\n onSelectionChange?.(\n resolveMatrixGridSelection(\n sparseIndex.entries,\n selectedCellIds,\n cell.id,\n anchorId.current,\n intent,\n selectionMode,\n ),\n );\n if (intent !== 'range') anchorId.current = cell.id;\n },\n [\n interactionPolicy,\n onSelectionChange,\n readOnly,\n selectedCellIds,\n selectionMode,\n sparseIndex.entries,\n ],\n );\n const handleKeyboardNavigation = React.useCallback(\n (targetId: string, originId: string, shiftKey: boolean) => {\n anchorId.current ??= originId;\n if (!shiftKey) return;\n const cell = sparseIndex.cellById.get(targetId);\n if (cell) activateCell(getMatrixCellPlacement(layout, cell), { shiftKey: true });\n },\n [activateCell, layout, sparseIndex],\n );\n const keyboard = useMatrixGridKeyboardFocus(\n sparseIndex,\n interactionPolicy === 'passive',\n visibleIds,\n handleKeyboardNavigation,\n );\n const focusedPlacement = React.useMemo(() => {\n const focusedCell =\n keyboard.focusedId === null ? undefined : sparseIndex.cellById.get(keyboard.focusedId);\n return focusedCell ? getMatrixCellPlacement(layout, focusedCell) : undefined;\n }, [keyboard.focusedId, layout, sparseIndex.cellById]);\n const gridItems = React.useMemo(\n () =>\n focusedPlacement && !cellPlacements.some((cell) => cell.id === focusedPlacement.id)\n ? [...cellPlacements, focusedPlacement]\n : cellPlacements,\n [cellPlacements, focusedPlacement],\n );\n const cellById = React.useMemo(\n () => new Map(gridItems.map((cell) => [cell.id, cell])),\n [gridItems],\n );\n\n return (\n <View accessibilityLabel={accessibilityLabel}>\n <GridView\n contentHeight={contentSize.height}\n contentWidth={contentSize.width}\n height={height}\n interactionPolicy={interactionPolicy}\n items={gridItems}\n focusedItemId={keyboard.focusedId ?? undefined}\n overscanPixels={0}\n onVisibleItemIdsChange={setVisibleIds}\n renderItem={(item) => {\n const cell = cellById.get(item.id);\n if (!cell) return null;\n return renderMatrixCell(\n cell,\n selectedCellIds,\n theme.semantics.neutral.divider,\n theme.colors.primary,\n renderCell,\n activateCell,\n keyboard,\n interactionPolicy === 'passive',\n readOnly,\n testID,\n );\n }}\n testID={testID}\n viewport={viewport}\n width={width}\n onViewportChange={handleViewportChange}\n />\n </View>\n );\n}\n\n/*** Render one positioned sparse cell with native/web accessible selection semantics. */\nfunction renderMatrixCell(\n cell: GridMatrixCellPlacement,\n selectedCellIds: readonly string[],\n dividerColor: string,\n selectedColor: string,\n renderCell: MatrixGridProps['renderCell'],\n activateCell: (cell: GridMatrixCellPlacement, event: unknown) => void,\n keyboard: ReturnType<typeof useMatrixGridKeyboardFocus>,\n passive: boolean,\n readOnly: boolean,\n testID: string | undefined,\n) {\n const selected = selectedCellIds.includes(cell.id);\n const canInteract = keyboard.tabStopId === cell.id;\n return (\n <MatrixGridKeyboardProxy\n onKeyDown={(event) => {\n if (keyboard.onKeyDown(cell.id, event.key, event.shiftKey)) return true;\n if (!['Enter', ' '].includes(event.key)) return false;\n if (passive || readOnly) return false;\n activateCell(cell, event);\n return true;\n }}\n >\n <NativePressable\n key={cell.id}\n accessibilityActions={[{ name: 'activate' }]}\n accessibilityLabel={`Cell ${cell.id}`}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: passive, selected }}\n disabled={passive}\n focusable={Platform.OS !== 'web' || canInteract}\n style={[\n styles.cell,\n {\n borderColor: selected || keyboard.focusedId === cell.id ? selectedColor : dividerColor,\n height: '100%',\n width: '100%',\n },\n ]}\n testID={testID === undefined ? undefined : `${testID}-cell-${cell.id}`}\n onAccessibilityAction={() => activateCell(cell, {})}\n onFocus={() => keyboard.onFocus(cell.id)}\n onPress={(event) => activateCell(cell, event)}\n ref={(node) => keyboard.registerCell(cell.id, node)}\n >\n {renderCell(cell)}\n </NativePressable>\n </MatrixGridKeyboardProxy>\n );\n}\n\n/*** Recognize range intent without changing the shared replace-or-toggle selection contract. */\nfunction isRangeSelectionEvent(event: unknown): boolean {\n if (!isRecord(event)) return false;\n if (event.shiftKey === true) return true;\n return (\n (isRecord(event.nativeEvent) && event.nativeEvent.shiftKey === true) ||\n (isRecord(event.originalEvent) && event.originalEvent.shiftKey === true)\n );\n}\n\nconst styles = StyleSheet.create({\n cell: { borderWidth: 1, height: '100%', overflow: 'hidden', width: '100%' },\n});\n\n/*** A code-first generic sparse matrix grid; formulas and domain vocabulary remain external. */\nexport const MatrixGrid = withZoraThemeScope(MatrixGridInner);\n"]}
|
|
1
|
+
{"version":3,"file":"MatrixGrid.js","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getMatrixCellPlacement,\n getVisibleMatrixCells,\n type GridMatrixCellPlacement,\n type GridViewport,\n} from '@ankhorage/grid-view';\nimport { isRecord } from '@ankhorage/utility/object';\nimport React from 'react';\nimport { Platform, Pressable as NativePressable, StyleSheet, View } from 'react-native';\n\nimport type { MatrixGridProps } from '../../../../types/matrix-grid';\nimport { GridView } from '../../../grid-view/public';\nimport { resolveSelectionEventIntent } from '../../../selection/public';\nimport { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope';\nimport { useZoraTheme } from '../../../theme/composition/useZoraTheme';\nimport { createMatrixGridSparseIndex } from '../../application/createMatrixGridSparseIndex';\nimport { resolveMatrixGridContentSize } from '../../application/resolveMatrixGridContentSize';\nimport { resolveMatrixGridSelection } from '../../application/resolveMatrixGridSelection';\nimport { MatrixGridKeyboardProxy } from './MatrixGridKeyboardProxy';\nimport { useMatrixGridKeyboardFocus } from './useMatrixGridKeyboardFocus';\n\n/*** Compose the canonical GridView around released sparse matrix placement and culling geometry. */\nfunction MatrixGridInner({\n accessibilityLabel = 'Matrix grid',\n cells,\n height,\n interactionPolicy,\n layout,\n onSelectionChange,\n onViewportChange,\n overscanPixels = 160,\n protectedCellIds = [],\n readOnly = false,\n renderCell,\n selectedCellIds = [],\n selectionMode = 'single',\n testID,\n viewport: controlledViewport,\n width,\n zoomX = 1,\n zoomY = 1,\n}: MatrixGridProps) {\n const { theme } = useZoraTheme();\n const contentSize = React.useMemo(() => resolveMatrixGridContentSize(layout), [layout]);\n const initialViewport = React.useMemo<GridViewport>(\n () => ({ height, offsetX: 0, offsetY: 0, pixelsPerUnitX: zoomX, pixelsPerUnitY: zoomY, width }),\n [height, width, zoomX, zoomY],\n );\n const [uncontrolledViewport, setUncontrolledViewport] = React.useState(initialViewport);\n const viewport = controlledViewport ?? uncontrolledViewport;\n const sparseIndex = React.useMemo(\n () => createMatrixGridSparseIndex(layout, cells),\n [cells, layout],\n );\n const cellPlacements = React.useMemo(\n () => getVisibleMatrixCells(layout, cells, viewport, overscanPixels),\n [cells, layout, overscanPixels, viewport],\n );\n const [visibleIds, setVisibleIds] = React.useState<readonly string[]>([]);\n const anchorId = React.useRef<string | null>(null);\n const protectedCellIdSet = React.useMemo(() => new Set(protectedCellIds), [protectedCellIds]);\n const isNavigable = React.useCallback(\n (id: string) => !protectedCellIdSet.has(id),\n [protectedCellIdSet],\n );\n\n const handleViewportChange = React.useCallback(\n (proposal: GridViewport) => {\n if (controlledViewport === undefined) setUncontrolledViewport(proposal);\n onViewportChange?.(proposal);\n },\n [controlledViewport, onViewportChange],\n );\n\n const activateCell = React.useCallback(\n (cell: GridMatrixCellPlacement, event: unknown) => {\n if (interactionPolicy === 'passive' || readOnly || protectedCellIdSet.has(cell.id)) return;\n const intent = isRangeSelectionEvent(event)\n ? 'range'\n : resolveSelectionEventIntent(event, 'pointer');\n onSelectionChange?.(\n resolveMatrixGridSelection(\n sparseIndex.entries,\n selectedCellIds,\n cell.id,\n anchorId.current,\n intent,\n selectionMode,\n protectedCellIdSet,\n ),\n );\n if (intent !== 'range') anchorId.current = cell.id;\n },\n [\n interactionPolicy,\n onSelectionChange,\n readOnly,\n selectedCellIds,\n selectionMode,\n sparseIndex.entries,\n protectedCellIdSet,\n ],\n );\n const handleKeyboardNavigation = React.useCallback(\n (targetId: string, originId: string, shiftKey: boolean) => {\n anchorId.current ??= originId;\n if (!shiftKey) return;\n const cell = sparseIndex.cellById.get(targetId);\n if (cell) activateCell(getMatrixCellPlacement(layout, cell), { shiftKey: true });\n },\n [activateCell, layout, sparseIndex],\n );\n const keyboard = useMatrixGridKeyboardFocus(\n sparseIndex,\n interactionPolicy === 'passive',\n visibleIds,\n isNavigable,\n handleKeyboardNavigation,\n );\n const focusedPlacement = React.useMemo(() => {\n const focusedCell =\n keyboard.focusedId === null ? undefined : sparseIndex.cellById.get(keyboard.focusedId);\n return focusedCell ? getMatrixCellPlacement(layout, focusedCell) : undefined;\n }, [keyboard.focusedId, layout, sparseIndex.cellById]);\n const gridItems = React.useMemo(\n () =>\n focusedPlacement && !cellPlacements.some((cell) => cell.id === focusedPlacement.id)\n ? [...cellPlacements, focusedPlacement]\n : cellPlacements,\n [cellPlacements, focusedPlacement],\n );\n const cellById = React.useMemo(\n () => new Map(gridItems.map((cell) => [cell.id, cell])),\n [gridItems],\n );\n\n return (\n <View accessibilityLabel={accessibilityLabel}>\n <GridView\n contentHeight={contentSize.height}\n contentWidth={contentSize.width}\n height={height}\n interactionPolicy={interactionPolicy}\n items={gridItems}\n focusedItemId={keyboard.focusedId ?? undefined}\n overscanPixels={0}\n onVisibleItemIdsChange={setVisibleIds}\n renderItem={(item) => {\n const cell = cellById.get(item.id);\n if (!cell) return null;\n return renderMatrixCell(\n cell,\n selectedCellIds,\n theme.semantics.neutral.divider,\n theme.colors.primary,\n renderCell,\n activateCell,\n keyboard,\n interactionPolicy === 'passive',\n protectedCellIdSet.has(cell.id),\n readOnly,\n testID,\n );\n }}\n testID={testID}\n viewport={viewport}\n width={width}\n onViewportChange={handleViewportChange}\n />\n </View>\n );\n}\n\n/*** Render one positioned sparse cell with native/web accessible selection semantics. */\nfunction renderMatrixCell(\n cell: GridMatrixCellPlacement,\n selectedCellIds: readonly string[],\n dividerColor: string,\n selectedColor: string,\n renderCell: MatrixGridProps['renderCell'],\n activateCell: (cell: GridMatrixCellPlacement, event: unknown) => void,\n keyboard: ReturnType<typeof useMatrixGridKeyboardFocus>,\n passive: boolean,\n protectedCell: boolean,\n readOnly: boolean,\n testID: string | undefined,\n) {\n const selected = selectedCellIds.includes(cell.id);\n const canInteract = keyboard.tabStopId === cell.id;\n return (\n <MatrixGridKeyboardProxy\n onKeyDown={(event) => {\n if (keyboard.onKeyDown(cell.id, event.key, event.shiftKey)) return true;\n if (!['Enter', ' '].includes(event.key)) return false;\n if (passive || protectedCell || readOnly) return false;\n activateCell(cell, event);\n return true;\n }}\n >\n <NativePressable\n key={cell.id}\n accessibilityActions={[{ name: 'activate' }]}\n accessibilityLabel={`Cell ${cell.id}`}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: passive || protectedCell, selected }}\n disabled={passive || protectedCell}\n focusable={!protectedCell && (Platform.OS !== 'web' || canInteract)}\n style={[\n styles.cell,\n {\n borderColor: selected || keyboard.focusedId === cell.id ? selectedColor : dividerColor,\n height: '100%',\n width: '100%',\n },\n ]}\n testID={testID === undefined ? undefined : `${testID}-cell-${cell.id}`}\n onAccessibilityAction={() => activateCell(cell, {})}\n onFocus={() => keyboard.onFocus(cell.id)}\n onPress={(event) => activateCell(cell, event)}\n ref={(node) => keyboard.registerCell(cell.id, node)}\n >\n {renderCell(cell)}\n </NativePressable>\n </MatrixGridKeyboardProxy>\n );\n}\n\n/*** Recognize range intent without changing the shared replace-or-toggle selection contract. */\nfunction isRangeSelectionEvent(event: unknown): boolean {\n if (!isRecord(event)) return false;\n if (event.shiftKey === true) return true;\n return (\n (isRecord(event.nativeEvent) && event.nativeEvent.shiftKey === true) ||\n (isRecord(event.originalEvent) && event.originalEvent.shiftKey === true)\n );\n}\n\nconst styles = StyleSheet.create({\n cell: { borderWidth: 1, height: '100%', overflow: 'hidden', width: '100%' },\n});\n\n/*** A code-first generic sparse matrix grid; formulas and domain vocabulary remain external. */\nexport const MatrixGrid = withZoraThemeScope(MatrixGridInner);\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MatrixGridSparseIndex } from '../../application/createMatrixGridSparseIndex';
|
|
2
2
|
/*** Keep one roving focus target and restore it after GridView virtualizes a sparse cell. */
|
|
3
|
-
export declare function useMatrixGridKeyboardFocus(index: MatrixGridSparseIndex, disabled: boolean, visibleIds: readonly string[], onNavigate: (targetId: string, originId: string, shiftKey: boolean) => void): {
|
|
3
|
+
export declare function useMatrixGridKeyboardFocus(index: MatrixGridSparseIndex, disabled: boolean, visibleIds: readonly string[], isNavigable: (id: string) => boolean, onNavigate: (targetId: string, originId: string, shiftKey: boolean) => void): {
|
|
4
4
|
focusedId: string | null;
|
|
5
5
|
onFocus: (id: string) => void;
|
|
6
6
|
onKeyDown: (id: string, key: string, shiftKey: boolean) => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMatrixGridKeyboardFocus.d.ts","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAG3F,6FAA6F;AAC7F,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI;;
|
|
1
|
+
{"version":3,"file":"useMatrixGridKeyboardFocus.d.ts","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAG3F,6FAA6F;AAC7F,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,EACpC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI;;kBAsBpC,MAAM;oBAmBtC,MAAM,OAAO,MAAM,YAAY,OAAO,KAAG,OAAO;uBAbX,MAAM,QAAQ,aAAa,GAAG,IAAI;;EAqC/E;AAkBD,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB"}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getMatrixGridNextFocusId } from '../../application/getMatrixGridNextFocusId';
|
|
3
3
|
/*** Keep one roving focus target and restore it after GridView virtualizes a sparse cell. */
|
|
4
|
-
export function useMatrixGridKeyboardFocus(index, disabled, visibleIds, onNavigate) {
|
|
4
|
+
export function useMatrixGridKeyboardFocus(index, disabled, visibleIds, isNavigable, onNavigate) {
|
|
5
5
|
const [focusedId, setFocusedId] = React.useState(null);
|
|
6
6
|
const focusedRef = React.useRef(null);
|
|
7
7
|
const pendingFocusRef = React.useRef(null);
|
|
8
8
|
const cellRefs = React.useRef(new Map());
|
|
9
9
|
const visibleIdSet = React.useMemo(() => new Set(visibleIds), [visibleIds]);
|
|
10
|
-
const tabStopId = focusedId !== null && visibleIdSet.has(focusedId)
|
|
10
|
+
const tabStopId = focusedId !== null && visibleIdSet.has(focusedId) && isNavigable(focusedId)
|
|
11
11
|
? focusedId
|
|
12
|
-
: visibleIds.find((id) => index.positionById.has(id));
|
|
12
|
+
: visibleIds.find((id) => index.positionById.has(id) && isNavigable(id));
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
if (focusedRef.current !== null && !isNavigable(focusedRef.current)) {
|
|
15
|
+
focusedRef.current = null;
|
|
16
|
+
setFocusedId(null);
|
|
17
|
+
}
|
|
18
|
+
if (pendingFocusRef.current !== null && !isNavigable(pendingFocusRef.current)) {
|
|
19
|
+
pendingFocusRef.current = null;
|
|
20
|
+
}
|
|
21
|
+
}, [isNavigable]);
|
|
13
22
|
const onFocus = React.useCallback((id) => {
|
|
14
23
|
pendingFocusRef.current = null;
|
|
15
24
|
focusedRef.current = id;
|
|
@@ -31,7 +40,7 @@ export function useMatrixGridKeyboardFocus(index, disabled, visibleIds, onNaviga
|
|
|
31
40
|
return false;
|
|
32
41
|
}
|
|
33
42
|
const originId = focusedRef.current ?? id;
|
|
34
|
-
const nextId =
|
|
43
|
+
const nextId = resolveNextNavigableId(index, originId, key, isNavigable);
|
|
35
44
|
if (nextId === null || nextId === originId)
|
|
36
45
|
return true;
|
|
37
46
|
focusedRef.current = nextId;
|
|
@@ -44,7 +53,18 @@ export function useMatrixGridKeyboardFocus(index, disabled, visibleIds, onNaviga
|
|
|
44
53
|
mounted.focus();
|
|
45
54
|
}
|
|
46
55
|
return true;
|
|
47
|
-
}, [disabled, index, onNavigate]);
|
|
56
|
+
}, [disabled, index, isNavigable, onNavigate]);
|
|
48
57
|
return { focusedId, onFocus, onKeyDown, registerCell, tabStopId };
|
|
49
58
|
}
|
|
59
|
+
/*** Skip protected sparse cells while retaining the nearest deterministic directional navigation. */
|
|
60
|
+
function resolveNextNavigableId(index, originId, key, isNavigable) {
|
|
61
|
+
let candidate = getMatrixGridNextFocusId(index, originId, key);
|
|
62
|
+
while (candidate !== null && candidate !== originId && !isNavigable(candidate)) {
|
|
63
|
+
const nextCandidate = getMatrixGridNextFocusId(index, candidate, key);
|
|
64
|
+
if (nextCandidate === candidate)
|
|
65
|
+
return originId;
|
|
66
|
+
candidate = nextCandidate;
|
|
67
|
+
}
|
|
68
|
+
return candidate;
|
|
69
|
+
}
|
|
50
70
|
//# sourceMappingURL=useMatrixGridKeyboardFocus.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMatrixGridKeyboardFocus.js","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAEtF,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B,CACxC,KAA4B,EAC5B,QAAiB,EACjB,UAA6B,EAC7B,UAA2E;IAE3E,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAgB,IAAI,CAAC,CAAC;IACrD,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,EAAyB,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5E,MAAM,SAAS,GACb,SAAS,KAAK,IAAI,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"useMatrixGridKeyboardFocus.js","sourceRoot":"","sources":["../../../../../src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport type { MatrixGridSparseIndex } from '../../application/createMatrixGridSparseIndex';\nimport { getMatrixGridNextFocusId } from '../../application/getMatrixGridNextFocusId';\n\n/*** Keep one roving focus target and restore it after GridView virtualizes a sparse cell. */\nexport function useMatrixGridKeyboardFocus(\n index: MatrixGridSparseIndex,\n disabled: boolean,\n visibleIds: readonly string[],\n isNavigable: (id: string) => boolean,\n onNavigate: (targetId: string, originId: string, shiftKey: boolean) => void,\n) {\n const [focusedId, setFocusedId] = React.useState<string | null>(null);\n const focusedRef = React.useRef<string | null>(null);\n const pendingFocusRef = React.useRef<string | null>(null);\n const cellRefs = React.useRef(new Map<string, FocusableCell>());\n const visibleIdSet = React.useMemo(() => new Set(visibleIds), [visibleIds]);\n const tabStopId =\n focusedId !== null && visibleIdSet.has(focusedId) && isNavigable(focusedId)\n ? focusedId\n : visibleIds.find((id) => index.positionById.has(id) && isNavigable(id));\n\n React.useEffect(() => {\n if (focusedRef.current !== null && !isNavigable(focusedRef.current)) {\n focusedRef.current = null;\n setFocusedId(null);\n }\n if (pendingFocusRef.current !== null && !isNavigable(pendingFocusRef.current)) {\n pendingFocusRef.current = null;\n }\n }, [isNavigable]);\n\n const onFocus = React.useCallback((id: string) => {\n pendingFocusRef.current = null;\n focusedRef.current = id;\n setFocusedId(id);\n }, []);\n\n const registerCell = React.useCallback((id: string, node: FocusableCell | null) => {\n if (node === null) {\n cellRefs.current.delete(id);\n return;\n }\n cellRefs.current.set(id, node);\n if (pendingFocusRef.current === id) {\n pendingFocusRef.current = null;\n node.focus();\n }\n }, []);\n\n const onKeyDown = React.useCallback(\n (id: string, key: string, shiftKey: boolean): boolean => {\n if (disabled || !['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(key)) {\n return false;\n }\n const originId = focusedRef.current ?? id;\n const nextId = resolveNextNavigableId(index, originId, key, isNavigable);\n if (nextId === null || nextId === originId) return true;\n\n focusedRef.current = nextId;\n pendingFocusRef.current = nextId;\n setFocusedId(nextId);\n onNavigate(nextId, originId, shiftKey);\n\n const mounted = cellRefs.current.get(nextId);\n if (mounted) {\n pendingFocusRef.current = null;\n mounted.focus();\n }\n return true;\n },\n [disabled, index, isNavigable, onNavigate],\n );\n\n return { focusedId, onFocus, onKeyDown, registerCell, tabStopId };\n}\n\n/*** Skip protected sparse cells while retaining the nearest deterministic directional navigation. */\nfunction resolveNextNavigableId(\n index: MatrixGridSparseIndex,\n originId: string,\n key: string,\n isNavigable: (id: string) => boolean,\n): string | null {\n let candidate = getMatrixGridNextFocusId(index, originId, key);\n while (candidate !== null && candidate !== originId && !isNavigable(candidate)) {\n const nextCandidate = getMatrixGridNextFocusId(index, candidate, key);\n if (nextCandidate === candidate) return originId;\n candidate = nextCandidate;\n }\n return candidate;\n}\n\ninterface FocusableCell {\n focus: () => void;\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { type SelectionIntent } from '@ankhorage/utility/selection';
|
|
|
2
2
|
import type { SelectionMode } from '../../../types/selection';
|
|
3
3
|
type MatrixGridSelectionIntent = SelectionIntent | 'range';
|
|
4
4
|
/*** Apply ZORA's canonical selection intent while retaining the configured selection mode. */
|
|
5
|
-
export declare function resolveMatrixGridSelection(cells: readonly MatrixGridSelectionCell[], selectedIds: readonly string[], cellId: string, anchorId: string | null, intent: MatrixGridSelectionIntent, mode: SelectionMode): readonly string[];
|
|
5
|
+
export declare function resolveMatrixGridSelection(cells: readonly MatrixGridSelectionCell[], selectedIds: readonly string[], cellId: string, anchorId: string | null, intent: MatrixGridSelectionIntent, mode: SelectionMode, protectedCellIds: ReadonlySet<string>): readonly string[];
|
|
6
6
|
type MatrixGridSelectionCell = Readonly<{
|
|
7
7
|
columnIndex: number;
|
|
8
8
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveMatrixGridSelection.d.ts","sourceRoot":"","sources":["../../../../src/features/matrix-grid/application/resolveMatrixGridSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,KAAK,yBAAyB,GAAG,eAAe,GAAG,OAAO,CAAC;AAE3D,8FAA8F;AAC9F,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,uBAAuB,EAAE,EACzC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"resolveMatrixGridSelection.d.ts","sourceRoot":"","sources":["../../../../src/features/matrix-grid/application/resolveMatrixGridSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,KAAK,yBAAyB,GAAG,eAAe,GAAG,OAAO,CAAC;AAE3D,8FAA8F;AAC9F,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,uBAAuB,EAAE,EACzC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE,aAAa,EACnB,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,GACpC,SAAS,MAAM,EAAE,CAmBnB;AAED,KAAK,uBAAuB,GAAG,QAAQ,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { applySelectionIntent } from '@ankhorage/utility/selection';
|
|
2
2
|
/*** Apply ZORA's canonical selection intent while retaining the configured selection mode. */
|
|
3
|
-
export function resolveMatrixGridSelection(cells, selectedIds, cellId, anchorId, intent, mode) {
|
|
3
|
+
export function resolveMatrixGridSelection(cells, selectedIds, cellId, anchorId, intent, mode, protectedCellIds) {
|
|
4
|
+
if (protectedCellIds.has(cellId))
|
|
5
|
+
return selectedIds;
|
|
4
6
|
if (mode === 'single')
|
|
5
7
|
return applySelectionIntent(selectedIds, cellId, 'replace');
|
|
6
8
|
if (intent !== 'range')
|
|
@@ -10,7 +12,8 @@ export function resolveMatrixGridSelection(cells, selectedIds, cellId, anchorId,
|
|
|
10
12
|
if (!anchor || !target)
|
|
11
13
|
return applySelectionIntent(selectedIds, cellId, 'replace');
|
|
12
14
|
return cells
|
|
13
|
-
.filter((cell) =>
|
|
15
|
+
.filter((cell) => !protectedCellIds.has(cell.id) &&
|
|
16
|
+
cell.rowIndex >= Math.min(anchor.rowIndex, target.rowIndex) &&
|
|
14
17
|
cell.rowIndex <= Math.max(anchor.rowIndex, target.rowIndex) &&
|
|
15
18
|
cell.columnIndex >= Math.min(anchor.columnIndex, target.columnIndex) &&
|
|
16
19
|
cell.columnIndex <= Math.max(anchor.columnIndex, target.columnIndex))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveMatrixGridSelection.js","sourceRoot":"","sources":["../../../../src/features/matrix-grid/application/resolveMatrixGridSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,8BAA8B,CAAC;AAM1F,8FAA8F;AAC9F,MAAM,UAAU,0BAA0B,CACxC,KAAyC,EACzC,WAA8B,EAC9B,MAAc,EACd,QAAuB,EACvB,MAAiC,EACjC,IAAmB;
|
|
1
|
+
{"version":3,"file":"resolveMatrixGridSelection.js","sourceRoot":"","sources":["../../../../src/features/matrix-grid/application/resolveMatrixGridSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,8BAA8B,CAAC;AAM1F,8FAA8F;AAC9F,MAAM,UAAU,0BAA0B,CACxC,KAAyC,EACzC,WAA8B,EAC9B,MAAc,EACd,QAAuB,EACvB,MAAiC,EACjC,IAAmB,EACnB,gBAAqC;IAErC,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IACrD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACnF,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjF,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;QAAE,OAAO,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAEpF,OAAO,KAAK;SACT,MAAM,CACL,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC3D,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC3D,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC;QACpE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,CACvE;SACA,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC","sourcesContent":["import { applySelectionIntent, type SelectionIntent } from '@ankhorage/utility/selection';\n\nimport type { SelectionMode } from '../../../types/selection';\n\ntype MatrixGridSelectionIntent = SelectionIntent | 'range';\n\n/*** Apply ZORA's canonical selection intent while retaining the configured selection mode. */\nexport function resolveMatrixGridSelection(\n cells: readonly MatrixGridSelectionCell[],\n selectedIds: readonly string[],\n cellId: string,\n anchorId: string | null,\n intent: MatrixGridSelectionIntent,\n mode: SelectionMode,\n protectedCellIds: ReadonlySet<string>,\n): readonly string[] {\n if (protectedCellIds.has(cellId)) return selectedIds;\n if (mode === 'single') return applySelectionIntent(selectedIds, cellId, 'replace');\n if (intent !== 'range') return applySelectionIntent(selectedIds, cellId, intent);\n\n const anchor = cells.find((cell) => cell.id === anchorId);\n const target = cells.find((cell) => cell.id === cellId);\n if (!anchor || !target) return applySelectionIntent(selectedIds, cellId, 'replace');\n\n return cells\n .filter(\n (cell) =>\n !protectedCellIds.has(cell.id) &&\n cell.rowIndex >= Math.min(anchor.rowIndex, target.rowIndex) &&\n cell.rowIndex <= Math.max(anchor.rowIndex, target.rowIndex) &&\n cell.columnIndex >= Math.min(anchor.columnIndex, target.columnIndex) &&\n cell.columnIndex <= Math.max(anchor.columnIndex, target.columnIndex),\n )\n .map((cell) => cell.id);\n}\n\ntype MatrixGridSelectionCell = Readonly<{\n columnIndex: number;\n id: string;\n rowIndex: number;\n}>;\n"]}
|
|
@@ -15,6 +15,8 @@ export interface MatrixGridProps extends ZoraBaseProps {
|
|
|
15
15
|
readonly overscanPixels?: number;
|
|
16
16
|
/** Retains focus navigation while suppressing cell-selection mutations. */
|
|
17
17
|
readonly readOnly?: boolean;
|
|
18
|
+
/** Cells that remain visible but cannot receive focus or change the controlled selection. */
|
|
19
|
+
readonly protectedCellIds?: readonly string[];
|
|
18
20
|
readonly selectedCellIds?: readonly string[];
|
|
19
21
|
readonly selectionMode?: SelectionMode;
|
|
20
22
|
readonly onSelectionChange?: (ids: readonly string[]) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matrix-grid.d.ts","sourceRoot":"","sources":["../../src/types/matrix-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,mGAAmG;AACnG,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;IAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC;IACxE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC"}
|
|
1
|
+
{"version":3,"file":"matrix-grid.d.ts","sourceRoot":"","sources":["../../src/types/matrix-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,mGAAmG;AACnG,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,6FAA6F;IAC7F,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;IAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC;IACxE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matrix-grid.js","sourceRoot":"","sources":["../../src/types/matrix-grid.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n GridMatrixCell,\n GridMatrixCellPlacement,\n GridMatrixLayout,\n GridViewport,\n} from '@ankhorage/grid-view';\nimport type React from 'react';\n\nimport type { ZoraBaseProps } from './base';\nimport type { SelectionMode } from './selection';\n\n/** Code-first virtualized sparse-matrix presentation over the published grid-view geometry API. */\nexport interface MatrixGridProps extends ZoraBaseProps {\n readonly layout: GridMatrixLayout;\n readonly cells: readonly GridMatrixCell[];\n readonly width: number;\n readonly height: number;\n /** A supplied viewport is controlled by the caller, including independent axis zoom. */\n readonly viewport?: GridViewport;\n readonly zoomX?: number;\n readonly zoomY?: number;\n readonly overscanPixels?: number;\n /** Retains focus navigation while suppressing cell-selection mutations. */\n readonly readOnly?: boolean;\n readonly selectedCellIds?: readonly string[];\n readonly selectionMode?: SelectionMode;\n readonly onSelectionChange?: (ids: readonly string[]) => void;\n readonly onViewportChange?: (viewport: GridViewport) => void;\n readonly renderCell: (cell: GridMatrixCellPlacement) => React.ReactNode;\n readonly accessibilityLabel?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"matrix-grid.js","sourceRoot":"","sources":["../../src/types/matrix-grid.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n GridMatrixCell,\n GridMatrixCellPlacement,\n GridMatrixLayout,\n GridViewport,\n} from '@ankhorage/grid-view';\nimport type React from 'react';\n\nimport type { ZoraBaseProps } from './base';\nimport type { SelectionMode } from './selection';\n\n/** Code-first virtualized sparse-matrix presentation over the published grid-view geometry API. */\nexport interface MatrixGridProps extends ZoraBaseProps {\n readonly layout: GridMatrixLayout;\n readonly cells: readonly GridMatrixCell[];\n readonly width: number;\n readonly height: number;\n /** A supplied viewport is controlled by the caller, including independent axis zoom. */\n readonly viewport?: GridViewport;\n readonly zoomX?: number;\n readonly zoomY?: number;\n readonly overscanPixels?: number;\n /** Retains focus navigation while suppressing cell-selection mutations. */\n readonly readOnly?: boolean;\n /** Cells that remain visible but cannot receive focus or change the controlled selection. */\n readonly protectedCellIds?: readonly string[];\n readonly selectedCellIds?: readonly string[];\n readonly selectionMode?: SelectionMode;\n readonly onSelectionChange?: (ids: readonly string[]) => void;\n readonly onViewportChange?: (viewport: GridViewport) => void;\n readonly renderCell: (cell: GridMatrixCellPlacement) => React.ReactNode;\n readonly accessibilityLabel?: string;\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.14",
|
|
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 --isolate",
|
|
132
|
+
"test": "bun run build && bun test src examples/grid-workspaces/time examples/grid-workspaces/plugins --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
|
},
|
|
@@ -66,7 +66,12 @@ test('keeps a mounted RNW keyboard boundary current across parent rerenders', as
|
|
|
66
66
|
const keyboard = host.querySelector<HTMLElement>(
|
|
67
67
|
'[aria-label="Grid interaction keyboard controls"]',
|
|
68
68
|
);
|
|
69
|
-
|
|
69
|
+
const surface = host.querySelector<HTMLElement>('[data-testid="grid-interactions"]');
|
|
70
|
+
if (!keyboard || !surface) throw new Error('Missing web grid interaction boundary');
|
|
71
|
+
expect(keyboard.getAttribute('role')).toBe('group');
|
|
72
|
+
expect(keyboard.getAttribute('tabindex')).toBe('0');
|
|
73
|
+
expect(keyboard.style.display).toBe('flex');
|
|
74
|
+
expect(surface.getAttribute('role')).toBeNull();
|
|
70
75
|
const resize = new browser.KeyboardEvent('keydown', {
|
|
71
76
|
altKey: true,
|
|
72
77
|
bubbles: true,
|
package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx
CHANGED
|
@@ -17,6 +17,7 @@ export function GridInteractionsKeyboardProxy({
|
|
|
17
17
|
return (
|
|
18
18
|
<div
|
|
19
19
|
aria-label="Grid interaction keyboard controls"
|
|
20
|
+
role="group"
|
|
20
21
|
onBlur={() => onSpaceKeyChange(false)}
|
|
21
22
|
onKeyDown={(event) => {
|
|
22
23
|
if (!enabled) return;
|
|
@@ -37,7 +38,7 @@ export function GridInteractionsKeyboardProxy({
|
|
|
37
38
|
onKeyUp={(event) => {
|
|
38
39
|
if (isSpaceKey(event)) onSpaceKeyChange(false);
|
|
39
40
|
}}
|
|
40
|
-
style={
|
|
41
|
+
style={keyboardBoundaryStyle}
|
|
41
42
|
tabIndex={enabled ? 0 : -1}
|
|
42
43
|
>
|
|
43
44
|
{children}
|
|
@@ -45,6 +46,14 @@ export function GridInteractionsKeyboardProxy({
|
|
|
45
46
|
);
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
const keyboardBoundaryStyle = {
|
|
50
|
+
alignSelf: 'stretch',
|
|
51
|
+
display: 'flex',
|
|
52
|
+
flex: 1,
|
|
53
|
+
minHeight: 0,
|
|
54
|
+
minWidth: 0,
|
|
55
|
+
} as const;
|
|
56
|
+
|
|
48
57
|
interface GridInteractionsKeyboardProxyProps {
|
|
49
58
|
readonly children: React.ReactNode;
|
|
50
59
|
readonly enabled: boolean;
|