@ankhorage/zora 25.5.0 → 25.6.2
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 +30 -0
- package/README.md +2 -1
- package/dist/capabilities/index.d.ts +2 -74
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +6158 -59
- package/dist/capabilities/index.js.map +118 -1
- package/dist/cli/index.d.ts +2 -74
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/features/grid-view/adapters/inbound/GridView.d.ts +2 -7
- package/dist/features/grid-view/adapters/inbound/GridView.d.ts.map +1 -1
- package/dist/features/grid-view/adapters/inbound/GridView.js +309 -51
- package/dist/features/grid-view/adapters/inbound/GridView.js.map +1 -1
- package/dist/features/grid-view/adapters/inbound/TileGrid.d.ts +1 -1
- package/dist/features/grid-view/adapters/inbound/TileGrid.d.ts.map +1 -1
- package/dist/features/grid-view/adapters/inbound/TileGrid.js +11 -4
- package/dist/features/grid-view/adapters/inbound/TileGrid.js.map +1 -1
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts +5 -0
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts.map +1 -0
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js +59 -0
- package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js.map +1 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.d.ts +7 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.d.ts.map +1 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.js +24 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.js.map +1 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts +5 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts.map +1 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js +8 -0
- package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js.map +1 -0
- package/dist/features/matrix-grid/meta.d.ts +11 -0
- package/dist/features/matrix-grid/meta.d.ts.map +1 -0
- package/dist/features/matrix-grid/meta.js +11 -0
- package/dist/features/matrix-grid/meta.js.map +1 -0
- package/dist/features/matrix-grid/public.d.ts +4 -0
- package/dist/features/matrix-grid/public.d.ts.map +1 -0
- package/dist/features/matrix-grid/public.js +3 -0
- package/dist/features/matrix-grid/public.js.map +1 -0
- package/dist/features/registry/ZORA_COMPONENT_REGISTRY.d.ts.map +1 -1
- package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js +2 -0
- package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js.map +1 -1
- package/dist/features/registry/componentMeta.d.ts.map +1 -1
- package/dist/features/registry/componentMeta.js +2 -0
- package/dist/features/registry/componentMeta.js.map +1 -1
- package/dist/features/registry/createEventCapabilities.d.ts +4 -0
- package/dist/features/registry/createEventCapabilities.d.ts.map +1 -0
- package/dist/features/registry/createEventCapabilities.js +76 -0
- package/dist/features/registry/createEventCapabilities.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/grid-view.d.ts +18 -1
- package/dist/types/grid-view.d.ts.map +1 -1
- package/dist/types/grid-view.js.map +1 -1
- package/dist/types/matrix-grid.d.ts +23 -0
- package/dist/types/matrix-grid.d.ts.map +1 -0
- package/dist/types/matrix-grid.js +2 -0
- package/dist/types/matrix-grid.js.map +1 -0
- package/package.json +1822 -4
- package/src/capabilities/index.test.ts +87 -15
- package/src/capabilities/index.ts +5 -2
- package/src/cli/index.test.ts +5 -3
- package/src/cli/index.ts +5 -2
- package/src/features/grid-view/adapters/inbound/GridView.interaction.test.tsx +585 -0
- package/src/features/grid-view/adapters/inbound/GridView.test.tsx +74 -0
- package/src/features/grid-view/adapters/inbound/GridView.tsx +506 -77
- package/src/features/grid-view/adapters/inbound/TileGrid.tsx +26 -3
- package/src/features/matrix-grid/adapters/inbound/MatrixGrid.test.tsx +232 -0
- package/src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx +142 -0
- package/src/features/matrix-grid/application/resolveMatrixGridContentSize.test.ts +32 -0
- package/src/features/matrix-grid/application/resolveMatrixGridContentSize.ts +28 -0
- package/src/features/matrix-grid/application/resolveMatrixGridSelection.test.ts +10 -0
- package/src/features/matrix-grid/application/resolveMatrixGridSelection.ts +15 -0
- package/src/features/matrix-grid/application/resolveMatrixGridVisibleCells.test.ts +35 -0
- package/src/features/matrix-grid/meta.ts +12 -0
- package/src/features/matrix-grid/public.ts +3 -0
- package/src/features/registry/ZORA_COMPONENT_REGISTRY.ts +2 -0
- package/src/features/registry/componentMeta.ts +2 -0
- package/src/features/registry/createEventCapabilities.ts +108 -0
- package/src/features/time-grid/adapters/inbound/TimeGrid.test.tsx +35 -8
- package/src/index.ts +2 -0
- package/src/structureOwnership.test.ts +5 -0
- package/src/types/grid-view.ts +27 -1
- package/src/types/matrix-grid.ts +29 -0
- package/web-dist/chunks/{Accordion-d6azgz09.js → Accordion-2r0gqk3x.js} +1 -1
- package/web-dist/chunks/Accordion-a6551mh4.js +515 -0
- package/web-dist/chunks/{Accordion-0my2z6qe.js → Accordion-bz04casa.js} +132 -3
- package/web-dist/chunks/{Accordion-7qfd4cb4.js → Accordion-jh72azm2.js} +1 -1
- package/web-dist/components/explorer/Explorer.js +2 -2
- package/web-dist/components/file-explorer/FileExplorer.js +2 -2
- package/web-dist/components/grid-line-overlay/GridLineOverlay.js +1 -1
- package/web-dist/components/grid-ruler/GridRuler.js +1 -1
- package/web-dist/components/grid-view/GridView.js +1 -1
- package/web-dist/components/matrix-grid/MatrixGrid.d.ts +3 -0
- package/web-dist/components/matrix-grid/MatrixGrid.js +153 -0
- package/web-dist/components/matrix-grid/index.d.ts +1 -0
- package/web-dist/components/matrix-grid/index.js +2 -0
- package/web-dist/components/media-explorer/MediaExplorer.js +2 -2
- package/web-dist/components/reader/Reader.js +1 -1
- package/web-dist/components/spatial-grid/SpatialGrid.js +1 -1
- package/web-dist/components/tile-grid/TileGrid.js +1 -1
- package/web-dist/components/time-grid/TimeGrid.js +2 -2
- package/web-dist/components/uploader/Uploader.js +2 -2
- package/web-dist/manifest.json +53 -24
- package/web-dist/chunks/Accordion-gxj5qbpr.js +0 -194
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,83 +1,11 @@
|
|
|
1
|
+
import type { Capability } from '@ankhorage/contracts/capability';
|
|
1
2
|
import { create } from './commands/create';
|
|
2
3
|
import { sync } from './commands/sync';
|
|
3
4
|
declare const provider: {
|
|
4
5
|
id: string;
|
|
5
6
|
category: string;
|
|
6
7
|
version: string;
|
|
7
|
-
capabilities:
|
|
8
|
-
readonly id: "zora.create";
|
|
9
|
-
readonly owner: "@ankhorage/zora";
|
|
10
|
-
readonly access: readonly ["invoke"];
|
|
11
|
-
readonly binding: {
|
|
12
|
-
readonly kind: "action";
|
|
13
|
-
readonly bindableAs: readonly ["target"];
|
|
14
|
-
};
|
|
15
|
-
readonly label: "Materialize ZORA web component";
|
|
16
|
-
readonly description: "Materialize a declared ZORA component and reconcile its web runtime.";
|
|
17
|
-
readonly input: {
|
|
18
|
-
readonly schema: {
|
|
19
|
-
readonly type: "object";
|
|
20
|
-
readonly required: readonly ["component", "platform"];
|
|
21
|
-
readonly properties: {
|
|
22
|
-
readonly component: {
|
|
23
|
-
readonly type: "string";
|
|
24
|
-
};
|
|
25
|
-
readonly platform: {
|
|
26
|
-
readonly type: "string";
|
|
27
|
-
readonly const: "web";
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
readonly additionalProperties: false;
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
readonly output: {
|
|
34
|
-
readonly schema: {
|
|
35
|
-
readonly type: "object";
|
|
36
|
-
readonly required: readonly ["exitCode"];
|
|
37
|
-
readonly properties: {
|
|
38
|
-
readonly exitCode: {
|
|
39
|
-
readonly type: "integer";
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
readonly additionalProperties: false;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
}, {
|
|
46
|
-
readonly id: "zora.sync";
|
|
47
|
-
readonly owner: "@ankhorage/zora";
|
|
48
|
-
readonly access: readonly ["invoke"];
|
|
49
|
-
readonly binding: {
|
|
50
|
-
readonly kind: "action";
|
|
51
|
-
readonly bindableAs: readonly ["target"];
|
|
52
|
-
};
|
|
53
|
-
readonly label: "Synchronize ZORA web components";
|
|
54
|
-
readonly description: "Reconcile the web runtime from the declared ZORA component set.";
|
|
55
|
-
readonly input: {
|
|
56
|
-
readonly schema: {
|
|
57
|
-
readonly type: "object";
|
|
58
|
-
readonly required: readonly ["platform"];
|
|
59
|
-
readonly properties: {
|
|
60
|
-
readonly platform: {
|
|
61
|
-
readonly type: "string";
|
|
62
|
-
readonly const: "web";
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
readonly additionalProperties: false;
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
readonly output: {
|
|
69
|
-
readonly schema: {
|
|
70
|
-
readonly type: "object";
|
|
71
|
-
readonly required: readonly ["exitCode"];
|
|
72
|
-
readonly properties: {
|
|
73
|
-
readonly exitCode: {
|
|
74
|
-
readonly type: "integer";
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
readonly additionalProperties: false;
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
}];
|
|
8
|
+
capabilities: Capability[];
|
|
81
9
|
commands: ({
|
|
82
10
|
readonly path: readonly ["create"];
|
|
83
11
|
readonly capability: "zora.create";
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGlE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAgBvC,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAgBwB,CAAC;AAEvC,eAAe,QAAQ,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -14,11 +14,12 @@ const SYNC_COMMAND = {
|
|
|
14
14
|
capability: 'zora.sync',
|
|
15
15
|
summary: 'Regenerate the declared ZORA web materialization.',
|
|
16
16
|
};
|
|
17
|
+
const COMMAND_CAPABILITIES = CAPABILITIES.filter((capability) => capability.access.includes('invoke'));
|
|
17
18
|
const provider = {
|
|
18
19
|
id: '@ankhorage/zora',
|
|
19
20
|
category: 'zora',
|
|
20
21
|
version: readPackageVersion(),
|
|
21
|
-
capabilities:
|
|
22
|
+
capabilities: COMMAND_CAPABILITIES,
|
|
22
23
|
commands: [CREATE_COMMAND, SYNC_COMMAND],
|
|
23
24
|
handlers: [
|
|
24
25
|
{
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAKzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,cAAc,GAAG;IACrB,IAAI,EAAE,CAAC,QAAQ,CAAC;IAChB,UAAU,EAAE,aAAwC;IACpD,OAAO,EAAE,+DAA+D;CAChE,CAAC;AACX,MAAM,YAAY,GAAG;IACnB,IAAI,EAAE,CAAC,MAAM,CAAC;IACd,UAAU,EAAE,WAAsC;IAClD,OAAO,EAAE,mDAAmD;CACpD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAKzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,cAAc,GAAG;IACrB,IAAI,EAAE,CAAC,QAAQ,CAAC;IAChB,UAAU,EAAE,aAAwC;IACpD,OAAO,EAAE,+DAA+D;CAChE,CAAC;AACX,MAAM,YAAY,GAAG;IACnB,IAAI,EAAE,CAAC,MAAM,CAAC;IACd,UAAU,EAAE,WAAsC;IAClD,OAAO,EAAE,mDAAmD;CACpD,CAAC;AACX,MAAM,oBAAoB,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAC9D,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACrC,CAAC;AAEF,MAAM,QAAQ,GAAG;IACf,EAAE,EAAE,iBAAiB;IACrB,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,kBAAkB,EAAE;IAC7B,YAAY,EAAE,oBAAoB;IAClC,QAAQ,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC;IACxC,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,OAAO,EAAE,MAAM;SAChB;QACD;YACE,IAAI,EAAE,YAAY,CAAC,IAAI;YACvB,OAAO,EAAE,IAAI;SACd;KACF;CACmC,CAAC;AAEvC,eAAe,QAAQ,CAAC;AAExB,uEAAuE;AACvE,SAAS,kBAAkB;IACzB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC,CAC3D,CAAC;IAEpC,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,IAAI,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC","sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { AnkhRuntimeCommandProvider } from '@ankhorage/ankh';\nimport type { Capability } from '@ankhorage/contracts/capability';\n\nimport { CAPABILITIES } from '../capabilities';\nimport { create } from './commands/create';\nimport { sync } from './commands/sync';\n\nconst CREATE_COMMAND = {\n path: ['create'],\n capability: 'zora.create' satisfies Capability['id'],\n summary: 'Materialize a canonical ZORA component for a target platform.',\n} as const;\nconst SYNC_COMMAND = {\n path: ['sync'],\n capability: 'zora.sync' satisfies Capability['id'],\n summary: 'Regenerate the declared ZORA web materialization.',\n} as const;\nconst COMMAND_CAPABILITIES = CAPABILITIES.filter((capability) =>\n capability.access.includes('invoke'),\n);\n\nconst provider = {\n id: '@ankhorage/zora',\n category: 'zora',\n version: readPackageVersion(),\n capabilities: COMMAND_CAPABILITIES,\n commands: [CREATE_COMMAND, SYNC_COMMAND],\n handlers: [\n {\n path: CREATE_COMMAND.path,\n handler: create,\n },\n {\n path: SYNC_COMMAND.path,\n handler: sync,\n },\n ],\n} satisfies AnkhRuntimeCommandProvider;\n\nexport default provider;\n\n/*** Read the installed ZORA version for the Ankh provider manifest. */\nfunction readPackageVersion(): string {\n const packageJson = JSON.parse(\n readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'), 'utf8'),\n ) as { readonly version?: unknown };\n\n if (typeof packageJson.version !== 'string' || packageJson.version.trim() === '') {\n throw new Error('ZORA package.json must define a non-empty version.');\n }\n\n return packageJson.version;\n}\n"]}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { GridViewProps } from '../../../../types/grid-view';
|
|
3
|
-
/***
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* This first renderer uses nested native scroll regions. Only visible items are mounted;
|
|
7
|
-
* logical cells and invisible elements are never rendered.
|
|
8
|
-
*/
|
|
9
|
-
export declare function GridView({ items, contentWidth, contentHeight, width, height, zoom, overscanPixels, focusedItemId, revealPaddingPixels, interactionPolicy, onViewportChange, onVisibleItemIdsChange, renderItem, testID, }: GridViewProps): React.JSX.Element;
|
|
3
|
+
/*** Renders a controlled or uncontrolled, virtualized 2D world through the canonical grid viewport engine. */
|
|
4
|
+
export declare function GridView({ items, contentWidth, contentHeight, width, height, viewport: controlledViewport, defaultViewport, viewportConstraints, zoomLimits, zoom, overscanPixels, focusedItemId, revealPaddingPixels, interactionPolicy, onViewportChange, onVisibleItemIdsChange, renderItem, testID, }: GridViewProps): React.JSX.Element;
|
|
10
5
|
//# sourceMappingURL=GridView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GridView.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-view/adapters/inbound/GridView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GridView.d.ts","sourceRoot":"","sources":["../../../../../src/features/grid-view/adapters/inbound/GridView.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBjE,8GAA8G;AAC9G,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,YAAY,EACZ,aAAa,EACb,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,kBAAkB,EAC5B,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,IAAQ,EACR,cAAoB,EACpB,aAAa,EACb,mBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,UAAU,EACV,MAAM,GACP,EAAE,aAAa,qBAwWf"}
|
|
@@ -1,29 +1,78 @@
|
|
|
1
|
-
import { getVisibleGridItems, revealWorldRect, worldToViewport, } from '@ankhorage/grid-view';
|
|
1
|
+
import { constrainViewport, getVisibleGridItems, panViewport, revealWorldRect, worldToViewport, zoomViewportAt, } from '@ankhorage/grid-view';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { ScrollView as NativeScrollView, View as NativeView } from 'react-native';
|
|
4
|
-
/***
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* This first renderer uses nested native scroll regions. Only visible items are mounted;
|
|
8
|
-
* logical cells and invisible elements are never rendered.
|
|
9
|
-
*/
|
|
10
|
-
export function GridView({ items, contentWidth, contentHeight, width, height, zoom = 1, overscanPixels = 160, focusedItemId, revealPaddingPixels = 8, interactionPolicy, onViewportChange, onVisibleItemIdsChange, renderItem, testID, }) {
|
|
11
|
-
const [scrollX, setScrollX] = React.useState(0);
|
|
12
|
-
const [scrollY, setScrollY] = React.useState(0);
|
|
3
|
+
import { Pressable as NativePressable, ScrollView as NativeScrollView, Text as NativeText, View as NativeView, } from 'react-native';
|
|
4
|
+
/*** Renders a controlled or uncontrolled, virtualized 2D world through the canonical grid viewport engine. */
|
|
5
|
+
export function GridView({ items, contentWidth, contentHeight, width, height, viewport: controlledViewport, defaultViewport, viewportConstraints, zoomLimits, zoom = 1, overscanPixels = 160, focusedItemId, revealPaddingPixels = 8, interactionPolicy, onViewportChange, onVisibleItemIdsChange, renderItem, testID, }) {
|
|
6
|
+
const [uncontrolledViewport, setUncontrolledViewport] = React.useState(() => createInitialViewport(width, height, zoom, defaultViewport));
|
|
13
7
|
const horizontalScrollRef = React.useRef(null);
|
|
14
8
|
const verticalScrollRef = React.useRef(null);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
const gridRef = React.useRef(null);
|
|
10
|
+
const scrollPositionRef = React.useRef({ x: 0, y: 0 });
|
|
11
|
+
const pinchGestureRef = React.useRef(undefined);
|
|
12
|
+
const pinchMeasurementRef = React.useRef(0);
|
|
13
|
+
const pinchSessionRef = React.useRef(0);
|
|
14
|
+
const viewportOriginRef = React.useRef({ x: 0, y: 0 });
|
|
15
|
+
const focusRevealProposalRef = React.useRef(undefined);
|
|
16
|
+
const focusRevealViewportRef = React.useRef(undefined);
|
|
17
|
+
const [isPinching, setIsPinching] = React.useState(false);
|
|
18
|
+
const constraints = React.useMemo(() => viewportConstraints ?? createContentConstraints(contentWidth, contentHeight), [contentHeight, contentWidth, viewportConstraints]);
|
|
19
|
+
const sourceViewport = controlledViewport ?? uncontrolledViewport;
|
|
20
|
+
const viewport = React.useMemo(() => constrainViewport({ ...sourceViewport, height, width }, constraints), [constraints, height, sourceViewport, width]);
|
|
21
|
+
const viewportRef = React.useRef(viewport);
|
|
24
22
|
const visibleItems = React.useMemo(() => getVisibleGridItems(items, viewport, overscanPixels), [items, overscanPixels, viewport]);
|
|
25
23
|
const viewportVisibleIds = React.useMemo(() => getVisibleGridItems(visibleItems, viewport).map((item) => item.id), [viewport, visibleItems]);
|
|
24
|
+
const focusedItem = items.find((item) => item.id === focusedItemId);
|
|
25
|
+
const focusedItemHeight = focusedItem?.height;
|
|
26
|
+
const focusedItemIdForReveal = focusedItem?.id;
|
|
27
|
+
const focusedItemWidth = focusedItem?.width;
|
|
28
|
+
const focusedItemX = focusedItem?.x;
|
|
29
|
+
const focusedItemY = focusedItem?.y;
|
|
26
30
|
const previousVisibleIdsRef = React.useRef([]);
|
|
31
|
+
const uncontrolled = controlledViewport === undefined;
|
|
32
|
+
const interactive = interactionPolicy !== 'passive';
|
|
33
|
+
const publish = React.useCallback((nextViewport) => {
|
|
34
|
+
if (areViewportsEqual(viewportRef.current, nextViewport))
|
|
35
|
+
return;
|
|
36
|
+
viewportRef.current = nextViewport;
|
|
37
|
+
if (uncontrolled)
|
|
38
|
+
setUncontrolledViewport(nextViewport);
|
|
39
|
+
onViewportChange?.(nextViewport);
|
|
40
|
+
}, [onViewportChange, uncontrolled]);
|
|
41
|
+
const publishRef = React.useRef(publish);
|
|
42
|
+
const pan = (displacement) => publish(panViewport(viewportRef.current, displacement, constraints));
|
|
43
|
+
const zoomAt = (focalPoint, factor) => publish(zoomViewportAt(viewportRef.current, focalPoint, {
|
|
44
|
+
pixelsPerUnitX: viewportRef.current.pixelsPerUnitX * factor,
|
|
45
|
+
pixelsPerUnitY: viewportRef.current.pixelsPerUnitY * factor,
|
|
46
|
+
}, zoomLimits, constraints));
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
viewportRef.current = viewport;
|
|
49
|
+
}, [viewport]);
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
publishRef.current = publish;
|
|
52
|
+
}, [publish]);
|
|
53
|
+
const refreshViewportOrigin = React.useCallback(() => {
|
|
54
|
+
gridRef.current?.measureInWindow((x, y) => {
|
|
55
|
+
viewportOriginRef.current = { x, y };
|
|
56
|
+
});
|
|
57
|
+
}, []);
|
|
58
|
+
const measurePinchGesture = React.useCallback((touches, session, onMeasured) => {
|
|
59
|
+
const measurement = pinchMeasurementRef.current + 1;
|
|
60
|
+
pinchMeasurementRef.current = measurement;
|
|
61
|
+
gridRef.current?.measureInWindow((x, y) => {
|
|
62
|
+
if (measurement !== pinchMeasurementRef.current || session !== pinchSessionRef.current) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
viewportOriginRef.current = { x, y };
|
|
66
|
+
onMeasured(getPinchGesture(touches, width, height, { x, y }));
|
|
67
|
+
});
|
|
68
|
+
}, [height, width]);
|
|
69
|
+
React.useLayoutEffect(() => {
|
|
70
|
+
refreshViewportOrigin();
|
|
71
|
+
}, [height, refreshViewportOrigin, width]);
|
|
72
|
+
React.useEffect(() => () => {
|
|
73
|
+
pinchMeasurementRef.current += 1;
|
|
74
|
+
pinchSessionRef.current += 1;
|
|
75
|
+
}, []);
|
|
27
76
|
React.useEffect(() => {
|
|
28
77
|
if (!onVisibleItemIdsChange)
|
|
29
78
|
return;
|
|
@@ -36,46 +85,255 @@ export function GridView({ items, contentWidth, contentHeight, width, height, zo
|
|
|
36
85
|
onVisibleItemIdsChange(viewportVisibleIds);
|
|
37
86
|
}, [onVisibleItemIdsChange, viewportVisibleIds]);
|
|
38
87
|
React.useEffect(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
88
|
+
const position = createScrollMapping(viewport, constraints);
|
|
89
|
+
const nextPosition = {
|
|
90
|
+
x: position.horizontal.position,
|
|
91
|
+
y: position.vertical.position,
|
|
92
|
+
};
|
|
93
|
+
if (areScrollPositionsEqual(scrollPositionRef.current, nextPosition))
|
|
94
|
+
return;
|
|
95
|
+
scrollPositionRef.current = nextPosition;
|
|
96
|
+
const animated = focusRevealViewportRef.current !== undefined &&
|
|
97
|
+
areViewportsEqual(viewport, focusRevealViewportRef.current);
|
|
98
|
+
horizontalScrollRef.current?.scrollTo({ animated, x: nextPosition.x });
|
|
99
|
+
verticalScrollRef.current?.scrollTo({ animated, y: nextPosition.y });
|
|
100
|
+
if (animated)
|
|
101
|
+
focusRevealViewportRef.current = undefined;
|
|
102
|
+
}, [constraints, viewport]);
|
|
45
103
|
React.useEffect(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
104
|
+
if (focusedItemHeight === undefined ||
|
|
105
|
+
focusedItemIdForReveal === undefined ||
|
|
106
|
+
focusedItemWidth === undefined ||
|
|
107
|
+
focusedItemX === undefined ||
|
|
108
|
+
focusedItemY === undefined) {
|
|
109
|
+
focusRevealProposalRef.current = undefined;
|
|
49
110
|
return;
|
|
50
|
-
const revealed = revealWorldRect(currentViewport, focusedItem, revealPaddingPixels);
|
|
51
|
-
if (revealed.offsetX !== currentViewport.offsetX) {
|
|
52
|
-
horizontalScrollRef.current?.scrollTo({ x: revealed.offsetX * scale, animated: true });
|
|
53
111
|
}
|
|
54
|
-
|
|
55
|
-
|
|
112
|
+
const sourceViewport = viewportRef.current;
|
|
113
|
+
const revealed = revealWorldRect(sourceViewport, {
|
|
114
|
+
height: focusedItemHeight,
|
|
115
|
+
width: focusedItemWidth,
|
|
116
|
+
x: focusedItemX,
|
|
117
|
+
y: focusedItemY,
|
|
118
|
+
}, revealPaddingPixels, constraints);
|
|
119
|
+
if (isRepeatedControlledFocusRevealProposal({
|
|
120
|
+
controlled: !uncontrolled,
|
|
121
|
+
focusedItemId: focusedItemIdForReveal,
|
|
122
|
+
previous: focusRevealProposalRef.current,
|
|
123
|
+
sourceViewport,
|
|
124
|
+
viewport: revealed,
|
|
125
|
+
})) {
|
|
126
|
+
return;
|
|
56
127
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
128
|
+
focusRevealProposalRef.current = {
|
|
129
|
+
focusedItemId: focusedItemIdForReveal,
|
|
130
|
+
sourceViewport,
|
|
131
|
+
viewport: revealed,
|
|
132
|
+
};
|
|
133
|
+
focusRevealViewportRef.current = areViewportsEqual(sourceViewport, revealed)
|
|
134
|
+
? undefined
|
|
135
|
+
: revealed;
|
|
136
|
+
publishRef.current(revealed);
|
|
137
|
+
}, [
|
|
138
|
+
constraints,
|
|
139
|
+
focusedItemHeight,
|
|
140
|
+
focusedItemIdForReveal,
|
|
141
|
+
focusedItemWidth,
|
|
142
|
+
focusedItemX,
|
|
143
|
+
focusedItemY,
|
|
144
|
+
revealPaddingPixels,
|
|
145
|
+
uncontrolled,
|
|
146
|
+
]);
|
|
147
|
+
const scrollMapping = createScrollMapping(viewport, constraints);
|
|
148
|
+
const syncScrollPosition = () => {
|
|
149
|
+
const nextMapping = createScrollMapping(viewportRef.current, constraints);
|
|
150
|
+
const nextPosition = {
|
|
151
|
+
x: nextMapping.horizontal.position,
|
|
152
|
+
y: nextMapping.vertical.position,
|
|
153
|
+
};
|
|
154
|
+
scrollPositionRef.current = nextPosition;
|
|
155
|
+
horizontalScrollRef.current?.scrollTo({ animated: false, x: nextPosition.x });
|
|
156
|
+
verticalScrollRef.current?.scrollTo({ animated: false, y: nextPosition.y });
|
|
157
|
+
};
|
|
158
|
+
return (<NativeView ref={gridRef} accessibilityLabel="Interactive grid viewport" onLayout={refreshViewportOrigin} onTouchEnd={() => {
|
|
159
|
+
const wasPinching = pinchGestureRef.current !== undefined;
|
|
160
|
+
pinchMeasurementRef.current += 1;
|
|
161
|
+
pinchSessionRef.current += 1;
|
|
162
|
+
pinchGestureRef.current = undefined;
|
|
163
|
+
setIsPinching(false);
|
|
164
|
+
if (wasPinching)
|
|
165
|
+
syncScrollPosition();
|
|
166
|
+
}} onTouchCancel={() => {
|
|
167
|
+
const wasPinching = pinchGestureRef.current !== undefined;
|
|
168
|
+
pinchMeasurementRef.current += 1;
|
|
169
|
+
pinchSessionRef.current += 1;
|
|
170
|
+
pinchGestureRef.current = undefined;
|
|
171
|
+
setIsPinching(false);
|
|
172
|
+
if (wasPinching)
|
|
173
|
+
syncScrollPosition();
|
|
174
|
+
}} onTouchMove={(event) => {
|
|
175
|
+
if (!interactive)
|
|
176
|
+
return;
|
|
177
|
+
measurePinchGesture([...event.nativeEvent.touches], pinchSessionRef.current, (pinch) => {
|
|
178
|
+
const previous = pinchGestureRef.current;
|
|
179
|
+
pinchGestureRef.current = pinch;
|
|
180
|
+
setIsPinching(pinch !== undefined);
|
|
181
|
+
if (!pinch || !previous)
|
|
182
|
+
return;
|
|
183
|
+
zoomAt(pinch.focalPoint, pinch.distance / previous.distance);
|
|
184
|
+
});
|
|
185
|
+
}} onTouchStart={(event) => {
|
|
186
|
+
const session = pinchSessionRef.current + 1;
|
|
187
|
+
pinchSessionRef.current = session;
|
|
188
|
+
measurePinchGesture([...event.nativeEvent.touches], session, (pinch) => {
|
|
189
|
+
pinchGestureRef.current = pinch;
|
|
190
|
+
setIsPinching(pinch !== undefined);
|
|
66
191
|
});
|
|
192
|
+
}} style={{ height, overflow: 'hidden', width }} testID={testID}>
|
|
193
|
+
<NativeScrollView ref={horizontalScrollRef} testID={testID ? `${testID}-horizontal-scroll` : undefined} horizontal scrollEnabled={interactive && !isPinching} scrollEventThrottle={16} showsHorizontalScrollIndicator style={{ height, width }} onScroll={(event) => {
|
|
194
|
+
if (pinchGestureRef.current)
|
|
195
|
+
return;
|
|
196
|
+
const { x } = event.nativeEvent.contentOffset;
|
|
197
|
+
const displacement = x - scrollPositionRef.current.x;
|
|
198
|
+
scrollPositionRef.current = { ...scrollPositionRef.current, x };
|
|
199
|
+
if (displacement !== 0)
|
|
200
|
+
pan({ x: -displacement, y: 0 });
|
|
201
|
+
}}>
|
|
202
|
+
<NativeScrollView ref={verticalScrollRef} testID={testID ? `${testID}-vertical-scroll` : undefined} scrollEnabled={interactive && !isPinching} scrollEventThrottle={16} showsVerticalScrollIndicator style={{ height, width: scrollMapping.horizontal.contentSize }} onScroll={(event) => {
|
|
203
|
+
if (pinchGestureRef.current)
|
|
204
|
+
return;
|
|
205
|
+
const { y } = event.nativeEvent.contentOffset;
|
|
206
|
+
const displacement = y - scrollPositionRef.current.y;
|
|
207
|
+
scrollPositionRef.current = { ...scrollPositionRef.current, y };
|
|
208
|
+
if (displacement !== 0)
|
|
209
|
+
pan({ x: 0, y: -displacement });
|
|
210
|
+
}}>
|
|
211
|
+
<NativeView style={{
|
|
212
|
+
height: scrollMapping.vertical.contentSize,
|
|
213
|
+
width: scrollMapping.horizontal.contentSize,
|
|
214
|
+
}}>
|
|
215
|
+
{visibleItems.map((item) => {
|
|
216
|
+
const position = worldToViewport({ x: item.x, y: item.y }, { ...viewport, offsetX: 0, offsetY: 0 });
|
|
67
217
|
return (<NativeView key={item.id} style={{
|
|
218
|
+
height: item.height * viewport.pixelsPerUnitY,
|
|
219
|
+
left: position.x + scrollMapping.horizontal.origin,
|
|
68
220
|
position: 'absolute',
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
width: item.width * scale,
|
|
72
|
-
height: item.height * scale,
|
|
221
|
+
top: position.y + scrollMapping.vertical.origin,
|
|
222
|
+
width: item.width * viewport.pixelsPerUnitX,
|
|
73
223
|
}}>
|
|
74
|
-
|
|
75
|
-
|
|
224
|
+
{renderItem(item)}
|
|
225
|
+
</NativeView>);
|
|
76
226
|
})}
|
|
77
|
-
|
|
227
|
+
</NativeView>
|
|
228
|
+
</NativeScrollView>
|
|
78
229
|
</NativeScrollView>
|
|
79
|
-
|
|
230
|
+
{interactive ? (<NativeView accessibilityLabel="Viewport controls" style={controlsStyle}>
|
|
231
|
+
<NativePressable accessibilityLabel="Pan left" accessibilityRole="button" onPress={() => pan({ x: 48, y: 0 })}>
|
|
232
|
+
<NativeText>←</NativeText>
|
|
233
|
+
</NativePressable>
|
|
234
|
+
<NativePressable accessibilityLabel="Pan right" accessibilityRole="button" onPress={() => pan({ x: -48, y: 0 })}>
|
|
235
|
+
<NativeText>→</NativeText>
|
|
236
|
+
</NativePressable>
|
|
237
|
+
<NativePressable accessibilityLabel="Pan up" accessibilityRole="button" onPress={() => pan({ x: 0, y: 48 })}>
|
|
238
|
+
<NativeText>↑</NativeText>
|
|
239
|
+
</NativePressable>
|
|
240
|
+
<NativePressable accessibilityLabel="Pan down" accessibilityRole="button" onPress={() => pan({ x: 0, y: -48 })}>
|
|
241
|
+
<NativeText>↓</NativeText>
|
|
242
|
+
</NativePressable>
|
|
243
|
+
<NativePressable accessibilityLabel="Zoom in" accessibilityRole="button" onPress={() => zoomAt({ x: width / 2, y: height / 2 }, 1.2)}>
|
|
244
|
+
<NativeText>+</NativeText>
|
|
245
|
+
</NativePressable>
|
|
246
|
+
<NativePressable accessibilityLabel="Zoom out" accessibilityRole="button" onPress={() => zoomAt({ x: width / 2, y: height / 2 }, 1 / 1.2)}>
|
|
247
|
+
<NativeText>−</NativeText>
|
|
248
|
+
</NativePressable>
|
|
249
|
+
</NativeView>) : null}
|
|
250
|
+
</NativeView>);
|
|
251
|
+
}
|
|
252
|
+
const controlsStyle = { bottom: 8, gap: 4, position: 'absolute', right: 8 };
|
|
253
|
+
/*** Builds the initial world position and independent scales for uncontrolled rendering. */
|
|
254
|
+
function createInitialViewport(width, height, zoom, viewport) {
|
|
255
|
+
return {
|
|
256
|
+
height,
|
|
257
|
+
offsetX: viewport?.offsetX ?? 0,
|
|
258
|
+
offsetY: viewport?.offsetY ?? 0,
|
|
259
|
+
pixelsPerUnitX: viewport?.pixelsPerUnitX ?? zoom,
|
|
260
|
+
pixelsPerUnitY: viewport?.pixelsPerUnitY ?? zoom,
|
|
261
|
+
width,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/*** Uses the rendered content extent as the default finite world boundary. */
|
|
265
|
+
function createContentConstraints(contentWidth, contentHeight) {
|
|
266
|
+
return { world: { height: contentHeight, width: contentWidth, x: 0, y: 0 } };
|
|
267
|
+
}
|
|
268
|
+
/*** Maps engine-constrained world offsets to nonnegative native scroll coordinates. */
|
|
269
|
+
function createScrollMapping(viewport, constraints) {
|
|
270
|
+
const horizontalMinimum = constrainViewport({ ...viewport, offsetX: -Number.MAX_SAFE_INTEGER }, constraints).offsetX;
|
|
271
|
+
const horizontalMaximum = constrainViewport({ ...viewport, offsetX: Number.MAX_SAFE_INTEGER }, constraints).offsetX;
|
|
272
|
+
const verticalMinimum = constrainViewport({ ...viewport, offsetY: -Number.MAX_SAFE_INTEGER }, constraints).offsetY;
|
|
273
|
+
const verticalMaximum = constrainViewport({ ...viewport, offsetY: Number.MAX_SAFE_INTEGER }, constraints).offsetY;
|
|
274
|
+
return {
|
|
275
|
+
horizontal: createScrollAxisMapping({
|
|
276
|
+
maximum: horizontalMaximum,
|
|
277
|
+
minimum: horizontalMinimum,
|
|
278
|
+
offset: viewport.offsetX,
|
|
279
|
+
scale: viewport.pixelsPerUnitX,
|
|
280
|
+
viewportSize: viewport.width,
|
|
281
|
+
}),
|
|
282
|
+
vertical: createScrollAxisMapping({
|
|
283
|
+
maximum: verticalMaximum,
|
|
284
|
+
minimum: verticalMinimum,
|
|
285
|
+
offset: viewport.offsetY,
|
|
286
|
+
scale: viewport.pixelsPerUnitY,
|
|
287
|
+
viewportSize: viewport.height,
|
|
288
|
+
}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/*** Derives a physical scroll axis from the published engine constraint result. */
|
|
292
|
+
function createScrollAxisMapping({ maximum, minimum, offset, scale, viewportSize, }) {
|
|
293
|
+
const range = (maximum - minimum) * scale;
|
|
294
|
+
return {
|
|
295
|
+
contentSize: viewportSize + range,
|
|
296
|
+
origin: -minimum * scale,
|
|
297
|
+
position: (offset - minimum) * scale,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/*** Compares canonical viewport fields without treating equal proposals as state changes. */
|
|
301
|
+
function areViewportsEqual(left, right) {
|
|
302
|
+
return (left.width === right.width &&
|
|
303
|
+
left.height === right.height &&
|
|
304
|
+
left.offsetX === right.offsetX &&
|
|
305
|
+
left.offsetY === right.offsetY &&
|
|
306
|
+
left.pixelsPerUnitX === right.pixelsPerUnitX &&
|
|
307
|
+
left.pixelsPerUnitY === right.pixelsPerUnitY);
|
|
308
|
+
}
|
|
309
|
+
/*** Avoids requesting native scrolling again when the physical position is already current. */
|
|
310
|
+
function areScrollPositionsEqual(left, right) {
|
|
311
|
+
return left.x === right.x && left.y === right.y;
|
|
312
|
+
}
|
|
313
|
+
/*** Skips a rejected controlled focus proposal until its focus or incoming viewport changes. */
|
|
314
|
+
function isRepeatedControlledFocusRevealProposal({ controlled, focusedItemId, previous, sourceViewport, viewport, }) {
|
|
315
|
+
return (controlled &&
|
|
316
|
+
focusedItemId !== undefined &&
|
|
317
|
+
previous?.focusedItemId === focusedItemId &&
|
|
318
|
+
areViewportsEqual(previous.sourceViewport, sourceViewport) &&
|
|
319
|
+
areViewportsEqual(previous.viewport, viewport));
|
|
320
|
+
}
|
|
321
|
+
/*** Extracts a stable two-finger focal point in the measured outer viewport coordinate space. */
|
|
322
|
+
function getPinchGesture(touches, width, height, viewportOrigin) {
|
|
323
|
+
const [first, second] = touches;
|
|
324
|
+
if (!first || !second)
|
|
325
|
+
return undefined;
|
|
326
|
+
const deltaX = second.pageX - first.pageX;
|
|
327
|
+
const deltaY = second.pageY - first.pageY;
|
|
328
|
+
const distance = Math.hypot(deltaX, deltaY);
|
|
329
|
+
if (!Number.isFinite(distance) || distance === 0)
|
|
330
|
+
return undefined;
|
|
331
|
+
return {
|
|
332
|
+
distance,
|
|
333
|
+
focalPoint: {
|
|
334
|
+
x: Math.min(width, Math.max(0, (first.pageX + second.pageX) / 2 - viewportOrigin.x)),
|
|
335
|
+
y: Math.min(height, Math.max(0, (first.pageY + second.pageY) / 2 - viewportOrigin.y)),
|
|
336
|
+
},
|
|
337
|
+
};
|
|
80
338
|
}
|
|
81
339
|
//# sourceMappingURL=GridView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GridView.js","sourceRoot":"","sources":["../../../../../src/features/grid-view/adapters/inbound/GridView.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getVisibleGridItems,\n type GridViewport,\n revealWorldRect,\n worldToViewport,\n} from '@ankhorage/grid-view';\nimport React from 'react';\nimport { ScrollView as NativeScrollView, View as NativeView } from 'react-native';\n\nimport type { GridViewProps } from '../../../../types/grid-view';\n\n/***\n * Renders world-positioned items on native and web using the canonical viewport/culling engine.\n *\n * This first renderer uses nested native scroll regions. Only visible items are mounted;\n * logical cells and invisible elements are never rendered.\n */\nexport function GridView({\n items,\n contentWidth,\n contentHeight,\n width,\n height,\n zoom = 1,\n overscanPixels = 160,\n focusedItemId,\n revealPaddingPixels = 8,\n interactionPolicy,\n onViewportChange,\n onVisibleItemIdsChange,\n renderItem,\n testID,\n}: GridViewProps) {\n const [scrollX, setScrollX] = React.useState(0);\n const [scrollY, setScrollY] = React.useState(0);\n const horizontalScrollRef = React.useRef<NativeScrollView>(null);\n const verticalScrollRef = React.useRef<NativeScrollView>(null);\n const scale = Math.max(0.01, zoom);\n const viewport = React.useMemo<GridViewport>(\n () => ({\n width,\n height,\n offsetX: scrollX / scale,\n offsetY: scrollY / scale,\n pixelsPerUnitX: scale,\n pixelsPerUnitY: scale,\n }),\n [height, scale, scrollX, scrollY, width],\n );\n const visibleItems = React.useMemo(\n () => getVisibleGridItems(items, viewport, overscanPixels),\n [items, overscanPixels, viewport],\n );\n const viewportVisibleIds = React.useMemo(\n () => getVisibleGridItems(visibleItems, viewport).map((item) => item.id),\n [viewport, visibleItems],\n );\n const previousVisibleIdsRef = React.useRef<readonly string[]>([]);\n\n React.useEffect(() => {\n if (!onVisibleItemIdsChange) return;\n const previous = previousVisibleIdsRef.current;\n if (\n previous.length === viewportVisibleIds.length &&\n previous.every((id, index) => id === viewportVisibleIds.at(index))\n ) {\n return;\n }\n previousVisibleIdsRef.current = viewportVisibleIds;\n onVisibleItemIdsChange(viewportVisibleIds);\n }, [onVisibleItemIdsChange, viewportVisibleIds]);\n\n React.useEffect(() => {\n onViewportChange?.(viewport);\n }, [onViewportChange, viewport]);\n\n const viewportRef = React.useRef(viewport);\n React.useEffect(() => {\n viewportRef.current = viewport;\n }, [viewport]);\n\n React.useEffect(() => {\n const currentViewport = viewportRef.current;\n const focusedItem = items.find((item) => item.id === focusedItemId);\n if (!focusedItem) return;\n const revealed = revealWorldRect(currentViewport, focusedItem, revealPaddingPixels);\n if (revealed.offsetX !== currentViewport.offsetX) {\n horizontalScrollRef.current?.scrollTo({ x: revealed.offsetX * scale, animated: true });\n }\n if (revealed.offsetY !== currentViewport.offsetY) {\n verticalScrollRef.current?.scrollTo({ y: revealed.offsetY * scale, animated: true });\n }\n }, [focusedItemId, height, items, revealPaddingPixels, scale, width]);\n\n return (\n <NativeScrollView\n ref={horizontalScrollRef}\n horizontal\n scrollEnabled={interactionPolicy !== 'passive'}\n scrollEventThrottle={32}\n showsHorizontalScrollIndicator\n style={{ width, height }}\n testID={testID}\n onScroll={(event) => setScrollX(event.nativeEvent.contentOffset.x)}\n >\n <NativeScrollView\n ref={verticalScrollRef}\n scrollEnabled={interactionPolicy !== 'passive'}\n scrollEventThrottle={32}\n showsVerticalScrollIndicator\n style={{ width: contentWidth * scale, height }}\n onScroll={(event) => setScrollY(event.nativeEvent.contentOffset.y)}\n >\n <NativeView style={{ width: contentWidth * scale, height: contentHeight * scale }}>\n {visibleItems.map((item) => {\n const position = worldToViewport(\n { x: item.x, y: item.y },\n {\n ...viewport,\n offsetX: 0,\n offsetY: 0,\n },\n );\n return (\n <NativeView\n key={item.id}\n style={{\n position: 'absolute',\n left: position.x,\n top: position.y,\n width: item.width * scale,\n height: item.height * scale,\n }}\n >\n {renderItem(item)}\n </NativeView>\n );\n })}\n </NativeView>\n </NativeScrollView>\n </NativeScrollView>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"GridView.js","sourceRoot":"","sources":["../../../../../src/features/grid-view/adapters/inbound/GridView.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n constrainViewport,\n getVisibleGridItems,\n type GridPoint,\n type GridViewport,\n type GridViewportConstraints,\n panViewport,\n revealWorldRect,\n worldToViewport,\n zoomViewportAt,\n} from '@ankhorage/grid-view';\nimport React from 'react';\nimport {\n Pressable as NativePressable,\n ScrollView as NativeScrollView,\n Text as NativeText,\n View as NativeView,\n} from 'react-native';\n\nimport type { GridViewProps } from '../../../../types/grid-view';\n\ntype ScrollPosition = Readonly<{ x: number; y: number }>;\ntype PinchGesture = Readonly<{ distance: number; focalPoint: GridPoint }>;\ntype ScrollAxisMapping = Readonly<{ contentSize: number; origin: number; position: number }>;\ntype ScrollMapping = Readonly<{\n horizontal: ScrollAxisMapping;\n vertical: ScrollAxisMapping;\n}>;\ntype ViewportOrigin = Readonly<{ x: number; y: number }>;\ntype FocusRevealProposal = Readonly<{\n focusedItemId: string;\n sourceViewport: GridViewport;\n viewport: GridViewport;\n}>;\ntype PinchTouch = Readonly<{ pageX: number; pageY: number }>;\n\n/*** Renders a controlled or uncontrolled, virtualized 2D world through the canonical grid viewport engine. */\nexport function GridView({\n items,\n contentWidth,\n contentHeight,\n width,\n height,\n viewport: controlledViewport,\n defaultViewport,\n viewportConstraints,\n zoomLimits,\n zoom = 1,\n overscanPixels = 160,\n focusedItemId,\n revealPaddingPixels = 8,\n interactionPolicy,\n onViewportChange,\n onVisibleItemIdsChange,\n renderItem,\n testID,\n}: GridViewProps) {\n const [uncontrolledViewport, setUncontrolledViewport] = React.useState(() =>\n createInitialViewport(width, height, zoom, defaultViewport),\n );\n const horizontalScrollRef = React.useRef<NativeScrollView>(null);\n const verticalScrollRef = React.useRef<NativeScrollView>(null);\n const gridRef = React.useRef<React.ComponentRef<typeof NativeView>>(null);\n const scrollPositionRef = React.useRef<ScrollPosition>({ x: 0, y: 0 });\n const pinchGestureRef = React.useRef<PinchGesture | undefined>(undefined);\n const pinchMeasurementRef = React.useRef(0);\n const pinchSessionRef = React.useRef(0);\n const viewportOriginRef = React.useRef<ViewportOrigin>({ x: 0, y: 0 });\n const focusRevealProposalRef = React.useRef<FocusRevealProposal | undefined>(undefined);\n const focusRevealViewportRef = React.useRef<GridViewport | undefined>(undefined);\n const [isPinching, setIsPinching] = React.useState(false);\n const constraints = React.useMemo(\n () => viewportConstraints ?? createContentConstraints(contentWidth, contentHeight),\n [contentHeight, contentWidth, viewportConstraints],\n );\n const sourceViewport = controlledViewport ?? uncontrolledViewport;\n const viewport = React.useMemo(\n () => constrainViewport({ ...sourceViewport, height, width }, constraints),\n [constraints, height, sourceViewport, width],\n );\n const viewportRef = React.useRef(viewport);\n const visibleItems = React.useMemo(\n () => getVisibleGridItems(items, viewport, overscanPixels),\n [items, overscanPixels, viewport],\n );\n const viewportVisibleIds = React.useMemo(\n () => getVisibleGridItems(visibleItems, viewport).map((item) => item.id),\n [viewport, visibleItems],\n );\n const focusedItem = items.find((item) => item.id === focusedItemId);\n const focusedItemHeight = focusedItem?.height;\n const focusedItemIdForReveal = focusedItem?.id;\n const focusedItemWidth = focusedItem?.width;\n const focusedItemX = focusedItem?.x;\n const focusedItemY = focusedItem?.y;\n const previousVisibleIdsRef = React.useRef<readonly string[]>([]);\n const uncontrolled = controlledViewport === undefined;\n const interactive = interactionPolicy !== 'passive';\n\n const publish = React.useCallback(\n (nextViewport: GridViewport) => {\n if (areViewportsEqual(viewportRef.current, nextViewport)) return;\n viewportRef.current = nextViewport;\n if (uncontrolled) setUncontrolledViewport(nextViewport);\n onViewportChange?.(nextViewport);\n },\n [onViewportChange, uncontrolled],\n );\n const publishRef = React.useRef(publish);\n const pan = (displacement: GridPoint) =>\n publish(panViewport(viewportRef.current, displacement, constraints));\n const zoomAt = (focalPoint: GridPoint, factor: number) =>\n publish(\n zoomViewportAt(\n viewportRef.current,\n focalPoint,\n {\n pixelsPerUnitX: viewportRef.current.pixelsPerUnitX * factor,\n pixelsPerUnitY: viewportRef.current.pixelsPerUnitY * factor,\n },\n zoomLimits,\n constraints,\n ),\n );\n\n React.useEffect(() => {\n viewportRef.current = viewport;\n }, [viewport]);\n\n React.useEffect(() => {\n publishRef.current = publish;\n }, [publish]);\n\n const refreshViewportOrigin = React.useCallback(() => {\n gridRef.current?.measureInWindow((x, y) => {\n viewportOriginRef.current = { x, y };\n });\n }, []);\n\n const measurePinchGesture = React.useCallback(\n (\n touches: readonly PinchTouch[],\n session: number,\n onMeasured: (pinch: PinchGesture | undefined) => void,\n ) => {\n const measurement = pinchMeasurementRef.current + 1;\n pinchMeasurementRef.current = measurement;\n gridRef.current?.measureInWindow((x, y) => {\n if (measurement !== pinchMeasurementRef.current || session !== pinchSessionRef.current) {\n return;\n }\n viewportOriginRef.current = { x, y };\n onMeasured(getPinchGesture(touches, width, height, { x, y }));\n });\n },\n [height, width],\n );\n\n React.useLayoutEffect(() => {\n refreshViewportOrigin();\n }, [height, refreshViewportOrigin, width]);\n\n React.useEffect(\n () => () => {\n pinchMeasurementRef.current += 1;\n pinchSessionRef.current += 1;\n },\n [],\n );\n\n React.useEffect(() => {\n if (!onVisibleItemIdsChange) return;\n const previous = previousVisibleIdsRef.current;\n if (\n previous.length === viewportVisibleIds.length &&\n previous.every((id, index) => id === viewportVisibleIds.at(index))\n ) {\n return;\n }\n previousVisibleIdsRef.current = viewportVisibleIds;\n onVisibleItemIdsChange(viewportVisibleIds);\n }, [onVisibleItemIdsChange, viewportVisibleIds]);\n\n React.useEffect(() => {\n const position = createScrollMapping(viewport, constraints);\n const nextPosition = {\n x: position.horizontal.position,\n y: position.vertical.position,\n };\n if (areScrollPositionsEqual(scrollPositionRef.current, nextPosition)) return;\n scrollPositionRef.current = nextPosition;\n const animated =\n focusRevealViewportRef.current !== undefined &&\n areViewportsEqual(viewport, focusRevealViewportRef.current);\n horizontalScrollRef.current?.scrollTo({ animated, x: nextPosition.x });\n verticalScrollRef.current?.scrollTo({ animated, y: nextPosition.y });\n if (animated) focusRevealViewportRef.current = undefined;\n }, [constraints, viewport]);\n\n React.useEffect(() => {\n if (\n focusedItemHeight === undefined ||\n focusedItemIdForReveal === undefined ||\n focusedItemWidth === undefined ||\n focusedItemX === undefined ||\n focusedItemY === undefined\n ) {\n focusRevealProposalRef.current = undefined;\n return;\n }\n const sourceViewport = viewportRef.current;\n const revealed = revealWorldRect(\n sourceViewport,\n {\n height: focusedItemHeight,\n width: focusedItemWidth,\n x: focusedItemX,\n y: focusedItemY,\n },\n revealPaddingPixels,\n constraints,\n );\n if (\n isRepeatedControlledFocusRevealProposal({\n controlled: !uncontrolled,\n focusedItemId: focusedItemIdForReveal,\n previous: focusRevealProposalRef.current,\n sourceViewport,\n viewport: revealed,\n })\n ) {\n return;\n }\n focusRevealProposalRef.current = {\n focusedItemId: focusedItemIdForReveal,\n sourceViewport,\n viewport: revealed,\n };\n focusRevealViewportRef.current = areViewportsEqual(sourceViewport, revealed)\n ? undefined\n : revealed;\n publishRef.current(revealed);\n }, [\n constraints,\n focusedItemHeight,\n focusedItemIdForReveal,\n focusedItemWidth,\n focusedItemX,\n focusedItemY,\n revealPaddingPixels,\n uncontrolled,\n ]);\n\n const scrollMapping = createScrollMapping(viewport, constraints);\n\n const syncScrollPosition = () => {\n const nextMapping = createScrollMapping(viewportRef.current, constraints);\n const nextPosition = {\n x: nextMapping.horizontal.position,\n y: nextMapping.vertical.position,\n };\n scrollPositionRef.current = nextPosition;\n horizontalScrollRef.current?.scrollTo({ animated: false, x: nextPosition.x });\n verticalScrollRef.current?.scrollTo({ animated: false, y: nextPosition.y });\n };\n\n return (\n <NativeView\n ref={gridRef}\n accessibilityLabel=\"Interactive grid viewport\"\n onLayout={refreshViewportOrigin}\n onTouchEnd={() => {\n const wasPinching = pinchGestureRef.current !== undefined;\n pinchMeasurementRef.current += 1;\n pinchSessionRef.current += 1;\n pinchGestureRef.current = undefined;\n setIsPinching(false);\n if (wasPinching) syncScrollPosition();\n }}\n onTouchCancel={() => {\n const wasPinching = pinchGestureRef.current !== undefined;\n pinchMeasurementRef.current += 1;\n pinchSessionRef.current += 1;\n pinchGestureRef.current = undefined;\n setIsPinching(false);\n if (wasPinching) syncScrollPosition();\n }}\n onTouchMove={(event) => {\n if (!interactive) return;\n measurePinchGesture([...event.nativeEvent.touches], pinchSessionRef.current, (pinch) => {\n const previous = pinchGestureRef.current;\n pinchGestureRef.current = pinch;\n setIsPinching(pinch !== undefined);\n if (!pinch || !previous) return;\n zoomAt(pinch.focalPoint, pinch.distance / previous.distance);\n });\n }}\n onTouchStart={(event) => {\n const session = pinchSessionRef.current + 1;\n pinchSessionRef.current = session;\n measurePinchGesture([...event.nativeEvent.touches], session, (pinch) => {\n pinchGestureRef.current = pinch;\n setIsPinching(pinch !== undefined);\n });\n }}\n style={{ height, overflow: 'hidden', width }}\n testID={testID}\n >\n <NativeScrollView\n ref={horizontalScrollRef}\n testID={testID ? `${testID}-horizontal-scroll` : undefined}\n horizontal\n scrollEnabled={interactive && !isPinching}\n scrollEventThrottle={16}\n showsHorizontalScrollIndicator\n style={{ height, width }}\n onScroll={(event) => {\n if (pinchGestureRef.current) return;\n const { x } = event.nativeEvent.contentOffset;\n const displacement = x - scrollPositionRef.current.x;\n scrollPositionRef.current = { ...scrollPositionRef.current, x };\n if (displacement !== 0) pan({ x: -displacement, y: 0 });\n }}\n >\n <NativeScrollView\n ref={verticalScrollRef}\n testID={testID ? `${testID}-vertical-scroll` : undefined}\n scrollEnabled={interactive && !isPinching}\n scrollEventThrottle={16}\n showsVerticalScrollIndicator\n style={{ height, width: scrollMapping.horizontal.contentSize }}\n onScroll={(event) => {\n if (pinchGestureRef.current) return;\n const { y } = event.nativeEvent.contentOffset;\n const displacement = y - scrollPositionRef.current.y;\n scrollPositionRef.current = { ...scrollPositionRef.current, y };\n if (displacement !== 0) pan({ x: 0, y: -displacement });\n }}\n >\n <NativeView\n style={{\n height: scrollMapping.vertical.contentSize,\n width: scrollMapping.horizontal.contentSize,\n }}\n >\n {visibleItems.map((item) => {\n const position = worldToViewport(\n { x: item.x, y: item.y },\n { ...viewport, offsetX: 0, offsetY: 0 },\n );\n return (\n <NativeView\n key={item.id}\n style={{\n height: item.height * viewport.pixelsPerUnitY,\n left: position.x + scrollMapping.horizontal.origin,\n position: 'absolute',\n top: position.y + scrollMapping.vertical.origin,\n width: item.width * viewport.pixelsPerUnitX,\n }}\n >\n {renderItem(item)}\n </NativeView>\n );\n })}\n </NativeView>\n </NativeScrollView>\n </NativeScrollView>\n {interactive ? (\n <NativeView accessibilityLabel=\"Viewport controls\" style={controlsStyle}>\n <NativePressable\n accessibilityLabel=\"Pan left\"\n accessibilityRole=\"button\"\n onPress={() => pan({ x: 48, y: 0 })}\n >\n <NativeText>←</NativeText>\n </NativePressable>\n <NativePressable\n accessibilityLabel=\"Pan right\"\n accessibilityRole=\"button\"\n onPress={() => pan({ x: -48, y: 0 })}\n >\n <NativeText>→</NativeText>\n </NativePressable>\n <NativePressable\n accessibilityLabel=\"Pan up\"\n accessibilityRole=\"button\"\n onPress={() => pan({ x: 0, y: 48 })}\n >\n <NativeText>↑</NativeText>\n </NativePressable>\n <NativePressable\n accessibilityLabel=\"Pan down\"\n accessibilityRole=\"button\"\n onPress={() => pan({ x: 0, y: -48 })}\n >\n <NativeText>↓</NativeText>\n </NativePressable>\n <NativePressable\n accessibilityLabel=\"Zoom in\"\n accessibilityRole=\"button\"\n onPress={() => zoomAt({ x: width / 2, y: height / 2 }, 1.2)}\n >\n <NativeText>+</NativeText>\n </NativePressable>\n <NativePressable\n accessibilityLabel=\"Zoom out\"\n accessibilityRole=\"button\"\n onPress={() => zoomAt({ x: width / 2, y: height / 2 }, 1 / 1.2)}\n >\n <NativeText>−</NativeText>\n </NativePressable>\n </NativeView>\n ) : null}\n </NativeView>\n );\n}\n\nconst controlsStyle = { bottom: 8, gap: 4, position: 'absolute' as const, right: 8 };\n\n/*** Builds the initial world position and independent scales for uncontrolled rendering. */\nfunction createInitialViewport(\n width: number,\n height: number,\n zoom: number,\n viewport: GridViewProps['defaultViewport'],\n): GridViewport {\n return {\n height,\n offsetX: viewport?.offsetX ?? 0,\n offsetY: viewport?.offsetY ?? 0,\n pixelsPerUnitX: viewport?.pixelsPerUnitX ?? zoom,\n pixelsPerUnitY: viewport?.pixelsPerUnitY ?? zoom,\n width,\n };\n}\n\n/*** Uses the rendered content extent as the default finite world boundary. */\nfunction createContentConstraints(\n contentWidth: number,\n contentHeight: number,\n): GridViewportConstraints {\n return { world: { height: contentHeight, width: contentWidth, x: 0, y: 0 } };\n}\n\n/*** Maps engine-constrained world offsets to nonnegative native scroll coordinates. */\nfunction createScrollMapping(\n viewport: GridViewport,\n constraints: GridViewportConstraints,\n): ScrollMapping {\n const horizontalMinimum = constrainViewport(\n { ...viewport, offsetX: -Number.MAX_SAFE_INTEGER },\n constraints,\n ).offsetX;\n const horizontalMaximum = constrainViewport(\n { ...viewport, offsetX: Number.MAX_SAFE_INTEGER },\n constraints,\n ).offsetX;\n const verticalMinimum = constrainViewport(\n { ...viewport, offsetY: -Number.MAX_SAFE_INTEGER },\n constraints,\n ).offsetY;\n const verticalMaximum = constrainViewport(\n { ...viewport, offsetY: Number.MAX_SAFE_INTEGER },\n constraints,\n ).offsetY;\n return {\n horizontal: createScrollAxisMapping({\n maximum: horizontalMaximum,\n minimum: horizontalMinimum,\n offset: viewport.offsetX,\n scale: viewport.pixelsPerUnitX,\n viewportSize: viewport.width,\n }),\n vertical: createScrollAxisMapping({\n maximum: verticalMaximum,\n minimum: verticalMinimum,\n offset: viewport.offsetY,\n scale: viewport.pixelsPerUnitY,\n viewportSize: viewport.height,\n }),\n };\n}\n\ntype ScrollAxisMappingInput = Readonly<{\n maximum: number;\n minimum: number;\n offset: number;\n scale: number;\n viewportSize: number;\n}>;\n\n/*** Derives a physical scroll axis from the published engine constraint result. */\nfunction createScrollAxisMapping({\n maximum,\n minimum,\n offset,\n scale,\n viewportSize,\n}: ScrollAxisMappingInput): ScrollAxisMapping {\n const range = (maximum - minimum) * scale;\n return {\n contentSize: viewportSize + range,\n origin: -minimum * scale,\n position: (offset - minimum) * scale,\n };\n}\n\n/*** Compares canonical viewport fields without treating equal proposals as state changes. */\nfunction areViewportsEqual(left: GridViewport, right: GridViewport): boolean {\n return (\n left.width === right.width &&\n left.height === right.height &&\n left.offsetX === right.offsetX &&\n left.offsetY === right.offsetY &&\n left.pixelsPerUnitX === right.pixelsPerUnitX &&\n left.pixelsPerUnitY === right.pixelsPerUnitY\n );\n}\n\n/*** Avoids requesting native scrolling again when the physical position is already current. */\nfunction areScrollPositionsEqual(left: ScrollPosition, right: ScrollPosition): boolean {\n return left.x === right.x && left.y === right.y;\n}\n\n/*** Skips a rejected controlled focus proposal until its focus or incoming viewport changes. */\nfunction isRepeatedControlledFocusRevealProposal({\n controlled,\n focusedItemId,\n previous,\n sourceViewport,\n viewport,\n}: Readonly<{\n controlled: boolean;\n focusedItemId: string | undefined;\n previous: FocusRevealProposal | undefined;\n sourceViewport: GridViewport;\n viewport: GridViewport;\n}>): boolean {\n return (\n controlled &&\n focusedItemId !== undefined &&\n previous?.focusedItemId === focusedItemId &&\n areViewportsEqual(previous.sourceViewport, sourceViewport) &&\n areViewportsEqual(previous.viewport, viewport)\n );\n}\n\n/*** Extracts a stable two-finger focal point in the measured outer viewport coordinate space. */\nfunction getPinchGesture(\n touches: readonly Readonly<{\n pageX: number;\n pageY: number;\n }>[],\n width: number,\n height: number,\n viewportOrigin: ViewportOrigin,\n): PinchGesture | undefined {\n const [first, second] = touches;\n if (!first || !second) return undefined;\n const deltaX = second.pageX - first.pageX;\n const deltaY = second.pageY - first.pageY;\n const distance = Math.hypot(deltaX, deltaY);\n if (!Number.isFinite(distance) || distance === 0) return undefined;\n return {\n distance,\n focalPoint: {\n x: Math.min(width, Math.max(0, (first.pageX + second.pageX) / 2 - viewportOrigin.x)),\n y: Math.min(height, Math.max(0, (first.pageY + second.pageY) / 2 - viewportOrigin.y)),\n },\n };\n}\n"]}
|