@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.
Files changed (106) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +2 -1
  3. package/dist/capabilities/index.d.ts +2 -74
  4. package/dist/capabilities/index.d.ts.map +1 -1
  5. package/dist/capabilities/index.js +6158 -59
  6. package/dist/capabilities/index.js.map +118 -1
  7. package/dist/cli/index.d.ts +2 -74
  8. package/dist/cli/index.d.ts.map +1 -1
  9. package/dist/cli/index.js +2 -1
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/features/grid-view/adapters/inbound/GridView.d.ts +2 -7
  12. package/dist/features/grid-view/adapters/inbound/GridView.d.ts.map +1 -1
  13. package/dist/features/grid-view/adapters/inbound/GridView.js +309 -51
  14. package/dist/features/grid-view/adapters/inbound/GridView.js.map +1 -1
  15. package/dist/features/grid-view/adapters/inbound/TileGrid.d.ts +1 -1
  16. package/dist/features/grid-view/adapters/inbound/TileGrid.d.ts.map +1 -1
  17. package/dist/features/grid-view/adapters/inbound/TileGrid.js +11 -4
  18. package/dist/features/grid-view/adapters/inbound/TileGrid.js.map +1 -1
  19. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts +5 -0
  20. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts.map +1 -0
  21. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js +59 -0
  22. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js.map +1 -0
  23. package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.d.ts +7 -0
  24. package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.d.ts.map +1 -0
  25. package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.js +24 -0
  26. package/dist/features/matrix-grid/application/resolveMatrixGridContentSize.js.map +1 -0
  27. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts +5 -0
  28. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts.map +1 -0
  29. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js +8 -0
  30. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js.map +1 -0
  31. package/dist/features/matrix-grid/meta.d.ts +11 -0
  32. package/dist/features/matrix-grid/meta.d.ts.map +1 -0
  33. package/dist/features/matrix-grid/meta.js +11 -0
  34. package/dist/features/matrix-grid/meta.js.map +1 -0
  35. package/dist/features/matrix-grid/public.d.ts +4 -0
  36. package/dist/features/matrix-grid/public.d.ts.map +1 -0
  37. package/dist/features/matrix-grid/public.js +3 -0
  38. package/dist/features/matrix-grid/public.js.map +1 -0
  39. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.d.ts.map +1 -1
  40. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js +2 -0
  41. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js.map +1 -1
  42. package/dist/features/registry/componentMeta.d.ts.map +1 -1
  43. package/dist/features/registry/componentMeta.js +2 -0
  44. package/dist/features/registry/componentMeta.js.map +1 -1
  45. package/dist/features/registry/createEventCapabilities.d.ts +4 -0
  46. package/dist/features/registry/createEventCapabilities.d.ts.map +1 -0
  47. package/dist/features/registry/createEventCapabilities.js +76 -0
  48. package/dist/features/registry/createEventCapabilities.js.map +1 -0
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/types/grid-view.d.ts +18 -1
  54. package/dist/types/grid-view.d.ts.map +1 -1
  55. package/dist/types/grid-view.js.map +1 -1
  56. package/dist/types/matrix-grid.d.ts +23 -0
  57. package/dist/types/matrix-grid.d.ts.map +1 -0
  58. package/dist/types/matrix-grid.js +2 -0
  59. package/dist/types/matrix-grid.js.map +1 -0
  60. package/package.json +1822 -4
  61. package/src/capabilities/index.test.ts +87 -15
  62. package/src/capabilities/index.ts +5 -2
  63. package/src/cli/index.test.ts +5 -3
  64. package/src/cli/index.ts +5 -2
  65. package/src/features/grid-view/adapters/inbound/GridView.interaction.test.tsx +585 -0
  66. package/src/features/grid-view/adapters/inbound/GridView.test.tsx +74 -0
  67. package/src/features/grid-view/adapters/inbound/GridView.tsx +506 -77
  68. package/src/features/grid-view/adapters/inbound/TileGrid.tsx +26 -3
  69. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.test.tsx +232 -0
  70. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx +142 -0
  71. package/src/features/matrix-grid/application/resolveMatrixGridContentSize.test.ts +32 -0
  72. package/src/features/matrix-grid/application/resolveMatrixGridContentSize.ts +28 -0
  73. package/src/features/matrix-grid/application/resolveMatrixGridSelection.test.ts +10 -0
  74. package/src/features/matrix-grid/application/resolveMatrixGridSelection.ts +15 -0
  75. package/src/features/matrix-grid/application/resolveMatrixGridVisibleCells.test.ts +35 -0
  76. package/src/features/matrix-grid/meta.ts +12 -0
  77. package/src/features/matrix-grid/public.ts +3 -0
  78. package/src/features/registry/ZORA_COMPONENT_REGISTRY.ts +2 -0
  79. package/src/features/registry/componentMeta.ts +2 -0
  80. package/src/features/registry/createEventCapabilities.ts +108 -0
  81. package/src/features/time-grid/adapters/inbound/TimeGrid.test.tsx +35 -8
  82. package/src/index.ts +2 -0
  83. package/src/structureOwnership.test.ts +5 -0
  84. package/src/types/grid-view.ts +27 -1
  85. package/src/types/matrix-grid.ts +29 -0
  86. package/web-dist/chunks/{Accordion-d6azgz09.js → Accordion-2r0gqk3x.js} +1 -1
  87. package/web-dist/chunks/Accordion-a6551mh4.js +515 -0
  88. package/web-dist/chunks/{Accordion-0my2z6qe.js → Accordion-bz04casa.js} +132 -3
  89. package/web-dist/chunks/{Accordion-7qfd4cb4.js → Accordion-jh72azm2.js} +1 -1
  90. package/web-dist/components/explorer/Explorer.js +2 -2
  91. package/web-dist/components/file-explorer/FileExplorer.js +2 -2
  92. package/web-dist/components/grid-line-overlay/GridLineOverlay.js +1 -1
  93. package/web-dist/components/grid-ruler/GridRuler.js +1 -1
  94. package/web-dist/components/grid-view/GridView.js +1 -1
  95. package/web-dist/components/matrix-grid/MatrixGrid.d.ts +3 -0
  96. package/web-dist/components/matrix-grid/MatrixGrid.js +153 -0
  97. package/web-dist/components/matrix-grid/index.d.ts +1 -0
  98. package/web-dist/components/matrix-grid/index.js +2 -0
  99. package/web-dist/components/media-explorer/MediaExplorer.js +2 -2
  100. package/web-dist/components/reader/Reader.js +1 -1
  101. package/web-dist/components/spatial-grid/SpatialGrid.js +1 -1
  102. package/web-dist/components/tile-grid/TileGrid.js +1 -1
  103. package/web-dist/components/time-grid/TimeGrid.js +2 -2
  104. package/web-dist/components/uploader/Uploader.js +2 -2
  105. package/web-dist/manifest.json +53 -24
  106. package/web-dist/chunks/Accordion-gxj5qbpr.js +0 -194
@@ -0,0 +1,108 @@
1
+ import { areCapabilitiesEqual, isCapabilityId } from '@ankhorage/capability';
2
+ import type { Capability } from '@ankhorage/contracts/capability';
3
+ import type { DataSchema } from '@ankhorage/contracts/data';
4
+
5
+ import type {
6
+ ZoraComponentEventMeta,
7
+ ZoraComponentEventPayloadFieldMeta,
8
+ } from '../../types/authoring';
9
+ import { ZORA_COMPONENT_META } from './componentMeta';
10
+
11
+ /*** Projects direct-manifest ZORA event metadata into unique portable event capabilities. */
12
+ export function createEventCapabilities(): readonly Capability[] {
13
+ const capabilitiesById = Object.values(ZORA_COMPONENT_META)
14
+ .filter((component) => component.directManifestNode)
15
+ .flatMap((component) => Object.values(component.events ?? {}))
16
+ .reduce<ReadonlyMap<Capability['id'], Capability>>(
17
+ (capabilities, event) => addEventCapability(capabilities, event),
18
+ new Map(),
19
+ );
20
+
21
+ return [...capabilitiesById.values()].sort((left, right) => left.id.localeCompare(right.id));
22
+ }
23
+
24
+ /*** Adds one namespaced metadata event while rejecting conflicting duplicate identities. */
25
+ function addEventCapability(
26
+ capabilities: ReadonlyMap<Capability['id'], Capability>,
27
+ event: ZoraComponentEventMeta,
28
+ ): ReadonlyMap<Capability['id'], Capability> {
29
+ if (!isCapabilityId(event.eventType)) return capabilities;
30
+
31
+ const capability = createEventCapability(event);
32
+ const existing = capabilities.get(capability.id);
33
+ if (existing !== undefined && !areCapabilitiesEqual(existing, capability)) {
34
+ throw new Error(`ZORA event metadata defines conflicting capability "${capability.id}".`);
35
+ }
36
+
37
+ return new Map([...capabilities, [capability.id, capability]]);
38
+ }
39
+
40
+ /*** Converts one emitted component event into its serializable canonical descriptor. */
41
+ function createEventCapability(event: ZoraComponentEventMeta): Capability {
42
+ if (!isCapabilityId(event.eventType)) {
43
+ throw new Error(
44
+ `ZORA event metadata must use a namespaced capability id: "${event.eventType}".`,
45
+ );
46
+ }
47
+
48
+ return {
49
+ id: event.eventType,
50
+ owner: '@ankhorage/zora',
51
+ access: ['emit'],
52
+ binding: { kind: 'event', bindableAs: ['source'] },
53
+ label: event.label,
54
+ ...(event.description === undefined ? {} : { description: event.description }),
55
+ output: { schema: createEventPayloadSchema(event.payloadFields ?? []) },
56
+ };
57
+ }
58
+
59
+ /*** Projects the documented event payload fields into an open object schema. */
60
+ function createEventPayloadSchema(
61
+ fields: readonly ZoraComponentEventPayloadFieldMeta[],
62
+ ): DataSchema {
63
+ return {
64
+ type: 'object',
65
+ properties: fields.reduce<Readonly<Record<string, DataSchema>>>(addPayloadField, {}),
66
+ };
67
+ }
68
+
69
+ /*** Adds one dotted metadata path to an immutable object-schema property map. */
70
+ function addPayloadField(
71
+ properties: Readonly<Record<string, DataSchema>>,
72
+ field: ZoraComponentEventPayloadFieldMeta,
73
+ ): Readonly<Record<string, DataSchema>> {
74
+ return addPayloadFieldAtPath(properties, field.path.split('.'), createPayloadFieldSchema(field));
75
+ }
76
+
77
+ /*** Recursively embeds a leaf schema below its documented object-property path. */
78
+ function addPayloadFieldAtPath(
79
+ properties: Readonly<Record<string, DataSchema>>,
80
+ path: readonly string[],
81
+ schema: DataSchema,
82
+ ): Readonly<Record<string, DataSchema>> {
83
+ const [key, ...remainingPath] = path;
84
+ if (key === undefined || key === '') return properties;
85
+ if (remainingPath.length === 0) return { ...properties, [key]: schema };
86
+
87
+ const current = Object.entries(properties).find(([propertyKey]) => propertyKey === key)?.[1];
88
+ const nestedProperties = addPayloadFieldAtPath(current?.properties ?? {}, remainingPath, schema);
89
+ return {
90
+ ...properties,
91
+ [key]: { ...current, type: 'object', properties: nestedProperties },
92
+ };
93
+ }
94
+
95
+ /*** Maps one authoring payload-field type and annotations to its portable data schema. */
96
+ function createPayloadFieldSchema(field: ZoraComponentEventPayloadFieldMeta): DataSchema {
97
+ return {
98
+ ...(field.type === 'object'
99
+ ? { type: 'object' as const }
100
+ : field.type === 'record'
101
+ ? { type: 'object' as const, additionalProperties: true }
102
+ : field.type === 'unknown'
103
+ ? {}
104
+ : { type: field.type }),
105
+ ...(field.label === undefined ? {} : { title: field.label }),
106
+ ...(field.description === undefined ? {} : { description: field.description }),
107
+ };
108
+ }
@@ -105,22 +105,49 @@ test('keeps mounted interval and virtualized variable-height lane labels synchro
105
105
  expect(arrowDown.defaultPrevented).toBe(true);
106
106
  expect(focusedIds).toEqual(['strings']);
107
107
 
108
- const verticalScroller = Array.from(host.querySelectorAll<HTMLElement>('div')).find(
109
- (element) =>
110
- element.getAttribute('style') === 'width: 200px; height: 30px;' &&
111
- Array.from(element.querySelectorAll('div')).some(
112
- (child) => child.getAttribute('style') === 'width: 200px; height: 180px;',
113
- ),
108
+ const verticalScroller = host.querySelector<HTMLElement>(
109
+ '[data-testid="time-grid-viewport-vertical-scroll"]',
114
110
  );
115
111
  if (!verticalScroller) throw new Error('Missing GridView vertical scroller');
116
- Object.defineProperty(verticalScroller, 'scrollTop', { configurable: true, value: 60 });
112
+ verticalScroller.scrollTop = 60;
117
113
  void act(() => verticalScroller.dispatchEvent(new browser.Event('scroll', { bubbles: true })));
118
114
 
119
- expect(viewportOffsets).toContain(30);
115
+ expect(viewportOffsets).toEqual([30]);
120
116
  expect(host.querySelector('[data-testid="time-grid-lane-label-drums"]')).toBeNull();
121
117
  expect(
122
118
  host.querySelector('[data-testid="time-grid-lane-label-bass"]')?.getAttribute('style'),
123
119
  ).toContain('top: -20px');
120
+
121
+ act(() => {
122
+ root.render(
123
+ <TimeGrid
124
+ contentWidth={100}
125
+ focusedIntervalId="strings"
126
+ height={30}
127
+ intervals={[
128
+ { extent: 10, id: 'drums', laneId: 'drums', start: 0 },
129
+ { extent: 10, id: 'strings', laneId: 'strings', start: 0 },
130
+ ]}
131
+ lanes={[
132
+ { height: 20, id: 'drums' },
133
+ { height: 40, id: 'bass' },
134
+ { height: 30, id: 'strings' },
135
+ ]}
136
+ onFocusedIntervalIdChange={(id) => focusedIds.push(id)}
137
+ onViewportChange={(viewport) => viewportOffsets.push(viewport.offsetY)}
138
+ overscanPixels={0}
139
+ renderInterval={(interval, selected) => (
140
+ <span data-selected={selected}>{interval.id}</span>
141
+ )}
142
+ renderLaneLabel={(lane) => <span>{lane.id}</span>}
143
+ selectedIntervalIds={['strings']}
144
+ testID="time-grid"
145
+ width={100}
146
+ zoom={2}
147
+ />,
148
+ );
149
+ });
150
+ expect(viewportOffsets).toEqual([30, 56]);
124
151
  await new Promise((resolve) => setTimeout(resolve, 100));
125
152
  } finally {
126
153
  act(() => root.unmount());
package/src/index.ts CHANGED
@@ -314,6 +314,8 @@ export type {
314
314
  } from './features/list/public';
315
315
  export { FlatList, SectionList } from './features/list/public';
316
316
  export { List, ListItem, ListSection } from './features/list/public';
317
+ export type { MatrixGridProps } from './features/matrix-grid/public';
318
+ export { MatrixGrid } from './features/matrix-grid/public';
317
319
  export { MissingElement, type MissingElementProps } from './features/missing-element/public';
318
320
  export { Pagination, type PaginationProps } from './features/pagination/public';
319
321
  export type { PaletteItemProps } from './features/palette-item/public';
@@ -63,4 +63,9 @@ describe('src ownership', () => {
63
63
  expect(existsSync(path), path).toBe(false);
64
64
  }
65
65
  });
66
+
67
+ test('keeps event capability projection with the registry metadata owner', () => {
68
+ expect(existsSync('src/features/registry/createEventCapabilities.ts')).toBe(true);
69
+ expect(existsSync('src/capabilities/createEventCapabilities.ts')).toBe(false);
70
+ });
66
71
  });
@@ -1,4 +1,9 @@
1
- import type { GridRectItem, GridViewport } from '@ankhorage/grid-view';
1
+ import type {
2
+ GridRectItem,
3
+ GridViewport,
4
+ GridViewportConstraints,
5
+ GridZoomLimits,
6
+ } from '@ankhorage/grid-view';
2
7
  import type React from 'react';
3
8
 
4
9
  import type { ZoraBaseProps } from './base';
@@ -10,6 +15,20 @@ export interface GridViewProps extends ZoraBaseProps {
10
15
  contentHeight: number;
11
16
  width: number;
12
17
  height: number;
18
+ /**
19
+ * Canonical controlled world viewport. Supplying this value makes `onViewportChange`
20
+ * report interaction proposals without mutating renderer-owned state.
21
+ */
22
+ viewport?: GridViewport;
23
+ /** Initial world position and independent axis scales for uncontrolled rendering. */
24
+ defaultViewport?: Partial<
25
+ Pick<GridViewport, 'offsetX' | 'offsetY' | 'pixelsPerUnitX' | 'pixelsPerUnitY'>
26
+ >;
27
+ /** Finite world bounds and optional overscroll for panning and focused-item reveal. */
28
+ viewportConstraints?: GridViewportConstraints;
29
+ /** Independent X/Y scale limits applied to focal-point zoom interactions. */
30
+ zoomLimits?: GridZoomLimits;
31
+ /** @deprecated Use `defaultViewport` for new code. */
13
32
  zoom?: number;
14
33
  overscanPixels?: number;
15
34
  /** Stable item identity that must be scrolled into view without duplicating viewport geometry. */
@@ -34,11 +53,18 @@ export interface TileGridProps extends ZoraBaseProps {
34
53
  tileSize?: number;
35
54
  gap?: number;
36
55
  zoom?: number;
56
+ viewport?: GridViewport;
57
+ defaultViewport?: Partial<
58
+ Pick<GridViewport, 'offsetX' | 'offsetY' | 'pixelsPerUnitX' | 'pixelsPerUnitY'>
59
+ >;
60
+ viewportConstraints?: GridViewportConstraints;
61
+ zoomLimits?: GridZoomLimits;
37
62
  overscanPixels?: number;
38
63
  focusedItemId?: string;
39
64
  revealPaddingPixels?: number;
40
65
  /** Reports the same measured column count used to position tiles. */
41
66
  onColumnsChange?: (columns: number) => void;
67
+ onViewportChange?: (viewport: GridViewport) => void;
42
68
  onVisibleItemIdsChange?: (ids: readonly string[]) => void;
43
69
  renderItem: (item: TileGridItem) => React.ReactNode;
44
70
  }
@@ -0,0 +1,29 @@
1
+ import type {
2
+ GridMatrixCell,
3
+ GridMatrixCellPlacement,
4
+ GridMatrixLayout,
5
+ GridViewport,
6
+ } from '@ankhorage/grid-view';
7
+ import type React from 'react';
8
+
9
+ import type { ZoraBaseProps } from './base';
10
+ import type { SelectionMode } from './selection';
11
+
12
+ /** Code-first virtualized sparse-matrix presentation over the published grid-view geometry API. */
13
+ export interface MatrixGridProps extends ZoraBaseProps {
14
+ readonly layout: GridMatrixLayout;
15
+ readonly cells: readonly GridMatrixCell[];
16
+ readonly width: number;
17
+ readonly height: number;
18
+ /** A supplied viewport is controlled by the caller, including independent axis zoom. */
19
+ readonly viewport?: GridViewport;
20
+ readonly zoomX?: number;
21
+ readonly zoomY?: number;
22
+ readonly overscanPixels?: number;
23
+ readonly selectedCellIds?: readonly string[];
24
+ readonly selectionMode?: SelectionMode;
25
+ readonly onSelectionChange?: (ids: readonly string[]) => void;
26
+ readonly onViewportChange?: (viewport: GridViewport) => void;
27
+ readonly renderCell: (cell: GridMatrixCellPlacement) => React.ReactNode;
28
+ readonly accessibilityLabel?: string;
29
+ }
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  TileGrid2
4
- } from "./Accordion-gxj5qbpr.js";
4
+ } from "./Accordion-a6551mh4.js";
5
5
  import {
6
6
  withZoraThemeScope
7
7
  } from "./Accordion-42jfy1a1.js";