@tastic/hud 0.13.0 → 0.14.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/README.md +39 -2
- package/dist/index.d.mts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +293 -261
- package/dist/index.mjs +214 -183
- package/package.json +5 -5
- package/src/BaseSettingsDialog.tsx +174 -161
- package/src/RotatedFrame.tsx +71 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -94,6 +94,43 @@ position can be nowhere near where the trigger actually is on screen, which is e
|
|
|
94
94
|
rotation-awareness fixes: without it, a popover can pick an alignment for where its trigger *used to
|
|
95
95
|
be* before rotating, clipping off the edge it's actually now near instead of the one it was near.
|
|
96
96
|
|
|
97
|
+
### `BaseSettingsDialog` lays out in its own turned frame
|
|
98
|
+
|
|
99
|
+
The settings sheet doesn't rotate its card in place. It renders through one Paper `Portal` into a
|
|
100
|
+
`RotatedFrame` (below) turned by its `rotation`, sized to the turned footprint, with rotated safe-area
|
|
101
|
+
insets and its own `Portal.Host`, and the Dialog portals into that host. So at +/-90 the sheet's
|
|
102
|
+
`maxHeight: '90%'` is measured against the phone's short edge and the whole list scrolls, instead of
|
|
103
|
+
running off the screen with its top row (Lock Orientation) out of reach. The frame is pass-through and
|
|
104
|
+
an identity frame at 0, so nothing changes in portrait. Pass `rotation={0}` when the dialog already
|
|
105
|
+
sits under a `Portal.Host` inside a turned frame of your own.
|
|
106
|
+
|
|
107
|
+
This needs `@rific/auto-paper` 0.12.2 or later: the Dialog's own body now renders at the portal host,
|
|
108
|
+
and only 0.12.2+ mounts that host inside auto-paper's own contexts (blur, Reanimated, defaults).
|
|
109
|
+
|
|
110
|
+
### `RotatedFrame`
|
|
111
|
+
|
|
112
|
+
The frame itself, exported for a screen or overlay that should follow the phone as one piece:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { RotatedFrame } from '@tastic/hud'
|
|
116
|
+
|
|
117
|
+
<RotatedFrame portalHost rotatedSafeArea maxContentWidth={640}>
|
|
118
|
+
<StatsList />
|
|
119
|
+
</RotatedFrame>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `rotation`: the angle to turn by; defaults to the live `useRotation()`.
|
|
123
|
+
- `rotatedSafeArea`: re-provide the safe-area insets remapped onto the frame's own edges, for content
|
|
124
|
+
that reads `useSafeAreaInsets()` itself. Leave it off for content that rotates insets on its own.
|
|
125
|
+
- `portalHost`: mount a Paper `Portal.Host` inside the frame, so dialogs lay out and turn with it (tell
|
|
126
|
+
them `rotation={0}`). Must be static per call site: flipping it remounts the contents.
|
|
127
|
+
- `passThrough`: let touches fall through the frame's own boxes, for a frame laid over other content.
|
|
128
|
+
- `maxContentWidth`: cap the centred content column, in the wide (landscape) footprint only.
|
|
129
|
+
|
|
130
|
+
The tree is identical at every angle, so turning the phone never remounts what's inside. Tap-driven
|
|
131
|
+
content only: react-native-gesture-handler's deltas are untransformed, so never put a gesture-tracked
|
|
132
|
+
board inside it.
|
|
133
|
+
|
|
97
134
|
## The press-away pattern (read this before wiring it up)
|
|
98
135
|
|
|
99
136
|
A single full-screen `PressAwayOverlay` works fine for a one-player screen. It does **not** work
|
|
@@ -545,10 +582,10 @@ resolve module".
|
|
|
545
582
|
| `react`, `react-native` | >=19.0.0, >=0.76.0 | every entry |
|
|
546
583
|
| `react-native-paper` | >=5.0.0 | main entry, `./guide` |
|
|
547
584
|
| `react-native-reanimated` | >=3.0.0 | main entry (`StaggeredWord`), `./guide` |
|
|
548
|
-
| `@rific/auto-paper` | >=0.
|
|
585
|
+
| `@rific/auto-paper` | >=0.12.2 | main entry (`BaseSettingsDialog` needs the nested `Portal.Host` first shipped in 0.12.2), `./guide` |
|
|
549
586
|
| `@rific/feedback-press` | >=0.10.2 | main entry (`useSoundSettings` first shipped in 0.10.2), `./guide` |
|
|
550
587
|
| `react-native-safe-area-context` | >=5.0.0 | main entry (`useZoneClampedAlign`) |
|
|
551
|
-
| `@tastic/core` | >=0.
|
|
588
|
+
| `@tastic/core` | >=0.12.0, optional | main entry: `useRotation()` and most dialogs; `RotatedFrame` and `BaseSettingsDialog` need `FakeLandscapeView`'s `rotation`/`passThrough`, first shipped in 0.12.0 |
|
|
552
589
|
| `@rific/updater` | >=0.4.0, optional | main entry: `BaseSettingsDialog`, `UpdateDialog` |
|
|
553
590
|
| `@tastic/profile` | >=0.4.0, optional | main entry: `PlayerSetupPanel`, `AchievementUnlockList` (`ProfileChip`) |
|
|
554
591
|
| `@tastic/split-screen` | >=0.5.0, optional | main entry: `useZoneClampedAlign` |
|
package/dist/index.d.mts
CHANGED
|
@@ -398,6 +398,40 @@ interface Props$3 {
|
|
|
398
398
|
}
|
|
399
399
|
declare function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily }: Props$3): react.JSX.Element;
|
|
400
400
|
|
|
401
|
+
interface RotatedFrameProps {
|
|
402
|
+
children: ReactNode;
|
|
403
|
+
/** The angle to turn the frame by. Defaults to the live physical hold (@tastic/core's useRotation); pass it
|
|
404
|
+
* when the caller already has the exact angle it wants, e.g. a dialog handed its caller's rotation, or 0 for
|
|
405
|
+
* one that already sits inside a turned frame. */
|
|
406
|
+
rotation?: ViewRotation;
|
|
407
|
+
/** Give everything inside the frame the safe-area insets already remapped onto the frame's own edges (see
|
|
408
|
+
* @tastic/core's rotateInsets), so a component that reads `useSafeAreaInsets()` itself (a library screen,
|
|
409
|
+
* Paper's Modal, a toaster) lands on the right edge when the frame is turned. Leave it off for content that
|
|
410
|
+
* rotates the insets itself, which would otherwise rotate them twice. */
|
|
411
|
+
rotatedSafeArea?: boolean;
|
|
412
|
+
/** Mount a Paper Portal.Host INSIDE the frame, so a dialog (Paper's Portal otherwise mounts at the app root,
|
|
413
|
+
* outside the frame) is laid out against the frame's footprint and turned with it. The dialog must then be
|
|
414
|
+
* told `rotation={0}`. */
|
|
415
|
+
portalHost?: boolean;
|
|
416
|
+
/** Let touches fall through the frame to whatever is beneath it, so only its children claim them. For a frame
|
|
417
|
+
* laid OVER other content (a dialog layer, a root-level overlay); a screen that owns the window leaves it off. */
|
|
418
|
+
passThrough?: boolean;
|
|
419
|
+
/** Cap on the content column's width in the WIDE layout only (the rotated footprint is landscape), centred: a
|
|
420
|
+
* stats list or a roster stretched across an 874pt-wide landscape screen reads badly. Portrait is never capped,
|
|
421
|
+
* so a portrait iPad or desktop window keeps its full width. Omit for no cap. */
|
|
422
|
+
maxContentWidth?: number;
|
|
423
|
+
}
|
|
424
|
+
/** A frame turned to follow the phone while the OS stays portrait-locked (see @tastic/core's FakeLandscapeView),
|
|
425
|
+
* sized to the turned footprint so its content lays out against the short edge at +/-90. The tree is IDENTICAL
|
|
426
|
+
* at every angle, so a rotation never remounts what is inside: `FakeLandscapeView > SafeArea provider >
|
|
427
|
+
* [Portal.Host] > column`. `rotation`, `rotatedSafeArea`, `passThrough` and `maxContentWidth` only change styles
|
|
428
|
+
* and values, but `portalHost` adds or removes the Portal.Host element, so it must be STATIC per call site
|
|
429
|
+
* (flipping it at runtime would remount everything inside).
|
|
430
|
+
*
|
|
431
|
+
* Safe for tap-driven content only: react-native-gesture-handler's translation deltas are untransformed, so
|
|
432
|
+
* never put a gesture-tracked game board inside it. */
|
|
433
|
+
declare function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea, portalHost, passThrough, maxContentWidth }: RotatedFrameProps): react.JSX.Element;
|
|
434
|
+
|
|
401
435
|
interface Props$2 {
|
|
402
436
|
onBack: () => void;
|
|
403
437
|
onSettings: () => void;
|
|
@@ -474,4 +508,4 @@ declare function useQuitConfirmation(hasProgress: () => boolean, onConfirmedBack
|
|
|
474
508
|
|
|
475
509
|
declare function useZoneClampedAlign(open: boolean, contentWidth: number, contentHeight: number, rotation?: PopoverRotation): ReturnType<typeof useAutoAlign>;
|
|
476
510
|
|
|
477
|
-
export { AchievementCatalogSection, AchievementRow, AchievementUnlockList, type AchievementUnlockOwner, ActivityStatSection, type AlignResult, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, ControlSchemePicker, type ControlSchemePickerProps, CornerActionButtons, type CornerStatusBadgeSeat, CornerStatusBadges, type CornerStatusBadgesProps, InlineColorPicker, LABELED_DROPDOWN_POPOVER_WIDTH, LOCKED_BADGE_COLOR, LabeledDropdown, type LabeledDropdownOption, MONO_FONT, type MenuOption, type MenuSection, type MultiSelectSection, type OrientationMode, type OverlayCardColors, PlayerSetupPanel, type PlayerSetupPanelProps, type PopoverAlign, PopoverBody, type PopoverHost, type PopoverRotation, type PopoverVerticalAlign, PressAwayOverlay, type QuitConfirmation, ReadyButton, SectionedDropdown, SharedActionBand, type SingleSelectSection, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, TriggerGaugeHost, type TriggerGaugeProps, type UnlockedAchievement, UpdateDialog, type UpdateDialogProps, getColorPopoverId, getCpuDifficultyPopoverId, getInlineColorPickerContentSize, getLabeledDropdownContentHeight, getOverlayCardColors, getStaggeredWordDuration, loadSkiaWeb, overlayActionStyles, overlayStyles, useAutoAlign, usePopoverHost, useQuitConfirmation, useZoneClampedAlign };
|
|
511
|
+
export { AchievementCatalogSection, AchievementRow, AchievementUnlockList, type AchievementUnlockOwner, ActivityStatSection, type AlignResult, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, ControlSchemePicker, type ControlSchemePickerProps, CornerActionButtons, type CornerStatusBadgeSeat, CornerStatusBadges, type CornerStatusBadgesProps, InlineColorPicker, LABELED_DROPDOWN_POPOVER_WIDTH, LOCKED_BADGE_COLOR, LabeledDropdown, type LabeledDropdownOption, MONO_FONT, type MenuOption, type MenuSection, type MultiSelectSection, type OrientationMode, type OverlayCardColors, PlayerSetupPanel, type PlayerSetupPanelProps, type PopoverAlign, PopoverBody, type PopoverHost, type PopoverRotation, type PopoverVerticalAlign, PressAwayOverlay, type QuitConfirmation, ReadyButton, RotatedFrame, type RotatedFrameProps, SectionedDropdown, SharedActionBand, type SingleSelectSection, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, TriggerGaugeHost, type TriggerGaugeProps, type UnlockedAchievement, UpdateDialog, type UpdateDialogProps, getColorPopoverId, getCpuDifficultyPopoverId, getInlineColorPickerContentSize, getLabeledDropdownContentHeight, getOverlayCardColors, getStaggeredWordDuration, loadSkiaWeb, overlayActionStyles, overlayStyles, useAutoAlign, usePopoverHost, useQuitConfirmation, useZoneClampedAlign };
|
package/dist/index.d.ts
CHANGED
|
@@ -398,6 +398,40 @@ interface Props$3 {
|
|
|
398
398
|
}
|
|
399
399
|
declare function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily }: Props$3): react.JSX.Element;
|
|
400
400
|
|
|
401
|
+
interface RotatedFrameProps {
|
|
402
|
+
children: ReactNode;
|
|
403
|
+
/** The angle to turn the frame by. Defaults to the live physical hold (@tastic/core's useRotation); pass it
|
|
404
|
+
* when the caller already has the exact angle it wants, e.g. a dialog handed its caller's rotation, or 0 for
|
|
405
|
+
* one that already sits inside a turned frame. */
|
|
406
|
+
rotation?: ViewRotation;
|
|
407
|
+
/** Give everything inside the frame the safe-area insets already remapped onto the frame's own edges (see
|
|
408
|
+
* @tastic/core's rotateInsets), so a component that reads `useSafeAreaInsets()` itself (a library screen,
|
|
409
|
+
* Paper's Modal, a toaster) lands on the right edge when the frame is turned. Leave it off for content that
|
|
410
|
+
* rotates the insets itself, which would otherwise rotate them twice. */
|
|
411
|
+
rotatedSafeArea?: boolean;
|
|
412
|
+
/** Mount a Paper Portal.Host INSIDE the frame, so a dialog (Paper's Portal otherwise mounts at the app root,
|
|
413
|
+
* outside the frame) is laid out against the frame's footprint and turned with it. The dialog must then be
|
|
414
|
+
* told `rotation={0}`. */
|
|
415
|
+
portalHost?: boolean;
|
|
416
|
+
/** Let touches fall through the frame to whatever is beneath it, so only its children claim them. For a frame
|
|
417
|
+
* laid OVER other content (a dialog layer, a root-level overlay); a screen that owns the window leaves it off. */
|
|
418
|
+
passThrough?: boolean;
|
|
419
|
+
/** Cap on the content column's width in the WIDE layout only (the rotated footprint is landscape), centred: a
|
|
420
|
+
* stats list or a roster stretched across an 874pt-wide landscape screen reads badly. Portrait is never capped,
|
|
421
|
+
* so a portrait iPad or desktop window keeps its full width. Omit for no cap. */
|
|
422
|
+
maxContentWidth?: number;
|
|
423
|
+
}
|
|
424
|
+
/** A frame turned to follow the phone while the OS stays portrait-locked (see @tastic/core's FakeLandscapeView),
|
|
425
|
+
* sized to the turned footprint so its content lays out against the short edge at +/-90. The tree is IDENTICAL
|
|
426
|
+
* at every angle, so a rotation never remounts what is inside: `FakeLandscapeView > SafeArea provider >
|
|
427
|
+
* [Portal.Host] > column`. `rotation`, `rotatedSafeArea`, `passThrough` and `maxContentWidth` only change styles
|
|
428
|
+
* and values, but `portalHost` adds or removes the Portal.Host element, so it must be STATIC per call site
|
|
429
|
+
* (flipping it at runtime would remount everything inside).
|
|
430
|
+
*
|
|
431
|
+
* Safe for tap-driven content only: react-native-gesture-handler's translation deltas are untransformed, so
|
|
432
|
+
* never put a gesture-tracked game board inside it. */
|
|
433
|
+
declare function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea, portalHost, passThrough, maxContentWidth }: RotatedFrameProps): react.JSX.Element;
|
|
434
|
+
|
|
401
435
|
interface Props$2 {
|
|
402
436
|
onBack: () => void;
|
|
403
437
|
onSettings: () => void;
|
|
@@ -474,4 +508,4 @@ declare function useQuitConfirmation(hasProgress: () => boolean, onConfirmedBack
|
|
|
474
508
|
|
|
475
509
|
declare function useZoneClampedAlign(open: boolean, contentWidth: number, contentHeight: number, rotation?: PopoverRotation): ReturnType<typeof useAutoAlign>;
|
|
476
510
|
|
|
477
|
-
export { AchievementCatalogSection, AchievementRow, AchievementUnlockList, type AchievementUnlockOwner, ActivityStatSection, type AlignResult, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, ControlSchemePicker, type ControlSchemePickerProps, CornerActionButtons, type CornerStatusBadgeSeat, CornerStatusBadges, type CornerStatusBadgesProps, InlineColorPicker, LABELED_DROPDOWN_POPOVER_WIDTH, LOCKED_BADGE_COLOR, LabeledDropdown, type LabeledDropdownOption, MONO_FONT, type MenuOption, type MenuSection, type MultiSelectSection, type OrientationMode, type OverlayCardColors, PlayerSetupPanel, type PlayerSetupPanelProps, type PopoverAlign, PopoverBody, type PopoverHost, type PopoverRotation, type PopoverVerticalAlign, PressAwayOverlay, type QuitConfirmation, ReadyButton, SectionedDropdown, SharedActionBand, type SingleSelectSection, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, TriggerGaugeHost, type TriggerGaugeProps, type UnlockedAchievement, UpdateDialog, type UpdateDialogProps, getColorPopoverId, getCpuDifficultyPopoverId, getInlineColorPickerContentSize, getLabeledDropdownContentHeight, getOverlayCardColors, getStaggeredWordDuration, loadSkiaWeb, overlayActionStyles, overlayStyles, useAutoAlign, usePopoverHost, useQuitConfirmation, useZoneClampedAlign };
|
|
511
|
+
export { AchievementCatalogSection, AchievementRow, AchievementUnlockList, type AchievementUnlockOwner, ActivityStatSection, type AlignResult, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, ControlSchemePicker, type ControlSchemePickerProps, CornerActionButtons, type CornerStatusBadgeSeat, CornerStatusBadges, type CornerStatusBadgesProps, InlineColorPicker, LABELED_DROPDOWN_POPOVER_WIDTH, LOCKED_BADGE_COLOR, LabeledDropdown, type LabeledDropdownOption, MONO_FONT, type MenuOption, type MenuSection, type MultiSelectSection, type OrientationMode, type OverlayCardColors, PlayerSetupPanel, type PlayerSetupPanelProps, type PopoverAlign, PopoverBody, type PopoverHost, type PopoverRotation, type PopoverVerticalAlign, PressAwayOverlay, type QuitConfirmation, ReadyButton, RotatedFrame, type RotatedFrameProps, SectionedDropdown, SharedActionBand, type SingleSelectSection, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, TriggerGaugeHost, type TriggerGaugeProps, type UnlockedAchievement, UpdateDialog, type UpdateDialogProps, getColorPopoverId, getCpuDifficultyPopoverId, getInlineColorPickerContentSize, getLabeledDropdownContentHeight, getOverlayCardColors, getStaggeredWordDuration, loadSkiaWeb, overlayActionStyles, overlayStyles, useAutoAlign, usePopoverHost, useQuitConfirmation, useZoneClampedAlign };
|