@ankhorage/zora 25.2.1 → 25.4.0
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 +2 -1
- package/dist/features/grid-rulers/adapters/inbound/GridLineOverlay.d.ts +5 -0
- package/dist/features/grid-rulers/adapters/inbound/GridLineOverlay.d.ts.map +1 -0
- package/dist/features/grid-rulers/adapters/inbound/GridLineOverlay.js +69 -0
- package/dist/features/grid-rulers/adapters/inbound/GridLineOverlay.js.map +1 -0
- package/dist/features/grid-rulers/adapters/inbound/GridRuler.d.ts +5 -0
- package/dist/features/grid-rulers/adapters/inbound/GridRuler.d.ts.map +1 -0
- package/dist/features/grid-rulers/adapters/inbound/GridRuler.js +59 -0
- package/dist/features/grid-rulers/adapters/inbound/GridRuler.js.map +1 -0
- package/dist/features/grid-rulers/application/projectGridRulerPosition.d.ts +4 -0
- package/dist/features/grid-rulers/application/projectGridRulerPosition.d.ts.map +1 -0
- package/dist/features/grid-rulers/application/projectGridRulerPosition.js +10 -0
- package/dist/features/grid-rulers/application/projectGridRulerPosition.js.map +1 -0
- package/dist/features/grid-rulers/application/resolveGridRulerMarks.d.ts +5 -0
- package/dist/features/grid-rulers/application/resolveGridRulerMarks.d.ts.map +1 -0
- package/dist/features/grid-rulers/application/resolveGridRulerMarks.js +22 -0
- package/dist/features/grid-rulers/application/resolveGridRulerMarks.js.map +1 -0
- package/dist/features/grid-rulers/meta.d.ts +21 -0
- package/dist/features/grid-rulers/meta.d.ts.map +1 -0
- package/dist/features/grid-rulers/meta.js +22 -0
- package/dist/features/grid-rulers/meta.js.map +1 -0
- package/dist/features/grid-rulers/public.d.ts +5 -0
- package/dist/features/grid-rulers/public.d.ts.map +1 -0
- package/dist/features/grid-rulers/public.js +4 -0
- package/dist/features/grid-rulers/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 +5 -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 +5 -0
- package/dist/features/registry/componentMeta.js.map +1 -1
- package/dist/features/spatial-grid/adapters/inbound/SpatialGrid.d.ts +5 -0
- package/dist/features/spatial-grid/adapters/inbound/SpatialGrid.d.ts.map +1 -0
- package/dist/features/spatial-grid/adapters/inbound/SpatialGrid.js +38 -0
- package/dist/features/spatial-grid/adapters/inbound/SpatialGrid.js.map +1 -0
- package/dist/features/spatial-grid/application/sortSpatialGridItems.d.ts +4 -0
- package/dist/features/spatial-grid/application/sortSpatialGridItems.d.ts.map +1 -0
- package/dist/features/spatial-grid/application/sortSpatialGridItems.js +8 -0
- package/dist/features/spatial-grid/application/sortSpatialGridItems.js.map +1 -0
- package/dist/features/spatial-grid/meta.d.ts +11 -0
- package/dist/features/spatial-grid/meta.d.ts.map +1 -0
- package/dist/features/spatial-grid/meta.js +11 -0
- package/dist/features/spatial-grid/meta.js.map +1 -0
- package/dist/features/spatial-grid/public.d.ts +3 -0
- package/dist/features/spatial-grid/public.d.ts.map +1 -0
- package/dist/features/spatial-grid/public.js +2 -0
- package/dist/features/spatial-grid/public.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/types/grid-rulers.d.ts +48 -0
- package/dist/types/grid-rulers.d.ts.map +1 -0
- package/dist/types/grid-rulers.js +2 -0
- package/dist/types/grid-rulers.js.map +1 -0
- package/dist/types/spatial-grid.d.ts +41 -0
- package/dist/types/spatial-grid.d.ts.map +1 -0
- package/dist/types/spatial-grid.js +2 -0
- package/dist/types/spatial-grid.js.map +1 -0
- package/package.json +3 -3
- package/src/features/grid-rulers/adapters/inbound/GridLineOverlay.tsx +123 -0
- package/src/features/grid-rulers/adapters/inbound/GridRuler.tsx +96 -0
- package/src/features/grid-rulers/adapters/inbound/GridRulers.test.tsx +162 -0
- package/src/features/grid-rulers/application/projectGridRulerPosition.ts +19 -0
- package/src/features/grid-rulers/application/resolveGridRulerMarks.test.ts +90 -0
- package/src/features/grid-rulers/application/resolveGridRulerMarks.ts +39 -0
- package/src/features/grid-rulers/meta.ts +26 -0
- package/src/features/grid-rulers/public.ts +10 -0
- package/src/features/registry/ZORA_COMPONENT_REGISTRY.ts +5 -0
- package/src/features/registry/componentMeta.ts +5 -0
- package/src/features/spatial-grid/adapters/inbound/SpatialGrid.test.tsx +248 -0
- package/src/features/spatial-grid/adapters/inbound/SpatialGrid.tsx +112 -0
- package/src/features/spatial-grid/application/sortSpatialGridItems.test.ts +42 -0
- package/src/features/spatial-grid/application/sortSpatialGridItems.ts +14 -0
- package/src/features/spatial-grid/meta.ts +12 -0
- package/src/features/spatial-grid/public.ts +6 -0
- package/src/index.ts +14 -0
- package/src/types/grid-rulers.ts +59 -0
- package/src/types/spatial-grid.ts +44 -0
- package/web-dist/chunks/{Accordion-mh67wb34.js → Accordion-090y18cv.js} +2 -2
- package/web-dist/chunks/{Accordion-7yazfxr6.js → Accordion-3d4wkzg7.js} +4 -4
- package/web-dist/chunks/{Accordion-ndf3jqhb.js → Accordion-40tenzcg.js} +3 -3
- package/web-dist/chunks/{Accordion-cbfa2b8j.js → Accordion-4s68t3h7.js} +5 -5
- package/web-dist/chunks/{Accordion-7asr7vb0.js → Accordion-4zqd7twp.js} +4 -4
- package/web-dist/chunks/{Accordion-sdcfa14w.js → Accordion-5g05kfmx.js} +3 -3
- package/web-dist/chunks/Accordion-60wn9xn9.js +201 -0
- package/web-dist/chunks/{Accordion-wgejzesa.js → Accordion-61ntvh14.js} +3 -3
- package/web-dist/chunks/{Accordion-q09fjenx.js → Accordion-6rrqpb2h.js} +1 -1
- package/web-dist/chunks/{Accordion-mej71qnw.js → Accordion-a4cma5b6.js} +2 -2
- package/web-dist/chunks/{Accordion-9dqkq9am.js → Accordion-ejx5gcf9.js} +8 -8
- package/web-dist/chunks/{Accordion-pe66x9cv.js → Accordion-ewf1zen9.js} +4 -4
- package/web-dist/chunks/{Accordion-xvzrx6bh.js → Accordion-fjxcn6g9.js} +7 -7
- package/web-dist/chunks/{Accordion-teg40mpt.js → Accordion-fmzxat3v.js} +3 -3
- package/web-dist/chunks/{Accordion-016wvwpg.js → Accordion-h3y8qev2.js} +1 -1
- package/web-dist/chunks/{Accordion-t0hbez90.js → Accordion-h7zsx9eg.js} +2 -2
- package/web-dist/chunks/{Accordion-ch4q5w0z.js → Accordion-jnc315h7.js} +1 -1
- package/web-dist/chunks/{Accordion-fkc50r9z.js → Accordion-kjtcbe09.js} +2 -2
- package/web-dist/chunks/{Accordion-tmrjy46p.js → Accordion-nssr5gep.js} +1 -1
- package/web-dist/chunks/{Accordion-54k0ncs3.js → Accordion-nxsda1my.js} +5 -73
- package/web-dist/chunks/{Accordion-ntndskbj.js → Accordion-pvj5npzc.js} +2 -2
- package/web-dist/chunks/{Accordion-ggwxv6k3.js → Accordion-r1cj2w9e.js} +2 -2
- package/web-dist/chunks/Accordion-r2w86rwv.js +209 -0
- package/web-dist/chunks/{Accordion-kkr7xfh2.js → Accordion-s3d6g59s.js} +4 -4
- package/web-dist/chunks/{Accordion-5ga4549x.js → Accordion-sp4k08kw.js} +3 -3
- package/web-dist/chunks/{Accordion-1p9r2ack.js → Accordion-sy1cc5my.js} +2 -2
- package/web-dist/chunks/{Accordion-hmhdwvqp.js → Accordion-tqpsmdrk.js} +3 -3
- package/web-dist/chunks/{Accordion-fzkrrc4z.js → Accordion-v0grdh6q.js} +1 -1
- package/web-dist/chunks/{Accordion-ajawc6nq.js → Accordion-zt9yghdz.js} +3 -3
- package/web-dist/components/accordion/Accordion.js +4 -4
- package/web-dist/components/accordion-item/AccordionItem.js +4 -4
- package/web-dist/components/app-bar/AppBar.js +6 -6
- package/web-dist/components/app-header/AppHeader.js +6 -6
- package/web-dist/components/app-shell/AppShell.js +1 -1
- package/web-dist/components/auth-screen/AuthScreen.js +9 -9
- package/web-dist/components/avatar/Avatar.js +4 -4
- package/web-dist/components/avatar-group/AvatarGroup.js +4 -4
- package/web-dist/components/badge/Badge.js +1 -1
- package/web-dist/components/barcode-scanner-view/BarcodeScannerView.js +9 -9
- package/web-dist/components/bottom-sheet/BottomSheet.js +2 -2
- package/web-dist/components/bottom-sheet-flat-list/BottomSheetFlatList.js +2 -2
- package/web-dist/components/bottom-sheet-scroll-view/BottomSheetScrollView.js +2 -2
- package/web-dist/components/bottom-sheet-section-list/BottomSheetSectionList.js +2 -2
- package/web-dist/components/bottom-sheet-view/BottomSheetView.js +2 -2
- package/web-dist/components/bottom-sheet-virtualized-list/BottomSheetVirtualizedList.js +2 -2
- package/web-dist/components/breadcrumbs/Breadcrumbs.js +4 -4
- package/web-dist/components/button/Button.js +2 -2
- package/web-dist/components/button-group/ButtonGroup.js +2 -2
- package/web-dist/components/camera-permission-view/CameraPermissionView.js +9 -9
- package/web-dist/components/card/Card.js +8 -8
- package/web-dist/components/card-back/CardBack.js +1 -1
- package/web-dist/components/card-hand/CardHand.js +1 -1
- package/web-dist/components/chat-list-item/ChatListItem.js +6 -6
- package/web-dist/components/checkbox/Checkbox.js +3 -3
- package/web-dist/components/checkbox-group/CheckboxGroup.js +3 -3
- package/web-dist/components/chess-board/ChessBoard.js +1 -1
- package/web-dist/components/chip/Chip.js +4 -4
- package/web-dist/components/chip-group/ChipGroup.js +4 -4
- package/web-dist/components/collection-editor/CollectionEditor.js +8 -8
- package/web-dist/components/content-rail/ContentRail.js +3 -3
- package/web-dist/components/data-table/DataTable.js +13 -13
- package/web-dist/components/date-picker/DatePicker.js +4 -4
- package/web-dist/components/dialog/Dialog.js +3 -3
- package/web-dist/components/divider/Divider.js +1 -1
- package/web-dist/components/empty-state/EmptyState.js +9 -9
- package/web-dist/components/explorer/Explorer.js +6 -6
- package/web-dist/components/field/Field.js +3 -3
- package/web-dist/components/file-explorer/FileExplorer.js +6 -6
- package/web-dist/components/flat-list/FlatList.js +4 -4
- package/web-dist/components/forgot-password-form/ForgotPasswordForm.js +9 -9
- package/web-dist/components/form/Form.js +5 -5
- package/web-dist/components/form-actions/FormActions.js +5 -5
- package/web-dist/components/form-error/FormError.js +5 -5
- package/web-dist/components/game/Game.js +2 -2
- package/web-dist/components/game-entity/GameEntity.js +2 -2
- package/web-dist/components/game-field/GameField.js +2 -2
- package/web-dist/components/game-input-zone/GameInputZone.js +2 -2
- package/web-dist/components/game-measurement-probe/GameMeasurementProbe.js +2 -2
- package/web-dist/components/game-overlay/GameOverlay.js +2 -2
- package/web-dist/components/gradient/Gradient.js +2 -2
- package/web-dist/components/gradient-renderer-provider/GradientRendererProvider.js +2 -2
- package/web-dist/components/grid/Grid.js +1 -1
- package/web-dist/components/grid-line-overlay/GridLineOverlay.d.ts +4 -0
- package/web-dist/components/grid-line-overlay/GridLineOverlay.js +13 -0
- package/web-dist/components/grid-line-overlay/index.d.ts +1 -0
- package/web-dist/components/grid-line-overlay/index.js +2 -0
- package/web-dist/components/grid-ruler/GridRuler.d.ts +4 -0
- package/web-dist/components/grid-ruler/GridRuler.js +13 -0
- package/web-dist/components/grid-ruler/index.d.ts +1 -0
- package/web-dist/components/grid-ruler/index.js +2 -0
- package/web-dist/components/grid-view/GridView.js +1 -1
- package/web-dist/components/heading/Heading.js +1 -1
- package/web-dist/components/hero/Hero.js +8 -8
- package/web-dist/components/icon/Icon.js +1 -1
- package/web-dist/components/icon-button/IconButton.js +2 -2
- package/web-dist/components/image/Image.js +3 -3
- package/web-dist/components/keyboard-avoiding-view/KeyboardAvoidingView.js +1 -1
- package/web-dist/components/list/List.js +4 -4
- package/web-dist/components/list-item/ListItem.js +4 -4
- package/web-dist/components/list-section/ListSection.js +4 -4
- package/web-dist/components/media-card/MediaCard.js +8 -8
- package/web-dist/components/media-explorer/MediaExplorer.js +6 -6
- package/web-dist/components/message-bubble/MessageBubble.js +6 -6
- package/web-dist/components/metric-card/MetricCard.js +8 -8
- package/web-dist/components/missing-element/MissingElement.js +3 -3
- package/web-dist/components/o-auth-provider-button/OAuthProviderButton.js +9 -9
- package/web-dist/components/o-auth-provider-list/OAuthProviderList.js +9 -9
- package/web-dist/components/opening-book/OpeningBook.js +1 -1
- package/web-dist/components/otp-form/OtpForm.js +9 -9
- package/web-dist/components/pagination/Pagination.js +3 -3
- package/web-dist/components/palette-item/PaletteItem.js +8 -8
- package/web-dist/components/playing-card/PlayingCard.js +1 -1
- package/web-dist/components/poker-training-table/PokerTrainingTable.js +1 -1
- package/web-dist/components/popover-menu/PopoverMenu.js +3 -3
- package/web-dist/components/post-card/PostCard.js +8 -8
- package/web-dist/components/product-card/ProductCard.js +8 -8
- package/web-dist/components/progress/Progress.js +3 -3
- package/web-dist/components/progress-ring/ProgressRing.js +3 -3
- package/web-dist/components/radio/Radio.js +4 -4
- package/web-dist/components/radio-group/RadioGroup.js +4 -4
- package/web-dist/components/rating/Rating.js +2 -2
- package/web-dist/components/reader/Reader.js +9 -9
- package/web-dist/components/scan-overlay/ScanOverlay.js +9 -9
- package/web-dist/components/screen/Screen.js +1 -1
- package/web-dist/components/screen-section/ScreenSection.js +3 -3
- package/web-dist/components/scroll-view/ScrollView.js +1 -1
- package/web-dist/components/search-input/SearchInput.js +3 -3
- package/web-dist/components/section-header/SectionHeader.js +3 -3
- package/web-dist/components/section-list/SectionList.js +4 -4
- package/web-dist/components/select/Select.js +3 -3
- package/web-dist/components/selectable-item/SelectableItem.js +1 -1
- package/web-dist/components/selection-provider/SelectionProvider.js +1 -1
- package/web-dist/components/sign-in-form/SignInForm.js +9 -9
- package/web-dist/components/sign-up-form/SignUpForm.js +9 -9
- package/web-dist/components/skeleton/Skeleton.js +11 -11
- package/web-dist/components/skeleton-card/SkeletonCard.js +11 -11
- package/web-dist/components/skeleton-list/SkeletonList.js +11 -11
- package/web-dist/components/skeleton-text/SkeletonText.js +11 -11
- package/web-dist/components/spatial-grid/SpatialGrid.d.ts +3 -0
- package/web-dist/components/spatial-grid/SpatialGrid.js +102 -0
- package/web-dist/components/spatial-grid/index.d.ts +1 -0
- package/web-dist/components/spatial-grid/index.js +2 -0
- package/web-dist/components/surface/Surface.js +1 -1
- package/web-dist/components/tab/Tab.js +1 -1
- package/web-dist/components/tab-list/TabList.js +1 -1
- package/web-dist/components/tab-panel/TabPanel.js +1 -1
- package/web-dist/components/tabletop-table/TabletopTable.js +1 -1
- package/web-dist/components/tabs/Tabs.js +1 -1
- package/web-dist/components/text/Text.js +1 -1
- package/web-dist/components/text-input/TextInput.js +3 -3
- package/web-dist/components/tile-grid/TileGrid.js +1 -1
- package/web-dist/components/time-picker/TimePicker.js +4 -4
- package/web-dist/components/timeline/Timeline.js +3 -3
- package/web-dist/components/toast/Toast.js +1 -1
- package/web-dist/components/toast-provider/ToastProvider.js +1 -1
- package/web-dist/components/toolbar/Toolbar.js +8 -8
- package/web-dist/components/tree-item/TreeItem.js +7 -7
- package/web-dist/components/uploader/Uploader.js +11 -11
- package/web-dist/components/view/View.js +1 -1
- package/web-dist/manifest.json +555 -493
- package/web-dist/runtime/ZoraProvider.js +3 -3
- /package/web-dist/chunks/{Accordion-qkh5yy94.js → Accordion-7kq500r1.js} +0 -0
- /package/web-dist/chunks/{Accordion-f0jsd9f9.js → Accordion-7tjz25jh.js} +0 -0
- /package/web-dist/chunks/{Accordion-3m3572sa.js → Accordion-88t8fgxv.js} +0 -0
- /package/web-dist/chunks/{Accordion-afmybp67.js → Accordion-9w8wg9mf.js} +0 -0
- /package/web-dist/chunks/{Accordion-bb8y34wh.js → Accordion-b96evb46.js} +0 -0
- /package/web-dist/chunks/{Accordion-nmpmdrba.js → Accordion-dbf93gxh.js} +0 -0
- /package/web-dist/chunks/{Accordion-x12fca21.js → Accordion-fhnazn3n.js} +0 -0
- /package/web-dist/chunks/{Accordion-n9c5tsqw.js → Accordion-qn398m1x.js} +0 -0
- /package/web-dist/chunks/{Accordion-2rgexkze.js → Accordion-rsvphbe4.js} +0 -0
- /package/web-dist/chunks/{Accordion-c69374jn.js → Accordion-sw7ep29d.js} +0 -0
- /package/web-dist/chunks/{Accordion-8jxc7tx9.js → Accordion-w6qedeba.js} +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { expect, test } from 'bun:test';
|
|
5
|
+
import { Window } from 'happy-dom';
|
|
6
|
+
import React, { act } from 'react';
|
|
7
|
+
import { createRoot } from 'react-dom/client';
|
|
8
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
9
|
+
|
|
10
|
+
import type { SpatialGrid as SpatialGridComponent } from './SpatialGrid';
|
|
11
|
+
|
|
12
|
+
const webDistRoot = join(import.meta.dir, '../../../../../web-dist');
|
|
13
|
+
|
|
14
|
+
/** Loads the independently bundled React Native Web component through its public artifact path. */
|
|
15
|
+
function loadSpatialGrid() {
|
|
16
|
+
return import(
|
|
17
|
+
pathToFileURL(join(webDistRoot, 'components/spatial-grid/index.js')).href
|
|
18
|
+
) as Promise<{
|
|
19
|
+
SpatialGrid: typeof SpatialGridComponent;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('renders a bounded, accessible 10k free-placement catalog in deterministic overlap order', async () => {
|
|
24
|
+
const items = [
|
|
25
|
+
{ height: 30, id: 'back', width: 30, x: 0, y: 0, zIndex: -1 },
|
|
26
|
+
{ height: 30, id: 'front', width: 30, x: 0, y: 0, zIndex: 1 },
|
|
27
|
+
...Array.from({ length: 9998 }, (_, index) => ({
|
|
28
|
+
height: 20,
|
|
29
|
+
id: `item-${index}`,
|
|
30
|
+
width: 20,
|
|
31
|
+
x: 1000 + (index % 100) * 100,
|
|
32
|
+
y: 1000 + Math.floor(index / 100) * 100,
|
|
33
|
+
})),
|
|
34
|
+
];
|
|
35
|
+
await withBrowserRoot(async (browserWindow) => {
|
|
36
|
+
const { SpatialGrid } = await loadSpatialGrid();
|
|
37
|
+
browserWindow.document.body.innerHTML = renderToStaticMarkup(
|
|
38
|
+
<SpatialGrid
|
|
39
|
+
contentHeight={11000}
|
|
40
|
+
contentWidth={11000}
|
|
41
|
+
height={100}
|
|
42
|
+
items={items}
|
|
43
|
+
selectedItemId="front"
|
|
44
|
+
testID="board"
|
|
45
|
+
width={100}
|
|
46
|
+
zoom={2}
|
|
47
|
+
renderItem={(item) => <span>{item.id}</span>}
|
|
48
|
+
/>,
|
|
49
|
+
);
|
|
50
|
+
const mounted = [...browserWindow.document.querySelectorAll('[data-testid^="board-item-"]')];
|
|
51
|
+
|
|
52
|
+
expect(mounted).toHaveLength(2);
|
|
53
|
+
expect(mounted.map((element) => element.getAttribute('data-testid'))).toEqual([
|
|
54
|
+
'board-item-back',
|
|
55
|
+
'board-item-front',
|
|
56
|
+
]);
|
|
57
|
+
expect(
|
|
58
|
+
browserWindow.document
|
|
59
|
+
.querySelector('[data-testid="board-item-front"]')
|
|
60
|
+
?.getAttribute('role'),
|
|
61
|
+
).toBe('button');
|
|
62
|
+
expect(
|
|
63
|
+
browserWindow.document
|
|
64
|
+
.querySelector('[data-testid="board-item-front"]')
|
|
65
|
+
?.getAttribute('tabindex'),
|
|
66
|
+
).toBe('0');
|
|
67
|
+
expect(
|
|
68
|
+
browserWindow.document
|
|
69
|
+
.querySelector('[data-testid="board-item-front"]')
|
|
70
|
+
?.getAttribute('aria-label'),
|
|
71
|
+
).toBe('Spatial grid item front');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('activates the topmost overlapping item by pointer and keyboard', async () => {
|
|
76
|
+
const { SpatialGrid } = await loadSpatialGrid();
|
|
77
|
+
const selection: string[] = [];
|
|
78
|
+
const activation: string[] = [];
|
|
79
|
+
await withBrowserRoot(async (browserWindow, host, root) => {
|
|
80
|
+
await renderSpatialGrid(
|
|
81
|
+
root,
|
|
82
|
+
<SpatialGrid
|
|
83
|
+
contentHeight={100}
|
|
84
|
+
contentWidth={100}
|
|
85
|
+
height={100}
|
|
86
|
+
items={[
|
|
87
|
+
{ height: 50, id: 'back', width: 50, x: 10, y: 20, zIndex: -1 },
|
|
88
|
+
{ height: 50, id: 'front', width: 50, x: 10, y: 20, zIndex: 1 },
|
|
89
|
+
]}
|
|
90
|
+
testID="interactive-board"
|
|
91
|
+
width={100}
|
|
92
|
+
onActivation={(id) => activation.push(id)}
|
|
93
|
+
onSelectionChange={(id) => selection.push(id)}
|
|
94
|
+
renderItem={(item) => <span>{item.id}</span>}
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
const front = requireItem(host, 'interactive-board-item-front');
|
|
98
|
+
await dispatch(front, new browserWindow.MouseEvent('click', { bubbles: true }));
|
|
99
|
+
await dispatch(
|
|
100
|
+
front,
|
|
101
|
+
new browserWindow.KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }),
|
|
102
|
+
);
|
|
103
|
+
await dispatch(
|
|
104
|
+
front,
|
|
105
|
+
new browserWindow.KeyboardEvent('keyup', { bubbles: true, key: 'Enter' }),
|
|
106
|
+
);
|
|
107
|
+
// happy-dom does not synthesize the browser click dispatched for an activated button.
|
|
108
|
+
await dispatch(front, new browserWindow.MouseEvent('click', { bubbles: true }));
|
|
109
|
+
expect(selection).toEqual(['front', 'front']);
|
|
110
|
+
expect(activation).toEqual(['front', 'front']);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('separates disabled, read-only, and passive interaction behavior', async () => {
|
|
115
|
+
const { SpatialGrid } = await loadSpatialGrid();
|
|
116
|
+
const selection: string[] = [];
|
|
117
|
+
const activation: string[] = [];
|
|
118
|
+
const focus: string[] = [];
|
|
119
|
+
await withBrowserRoot(async (browserWindow, host, root) => {
|
|
120
|
+
const render = (
|
|
121
|
+
props: Pick<
|
|
122
|
+
React.ComponentProps<typeof SpatialGrid>,
|
|
123
|
+
'disabled' | 'readOnly' | 'interactionPolicy'
|
|
124
|
+
>,
|
|
125
|
+
) => (
|
|
126
|
+
<SpatialGrid
|
|
127
|
+
contentHeight={100}
|
|
128
|
+
contentWidth={100}
|
|
129
|
+
height={100}
|
|
130
|
+
items={[{ height: 50, id: 'node', width: 50, x: 10, y: 20 }]}
|
|
131
|
+
testID="interactive-board"
|
|
132
|
+
width={100}
|
|
133
|
+
onActivation={(id) => activation.push(id)}
|
|
134
|
+
onFocusChange={(id) => focus.push(id)}
|
|
135
|
+
onSelectionChange={(id) => selection.push(id)}
|
|
136
|
+
renderItem={(item) => <span>{item.id}</span>}
|
|
137
|
+
{...props}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
for (const props of [
|
|
141
|
+
{ disabled: true },
|
|
142
|
+
{ readOnly: true },
|
|
143
|
+
{ interactionPolicy: 'passive' as const },
|
|
144
|
+
]) {
|
|
145
|
+
await renderSpatialGrid(root, render(props));
|
|
146
|
+
const item = requireItem(host, 'interactive-board-item-node');
|
|
147
|
+
await dispatch(item, new browserWindow.FocusEvent('focusin', { bubbles: true }));
|
|
148
|
+
await dispatch(item, new browserWindow.MouseEvent('click', { bubbles: true }));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
expect(selection).toEqual([]);
|
|
152
|
+
expect(activation).toEqual([]);
|
|
153
|
+
expect(focus).toEqual(['node']);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('reveals a focused offscreen item through the composed GridView viewport', async () => {
|
|
158
|
+
const { SpatialGrid } = await loadSpatialGrid();
|
|
159
|
+
await withBrowserRoot(async (_browserWindow, host, root) => {
|
|
160
|
+
const scrollCalls: unknown[] = [];
|
|
161
|
+
const render = (focusedItemId?: string) => (
|
|
162
|
+
<SpatialGrid
|
|
163
|
+
contentHeight={1000}
|
|
164
|
+
contentWidth={1000}
|
|
165
|
+
focusedItemId={focusedItemId}
|
|
166
|
+
height={100}
|
|
167
|
+
items={[{ height: 20, id: 'distant', width: 20, x: 400, y: 500 }]}
|
|
168
|
+
revealPaddingPixels={10}
|
|
169
|
+
width={100}
|
|
170
|
+
renderItem={(item) => <span>{item.id}</span>}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
await renderSpatialGrid(root, render());
|
|
174
|
+
const scrollableNodes = [...host.querySelectorAll('div')] as HTMLElement[];
|
|
175
|
+
for (const node of scrollableNodes) {
|
|
176
|
+
node.scrollTo = (options: unknown) => scrollCalls.push(options);
|
|
177
|
+
}
|
|
178
|
+
await renderSpatialGrid(root, render('distant'));
|
|
179
|
+
|
|
180
|
+
expect(scrollCalls).toContainEqual({ animated: true, x: 330 });
|
|
181
|
+
expect(scrollCalls).toContainEqual({ animated: true, y: 430 });
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/** Runs a mounted RNW test with its DOM globals restored after the assertion. */
|
|
186
|
+
async function withBrowserRoot(
|
|
187
|
+
callback: (
|
|
188
|
+
browserWindow: Window,
|
|
189
|
+
host: HTMLDivElement,
|
|
190
|
+
root: ReturnType<typeof createRoot>,
|
|
191
|
+
) => Promise<void>,
|
|
192
|
+
) {
|
|
193
|
+
const browserWindow = new Window();
|
|
194
|
+
const keys = [
|
|
195
|
+
'window',
|
|
196
|
+
'document',
|
|
197
|
+
'Node',
|
|
198
|
+
'navigator',
|
|
199
|
+
'ShadowRoot',
|
|
200
|
+
'IS_REACT_ACT_ENVIRONMENT',
|
|
201
|
+
] as const;
|
|
202
|
+
const previous = keys.map(
|
|
203
|
+
(key) => [key, Object.getOwnPropertyDescriptor(globalThis, key)] as const,
|
|
204
|
+
);
|
|
205
|
+
Object.assign(globalThis, {
|
|
206
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
207
|
+
window: browserWindow,
|
|
208
|
+
document: browserWindow.document,
|
|
209
|
+
Node: browserWindow.Node,
|
|
210
|
+
navigator: browserWindow.navigator,
|
|
211
|
+
ShadowRoot: browserWindow.ShadowRoot,
|
|
212
|
+
});
|
|
213
|
+
const host = document.createElement('div');
|
|
214
|
+
document.body.appendChild(host);
|
|
215
|
+
const root = createRoot(host);
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
await callback(browserWindow, host, root);
|
|
219
|
+
} finally {
|
|
220
|
+
await act(() => Promise.resolve().then(() => root.unmount()));
|
|
221
|
+
browserWindow.close();
|
|
222
|
+
for (const [key, descriptor] of previous) {
|
|
223
|
+
if (descriptor) Object.defineProperty(globalThis, key, descriptor);
|
|
224
|
+
else Reflect.deleteProperty(globalThis, key);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Renders a component through React's mounted interaction boundary. */
|
|
230
|
+
function renderSpatialGrid(root: ReturnType<typeof createRoot>, component: React.ReactNode) {
|
|
231
|
+
return act(() => Promise.resolve().then(() => root.render(component)));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Retrieves one mounted item by its stable test identifier. */
|
|
235
|
+
function requireItem(host: HTMLDivElement, testID: string): HTMLElement {
|
|
236
|
+
const item = host.querySelector(`[data-testid="${testID}"]`);
|
|
237
|
+
if (!item) throw new Error(`SpatialGrid did not render ${testID}.`);
|
|
238
|
+
return item as HTMLElement;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Dispatches one mounted native-web event through React's synthetic event boundary. */
|
|
242
|
+
function dispatch(target: EventTarget, event: Event) {
|
|
243
|
+
return act(() =>
|
|
244
|
+
Promise.resolve().then(() => {
|
|
245
|
+
target.dispatchEvent(event);
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Pressable as NativePressable, View as NativeView } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { SpatialGridItem, SpatialGridProps } from '../../../../types/spatial-grid';
|
|
5
|
+
import { GridView } from '../../../grid-view/public';
|
|
6
|
+
import { sortSpatialGridItems } from '../../application/sortSpatialGridItems';
|
|
7
|
+
|
|
8
|
+
/** Renders virtualized free-placement items without owning geometry, pan, zoom, or drag orchestration. */
|
|
9
|
+
export function SpatialGrid({
|
|
10
|
+
items,
|
|
11
|
+
contentWidth,
|
|
12
|
+
contentHeight,
|
|
13
|
+
width,
|
|
14
|
+
height,
|
|
15
|
+
zoom,
|
|
16
|
+
overscanPixels,
|
|
17
|
+
selectedItemId,
|
|
18
|
+
focusedItemId,
|
|
19
|
+
revealPaddingPixels,
|
|
20
|
+
disabled = false,
|
|
21
|
+
readOnly = false,
|
|
22
|
+
interactionPolicy,
|
|
23
|
+
onSelectionChange,
|
|
24
|
+
onActivation,
|
|
25
|
+
onFocusChange,
|
|
26
|
+
onViewportChange,
|
|
27
|
+
onVisibleItemIdsChange,
|
|
28
|
+
renderItem,
|
|
29
|
+
testID,
|
|
30
|
+
}: SpatialGridProps) {
|
|
31
|
+
const orderedItems = React.useMemo(() => sortSpatialGridItems(items), [items]);
|
|
32
|
+
const itemLookup = React.useMemo(
|
|
33
|
+
() => new Map(orderedItems.map((item) => [item.id, item])),
|
|
34
|
+
[orderedItems],
|
|
35
|
+
);
|
|
36
|
+
const interactionBlocked = disabled || interactionPolicy === 'passive';
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<NativeView style={{ height, overflow: 'hidden', width }} testID={testID}>
|
|
40
|
+
<GridView
|
|
41
|
+
contentHeight={contentHeight}
|
|
42
|
+
contentWidth={contentWidth}
|
|
43
|
+
focusedItemId={focusedItemId ?? selectedItemId}
|
|
44
|
+
height={height}
|
|
45
|
+
interactionPolicy={interactionPolicy}
|
|
46
|
+
items={orderedItems}
|
|
47
|
+
onViewportChange={onViewportChange}
|
|
48
|
+
onVisibleItemIdsChange={onVisibleItemIdsChange}
|
|
49
|
+
overscanPixels={overscanPixels}
|
|
50
|
+
renderItem={(gridItem) => {
|
|
51
|
+
const item = itemLookup.get(gridItem.id);
|
|
52
|
+
if (!item) return null;
|
|
53
|
+
return renderSpatialGridItem(
|
|
54
|
+
item,
|
|
55
|
+
item.id === selectedItemId,
|
|
56
|
+
item.id === focusedItemId,
|
|
57
|
+
interactionBlocked,
|
|
58
|
+
readOnly,
|
|
59
|
+
onSelectionChange,
|
|
60
|
+
onActivation,
|
|
61
|
+
onFocusChange,
|
|
62
|
+
renderItem,
|
|
63
|
+
testID,
|
|
64
|
+
);
|
|
65
|
+
}}
|
|
66
|
+
revealPaddingPixels={revealPaddingPixels}
|
|
67
|
+
width={width}
|
|
68
|
+
zoom={zoom}
|
|
69
|
+
/>
|
|
70
|
+
</NativeView>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Adds serializable selection, activation, and focus boundaries around a caller-rendered item. */
|
|
75
|
+
function renderSpatialGridItem(
|
|
76
|
+
item: SpatialGridItem,
|
|
77
|
+
selected: boolean,
|
|
78
|
+
focused: boolean,
|
|
79
|
+
interactionBlocked: boolean,
|
|
80
|
+
readOnly: boolean,
|
|
81
|
+
onSelectionChange: SpatialGridProps['onSelectionChange'],
|
|
82
|
+
onActivation: SpatialGridProps['onActivation'],
|
|
83
|
+
onFocusChange: SpatialGridProps['onFocusChange'],
|
|
84
|
+
renderItem: SpatialGridProps['renderItem'],
|
|
85
|
+
testID: string | undefined,
|
|
86
|
+
) {
|
|
87
|
+
const interactionDisabled = interactionBlocked || item.disabled === true;
|
|
88
|
+
const editingDisabled = interactionDisabled || readOnly;
|
|
89
|
+
return (
|
|
90
|
+
<NativePressable
|
|
91
|
+
accessibilityLabel={item.accessibilityLabel ?? `Spatial grid item ${item.id}`}
|
|
92
|
+
accessibilityRole="button"
|
|
93
|
+
accessibilityState={{ disabled: editingDisabled, selected }}
|
|
94
|
+
disabled={interactionDisabled}
|
|
95
|
+
style={{ height: '100%', width: '100%' }}
|
|
96
|
+
testID={testID === undefined ? undefined : `${testID}-item-${item.id}`}
|
|
97
|
+
onFocus={() => {
|
|
98
|
+
if (!interactionDisabled) onFocusChange?.(item.id);
|
|
99
|
+
}}
|
|
100
|
+
onLongPress={() => {
|
|
101
|
+
if (!editingDisabled) onActivation?.(item.id);
|
|
102
|
+
}}
|
|
103
|
+
onPress={() => {
|
|
104
|
+
if (editingDisabled) return;
|
|
105
|
+
onSelectionChange?.(item.id);
|
|
106
|
+
onActivation?.(item.id);
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{renderItem(item, { disabled: editingDisabled, focused, selected })}
|
|
110
|
+
</NativePressable>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getVisibleGridItems } from '@ankhorage/grid-view';
|
|
2
|
+
import { describe, expect, test } from 'bun:test';
|
|
3
|
+
|
|
4
|
+
import { sortSpatialGridItems } from './sortSpatialGridItems';
|
|
5
|
+
|
|
6
|
+
describe('SpatialGrid presentation', () => {
|
|
7
|
+
test('preserves free world rectangles and establishes deterministic overlap hit order', () => {
|
|
8
|
+
const ordered = sortSpatialGridItems([
|
|
9
|
+
{ height: 40, id: 'middle', width: 40, x: 24, y: 18 },
|
|
10
|
+
{ height: 30, id: 'back', width: 30, x: 16, y: 12, zIndex: -1 },
|
|
11
|
+
{ height: 20, id: 'front', width: 20, x: 24, y: 18, zIndex: 2 },
|
|
12
|
+
{ height: 10, id: 'front-later', width: 10, x: 24, y: 18, zIndex: 2 },
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
expect(ordered.map((item) => item.id)).toEqual(['back', 'middle', 'front', 'front-later']);
|
|
16
|
+
expect(ordered[1]).toMatchObject({ height: 40, width: 40, x: 24, y: 18 });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('keeps a 10k free-placement catalogue bounded to the viewport subset', () => {
|
|
20
|
+
const items = sortSpatialGridItems(
|
|
21
|
+
Array.from({ length: 10000 }, (_, index) => ({
|
|
22
|
+
height: 20,
|
|
23
|
+
id: `item-${index}`,
|
|
24
|
+
width: 20,
|
|
25
|
+
x: (index % 100) * 100,
|
|
26
|
+
y: Math.floor(index / 100) * 100,
|
|
27
|
+
})),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const visible = getVisibleGridItems(items, {
|
|
31
|
+
height: 160,
|
|
32
|
+
offsetX: 0,
|
|
33
|
+
offsetY: 0,
|
|
34
|
+
pixelsPerUnitX: 1,
|
|
35
|
+
pixelsPerUnitY: 1,
|
|
36
|
+
width: 160,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(visible).toHaveLength(4);
|
|
40
|
+
expect(visible.map((item) => item.id)).toEqual(['item-0', 'item-1', 'item-100', 'item-101']);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SpatialGridItem } from '../../../types/spatial-grid';
|
|
2
|
+
|
|
3
|
+
/** Orders free-placement items for deterministic paint and overlap hit-testing. */
|
|
4
|
+
export function sortSpatialGridItems(
|
|
5
|
+
items: readonly SpatialGridItem[],
|
|
6
|
+
): readonly SpatialGridItem[] {
|
|
7
|
+
return [...items]
|
|
8
|
+
.map((item, index) => ({ index, item }))
|
|
9
|
+
.sort(
|
|
10
|
+
(left, right) =>
|
|
11
|
+
(left.item.zIndex ?? 0) - (right.item.zIndex ?? 0) || left.index - right.index,
|
|
12
|
+
)
|
|
13
|
+
.map(({ item }) => item);
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ZoraComponentMeta } from '../../types/authoring';
|
|
2
|
+
|
|
3
|
+
/** Free-placement rendering requires a caller-owned React renderer and is therefore code-only. */
|
|
4
|
+
export const spatialGridMeta = {
|
|
5
|
+
name: 'SpatialGrid',
|
|
6
|
+
category: 'foundation',
|
|
7
|
+
description: 'Virtualized free-placement board. Item rendering belongs to the code API.',
|
|
8
|
+
directManifestNode: false,
|
|
9
|
+
note: 'SpatialGrid requires a caller-owned React item renderer and cannot be serialized as a manifest node.',
|
|
10
|
+
allowedChildren: [],
|
|
11
|
+
props: {},
|
|
12
|
+
} as const satisfies ZoraComponentMeta;
|
package/src/index.ts
CHANGED
|
@@ -263,6 +263,14 @@ export type {
|
|
|
263
263
|
GradientRendererProviderProps,
|
|
264
264
|
} from './features/gradient/public';
|
|
265
265
|
export { Gradient, GradientRendererProvider } from './features/gradient/public';
|
|
266
|
+
export type {
|
|
267
|
+
GridGuide,
|
|
268
|
+
GridLineOverlayProps,
|
|
269
|
+
GridRulerMark,
|
|
270
|
+
GridRulerProps,
|
|
271
|
+
GridRulerTickSource,
|
|
272
|
+
} from './features/grid-rulers/public';
|
|
273
|
+
export { GridLineOverlay, GridRuler, resolveGridRulerMarks } from './features/grid-rulers/public';
|
|
266
274
|
export type { GridViewProps, TileGridItem, TileGridProps } from './features/grid-view/public';
|
|
267
275
|
export { GridView, layoutTileGrid, TileGrid } from './features/grid-view/public';
|
|
268
276
|
export {
|
|
@@ -408,6 +416,12 @@ export {
|
|
|
408
416
|
SkeletonText,
|
|
409
417
|
type SkeletonTextProps,
|
|
410
418
|
} from './features/skeleton/public';
|
|
419
|
+
export type {
|
|
420
|
+
SpatialGridItem,
|
|
421
|
+
SpatialGridItemState,
|
|
422
|
+
SpatialGridProps,
|
|
423
|
+
} from './features/spatial-grid/public';
|
|
424
|
+
export { SpatialGrid } from './features/spatial-grid/public';
|
|
411
425
|
export type { SurfaceProps, SurfaceVariant } from './features/surface/public';
|
|
412
426
|
export { Surface } from './features/surface/public';
|
|
413
427
|
export type {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GridAxisCategory,
|
|
3
|
+
GridAxisName,
|
|
4
|
+
GridAxisTick,
|
|
5
|
+
GridTickProvider,
|
|
6
|
+
GridTickSpecification,
|
|
7
|
+
GridViewport,
|
|
8
|
+
} from '@ankhorage/grid-view';
|
|
9
|
+
|
|
10
|
+
import type { ZoraBaseProps } from './base';
|
|
11
|
+
|
|
12
|
+
/** A tick source is display-only; it never configures interaction snapping. */
|
|
13
|
+
export type GridRulerTickSource =
|
|
14
|
+
| {
|
|
15
|
+
readonly kind: 'ticks';
|
|
16
|
+
readonly specification: GridTickSpecification;
|
|
17
|
+
readonly provider?: GridTickProvider;
|
|
18
|
+
}
|
|
19
|
+
| { readonly kind: 'categories'; readonly categories: readonly GridAxisCategory[] };
|
|
20
|
+
|
|
21
|
+
/** A projected mark is measured in viewport pixels from the supplied viewport's origin. */
|
|
22
|
+
export interface GridRulerMark {
|
|
23
|
+
readonly position: number;
|
|
24
|
+
readonly level: GridAxisTick['level'];
|
|
25
|
+
readonly label?: string;
|
|
26
|
+
readonly categoryId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A passive guide belongs to one world-space axis. */
|
|
30
|
+
export interface GridGuide {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly axis: GridAxisName;
|
|
33
|
+
readonly position: number;
|
|
34
|
+
readonly label?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A single horizontal or vertical passive ruler strip. */
|
|
38
|
+
export interface GridRulerProps extends ZoraBaseProps {
|
|
39
|
+
readonly axis: GridAxisName;
|
|
40
|
+
readonly viewport: GridViewport;
|
|
41
|
+
readonly tickSource: GridRulerTickSource;
|
|
42
|
+
readonly thickness?: number;
|
|
43
|
+
readonly offset?: number;
|
|
44
|
+
readonly position?: 'start' | 'end';
|
|
45
|
+
readonly direction?: 'ltr' | 'rtl';
|
|
46
|
+
readonly accessibilityLabel?: string;
|
|
47
|
+
readonly formatLabel?: (tick: GridAxisTick) => string | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A passive overlay of visible major/minor grid lines and explicit guides. */
|
|
51
|
+
export interface GridLineOverlayProps extends ZoraBaseProps {
|
|
52
|
+
readonly viewport: GridViewport;
|
|
53
|
+
/** Mirrors horizontal world positions using the same projection as an RTL GridRuler. */
|
|
54
|
+
readonly direction?: 'ltr' | 'rtl';
|
|
55
|
+
readonly xTickSource?: GridRulerTickSource;
|
|
56
|
+
readonly yTickSource?: GridRulerTickSource;
|
|
57
|
+
readonly guides?: readonly GridGuide[];
|
|
58
|
+
readonly accessibilityLabel?: string;
|
|
59
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { GridRectItem, GridViewport } from '@ankhorage/grid-view';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ZoraBaseProps } from './base';
|
|
5
|
+
|
|
6
|
+
/** A free-positioned item retains the grid-view world rectangle and adds presentation policy. */
|
|
7
|
+
export interface SpatialGridItem extends GridRectItem {
|
|
8
|
+
/** Higher values are painted and hit-tested above lower values; equal values preserve input order. */
|
|
9
|
+
readonly zIndex?: number;
|
|
10
|
+
/** Accessible name for the item; defaults to a stable ID-derived board-item name. */
|
|
11
|
+
readonly accessibilityLabel?: string;
|
|
12
|
+
/** Prevents selection and activation for this item while keeping it visible. */
|
|
13
|
+
readonly disabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** State supplied to the code-only item renderer. */
|
|
17
|
+
export interface SpatialGridItemState {
|
|
18
|
+
readonly selected: boolean;
|
|
19
|
+
readonly focused: boolean;
|
|
20
|
+
readonly disabled: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Props for the virtualized, free-placement presentation over canonical GridView geometry. */
|
|
24
|
+
export interface SpatialGridProps extends ZoraBaseProps {
|
|
25
|
+
readonly items: readonly SpatialGridItem[];
|
|
26
|
+
readonly contentWidth: number;
|
|
27
|
+
readonly contentHeight: number;
|
|
28
|
+
readonly width: number;
|
|
29
|
+
readonly height: number;
|
|
30
|
+
readonly zoom?: number;
|
|
31
|
+
readonly overscanPixels?: number;
|
|
32
|
+
readonly selectedItemId?: string;
|
|
33
|
+
readonly focusedItemId?: string;
|
|
34
|
+
readonly revealPaddingPixels?: number;
|
|
35
|
+
readonly disabled?: boolean;
|
|
36
|
+
/** Prevents selection and activation while retaining focus and viewport navigation. */
|
|
37
|
+
readonly readOnly?: boolean;
|
|
38
|
+
readonly onSelectionChange?: (id: string) => void;
|
|
39
|
+
readonly onActivation?: (id: string) => void;
|
|
40
|
+
readonly onFocusChange?: (id: string) => void;
|
|
41
|
+
readonly onViewportChange?: (viewport: GridViewport) => void;
|
|
42
|
+
readonly onVisibleItemIdsChange?: (ids: readonly string[]) => void;
|
|
43
|
+
readonly renderItem: (item: SpatialGridItem, state: SpatialGridItemState) => React.ReactNode;
|
|
44
|
+
}
|
|
@@ -20,11 +20,11 @@ import {
|
|
|
20
20
|
} from "./Accordion-42jfy1a1.js";
|
|
21
21
|
import {
|
|
22
22
|
View2
|
|
23
|
-
} from "./Accordion-
|
|
23
|
+
} from "./Accordion-7tjz25jh.js";
|
|
24
24
|
import {
|
|
25
25
|
Heading2,
|
|
26
26
|
Text3
|
|
27
|
-
} from "./Accordion-
|
|
27
|
+
} from "./Accordion-9w8wg9mf.js";
|
|
28
28
|
import {
|
|
29
29
|
resolveOverlayAnimation
|
|
30
30
|
} from "./Accordion-n7y08rz5.js";
|
|
@@ -7,16 +7,16 @@ import {
|
|
|
7
7
|
} from "./Accordion-42jfy1a1.js";
|
|
8
8
|
import {
|
|
9
9
|
View2
|
|
10
|
-
} from "./Accordion-
|
|
10
|
+
} from "./Accordion-7tjz25jh.js";
|
|
11
11
|
import {
|
|
12
12
|
Text3
|
|
13
|
-
} from "./Accordion-
|
|
13
|
+
} from "./Accordion-9w8wg9mf.js";
|
|
14
14
|
import {
|
|
15
15
|
Avatar2
|
|
16
|
-
} from "./Accordion-
|
|
16
|
+
} from "./Accordion-61ntvh14.js";
|
|
17
17
|
import {
|
|
18
18
|
Badge2
|
|
19
|
-
} from "./Accordion-
|
|
19
|
+
} from "./Accordion-7kq500r1.js";
|
|
20
20
|
import {
|
|
21
21
|
useZoraTheme2
|
|
22
22
|
} from "./Accordion-srjya6dz.js";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import {
|
|
3
3
|
Button2
|
|
4
|
-
} from "./Accordion-
|
|
4
|
+
} from "./Accordion-6rrqpb2h.js";
|
|
5
5
|
import {
|
|
6
6
|
withZoraThemeScope
|
|
7
7
|
} from "./Accordion-42jfy1a1.js";
|
|
8
8
|
import {
|
|
9
9
|
View2
|
|
10
|
-
} from "./Accordion-
|
|
10
|
+
} from "./Accordion-7tjz25jh.js";
|
|
11
11
|
import {
|
|
12
12
|
Text3
|
|
13
|
-
} from "./Accordion-
|
|
13
|
+
} from "./Accordion-9w8wg9mf.js";
|
|
14
14
|
import {
|
|
15
15
|
useZoraTheme2
|
|
16
16
|
} from "./Accordion-srjya6dz.js";
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import {
|
|
3
3
|
Button2
|
|
4
|
-
} from "./Accordion-
|
|
4
|
+
} from "./Accordion-6rrqpb2h.js";
|
|
5
5
|
import {
|
|
6
6
|
withZoraThemeScope
|
|
7
7
|
} from "./Accordion-42jfy1a1.js";
|
|
8
8
|
import {
|
|
9
9
|
View2
|
|
10
|
-
} from "./Accordion-
|
|
10
|
+
} from "./Accordion-7tjz25jh.js";
|
|
11
11
|
import {
|
|
12
12
|
Card2
|
|
13
|
-
} from "./Accordion-
|
|
13
|
+
} from "./Accordion-fjxcn6g9.js";
|
|
14
14
|
import {
|
|
15
15
|
Text3
|
|
16
|
-
} from "./Accordion-
|
|
16
|
+
} from "./Accordion-9w8wg9mf.js";
|
|
17
17
|
import {
|
|
18
18
|
Badge2
|
|
19
|
-
} from "./Accordion-
|
|
19
|
+
} from "./Accordion-7kq500r1.js";
|
|
20
20
|
import {
|
|
21
21
|
useZoraTheme2
|
|
22
22
|
} from "./Accordion-srjya6dz.js";
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
} from "./Accordion-42jfy1a1.js";
|
|
5
5
|
import {
|
|
6
6
|
View2
|
|
7
|
-
} from "./Accordion-
|
|
7
|
+
} from "./Accordion-7tjz25jh.js";
|
|
8
8
|
import {
|
|
9
9
|
ButtonGroup2
|
|
10
|
-
} from "./Accordion-
|
|
10
|
+
} from "./Accordion-6rrqpb2h.js";
|
|
11
11
|
import {
|
|
12
12
|
Card2
|
|
13
|
-
} from "./Accordion-
|
|
13
|
+
} from "./Accordion-fjxcn6g9.js";
|
|
14
14
|
import {
|
|
15
15
|
List2
|
|
16
|
-
} from "./Accordion-
|
|
16
|
+
} from "./Accordion-5g05kfmx.js";
|
|
17
17
|
import {
|
|
18
18
|
useZoraTheme2
|
|
19
19
|
} from "./Accordion-srjya6dz.js";
|