@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,123 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { GridLineOverlayProps } from '../../../../types/grid-rulers';
|
|
5
|
+
import { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope';
|
|
6
|
+
import { useZoraTheme } from '../../../theme/composition/useZoraTheme';
|
|
7
|
+
import { projectGridRulerPosition } from '../../application/projectGridRulerPosition';
|
|
8
|
+
import { resolveGridRulerMarks } from '../../application/resolveGridRulerMarks';
|
|
9
|
+
|
|
10
|
+
/*** Renders visible world-grid lines and explicit guides without owning viewport interaction. */
|
|
11
|
+
function GridLineOverlayInner({
|
|
12
|
+
accessibilityLabel = 'Grid guides',
|
|
13
|
+
direction = 'ltr',
|
|
14
|
+
guides = [],
|
|
15
|
+
testID,
|
|
16
|
+
viewport,
|
|
17
|
+
xTickSource,
|
|
18
|
+
yTickSource,
|
|
19
|
+
}: GridLineOverlayProps) {
|
|
20
|
+
const { theme } = useZoraTheme();
|
|
21
|
+
const xMarks = xTickSource
|
|
22
|
+
? resolveGridRulerMarks(viewport, 'x', xTickSource, undefined, direction)
|
|
23
|
+
: [];
|
|
24
|
+
const yMarks = yTickSource ? resolveGridRulerMarks(viewport, 'y', yTickSource) : [];
|
|
25
|
+
const visibleGuides = resolveVisibleGridGuides(guides, viewport, direction);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<View
|
|
29
|
+
accessible={false}
|
|
30
|
+
importantForAccessibility="no"
|
|
31
|
+
pointerEvents="none"
|
|
32
|
+
style={[styles.overlay, { height: viewport.height, width: viewport.width }]}
|
|
33
|
+
testID={testID}
|
|
34
|
+
>
|
|
35
|
+
{xMarks.map((mark, index) => (
|
|
36
|
+
<View
|
|
37
|
+
key={`x-${mark.position}-${index}`}
|
|
38
|
+
importantForAccessibility="no-hide-descendants"
|
|
39
|
+
style={[
|
|
40
|
+
styles.vertical,
|
|
41
|
+
{
|
|
42
|
+
backgroundColor: theme.semantics.neutral.divider,
|
|
43
|
+
left: mark.position,
|
|
44
|
+
opacity: mark.level === 'major' ? 0.5 : 0.25,
|
|
45
|
+
},
|
|
46
|
+
]}
|
|
47
|
+
testID={testID === undefined ? undefined : `${testID}-x-line-${index}`}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
{yMarks.map((mark, index) => (
|
|
51
|
+
<View
|
|
52
|
+
key={`y-${mark.position}-${index}`}
|
|
53
|
+
importantForAccessibility="no-hide-descendants"
|
|
54
|
+
style={[
|
|
55
|
+
styles.horizontal,
|
|
56
|
+
{
|
|
57
|
+
backgroundColor: theme.semantics.neutral.divider,
|
|
58
|
+
opacity: mark.level === 'major' ? 0.5 : 0.25,
|
|
59
|
+
top: mark.position,
|
|
60
|
+
},
|
|
61
|
+
]}
|
|
62
|
+
testID={testID === undefined ? undefined : `${testID}-y-line-${index}`}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
{visibleGuides.map(({ guide, position }) => (
|
|
66
|
+
<View
|
|
67
|
+
key={guide.id}
|
|
68
|
+
accessibilityLabel={createGuideAccessibilityLabel(accessibilityLabel, guide)}
|
|
69
|
+
accessibilityRole="text"
|
|
70
|
+
accessible
|
|
71
|
+
style={[
|
|
72
|
+
guide.axis === 'x' ? styles.vertical : styles.horizontal,
|
|
73
|
+
guide.axis === 'x'
|
|
74
|
+
? {
|
|
75
|
+
backgroundColor: theme.colors.primary,
|
|
76
|
+
left: position,
|
|
77
|
+
}
|
|
78
|
+
: {
|
|
79
|
+
backgroundColor: theme.colors.primary,
|
|
80
|
+
top: position,
|
|
81
|
+
},
|
|
82
|
+
]}
|
|
83
|
+
testID={testID === undefined ? undefined : `${testID}-guide-${guide.id}`}
|
|
84
|
+
/>
|
|
85
|
+
))}
|
|
86
|
+
</View>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const styles = StyleSheet.create({
|
|
91
|
+
horizontal: { height: 1, left: 0, position: 'absolute', right: 0 },
|
|
92
|
+
overlay: { overflow: 'hidden', position: 'absolute' },
|
|
93
|
+
vertical: { bottom: 0, position: 'absolute', top: 0, width: 1 },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/*** Selects finite guides whose shared world-to-visual projection falls inside the viewport. */
|
|
97
|
+
function resolveVisibleGridGuides(
|
|
98
|
+
guides: NonNullable<GridLineOverlayProps['guides']>,
|
|
99
|
+
viewport: GridLineOverlayProps['viewport'],
|
|
100
|
+
direction: NonNullable<GridLineOverlayProps['direction']>,
|
|
101
|
+
) {
|
|
102
|
+
return guides.flatMap((guide) => {
|
|
103
|
+
if (!Number.isFinite(guide.position)) return [];
|
|
104
|
+
|
|
105
|
+
const position = projectGridRulerPosition(viewport, guide.axis, guide.position, direction);
|
|
106
|
+
const size = guide.axis === 'x' ? viewport.width : viewport.height;
|
|
107
|
+
|
|
108
|
+
return Number.isFinite(position) && position >= 0 && position <= size
|
|
109
|
+
? [{ guide, position }]
|
|
110
|
+
: [];
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*** Gives each visible guide an independent screen-reader identity with its stable world position. */
|
|
115
|
+
function createGuideAccessibilityLabel(
|
|
116
|
+
overlayLabel: string,
|
|
117
|
+
guide: NonNullable<GridLineOverlayProps['guides']>[number],
|
|
118
|
+
): string {
|
|
119
|
+
return `${overlayLabel}: ${guide.label ?? `Guide ${guide.id}`} at ${guide.position}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*** A reusable passive world-space grid and guide overlay. */
|
|
123
|
+
export const GridLineOverlay = withZoraThemeScope(GridLineOverlayInner);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { GridRulerProps } from '../../../../types/grid-rulers';
|
|
5
|
+
import { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope';
|
|
6
|
+
import { useZoraTheme } from '../../../theme/composition/useZoraTheme';
|
|
7
|
+
import { resolveGridRulerMarks } from '../../application/resolveGridRulerMarks';
|
|
8
|
+
|
|
9
|
+
/*** Renders a passive axis ruler from published grid-view tick geometry. */
|
|
10
|
+
function GridRulerInner({
|
|
11
|
+
accessibilityLabel,
|
|
12
|
+
axis,
|
|
13
|
+
direction = 'ltr',
|
|
14
|
+
formatLabel,
|
|
15
|
+
offset = 0,
|
|
16
|
+
position = 'start',
|
|
17
|
+
testID,
|
|
18
|
+
thickness = 24,
|
|
19
|
+
tickSource,
|
|
20
|
+
viewport,
|
|
21
|
+
}: GridRulerProps) {
|
|
22
|
+
const { theme } = useZoraTheme();
|
|
23
|
+
const marks = resolveGridRulerMarks(viewport, axis, tickSource, formatLabel, direction);
|
|
24
|
+
const horizontal = axis === 'x';
|
|
25
|
+
const size = horizontal ? viewport.width : viewport.height;
|
|
26
|
+
const label = accessibilityLabel ?? `${horizontal ? 'Horizontal' : 'Vertical'} ruler`;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<View
|
|
30
|
+
accessibilityLabel={label}
|
|
31
|
+
accessible
|
|
32
|
+
pointerEvents="none"
|
|
33
|
+
style={[
|
|
34
|
+
styles.ruler,
|
|
35
|
+
horizontal ? { height: thickness, width: size } : { height: size, width: thickness },
|
|
36
|
+
position === 'start'
|
|
37
|
+
? horizontal
|
|
38
|
+
? { top: offset }
|
|
39
|
+
: { left: offset }
|
|
40
|
+
: horizontal
|
|
41
|
+
? { bottom: offset }
|
|
42
|
+
: { right: offset },
|
|
43
|
+
]}
|
|
44
|
+
testID={testID}
|
|
45
|
+
>
|
|
46
|
+
{marks.map((mark, index) => {
|
|
47
|
+
const coordinate = mark.position;
|
|
48
|
+
const tickStyle = horizontal
|
|
49
|
+
? {
|
|
50
|
+
height: mark.level === 'major' ? thickness : thickness / 2,
|
|
51
|
+
left: coordinate,
|
|
52
|
+
top: 0,
|
|
53
|
+
width: 1,
|
|
54
|
+
}
|
|
55
|
+
: {
|
|
56
|
+
height: 1,
|
|
57
|
+
left: 0,
|
|
58
|
+
top: coordinate,
|
|
59
|
+
width: mark.level === 'major' ? thickness : thickness / 2,
|
|
60
|
+
};
|
|
61
|
+
return (
|
|
62
|
+
<React.Fragment key={`${mark.position}-${index}`}>
|
|
63
|
+
<View
|
|
64
|
+
importantForAccessibility="no-hide-descendants"
|
|
65
|
+
style={[styles.tick, { backgroundColor: theme.semantics.neutral.divider }, tickStyle]}
|
|
66
|
+
testID={testID === undefined ? undefined : `${testID}-tick-${index}`}
|
|
67
|
+
/>
|
|
68
|
+
{mark.label !== undefined ? (
|
|
69
|
+
<Text
|
|
70
|
+
importantForAccessibility="no-hide-descendants"
|
|
71
|
+
numberOfLines={1}
|
|
72
|
+
style={[
|
|
73
|
+
styles.label,
|
|
74
|
+
{ color: theme.semantics.neutral.textMuted },
|
|
75
|
+
horizontal ? { left: coordinate + 3, top: 2 } : { left: 3, top: coordinate + 2 },
|
|
76
|
+
]}
|
|
77
|
+
testID={testID === undefined ? undefined : `${testID}-label-${index}`}
|
|
78
|
+
>
|
|
79
|
+
{mark.label}
|
|
80
|
+
</Text>
|
|
81
|
+
) : null}
|
|
82
|
+
</React.Fragment>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
label: { fontSize: 10, position: 'absolute' },
|
|
91
|
+
ruler: { overflow: 'hidden', position: 'absolute' },
|
|
92
|
+
tick: { position: 'absolute' },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/*** A reusable passive horizontal or vertical world-space ruler. */
|
|
96
|
+
export const GridRuler = withZoraThemeScope(GridRulerInner);
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import type { GridViewport } from '@ankhorage/grid-view';
|
|
5
|
+
import { expect, test } from 'bun:test';
|
|
6
|
+
import { Window } from 'happy-dom';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
9
|
+
|
|
10
|
+
import type { GridLineOverlayProps, GridRulerProps } from '../../../../types/grid-rulers';
|
|
11
|
+
import type { ZoraProviderProps } from '../../../theme/adapters/inbound/ZoraProvider';
|
|
12
|
+
|
|
13
|
+
const webDistRoot = join(import.meta.dir, '../../../../../web-dist');
|
|
14
|
+
|
|
15
|
+
/*** Loads the independently bundled React Native Web implementation under test. */
|
|
16
|
+
function load(path: string) {
|
|
17
|
+
return import(pathToFileURL(join(webDistRoot, path)).href);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const viewport: GridViewport = {
|
|
21
|
+
height: 100,
|
|
22
|
+
offsetX: 40,
|
|
23
|
+
offsetY: 20,
|
|
24
|
+
pixelsPerUnitX: 2,
|
|
25
|
+
pixelsPerUnitY: 2,
|
|
26
|
+
width: 300,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const xTickSource = { kind: 'ticks' as const, specification: { mode: 'fixed' as const, step: 20 } };
|
|
30
|
+
|
|
31
|
+
/*** Renders the public native components through React Native Web for behavioral DOM assertions. */
|
|
32
|
+
function renderGridRulers(
|
|
33
|
+
ZoraProvider: React.ComponentType<ZoraProviderProps>,
|
|
34
|
+
GridRuler: React.ComponentType<GridRulerProps>,
|
|
35
|
+
GridLineOverlay: React.ComponentType<GridLineOverlayProps>,
|
|
36
|
+
direction: 'ltr' | 'rtl',
|
|
37
|
+
guides = [{ axis: 'x' as const, id: 'playhead', label: 'Playhead', position: 60 }],
|
|
38
|
+
) {
|
|
39
|
+
return renderToStaticMarkup(
|
|
40
|
+
<ZoraProvider mode="light">
|
|
41
|
+
<GridRuler
|
|
42
|
+
axis="x"
|
|
43
|
+
direction={direction}
|
|
44
|
+
testID="ruler"
|
|
45
|
+
tickSource={xTickSource}
|
|
46
|
+
viewport={viewport}
|
|
47
|
+
/>
|
|
48
|
+
<GridLineOverlay
|
|
49
|
+
direction={direction}
|
|
50
|
+
guides={guides}
|
|
51
|
+
testID="overlay"
|
|
52
|
+
viewport={viewport}
|
|
53
|
+
xTickSource={xTickSource}
|
|
54
|
+
/>
|
|
55
|
+
</ZoraProvider>,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*** Reads React Native Web output without relying on implementation-only geometry helpers. */
|
|
60
|
+
function inspectMarkup(markup: string): Window {
|
|
61
|
+
const browserWindow = new Window();
|
|
62
|
+
browserWindow.document.body.innerHTML = markup;
|
|
63
|
+
return browserWindow;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test('renders ruler ticks, grid lines, and guides at the same non-zero-offset world projection in LTR and RTL', async () => {
|
|
67
|
+
const { ZoraProvider } = (await load('runtime/ZoraProvider.js')) as {
|
|
68
|
+
ZoraProvider: React.ComponentType<ZoraProviderProps>;
|
|
69
|
+
};
|
|
70
|
+
const { GridRuler } = (await load('components/grid-ruler/GridRuler.js')) as {
|
|
71
|
+
GridRuler: React.ComponentType<GridRulerProps>;
|
|
72
|
+
};
|
|
73
|
+
const { GridLineOverlay } = (await load('components/grid-line-overlay/GridLineOverlay.js')) as {
|
|
74
|
+
GridLineOverlay: React.ComponentType<GridLineOverlayProps>;
|
|
75
|
+
};
|
|
76
|
+
const ltr = inspectMarkup(renderGridRulers(ZoraProvider, GridRuler, GridLineOverlay, 'ltr'));
|
|
77
|
+
const rtl = inspectMarkup(renderGridRulers(ZoraProvider, GridRuler, GridLineOverlay, 'rtl'));
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
for (const [browserWindow, expectedPosition] of [
|
|
81
|
+
[ltr, '40px'],
|
|
82
|
+
[rtl, '260px'],
|
|
83
|
+
] as const) {
|
|
84
|
+
expect(
|
|
85
|
+
browserWindow.document.querySelector('[data-testid="ruler-tick-1"]')?.getAttribute('style'),
|
|
86
|
+
).toContain(`left:${expectedPosition}`);
|
|
87
|
+
expect(
|
|
88
|
+
browserWindow.document
|
|
89
|
+
.querySelector('[data-testid="overlay-x-line-1"]')
|
|
90
|
+
?.getAttribute('style'),
|
|
91
|
+
).toContain(`left:${expectedPosition}`);
|
|
92
|
+
expect(
|
|
93
|
+
browserWindow.document
|
|
94
|
+
.querySelector('[data-testid="overlay-guide-playhead"]')
|
|
95
|
+
?.getAttribute('style'),
|
|
96
|
+
).toContain(`left:${expectedPosition}`);
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
ltr.close();
|
|
100
|
+
rtl.close();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('renders only finite visible guides and exposes each guide without making grid decoration navigable', async () => {
|
|
105
|
+
const { ZoraProvider } = (await load('runtime/ZoraProvider.js')) as {
|
|
106
|
+
ZoraProvider: React.ComponentType<ZoraProviderProps>;
|
|
107
|
+
};
|
|
108
|
+
const { GridRuler } = (await load('components/grid-ruler/GridRuler.js')) as {
|
|
109
|
+
GridRuler: React.ComponentType<GridRulerProps>;
|
|
110
|
+
};
|
|
111
|
+
const { GridLineOverlay } = (await load('components/grid-line-overlay/GridLineOverlay.js')) as {
|
|
112
|
+
GridLineOverlay: React.ComponentType<GridLineOverlayProps>;
|
|
113
|
+
};
|
|
114
|
+
const browserWindow = inspectMarkup(
|
|
115
|
+
renderGridRulers(ZoraProvider, GridRuler, GridLineOverlay, 'ltr', [
|
|
116
|
+
{ axis: 'x', id: 'before', position: 39 },
|
|
117
|
+
{ axis: 'x', id: 'start', label: 'Start', position: 40 },
|
|
118
|
+
{ axis: 'x', id: 'end', label: 'End', position: 190 },
|
|
119
|
+
{ axis: 'x', id: 'after', position: 191 },
|
|
120
|
+
{ axis: 'y', id: 'invalid', position: Number.POSITIVE_INFINITY },
|
|
121
|
+
]),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
expect(browserWindow.document.querySelectorAll('[data-testid^="overlay-guide-"]')).toHaveLength(
|
|
126
|
+
2,
|
|
127
|
+
);
|
|
128
|
+
expect(
|
|
129
|
+
browserWindow.document.querySelector('[data-testid="overlay-guide-start"]'),
|
|
130
|
+
).not.toBeNull();
|
|
131
|
+
expect(
|
|
132
|
+
browserWindow.document.querySelector('[data-testid="overlay-guide-end"]'),
|
|
133
|
+
).not.toBeNull();
|
|
134
|
+
expect(browserWindow.document.querySelector('[data-testid="overlay-guide-before"]')).toBeNull();
|
|
135
|
+
expect(browserWindow.document.querySelector('[data-testid="overlay-guide-after"]')).toBeNull();
|
|
136
|
+
expect(
|
|
137
|
+
browserWindow.document.querySelector('[data-testid="overlay"]')?.getAttribute('aria-label'),
|
|
138
|
+
).toBeNull();
|
|
139
|
+
expect(
|
|
140
|
+
browserWindow.document
|
|
141
|
+
.querySelector('[data-testid="overlay-guide-start"]')
|
|
142
|
+
?.getAttribute('aria-label'),
|
|
143
|
+
).toBe('Grid guides: Start at 40');
|
|
144
|
+
expect(
|
|
145
|
+
browserWindow.document
|
|
146
|
+
.querySelector('[data-testid="overlay-guide-end"]')
|
|
147
|
+
?.getAttribute('aria-label'),
|
|
148
|
+
).toBe('Grid guides: End at 190');
|
|
149
|
+
expect(
|
|
150
|
+
browserWindow.document
|
|
151
|
+
.querySelector('[data-testid="overlay-x-line-1"]')
|
|
152
|
+
?.getAttribute('aria-label'),
|
|
153
|
+
).toBeNull();
|
|
154
|
+
expect(
|
|
155
|
+
browserWindow.document
|
|
156
|
+
.querySelector('[data-testid="overlay-x-line-1"]')
|
|
157
|
+
?.getAttribute('role'),
|
|
158
|
+
).toBeNull();
|
|
159
|
+
} finally {
|
|
160
|
+
browserWindow.close();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type GridAxisName, type GridViewport, worldToViewport } from '@ankhorage/grid-view';
|
|
2
|
+
|
|
3
|
+
/*** Projects a stable world-axis coordinate into the matching visual ruler or overlay position. */
|
|
4
|
+
export function projectGridRulerPosition(
|
|
5
|
+
viewport: GridViewport,
|
|
6
|
+
axis: GridAxisName,
|
|
7
|
+
position: number,
|
|
8
|
+
direction: 'ltr' | 'rtl' = 'ltr',
|
|
9
|
+
): number {
|
|
10
|
+
const point = worldToViewport(
|
|
11
|
+
axis === 'x' ? { x: position, y: viewport.offsetY } : { x: viewport.offsetX, y: position },
|
|
12
|
+
viewport,
|
|
13
|
+
);
|
|
14
|
+
const projectedPosition = axis === 'x' ? point.x : point.y;
|
|
15
|
+
|
|
16
|
+
return axis === 'x' && direction === 'rtl'
|
|
17
|
+
? viewport.width - projectedPosition
|
|
18
|
+
: projectedPosition;
|
|
19
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type GridViewport, snapWorldCoordinate } from '@ankhorage/grid-view';
|
|
2
|
+
import { describe, expect, test } from 'bun:test';
|
|
3
|
+
|
|
4
|
+
import { resolveGridRulerMarks } from './resolveGridRulerMarks';
|
|
5
|
+
|
|
6
|
+
const viewport: GridViewport = {
|
|
7
|
+
width: 300,
|
|
8
|
+
height: 160,
|
|
9
|
+
offsetX: 40,
|
|
10
|
+
offsetY: 20,
|
|
11
|
+
pixelsPerUnitX: 2,
|
|
12
|
+
pixelsPerUnitY: 4,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('resolveGridRulerMarks', () => {
|
|
16
|
+
test('aligns independent axis scales and scroll offsets through the published viewport transform', () => {
|
|
17
|
+
expect(
|
|
18
|
+
resolveGridRulerMarks(viewport, 'x', {
|
|
19
|
+
kind: 'ticks',
|
|
20
|
+
specification: { mode: 'fixed', step: 20 },
|
|
21
|
+
}).map((mark) => mark.position),
|
|
22
|
+
).toEqual([0, 40, 80, 120, 160, 200, 240, 280]);
|
|
23
|
+
expect(
|
|
24
|
+
resolveGridRulerMarks(viewport, 'y', {
|
|
25
|
+
kind: 'ticks',
|
|
26
|
+
specification: { mode: 'fixed', step: 10 },
|
|
27
|
+
}).map((mark) => mark.position),
|
|
28
|
+
).toEqual([0, 40, 80, 120, 160]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('projects only visible variable-width category starts', () => {
|
|
32
|
+
expect(
|
|
33
|
+
resolveGridRulerMarks(viewport, 'x', {
|
|
34
|
+
kind: 'categories',
|
|
35
|
+
categories: [
|
|
36
|
+
{ id: 'before', size: 10, start: 20 },
|
|
37
|
+
{ id: 'visible-a', size: 37, start: 40 },
|
|
38
|
+
{ id: 'visible-b', size: 91, start: 77 },
|
|
39
|
+
],
|
|
40
|
+
}),
|
|
41
|
+
).toEqual([
|
|
42
|
+
{ categoryId: 'visible-a', level: 'major', position: 0 },
|
|
43
|
+
{ categoryId: 'visible-b', level: 'major', position: 74 },
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('keeps a fixed interaction snap unchanged when display density changes', () => {
|
|
48
|
+
const snap = { mode: 'fixed' as const, step: 25 };
|
|
49
|
+
const sparse = resolveGridRulerMarks(viewport, 'x', {
|
|
50
|
+
kind: 'ticks',
|
|
51
|
+
specification: { mode: 'fixed', step: 50 },
|
|
52
|
+
});
|
|
53
|
+
const dense = resolveGridRulerMarks(viewport, 'x', {
|
|
54
|
+
kind: 'ticks',
|
|
55
|
+
specification: { mode: 'fixed', step: 5 },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(dense.length).toBeGreaterThan(sparse.length);
|
|
59
|
+
expect(snapWorldCoordinate(62, snap)).toBe(50);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('accepts externally supplied labels without introducing domain adapters', () => {
|
|
63
|
+
expect(
|
|
64
|
+
resolveGridRulerMarks(
|
|
65
|
+
viewport,
|
|
66
|
+
'x',
|
|
67
|
+
{ kind: 'ticks', specification: { mode: 'fixed', step: 20 } },
|
|
68
|
+
(tick) => `bar ${tick.position}`,
|
|
69
|
+
)[0],
|
|
70
|
+
).toEqual({ label: 'bar 40', level: 'major', position: 0 });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('projects horizontal marks consistently in RTL and evaluates each formatter once', () => {
|
|
74
|
+
let formatterCalls = 0;
|
|
75
|
+
|
|
76
|
+
const marks = resolveGridRulerMarks(
|
|
77
|
+
viewport,
|
|
78
|
+
'x',
|
|
79
|
+
{ kind: 'ticks', specification: { mode: 'fixed', step: 20 } },
|
|
80
|
+
(tick) => {
|
|
81
|
+
formatterCalls += 1;
|
|
82
|
+
return `tick ${tick.position}`;
|
|
83
|
+
},
|
|
84
|
+
'rtl',
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(marks.map((mark) => mark.position)).toEqual([300, 260, 220, 180, 140, 100, 60, 20]);
|
|
88
|
+
expect(formatterCalls).toBe(marks.length);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAxisCategoryTicks,
|
|
3
|
+
getAxisTicks,
|
|
4
|
+
type GridAxisName,
|
|
5
|
+
type GridAxisTick,
|
|
6
|
+
type GridViewport,
|
|
7
|
+
} from '@ankhorage/grid-view';
|
|
8
|
+
|
|
9
|
+
import type { GridRulerMark, GridRulerTickSource } from '../../../types/grid-rulers';
|
|
10
|
+
import { projectGridRulerPosition } from './projectGridRulerPosition';
|
|
11
|
+
|
|
12
|
+
/*** Resolves bounded engine ticks into viewport-pixel ruler marks without changing interaction state. */
|
|
13
|
+
export function resolveGridRulerMarks(
|
|
14
|
+
viewport: GridViewport,
|
|
15
|
+
axis: GridAxisName,
|
|
16
|
+
tickSource: GridRulerTickSource,
|
|
17
|
+
formatLabel?: (tick: GridAxisTick) => string | undefined,
|
|
18
|
+
direction: 'ltr' | 'rtl' = 'ltr',
|
|
19
|
+
): readonly GridRulerMark[] {
|
|
20
|
+
const ticks =
|
|
21
|
+
tickSource.kind === 'categories'
|
|
22
|
+
? getAxisCategoryTicks(viewport, axis, tickSource.categories)
|
|
23
|
+
: getAxisTicks(viewport, axis, tickSource.specification, tickSource.provider);
|
|
24
|
+
|
|
25
|
+
return ticks.map((tick) => {
|
|
26
|
+
const formattedLabel = formatLabel?.(tick);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
position: projectGridRulerPosition(viewport, axis, tick.position, direction),
|
|
30
|
+
level: tick.level,
|
|
31
|
+
...(tick.label !== undefined || formattedLabel !== undefined
|
|
32
|
+
? { label: formattedLabel ?? tick.label }
|
|
33
|
+
: {}),
|
|
34
|
+
...('categoryId' in tick && typeof tick.categoryId === 'string'
|
|
35
|
+
? { categoryId: tick.categoryId }
|
|
36
|
+
: {}),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ZoraComponentMeta } from '../../types/authoring';
|
|
2
|
+
|
|
3
|
+
const GRID_RULERS_NOTE =
|
|
4
|
+
'Requires a live viewport and optional executable tick/label providers; available through code APIs, not a serializable manifest node.';
|
|
5
|
+
|
|
6
|
+
/** Passive viewport decorations intentionally remain outside direct manifest authoring. */
|
|
7
|
+
export const gridLineOverlayMeta = {
|
|
8
|
+
name: 'GridLineOverlay',
|
|
9
|
+
category: 'foundation',
|
|
10
|
+
description: 'Projects bounded world-grid lines and explicit guides from a supplied viewport.',
|
|
11
|
+
directManifestNode: false,
|
|
12
|
+
note: GRID_RULERS_NOTE,
|
|
13
|
+
allowedChildren: [],
|
|
14
|
+
props: {},
|
|
15
|
+
} as const satisfies ZoraComponentMeta;
|
|
16
|
+
|
|
17
|
+
/** Rulers accept code-dependent tick and label providers and therefore remain code-only. */
|
|
18
|
+
export const gridRulerMeta = {
|
|
19
|
+
name: 'GridRuler',
|
|
20
|
+
category: 'foundation',
|
|
21
|
+
description: 'Projects visible world-axis ticks into a passive horizontal or vertical ruler.',
|
|
22
|
+
directManifestNode: false,
|
|
23
|
+
note: GRID_RULERS_NOTE,
|
|
24
|
+
allowedChildren: [],
|
|
25
|
+
props: {},
|
|
26
|
+
} as const satisfies ZoraComponentMeta;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
GridGuide,
|
|
3
|
+
GridLineOverlayProps,
|
|
4
|
+
GridRulerMark,
|
|
5
|
+
GridRulerProps,
|
|
6
|
+
GridRulerTickSource,
|
|
7
|
+
} from '../../types/grid-rulers';
|
|
8
|
+
export { GridLineOverlay } from './adapters/inbound/GridLineOverlay';
|
|
9
|
+
export { GridRuler } from './adapters/inbound/GridRuler';
|
|
10
|
+
export { resolveGridRulerMarks } from './application/resolveGridRulerMarks';
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
GameOverlay,
|
|
48
48
|
} from '../game/public';
|
|
49
49
|
import { Gradient } from '../gradient/public';
|
|
50
|
+
import { GridLineOverlay, GridRuler } from '../grid-rulers/public';
|
|
50
51
|
import { GridView, TileGrid } from '../grid-view/public';
|
|
51
52
|
import { Hero } from '../hero/public';
|
|
52
53
|
import { Icon } from '../icon/public';
|
|
@@ -65,6 +66,7 @@ import { BarcodeScannerView, CameraPermissionView, ScanOverlay } from '../scanne
|
|
|
65
66
|
import { ScreenSection, SectionHeader } from '../section/public';
|
|
66
67
|
import { SelectableItem } from '../selection/public';
|
|
67
68
|
import { Skeleton, SkeletonCard, SkeletonList, SkeletonText } from '../skeleton/public';
|
|
69
|
+
import { SpatialGrid } from '../spatial-grid/public';
|
|
68
70
|
import { Surface } from '../surface/public';
|
|
69
71
|
import {
|
|
70
72
|
CardBack,
|
|
@@ -194,6 +196,8 @@ const _ZORA_COMPONENT_REGISTRY = {
|
|
|
194
196
|
EmptyState,
|
|
195
197
|
Explorer,
|
|
196
198
|
FileExplorer,
|
|
199
|
+
GridLineOverlay,
|
|
200
|
+
GridRuler,
|
|
197
201
|
GridView,
|
|
198
202
|
TileGrid,
|
|
199
203
|
MediaExplorer,
|
|
@@ -212,6 +216,7 @@ const _ZORA_COMPONENT_REGISTRY = {
|
|
|
212
216
|
ScanOverlay,
|
|
213
217
|
SectionHeader,
|
|
214
218
|
SelectableItem,
|
|
219
|
+
SpatialGrid,
|
|
215
220
|
PaletteItem,
|
|
216
221
|
CardBack,
|
|
217
222
|
CardHand,
|
|
@@ -53,6 +53,7 @@ import { gameMeasurementProbeMeta } from '../game/meta/gameMeasurementProbeMeta'
|
|
|
53
53
|
import { gameMeta } from '../game/meta/gameMeta';
|
|
54
54
|
import { gameOverlayMeta } from '../game/meta/gameOverlayMeta';
|
|
55
55
|
import { gradientMeta } from '../gradient/gradientMeta';
|
|
56
|
+
import { gridLineOverlayMeta, gridRulerMeta } from '../grid-rulers/meta';
|
|
56
57
|
import { gridViewMeta, tileGridMeta } from '../grid-view/meta';
|
|
57
58
|
import { heroMeta } from '../hero/heroMeta';
|
|
58
59
|
import { iconMeta } from '../icon/iconMeta';
|
|
@@ -84,6 +85,7 @@ import {
|
|
|
84
85
|
skeletonMeta,
|
|
85
86
|
skeletonTextMeta,
|
|
86
87
|
} from '../skeleton/skeletonMeta';
|
|
88
|
+
import { spatialGridMeta } from '../spatial-grid/meta';
|
|
87
89
|
import { surfaceMeta } from '../surface/surfaceMeta';
|
|
88
90
|
import {
|
|
89
91
|
cardBackMeta,
|
|
@@ -198,6 +200,8 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
|
|
|
198
200
|
Explorer: explorerMeta,
|
|
199
201
|
FileExplorer: fileExplorerMeta,
|
|
200
202
|
MediaExplorer: mediaExplorerMeta,
|
|
203
|
+
GridLineOverlay: gridLineOverlayMeta,
|
|
204
|
+
GridRuler: gridRulerMeta,
|
|
201
205
|
GridView: gridViewMeta,
|
|
202
206
|
TileGrid: tileGridMeta,
|
|
203
207
|
Hero: heroMeta,
|
|
@@ -215,6 +219,7 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
|
|
|
215
219
|
ScanOverlay: scanOverlayMeta,
|
|
216
220
|
SectionHeader: sectionHeaderMeta,
|
|
217
221
|
SelectableItem: selectableItemMeta,
|
|
222
|
+
SpatialGrid: spatialGridMeta,
|
|
218
223
|
SelectionProvider: selectionProviderMeta,
|
|
219
224
|
PaletteItem: paletteItemMeta,
|
|
220
225
|
Timeline: timelineMeta,
|