@dreamboard-games/ui-sdk 0.0.41
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/LICENSE +89 -0
- package/NOTICE +1 -0
- package/README.md +154 -0
- package/dist/components/ActionButton.d.ts +13 -0
- package/dist/components/ActionButton.d.ts.map +1 -0
- package/dist/components/ActionButton.js +14 -0
- package/dist/components/ActionPanel.d.ts +33 -0
- package/dist/components/ActionPanel.d.ts.map +1 -0
- package/dist/components/ActionPanel.js +148 -0
- package/dist/components/Card.d.ts +29 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +220 -0
- package/dist/components/ChromeSuppressionContext.d.ts +7 -0
- package/dist/components/ChromeSuppressionContext.d.ts.map +1 -0
- package/dist/components/ChromeSuppressionContext.js +34 -0
- package/dist/components/CostDisplay.d.ts +22 -0
- package/dist/components/CostDisplay.d.ts.map +1 -0
- package/dist/components/CostDisplay.js +41 -0
- package/dist/components/DiceRoller.d.ts +30 -0
- package/dist/components/DiceRoller.d.ts.map +1 -0
- package/dist/components/DiceRoller.js +319 -0
- package/dist/components/Drawer.d.ts +19 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +55 -0
- package/dist/components/ErrorBoundary.d.ts +24 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +37 -0
- package/dist/components/GameEndDisplay.d.ts +27 -0
- package/dist/components/GameEndDisplay.d.ts.map +1 -0
- package/dist/components/GameEndDisplay.js +185 -0
- package/dist/components/GameSkeleton.d.ts +12 -0
- package/dist/components/GameSkeleton.d.ts.map +1 -0
- package/dist/components/GameSkeleton.js +54 -0
- package/dist/components/Hand.d.ts +99 -0
- package/dist/components/Hand.d.ts.map +1 -0
- package/dist/components/Hand.js +162 -0
- package/dist/components/HandDock.d.ts +35 -0
- package/dist/components/HandDock.d.ts.map +1 -0
- package/dist/components/HandDock.js +124 -0
- package/dist/components/InteractionForm.d.ts +50 -0
- package/dist/components/InteractionForm.d.ts.map +1 -0
- package/dist/components/InteractionForm.js +402 -0
- package/dist/components/MoreActions.d.ts +49 -0
- package/dist/components/MoreActions.d.ts.map +1 -0
- package/dist/components/MoreActions.js +64 -0
- package/dist/components/PhaseIndicator.d.ts +35 -0
- package/dist/components/PhaseIndicator.d.ts.map +1 -0
- package/dist/components/PhaseIndicator.js +212 -0
- package/dist/components/PlayArea.d.ts +28 -0
- package/dist/components/PlayArea.d.ts.map +1 -0
- package/dist/components/PlayArea.js +48 -0
- package/dist/components/PluginRuntime.d.ts +37 -0
- package/dist/components/PluginRuntime.d.ts.map +1 -0
- package/dist/components/PluginRuntime.js +47 -0
- package/dist/components/PrimaryActionButton.d.ts +98 -0
- package/dist/components/PrimaryActionButton.d.ts.map +1 -0
- package/dist/components/PrimaryActionButton.js +183 -0
- package/dist/components/PrimaryButton.d.ts +20 -0
- package/dist/components/PrimaryButton.d.ts.map +1 -0
- package/dist/components/PrimaryButton.js +5 -0
- package/dist/components/PromptDialogHost.d.ts +15 -0
- package/dist/components/PromptDialogHost.d.ts.map +1 -0
- package/dist/components/PromptDialogHost.js +22 -0
- package/dist/components/ResourceCounter.d.ts +38 -0
- package/dist/components/ResourceCounter.d.ts.map +1 -0
- package/dist/components/ResourceCounter.js +118 -0
- package/dist/components/ThemedButton.d.ts +12 -0
- package/dist/components/ThemedButton.d.ts.map +1 -0
- package/dist/components/ThemedButton.js +38 -0
- package/dist/components/Toast.d.ts +35 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +116 -0
- package/dist/components/board/HexGrid.d.ts +344 -0
- package/dist/components/board/HexGrid.d.ts.map +1 -0
- package/dist/components/board/HexGrid.js +340 -0
- package/dist/components/board/NetworkGraph.d.ts +100 -0
- package/dist/components/board/NetworkGraph.d.ts.map +1 -0
- package/dist/components/board/NetworkGraph.js +123 -0
- package/dist/components/board/SlotSystem.d.ts +71 -0
- package/dist/components/board/SlotSystem.d.ts.map +1 -0
- package/dist/components/board/SlotSystem.js +87 -0
- package/dist/components/board/SquareGrid.d.ts +188 -0
- package/dist/components/board/SquareGrid.d.ts.map +1 -0
- package/dist/components/board/SquareGrid.js +328 -0
- package/dist/components/board/TrackBoard.d.ts +113 -0
- package/dist/components/board/TrackBoard.d.ts.map +1 -0
- package/dist/components/board/TrackBoard.js +135 -0
- package/dist/components/board/ZoneMap.d.ts +88 -0
- package/dist/components/board/ZoneMap.d.ts.map +1 -0
- package/dist/components/board/ZoneMap.js +133 -0
- package/dist/components/board/hex-board-view.d.ts +69 -0
- package/dist/components/board/hex-board-view.d.ts.map +1 -0
- package/dist/components/board/hex-board-view.js +60 -0
- package/dist/components/board/index.d.ts +23 -0
- package/dist/components/board/index.d.ts.map +1 -0
- package/dist/components/board/index.js +40 -0
- package/dist/components/board/interaction-accessibility.d.ts +5 -0
- package/dist/components/board/interaction-accessibility.d.ts.map +1 -0
- package/dist/components/board/interaction-accessibility.js +13 -0
- package/dist/components/board/target-layer.d.ts +13 -0
- package/dist/components/board/target-layer.d.ts.map +1 -0
- package/dist/components/board/target-layer.js +10 -0
- package/dist/components/card-render-content.type-test.d.ts +2 -0
- package/dist/components/card-render-content.type-test.d.ts.map +1 -0
- package/dist/components/card-render-content.type-test.js +1 -0
- package/dist/components/index.d.ts +34 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +35 -0
- package/dist/components/interaction-dialog-behavior.d.ts +15 -0
- package/dist/components/interaction-dialog-behavior.d.ts.map +1 -0
- package/dist/components/interaction-dialog-behavior.js +9 -0
- package/dist/components/surfaces/BlockerSurface.d.ts +27 -0
- package/dist/components/surfaces/BlockerSurface.d.ts.map +1 -0
- package/dist/components/surfaces/BlockerSurface.js +38 -0
- package/dist/components/surfaces/BoardSurface.d.ts +77 -0
- package/dist/components/surfaces/BoardSurface.d.ts.map +1 -0
- package/dist/components/surfaces/BoardSurface.js +180 -0
- package/dist/components/surfaces/ChromeSurface.d.ts +29 -0
- package/dist/components/surfaces/ChromeSurface.d.ts.map +1 -0
- package/dist/components/surfaces/ChromeSurface.js +34 -0
- package/dist/components/surfaces/ExhaustivenessAudit.d.ts +32 -0
- package/dist/components/surfaces/ExhaustivenessAudit.d.ts.map +1 -0
- package/dist/components/surfaces/ExhaustivenessAudit.js +65 -0
- package/dist/components/surfaces/InboxSurface.d.ts +40 -0
- package/dist/components/surfaces/InboxSurface.d.ts.map +1 -0
- package/dist/components/surfaces/InboxSurface.js +99 -0
- package/dist/components/surfaces/MarketSurface.d.ts +62 -0
- package/dist/components/surfaces/MarketSurface.d.ts.map +1 -0
- package/dist/components/surfaces/MarketSurface.js +242 -0
- package/dist/components/surfaces/PanelSurface.d.ts +111 -0
- package/dist/components/surfaces/PanelSurface.d.ts.map +1 -0
- package/dist/components/surfaces/PanelSurface.js +180 -0
- package/dist/components/surfaces/PlayerCardsSurface.d.ts +104 -0
- package/dist/components/surfaces/PlayerCardsSurface.d.ts.map +1 -0
- package/dist/components/surfaces/PlayerCardsSurface.js +178 -0
- package/dist/components/surfaces/internal/CardZoneFollowUpForm.d.ts +7 -0
- package/dist/components/surfaces/internal/CardZoneFollowUpForm.d.ts.map +1 -0
- package/dist/components/surfaces/internal/CardZoneFollowUpForm.js +9 -0
- package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts +71 -0
- package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -0
- package/dist/components/surfaces/internal/DefaultInteractionButton.js +82 -0
- package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +21 -0
- package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -0
- package/dist/components/surfaces/internal/useCardZoneInteractions.js +202 -0
- package/dist/components/surfaces/types.d.ts +59 -0
- package/dist/components/surfaces/types.d.ts.map +1 -0
- package/dist/components/surfaces/types.js +1 -0
- package/dist/context/ClientParamSchemaContext.d.ts +21 -0
- package/dist/context/ClientParamSchemaContext.d.ts.map +1 -0
- package/dist/context/ClientParamSchemaContext.js +12 -0
- package/dist/context/InteractionDraftContext.d.ts +69 -0
- package/dist/context/InteractionDraftContext.d.ts.map +1 -0
- package/dist/context/InteractionDraftContext.js +145 -0
- package/dist/context/PluginSessionContext.d.ts +33 -0
- package/dist/context/PluginSessionContext.d.ts.map +1 -0
- package/dist/context/PluginSessionContext.js +38 -0
- package/dist/context/PluginStateContext.d.ts +116 -0
- package/dist/context/PluginStateContext.d.ts.map +1 -0
- package/dist/context/PluginStateContext.js +186 -0
- package/dist/context/RuntimeContext.d.ts +49 -0
- package/dist/context/RuntimeContext.d.ts.map +1 -0
- package/dist/context/RuntimeContext.js +67 -0
- package/dist/defaults/components.d.ts +52 -0
- package/dist/defaults/components.d.ts.map +1 -0
- package/dist/defaults/components.js +159 -0
- package/dist/defaults/index.d.ts +2 -0
- package/dist/defaults/index.d.ts.map +1 -0
- package/dist/defaults/index.js +1 -0
- package/dist/errors/ValidationError.d.ts +10 -0
- package/dist/errors/ValidationError.d.ts.map +1 -0
- package/dist/errors/ValidationError.js +23 -0
- package/dist/helpers/cards.d.ts +3 -0
- package/dist/helpers/cards.d.ts.map +1 -0
- package/dist/helpers/cards.js +11 -0
- package/dist/helpers/track-board.d.ts +79 -0
- package/dist/helpers/track-board.d.ts.map +1 -0
- package/dist/helpers/track-board.js +56 -0
- package/dist/hooks/useActivePlayers.d.ts +16 -0
- package/dist/hooks/useActivePlayers.d.ts.map +1 -0
- package/dist/hooks/useActivePlayers.js +17 -0
- package/dist/hooks/useBoardInteractions.d.ts +110 -0
- package/dist/hooks/useBoardInteractions.d.ts.map +1 -0
- package/dist/hooks/useBoardInteractions.js +248 -0
- package/dist/hooks/useBoardTopology.d.ts +23 -0
- package/dist/hooks/useBoardTopology.d.ts.map +1 -0
- package/dist/hooks/useBoardTopology.js +128 -0
- package/dist/hooks/useCards.d.ts +3 -0
- package/dist/hooks/useCards.d.ts.map +1 -0
- package/dist/hooks/useCards.js +5 -0
- package/dist/hooks/useGameSelector.d.ts +13 -0
- package/dist/hooks/useGameSelector.d.ts.map +1 -0
- package/dist/hooks/useGameSelector.js +67 -0
- package/dist/hooks/useGameView.d.ts +6 -0
- package/dist/hooks/useGameView.d.ts.map +1 -0
- package/dist/hooks/useGameView.js +7 -0
- package/dist/hooks/useHandLayout.d.ts +120 -0
- package/dist/hooks/useHandLayout.d.ts.map +1 -0
- package/dist/hooks/useHandLayout.js +235 -0
- package/dist/hooks/useHexBoard.d.ts +19 -0
- package/dist/hooks/useHexBoard.d.ts.map +1 -0
- package/dist/hooks/useHexBoard.js +28 -0
- package/dist/hooks/useHexGrid.d.ts +56 -0
- package/dist/hooks/useHexGrid.d.ts.map +1 -0
- package/dist/hooks/useHexGrid.js +112 -0
- package/dist/hooks/useInteractionByKey.d.ts +29 -0
- package/dist/hooks/useInteractionByKey.d.ts.map +1 -0
- package/dist/hooks/useInteractionByKey.js +263 -0
- package/dist/hooks/useInteractionHandle.d.ts +103 -0
- package/dist/hooks/useInteractionHandle.d.ts.map +1 -0
- package/dist/hooks/useInteractionHandle.js +254 -0
- package/dist/hooks/useIsMobile.d.ts +7 -0
- package/dist/hooks/useIsMobile.d.ts.map +1 -0
- package/dist/hooks/useIsMobile.js +29 -0
- package/dist/hooks/useIsMyTurn.d.ts +6 -0
- package/dist/hooks/useIsMyTurn.d.ts.map +1 -0
- package/dist/hooks/useIsMyTurn.js +11 -0
- package/dist/hooks/useLobby.d.ts +28 -0
- package/dist/hooks/useLobby.d.ts.map +1 -0
- package/dist/hooks/useLobby.js +60 -0
- package/dist/hooks/useMe.d.ts +11 -0
- package/dist/hooks/useMe.d.ts.map +1 -0
- package/dist/hooks/useMe.js +32 -0
- package/dist/hooks/usePanZoom.d.ts +113 -0
- package/dist/hooks/usePanZoom.d.ts.map +1 -0
- package/dist/hooks/usePanZoom.js +165 -0
- package/dist/hooks/usePlayerInfo.d.ts +4 -0
- package/dist/hooks/usePlayerInfo.d.ts.map +1 -0
- package/dist/hooks/usePlayerInfo.js +21 -0
- package/dist/hooks/usePlayerTurnOrder.d.ts +15 -0
- package/dist/hooks/usePlayerTurnOrder.d.ts.map +1 -0
- package/dist/hooks/usePlayerTurnOrder.js +22 -0
- package/dist/hooks/usePluginRuntime.d.ts +45 -0
- package/dist/hooks/usePluginRuntime.d.ts.map +1 -0
- package/dist/hooks/usePluginRuntime.js +92 -0
- package/dist/hooks/useSeatInbox.d.ts +22 -0
- package/dist/hooks/useSeatInbox.d.ts.map +1 -0
- package/dist/hooks/useSeatInbox.js +43 -0
- package/dist/hooks/useSimultaneousPhase.d.ts +7 -0
- package/dist/hooks/useSimultaneousPhase.d.ts.map +1 -0
- package/dist/hooks/useSimultaneousPhase.js +8 -0
- package/dist/hooks/useSquareBoard.d.ts +21 -0
- package/dist/hooks/useSquareBoard.d.ts.map +1 -0
- package/dist/hooks/useSquareBoard.js +67 -0
- package/dist/hooks/useSquareGrid.d.ts +96 -0
- package/dist/hooks/useSquareGrid.d.ts.map +1 -0
- package/dist/hooks/useSquareGrid.js +152 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/internal/ui/alert.d.ts +8 -0
- package/dist/internal/ui/alert.d.ts.map +1 -0
- package/dist/internal/ui/alert.js +11 -0
- package/dist/internal/ui/button.d.ts +10 -0
- package/dist/internal/ui/button.d.ts.map +1 -0
- package/dist/internal/ui/button.js +21 -0
- package/dist/internal/ui/dialog.d.ts +16 -0
- package/dist/internal/ui/dialog.d.ts.map +1 -0
- package/dist/internal/ui/dialog.js +35 -0
- package/dist/internal/ui/input.d.ts +3 -0
- package/dist/internal/ui/input.d.ts.map +1 -0
- package/dist/internal/ui/input.js +5 -0
- package/dist/internal/ui/label.d.ts +4 -0
- package/dist/internal/ui/label.d.ts.map +1 -0
- package/dist/internal/ui/label.js +7 -0
- package/dist/internal/ui/select.d.ts +9 -0
- package/dist/internal/ui/select.d.ts.map +1 -0
- package/dist/internal/ui/select.js +23 -0
- package/dist/internal/ui/tooltip.d.ts +7 -0
- package/dist/internal/ui/tooltip.d.ts.map +1 -0
- package/dist/internal/ui/tooltip.js +16 -0
- package/dist/internal/ui/utils.d.ts +3 -0
- package/dist/internal/ui/utils.d.ts.map +1 -0
- package/dist/internal/ui/utils.js +4 -0
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/plugin-styles.css +246 -0
- package/dist/primitives/board.d.ts +29 -0
- package/dist/primitives/board.d.ts.map +1 -0
- package/dist/primitives/board.js +163 -0
- package/dist/primitives/game-ui-provider.d.ts +12 -0
- package/dist/primitives/game-ui-provider.d.ts.map +1 -0
- package/dist/primitives/game-ui-provider.js +7 -0
- package/dist/primitives/index.d.ts +8 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +7 -0
- package/dist/primitives/interaction.d.ts +52 -0
- package/dist/primitives/interaction.d.ts.map +1 -0
- package/dist/primitives/interaction.js +250 -0
- package/dist/primitives/phase.d.ts +15 -0
- package/dist/primitives/phase.d.ts.map +1 -0
- package/dist/primitives/phase.js +18 -0
- package/dist/primitives/player-roster.d.ts +64 -0
- package/dist/primitives/player-roster.d.ts.map +1 -0
- package/dist/primitives/player-roster.js +149 -0
- package/dist/primitives/primitive-props.d.ts +15 -0
- package/dist/primitives/primitive-props.d.ts.map +1 -0
- package/dist/primitives/primitive-props.js +39 -0
- package/dist/primitives/prompt.d.ts +44 -0
- package/dist/primitives/prompt.d.ts.map +1 -0
- package/dist/primitives/prompt.js +101 -0
- package/dist/primitives/zone.d.ts +31 -0
- package/dist/primitives/zone.d.ts.map +1 -0
- package/dist/primitives/zone.js +58 -0
- package/dist/reducer.d.ts +21 -0
- package/dist/reducer.d.ts.map +1 -0
- package/dist/reducer.js +14 -0
- package/dist/runtime/createPluginRuntimeAPI.d.ts +67 -0
- package/dist/runtime/createPluginRuntimeAPI.d.ts.map +1 -0
- package/dist/runtime/createPluginRuntimeAPI.js +419 -0
- package/dist/theme/ThemeProvider.d.ts +98 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.js +148 -0
- package/dist/theme/board.d.ts +42 -0
- package/dist/theme/board.d.ts.map +1 -0
- package/dist/theme/board.js +34 -0
- package/dist/theme/css-vars.d.ts +31 -0
- package/dist/theme/css-vars.d.ts.map +1 -0
- package/dist/theme/css-vars.js +88 -0
- package/dist/theme/derive.d.ts +66 -0
- package/dist/theme/derive.d.ts.map +1 -0
- package/dist/theme/derive.js +161 -0
- package/dist/theme/index.d.ts +22 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +20 -0
- package/dist/theme/presets/arcade.d.ts +10 -0
- package/dist/theme/presets/arcade.d.ts.map +1 -0
- package/dist/theme/presets/arcade.js +257 -0
- package/dist/theme/presets/studio.d.ts +10 -0
- package/dist/theme/presets/studio.d.ts.map +1 -0
- package/dist/theme/presets/studio.js +257 -0
- package/dist/theme/presets/tabletop.d.ts +15 -0
- package/dist/theme/presets/tabletop.d.ts.map +1 -0
- package/dist/theme/presets/tabletop.js +262 -0
- package/dist/theme/tokens.d.ts +345 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +57 -0
- package/dist/types/player-state.d.ts +337 -0
- package/dist/types/player-state.d.ts.map +1 -0
- package/dist/types/player-state.js +1 -0
- package/dist/types/plugin-state.d.ts +324 -0
- package/dist/types/plugin-state.d.ts.map +1 -0
- package/dist/types/plugin-state.js +1 -0
- package/dist/types/reducer-state.d.ts +10 -0
- package/dist/types/reducer-state.d.ts.map +1 -0
- package/dist/types/reducer-state.js +1 -0
- package/dist/types/runtime-api.d.ts +99 -0
- package/dist/types/runtime-api.d.ts.map +1 -0
- package/dist/types/runtime-api.js +1 -0
- package/dist/types/tiled-board.d.ts +187 -0
- package/dist/types/tiled-board.d.ts.map +1 -0
- package/dist/types/tiled-board.js +226 -0
- package/dist/ui-contract.d.ts +78 -0
- package/dist/ui-contract.d.ts.map +1 -0
- package/dist/ui-contract.js +15 -0
- package/dist/ui-sdk.d.ts +3409 -0
- package/dist/utils/interaction-inputs.d.ts +22 -0
- package/dist/utils/interaction-inputs.d.ts.map +1 -0
- package/dist/utils/interaction-inputs.js +219 -0
- package/dist/utils/interaction-labels.d.ts +4 -0
- package/dist/utils/interaction-labels.d.ts.map +1 -0
- package/dist/utils/interaction-labels.js +18 -0
- package/dist/utils/interaction-status.d.ts +15 -0
- package/dist/utils/interaction-status.d.ts.map +1 -0
- package/dist/utils/interaction-status.js +31 -0
- package/package.json +101 -0
- package/src/components/ActionButton.tsx +48 -0
- package/src/components/ActionPanel.tsx +310 -0
- package/src/components/Card.tsx +385 -0
- package/src/components/ChromeSuppressionContext.tsx +70 -0
- package/src/components/CostDisplay.test.tsx +23 -0
- package/src/components/CostDisplay.tsx +145 -0
- package/src/components/DiceRoller.tsx +601 -0
- package/src/components/Drawer.tsx +179 -0
- package/src/components/ErrorBoundary.tsx +119 -0
- package/src/components/GameEndDisplay.test.tsx +19 -0
- package/src/components/GameEndDisplay.tsx +398 -0
- package/src/components/GameSkeleton.tsx +260 -0
- package/src/components/Hand.tsx +387 -0
- package/src/components/HandDock.tsx +257 -0
- package/src/components/InteractionForm.test.tsx +303 -0
- package/src/components/InteractionForm.tsx +1029 -0
- package/src/components/MoreActions.test.tsx +93 -0
- package/src/components/MoreActions.tsx +143 -0
- package/src/components/PhaseIndicator.tsx +341 -0
- package/src/components/PlayArea.tsx +125 -0
- package/src/components/PluginRuntime.tsx +92 -0
- package/src/components/PrimaryActionButton.test.tsx +138 -0
- package/src/components/PrimaryActionButton.tsx +351 -0
- package/src/components/PrimaryButton.tsx +44 -0
- package/src/components/PromptDialogHost.tsx +92 -0
- package/src/components/ResourceCounter.test.tsx +29 -0
- package/src/components/ResourceCounter.tsx +275 -0
- package/src/components/ThemedButton.tsx +78 -0
- package/src/components/Toast.tsx +251 -0
- package/src/components/__fixtures__/ActionButton.fixture.tsx +234 -0
- package/src/components/__fixtures__/ActionPanel.fixture.tsx +298 -0
- package/src/components/__fixtures__/Card.fixture.tsx +185 -0
- package/src/components/__fixtures__/CostDisplay.fixture.tsx +156 -0
- package/src/components/__fixtures__/DiceRoller.fixture.tsx +435 -0
- package/src/components/__fixtures__/Drawer.fixture.tsx +113 -0
- package/src/components/__fixtures__/ErrorBoundary.fixture.tsx +82 -0
- package/src/components/__fixtures__/GameEndDisplay.fixture.tsx +188 -0
- package/src/components/__fixtures__/GameSkeleton.fixture.tsx +46 -0
- package/src/components/__fixtures__/Hand.fixture.tsx +522 -0
- package/src/components/__fixtures__/HexGrid.fixture.tsx +1181 -0
- package/src/components/__fixtures__/NetworkGraph.fixture.tsx +599 -0
- package/src/components/__fixtures__/PhaseIndicator.fixture.tsx +181 -0
- package/src/components/__fixtures__/PlayArea.fixture.tsx +221 -0
- package/src/components/__fixtures__/ResourceCounter.fixture.tsx +227 -0
- package/src/components/__fixtures__/SlotSystem.fixture.tsx +824 -0
- package/src/components/__fixtures__/SquareGrid.fixture.tsx +764 -0
- package/src/components/__fixtures__/Toast.fixture.tsx +97 -0
- package/src/components/__fixtures__/TrackBoard.fixture.tsx +685 -0
- package/src/components/__fixtures__/ZoneMap.fixture.tsx +754 -0
- package/src/components/board/HexGrid.tsx +1294 -0
- package/src/components/board/NetworkGraph.tsx +476 -0
- package/src/components/board/SlotSystem.tsx +339 -0
- package/src/components/board/SquareGrid.tsx +1165 -0
- package/src/components/board/TrackBoard.tsx +496 -0
- package/src/components/board/ZoneMap.tsx +448 -0
- package/src/components/board/hex-board-view.test.tsx +114 -0
- package/src/components/board/hex-board-view.ts +123 -0
- package/src/components/board/index.ts +142 -0
- package/src/components/board/interaction-accessibility.ts +21 -0
- package/src/components/board/target-layer-grids.test.tsx +420 -0
- package/src/components/board/target-layer.ts +30 -0
- package/src/components/card-render-content.type-test.ts +27 -0
- package/src/components/index.ts +208 -0
- package/src/components/interaction-dialog-behavior.test.ts +23 -0
- package/src/components/interaction-dialog-behavior.ts +22 -0
- package/src/components/surfaces/BlockerSurface.test.tsx +158 -0
- package/src/components/surfaces/BlockerSurface.tsx +127 -0
- package/src/components/surfaces/BoardSurface.tsx +340 -0
- package/src/components/surfaces/ChromeSurface.tsx +123 -0
- package/src/components/surfaces/ExhaustivenessAudit.tsx +91 -0
- package/src/components/surfaces/InboxSurface.test.tsx +149 -0
- package/src/components/surfaces/InboxSurface.tsx +245 -0
- package/src/components/surfaces/MarketSurface.tsx +544 -0
- package/src/components/surfaces/PanelSurface.test.tsx +496 -0
- package/src/components/surfaces/PanelSurface.tsx +458 -0
- package/src/components/surfaces/PlayerCardsSurface.tsx +525 -0
- package/src/components/surfaces/internal/CardZoneFollowUpForm.tsx +35 -0
- package/src/components/surfaces/internal/DefaultInteractionButton.tsx +219 -0
- package/src/components/surfaces/internal/useCardZoneInteractions.ts +311 -0
- package/src/components/surfaces/types.ts +100 -0
- package/src/context/ClientParamSchemaContext.tsx +44 -0
- package/src/context/InteractionDraftContext.tsx +204 -0
- package/src/context/PluginSessionContext.tsx +47 -0
- package/src/context/PluginStateContext.tsx +254 -0
- package/src/context/RuntimeContext.tsx +96 -0
- package/src/defaults/components.tsx +442 -0
- package/src/defaults/defaults.test.tsx +230 -0
- package/src/defaults/index.ts +1 -0
- package/src/errors/ValidationError.ts +29 -0
- package/src/helpers/cards.ts +19 -0
- package/src/helpers/track-board.ts +211 -0
- package/src/hooks/useActivePlayers.ts +19 -0
- package/src/hooks/useBoardInteractions.test.tsx +622 -0
- package/src/hooks/useBoardInteractions.ts +434 -0
- package/src/hooks/useBoardTopology.ts +316 -0
- package/src/hooks/useCards.test.tsx +129 -0
- package/src/hooks/useCards.ts +10 -0
- package/src/hooks/useGameSelector.ts +105 -0
- package/src/hooks/useGameView.ts +9 -0
- package/src/hooks/useHandLayout.ts +349 -0
- package/src/hooks/useHexBoard.ts +74 -0
- package/src/hooks/useHexGrid.ts +185 -0
- package/src/hooks/useInteractionByKey.ts +349 -0
- package/src/hooks/useInteractionHandle.ts +437 -0
- package/src/hooks/useIsMobile.ts +35 -0
- package/src/hooks/useIsMyTurn.test.tsx +99 -0
- package/src/hooks/useIsMyTurn.ts +15 -0
- package/src/hooks/useLobby.ts +76 -0
- package/src/hooks/useMe.ts +48 -0
- package/src/hooks/usePanZoom.ts +278 -0
- package/src/hooks/usePlayerInfo.ts +28 -0
- package/src/hooks/usePlayerTurnOrder.ts +23 -0
- package/src/hooks/usePluginRuntime.test.tsx +102 -0
- package/src/hooks/usePluginRuntime.ts +130 -0
- package/src/hooks/useSeatInbox.ts +61 -0
- package/src/hooks/useSimultaneousPhase.ts +10 -0
- package/src/hooks/useSquareBoard.ts +124 -0
- package/src/hooks/useSquareGrid.ts +328 -0
- package/src/index.test.ts +474 -0
- package/src/index.ts +148 -0
- package/src/internal/ui/alert.tsx +51 -0
- package/src/internal/ui/button.tsx +58 -0
- package/src/internal/ui/dialog.tsx +134 -0
- package/src/internal/ui/input.tsx +21 -0
- package/src/internal/ui/label.tsx +21 -0
- package/src/internal/ui/select.tsx +129 -0
- package/src/internal/ui/tooltip.tsx +54 -0
- package/src/internal/ui/utils.ts +5 -0
- package/src/internal.ts +18 -0
- package/src/plugin-styles.css +246 -0
- package/src/primitives/board.test.tsx +139 -0
- package/src/primitives/board.tsx +267 -0
- package/src/primitives/game-ui-provider.tsx +35 -0
- package/src/primitives/index.ts +83 -0
- package/src/primitives/interaction.test.tsx +420 -0
- package/src/primitives/interaction.tsx +405 -0
- package/src/primitives/phase.test.tsx +82 -0
- package/src/primitives/phase.tsx +43 -0
- package/src/primitives/player-roster.test.tsx +168 -0
- package/src/primitives/player-roster.tsx +301 -0
- package/src/primitives/primitive-props.tsx +82 -0
- package/src/primitives/prompt.test.tsx +159 -0
- package/src/primitives/prompt.tsx +203 -0
- package/src/primitives/zone.tsx +113 -0
- package/src/reducer.ts +42 -0
- package/src/runtime/createPluginRuntimeAPI.ts +605 -0
- package/src/theme/ThemeProvider.test.tsx +36 -0
- package/src/theme/ThemeProvider.tsx +252 -0
- package/src/theme/board.ts +61 -0
- package/src/theme/css-vars.ts +105 -0
- package/src/theme/derive.ts +240 -0
- package/src/theme/index.ts +61 -0
- package/src/theme/presets/arcade.ts +261 -0
- package/src/theme/presets/studio.ts +261 -0
- package/src/theme/presets/tabletop.ts +266 -0
- package/src/theme/theme.test.ts +258 -0
- package/src/theme/tokens.ts +392 -0
- package/src/types/player-state.ts +445 -0
- package/src/types/plugin-state.ts +407 -0
- package/src/types/reducer-state.ts +24 -0
- package/src/types/runtime-api.ts +114 -0
- package/src/types/tiled-board.ts +785 -0
- package/src/ui-contract.ts +168 -0
- package/src/utils/interaction-inputs.test.ts +109 -0
- package/src/utils/interaction-inputs.ts +331 -0
- package/src/utils/interaction-labels.ts +23 -0
- package/src/utils/interaction-status.ts +59 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { InteractionUiProvider } from "../context/InteractionDraftContext.js";
|
|
3
|
+
import { RuntimeProvider } from "../context/RuntimeContext.js";
|
|
4
|
+
import { usePluginSession } from "../context/PluginSessionContext.js";
|
|
5
|
+
import { usePluginRuntime } from "../hooks/usePluginRuntime.js";
|
|
6
|
+
import { GameSkeleton } from "./GameSkeleton.js";
|
|
7
|
+
|
|
8
|
+
export interface PluginRuntimeProps {
|
|
9
|
+
/** Child components to render after state sync has started */
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Timeout in milliseconds to wait for the first state-sync snapshot.
|
|
13
|
+
* @default 10000 (10 seconds)
|
|
14
|
+
*/
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/** Custom loading component to show while waiting for state sync */
|
|
17
|
+
loadingComponent?: React.ReactNode;
|
|
18
|
+
/** Custom error component to show when initialization fails */
|
|
19
|
+
errorComponent?: (error: string) => React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* PluginRuntime provides the RuntimeContext for plugin components.
|
|
24
|
+
*
|
|
25
|
+
* This component:
|
|
26
|
+
* - Creates a RuntimeAPI instance using the SDK-provided implementation
|
|
27
|
+
* - Waits for the first reducer-native state-sync snapshot before rendering children
|
|
28
|
+
* - Provides RuntimeAPI and session state to all child components
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* // In your plugin's index.tsx
|
|
33
|
+
* import { PluginRuntime } from "./components/dreamboard";
|
|
34
|
+
* import App from './App';
|
|
35
|
+
*
|
|
36
|
+
* ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
37
|
+
* <PluginRuntime>
|
|
38
|
+
* <App />
|
|
39
|
+
* </PluginRuntime>
|
|
40
|
+
* );
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function PluginRuntime({
|
|
44
|
+
children,
|
|
45
|
+
timeout = 10000,
|
|
46
|
+
loadingComponent,
|
|
47
|
+
errorComponent,
|
|
48
|
+
}: PluginRuntimeProps) {
|
|
49
|
+
const { runtime, isReady, error } = usePluginRuntime({ timeout });
|
|
50
|
+
|
|
51
|
+
if (error) {
|
|
52
|
+
if (errorComponent) {
|
|
53
|
+
return <>{errorComponent(error)}</>;
|
|
54
|
+
}
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex h-full w-full items-center justify-center bg-gray-50">
|
|
57
|
+
<div className="text-center p-6">
|
|
58
|
+
<p className="text-red-600 font-medium mb-2">Failed to load game</p>
|
|
59
|
+
<p className="text-gray-600 text-sm">{error}</p>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!isReady) {
|
|
66
|
+
if (loadingComponent) {
|
|
67
|
+
return <>{loadingComponent}</>;
|
|
68
|
+
}
|
|
69
|
+
return <GameSkeleton message="Waiting for game state..." />;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<RuntimeProvider runtime={runtime}>
|
|
74
|
+
<SessionScopedInteractionUiProvider>
|
|
75
|
+
{children}
|
|
76
|
+
</SessionScopedInteractionUiProvider>
|
|
77
|
+
</RuntimeProvider>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function SessionScopedInteractionUiProvider({
|
|
82
|
+
children,
|
|
83
|
+
}: {
|
|
84
|
+
children: React.ReactNode;
|
|
85
|
+
}) {
|
|
86
|
+
const { controllingPlayerId } = usePluginSession();
|
|
87
|
+
return (
|
|
88
|
+
<InteractionUiProvider key={controllingPlayerId ?? "__no_player__"}>
|
|
89
|
+
{children}
|
|
90
|
+
</InteractionUiProvider>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { renderToString } from "react-dom/server";
|
|
3
|
+
import { ThemeProvider } from "../theme/ThemeProvider.js";
|
|
4
|
+
import type { InteractionHandle } from "../hooks/useInteractionHandle.js";
|
|
5
|
+
import type { InteractionDescriptor } from "../types/plugin-state.js";
|
|
6
|
+
import { PrimaryActionButton } from "./PrimaryActionButton.js";
|
|
7
|
+
|
|
8
|
+
function makeHandle(
|
|
9
|
+
overrides: Partial<InteractionDescriptor> = {},
|
|
10
|
+
handleOverrides: Partial<InteractionHandle> = {},
|
|
11
|
+
): InteractionHandle {
|
|
12
|
+
const descriptor: InteractionDescriptor = {
|
|
13
|
+
phaseName: "play",
|
|
14
|
+
interactionKey: "play.rollDice",
|
|
15
|
+
interactionId: "rollDice",
|
|
16
|
+
kind: "action",
|
|
17
|
+
inputs: [],
|
|
18
|
+
commit: { mode: "manual" },
|
|
19
|
+
available: true,
|
|
20
|
+
...overrides,
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
descriptor,
|
|
24
|
+
commit: descriptor.commit,
|
|
25
|
+
submit: async () => {},
|
|
26
|
+
validate: async () => {},
|
|
27
|
+
validateDraft: () => ({
|
|
28
|
+
ok: true,
|
|
29
|
+
params: {},
|
|
30
|
+
fieldErrors: {},
|
|
31
|
+
formErrors: [],
|
|
32
|
+
missing: [],
|
|
33
|
+
}),
|
|
34
|
+
validateDraftServer: async () => {},
|
|
35
|
+
submitDraft: async () => {},
|
|
36
|
+
available: descriptor.available,
|
|
37
|
+
unavailableReason: descriptor.unavailableReason,
|
|
38
|
+
draft: {},
|
|
39
|
+
values: {},
|
|
40
|
+
setInput: () => {},
|
|
41
|
+
clearInput: () => {},
|
|
42
|
+
isReady: true,
|
|
43
|
+
isArmed: false,
|
|
44
|
+
arm: () => {},
|
|
45
|
+
disarm: () => {},
|
|
46
|
+
...handleOverrides,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test("PrimaryActionButton renders fallback label and interaction id", () => {
|
|
51
|
+
const html = renderToString(
|
|
52
|
+
<ThemeProvider>
|
|
53
|
+
<PrimaryActionButton handle={makeHandle()} />
|
|
54
|
+
</ThemeProvider>,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(html).toContain("data-dreamboard-primary-action");
|
|
58
|
+
expect(html).toContain('data-available="true"');
|
|
59
|
+
expect(html).toContain('data-interaction-id="rollDice"');
|
|
60
|
+
expect(html).toContain("Roll Dice");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("PrimaryActionButton disables and surfaces unavailableReason", () => {
|
|
64
|
+
const handle = makeHandle({
|
|
65
|
+
available: false,
|
|
66
|
+
unavailableReason: "Wait for your turn",
|
|
67
|
+
});
|
|
68
|
+
const html = renderToString(
|
|
69
|
+
<ThemeProvider>
|
|
70
|
+
<PrimaryActionButton handle={handle} />
|
|
71
|
+
</ThemeProvider>,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(html).toContain('data-available="false"');
|
|
75
|
+
// Native `disabled` and `aria-disabled` both flip on so screen
|
|
76
|
+
// readers and pointer devices both see the disabled state.
|
|
77
|
+
expect(html).toContain("disabled=");
|
|
78
|
+
expect(html).toContain('aria-disabled="true"');
|
|
79
|
+
// The unavailable reason becomes the `title` tooltip so the
|
|
80
|
+
// player can see why the action is blocked.
|
|
81
|
+
expect(html).toContain("Wait for your turn");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("PrimaryActionButton supports a label override", () => {
|
|
85
|
+
const html = renderToString(
|
|
86
|
+
<ThemeProvider>
|
|
87
|
+
<PrimaryActionButton handle={makeHandle()} label="Confirm" />
|
|
88
|
+
</ThemeProvider>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Override wins over the generated fallback label.
|
|
92
|
+
expect(html).toContain("Confirm");
|
|
93
|
+
expect(html).not.toContain("Roll Dice");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("PrimaryActionButton suppresses the halo when attention='off'", () => {
|
|
97
|
+
// The halo is implemented via `motion.span` siblings of the
|
|
98
|
+
// `<button>`. With `attention="off"` they should be absent so the
|
|
99
|
+
// button stands alone — useful for screens where the author does
|
|
100
|
+
// not want a pulse competing with a richer custom indicator.
|
|
101
|
+
const htmlWithHalo = renderToString(
|
|
102
|
+
<ThemeProvider>
|
|
103
|
+
<PrimaryActionButton handle={makeHandle()} attention="always" />
|
|
104
|
+
</ThemeProvider>,
|
|
105
|
+
);
|
|
106
|
+
const htmlWithoutHalo = renderToString(
|
|
107
|
+
<ThemeProvider>
|
|
108
|
+
<PrimaryActionButton handle={makeHandle()} attention="off" />
|
|
109
|
+
</ThemeProvider>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// The halo wrappers are detected via the `boxShadow` keyframe
|
|
113
|
+
// that the announce pulse renders inline. With the halo off the
|
|
114
|
+
// wrapper still mounts but the inner motion spans are gone.
|
|
115
|
+
expect(htmlWithHalo).toContain("box-shadow");
|
|
116
|
+
// Sanity: the button itself still mounts in both cases.
|
|
117
|
+
expect(htmlWithoutHalo).toContain('data-interaction-id="rollDice"');
|
|
118
|
+
// The announce-pulse motion span uses an inset:0 absolute layer.
|
|
119
|
+
// It is present with the halo on, absent with attention="off".
|
|
120
|
+
expect(htmlWithHalo.split("position:absolute").length).toBeGreaterThan(
|
|
121
|
+
htmlWithoutHalo.split("position:absolute").length,
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("PrimaryActionButton skips the halo entirely when reduced motion is requested", () => {
|
|
126
|
+
// Reduced-motion pulses would defeat the accessibility contract.
|
|
127
|
+
// The button must still render — only the decorative halo is
|
|
128
|
+
// suppressed.
|
|
129
|
+
const html = renderToString(
|
|
130
|
+
<ThemeProvider reducedMotion="force">
|
|
131
|
+
<PrimaryActionButton handle={makeHandle()} attention="always" />
|
|
132
|
+
</ThemeProvider>,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(html).toContain('data-interaction-id="rollDice"');
|
|
136
|
+
// No motion spans — only the static button frame.
|
|
137
|
+
expect(html.split("position:absolute").length).toBe(1);
|
|
138
|
+
});
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-aware primary CTA button — the dominant call-to-action on
|
|
3
|
+
* the screen at any given moment ("Roll dice", "End turn", "Confirm
|
|
4
|
+
* trade", "Place settlement").
|
|
5
|
+
*
|
|
6
|
+
* The shell's `chrome.primaryAction` slot accepts either a
|
|
7
|
+
* pre-rendered React node *or* an {@link InteractionHandle}. When a
|
|
8
|
+
* handle is supplied, the shell wraps it in this component so the
|
|
9
|
+
* call site collapses from ~10 lines to one. Authors who need a
|
|
10
|
+
* fully bespoke CTA can still pass any node.
|
|
11
|
+
*
|
|
12
|
+
* Visual contract (Laws of UX cross-references):
|
|
13
|
+
*
|
|
14
|
+
* - **Fitts** — defaults to `lg` size (min 56px tall, generous
|
|
15
|
+
* horizontal padding) so the dock target is easy to land on.
|
|
16
|
+
* Authors can opt down to `md`.
|
|
17
|
+
* - **Von Restorff (isolation)** — uses `intent.primary.solid` with
|
|
18
|
+
* `elevation.lifted` and an animated halo when `attention="auto"`
|
|
19
|
+
* and the descriptor is available, so the button outranks every
|
|
20
|
+
* other element in its peripheral neighbourhood.
|
|
21
|
+
* - **Peak-end** — when the action becomes available (handle flips
|
|
22
|
+
* from disabled → enabled), the halo pulses for one breath cycle
|
|
23
|
+
* so the eye finds the change without re-scanning the screen.
|
|
24
|
+
* - **Doherty / responsiveness** — clicks set an internal `pending`
|
|
25
|
+
* flag the moment submit fires so the button visibly absorbs the
|
|
26
|
+
* tap, even on slow networks. Throwing submitters are swallowed
|
|
27
|
+
* here for the same reason `<DefaultInteractionButton>` does:
|
|
28
|
+
* descriptor availability is authoritative.
|
|
29
|
+
* - **Accessibility** — minimum 56×56 hit area satisfies WCAG 2.5.5.
|
|
30
|
+
* `prefers-reduced-motion` zeroes out the halo and press
|
|
31
|
+
* transitions through the theme's `motion.reducedMotion` token.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
useEffect,
|
|
36
|
+
useRef,
|
|
37
|
+
useState,
|
|
38
|
+
type CSSProperties,
|
|
39
|
+
type ReactNode,
|
|
40
|
+
} from "react";
|
|
41
|
+
import { motion } from "framer-motion";
|
|
42
|
+
import { useTheme } from "../theme/ThemeProvider.js";
|
|
43
|
+
import {
|
|
44
|
+
intentForVariant,
|
|
45
|
+
type ButtonSize,
|
|
46
|
+
type ButtonVariant,
|
|
47
|
+
} from "../theme/derive.js";
|
|
48
|
+
import type {
|
|
49
|
+
InteractionHandle,
|
|
50
|
+
InteractionParamsShape,
|
|
51
|
+
} from "../hooks/useInteractionHandle.js";
|
|
52
|
+
import { interactionLabel } from "../utils/interaction-labels.js";
|
|
53
|
+
import { ThemedButton } from "./ThemedButton.js";
|
|
54
|
+
import type { SubmittedActionConfig } from "./surfaces/internal/DefaultInteractionButton.js";
|
|
55
|
+
|
|
56
|
+
/** Attention-pulse policy for the trailing halo. */
|
|
57
|
+
export type PrimaryActionAttention = "auto" | "always" | "off";
|
|
58
|
+
|
|
59
|
+
export interface PrimaryActionButtonProps<
|
|
60
|
+
Params extends InteractionParamsShape = InteractionParamsShape,
|
|
61
|
+
> {
|
|
62
|
+
/**
|
|
63
|
+
* Bound interaction handle (typically from `useInteractionByKey`).
|
|
64
|
+
* The button mirrors `descriptor.label`, disables itself when the
|
|
65
|
+
* descriptor is unavailable, surfaces `unavailableReason` as a
|
|
66
|
+
* tooltip, and submits explicit `params` or the current draft on click.
|
|
67
|
+
*/
|
|
68
|
+
handle: InteractionHandle<Params>;
|
|
69
|
+
/**
|
|
70
|
+
* Override the visual variant. Defaults to `primary` (which maps
|
|
71
|
+
* to `intent.primary` regardless of the descriptor's `emphasis`
|
|
72
|
+
* hint — the shell's primary slot is, by definition, primary).
|
|
73
|
+
*/
|
|
74
|
+
variant?: ButtonVariant;
|
|
75
|
+
/**
|
|
76
|
+
* Sizing. Defaults to `lg` so the dock target is comfortable on
|
|
77
|
+
* touch and visually outranks panel buttons sized `md`.
|
|
78
|
+
*/
|
|
79
|
+
size?: ButtonSize;
|
|
80
|
+
/**
|
|
81
|
+
* Override the label inferred from `descriptor.label`. Use only
|
|
82
|
+
* when the descriptor's label needs phase-specific copy that the
|
|
83
|
+
* authoring layer can't express.
|
|
84
|
+
*/
|
|
85
|
+
label?: ReactNode;
|
|
86
|
+
/** Client-side draft readiness. Reducer availability remains authoritative. */
|
|
87
|
+
ready?: boolean;
|
|
88
|
+
/** Copy and visual overrides once this interaction has been submitted. */
|
|
89
|
+
whenSubmitted?: SubmittedActionConfig;
|
|
90
|
+
/**
|
|
91
|
+
* Optional leading icon override. When omitted, falls back to
|
|
92
|
+
* `descriptor.icon` (an emoji glyph from the authoring spec).
|
|
93
|
+
*/
|
|
94
|
+
icon?: ReactNode;
|
|
95
|
+
/**
|
|
96
|
+
* Submit params forwarded to `handle.submit`. When omitted, the button
|
|
97
|
+
* submits the handle's current draft via `handle.submitDraft()`.
|
|
98
|
+
*/
|
|
99
|
+
params?: Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Attention-halo policy. `auto` (default) pulses the halo for one
|
|
102
|
+
* breath when the button transitions from disabled → enabled (so
|
|
103
|
+
* the user sees the moment the action becomes available), then
|
|
104
|
+
* settles into a slow ambient breath while the action remains
|
|
105
|
+
* available. `always` keeps the breath running unconditionally.
|
|
106
|
+
* `off` suppresses the halo entirely.
|
|
107
|
+
*
|
|
108
|
+
* Ignored when `theme.motion.reducedMotion === "true"`.
|
|
109
|
+
*/
|
|
110
|
+
attention?: PrimaryActionAttention;
|
|
111
|
+
/** Additional inline style merged after the resolved button style. */
|
|
112
|
+
style?: CSSProperties;
|
|
113
|
+
/** Optional className for downstream styling hooks. */
|
|
114
|
+
className?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @see PrimaryActionButtonProps
|
|
119
|
+
*/
|
|
120
|
+
export function PrimaryActionButton<
|
|
121
|
+
Params extends InteractionParamsShape = InteractionParamsShape,
|
|
122
|
+
>({
|
|
123
|
+
handle,
|
|
124
|
+
variant = "primary",
|
|
125
|
+
size = "lg",
|
|
126
|
+
label,
|
|
127
|
+
ready = true,
|
|
128
|
+
whenSubmitted,
|
|
129
|
+
icon,
|
|
130
|
+
params,
|
|
131
|
+
attention = "auto",
|
|
132
|
+
style,
|
|
133
|
+
className,
|
|
134
|
+
}: PrimaryActionButtonProps<Params>) {
|
|
135
|
+
const theme = useTheme();
|
|
136
|
+
const reducedMotion = theme.motion.reducedMotion === "true";
|
|
137
|
+
const [pending, setPending] = useState(false);
|
|
138
|
+
|
|
139
|
+
const descriptor = handle.descriptor;
|
|
140
|
+
const submitted = handle.status === "submitted";
|
|
141
|
+
const submitting = handle.status === "submitting" || pending;
|
|
142
|
+
const available = descriptor.available && ready && !submitted && !submitting;
|
|
143
|
+
const resolvedVariant = submitted
|
|
144
|
+
? (whenSubmitted?.variant ?? "submitted")
|
|
145
|
+
: variant;
|
|
146
|
+
const disabled = !available;
|
|
147
|
+
const intent = intentForVariant(theme, resolvedVariant);
|
|
148
|
+
|
|
149
|
+
// Pulse the halo for one breath when availability flips on. After
|
|
150
|
+
// the breath we settle into the ambient cadence (or stop, when
|
|
151
|
+
// `attention` is `off`). Tracking the previous availability lets
|
|
152
|
+
// us catch the transition without re-mounting the component.
|
|
153
|
+
const previouslyAvailableRef = useRef(available);
|
|
154
|
+
const [pulseKey, setPulseKey] = useState(0);
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!previouslyAvailableRef.current && available) {
|
|
157
|
+
setPulseKey((n) => n + 1);
|
|
158
|
+
}
|
|
159
|
+
previouslyAvailableRef.current = available;
|
|
160
|
+
}, [available]);
|
|
161
|
+
|
|
162
|
+
const haloEnabled =
|
|
163
|
+
!reducedMotion && available && attention !== "off" && !submitted;
|
|
164
|
+
|
|
165
|
+
const fallbackLabel = interactionLabel(descriptor);
|
|
166
|
+
const tooltip = available
|
|
167
|
+
? undefined
|
|
168
|
+
: formatUnavailableReason(descriptor.unavailableReason);
|
|
169
|
+
|
|
170
|
+
const resolvedLabel: ReactNode = submitted
|
|
171
|
+
? (whenSubmitted?.label ?? label ?? fallbackLabel)
|
|
172
|
+
: (label ?? fallbackLabel);
|
|
173
|
+
const resolvedIcon: ReactNode =
|
|
174
|
+
submitted && whenSubmitted?.icon ? (
|
|
175
|
+
<span aria-hidden style={{ fontSize: "1.15em" }}>
|
|
176
|
+
{whenSubmitted.icon}
|
|
177
|
+
</span>
|
|
178
|
+
) : (
|
|
179
|
+
(icon ?? null)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<span
|
|
184
|
+
data-dreamboard-primary-action
|
|
185
|
+
data-available={available ? "true" : "false"}
|
|
186
|
+
data-pending={submitting ? "true" : undefined}
|
|
187
|
+
data-action-state={
|
|
188
|
+
submitted
|
|
189
|
+
? "submitted"
|
|
190
|
+
: submitting
|
|
191
|
+
? "submitting"
|
|
192
|
+
: available
|
|
193
|
+
? "available"
|
|
194
|
+
: "unavailable"
|
|
195
|
+
}
|
|
196
|
+
style={{
|
|
197
|
+
position: "relative",
|
|
198
|
+
display: "inline-flex",
|
|
199
|
+
alignItems: "center",
|
|
200
|
+
justifyContent: "center",
|
|
201
|
+
// The halo overflows the button bounds; the wrapper reserves
|
|
202
|
+
// a transparent buffer so it doesn't get clipped by the
|
|
203
|
+
// dock's safe-area frame.
|
|
204
|
+
padding: theme.space[1],
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
{haloEnabled ? (
|
|
208
|
+
<>
|
|
209
|
+
{/*
|
|
210
|
+
Ambient breath — slow, low-amplitude, runs as long as the
|
|
211
|
+
action is available. The outer `haloEnabled` already
|
|
212
|
+
short-circuits when `attention === "off"`, so this layer
|
|
213
|
+
is gated purely on the availability + reduced-motion
|
|
214
|
+
checks.
|
|
215
|
+
*/}
|
|
216
|
+
<motion.span
|
|
217
|
+
aria-hidden
|
|
218
|
+
style={{
|
|
219
|
+
position: "absolute",
|
|
220
|
+
inset: 0,
|
|
221
|
+
borderRadius: theme.radius.md,
|
|
222
|
+
background: intent.soft,
|
|
223
|
+
opacity: 0.55,
|
|
224
|
+
pointerEvents: "none",
|
|
225
|
+
}}
|
|
226
|
+
animate={{
|
|
227
|
+
scale: [1, 1.06, 1],
|
|
228
|
+
opacity: [0.45, 0.18, 0.45],
|
|
229
|
+
}}
|
|
230
|
+
transition={{
|
|
231
|
+
repeat: Infinity,
|
|
232
|
+
duration: 2.4,
|
|
233
|
+
ease: "easeInOut",
|
|
234
|
+
}}
|
|
235
|
+
/>
|
|
236
|
+
{/*
|
|
237
|
+
One-shot announce pulse keyed on `pulseKey` — re-mounts
|
|
238
|
+
(and thus re-runs) every time availability flips on so the
|
|
239
|
+
eye registers the change. We use a separate layer (rather
|
|
240
|
+
than retriggering the ambient breath) so the announce is
|
|
241
|
+
visibly louder than the steady-state cadence.
|
|
242
|
+
*/}
|
|
243
|
+
<motion.span
|
|
244
|
+
key={pulseKey}
|
|
245
|
+
aria-hidden
|
|
246
|
+
style={{
|
|
247
|
+
position: "absolute",
|
|
248
|
+
inset: 0,
|
|
249
|
+
borderRadius: theme.radius.md,
|
|
250
|
+
boxShadow: `0 0 0 0 ${intent.solid}`,
|
|
251
|
+
pointerEvents: "none",
|
|
252
|
+
}}
|
|
253
|
+
initial={{ opacity: 0.7 }}
|
|
254
|
+
animate={{
|
|
255
|
+
boxShadow: [
|
|
256
|
+
`0 0 0 0 ${withAlpha(intent.solid, 0.55)}`,
|
|
257
|
+
`0 0 0 14px ${withAlpha(intent.solid, 0)}`,
|
|
258
|
+
],
|
|
259
|
+
opacity: [0.7, 0],
|
|
260
|
+
}}
|
|
261
|
+
transition={{ duration: 0.9, ease: "easeOut" }}
|
|
262
|
+
/>
|
|
263
|
+
</>
|
|
264
|
+
) : null}
|
|
265
|
+
<ThemedButton
|
|
266
|
+
type="button"
|
|
267
|
+
variant={resolvedVariant}
|
|
268
|
+
size={size}
|
|
269
|
+
pressed={submitting}
|
|
270
|
+
className={className}
|
|
271
|
+
aria-label={
|
|
272
|
+
typeof resolvedLabel === "string" ? resolvedLabel : fallbackLabel
|
|
273
|
+
}
|
|
274
|
+
aria-disabled={disabled || undefined}
|
|
275
|
+
data-interaction-id={descriptor.interactionId}
|
|
276
|
+
data-emphasis="primary"
|
|
277
|
+
title={tooltip}
|
|
278
|
+
disabled={disabled}
|
|
279
|
+
style={{
|
|
280
|
+
// Sit above the halo so clicks land on the button.
|
|
281
|
+
position: "relative",
|
|
282
|
+
zIndex: 1,
|
|
283
|
+
boxShadow: disabled || submitted ? undefined : theme.elevation.lifted,
|
|
284
|
+
...style,
|
|
285
|
+
}}
|
|
286
|
+
onClick={async (event) => {
|
|
287
|
+
event.preventDefault();
|
|
288
|
+
if (disabled) return;
|
|
289
|
+
setPending(true);
|
|
290
|
+
try {
|
|
291
|
+
if (params !== undefined) {
|
|
292
|
+
await handle.submit(params as Params);
|
|
293
|
+
} else {
|
|
294
|
+
await handle.submitDraft();
|
|
295
|
+
}
|
|
296
|
+
} catch {
|
|
297
|
+
// Descriptor availability is authoritative; submit errors
|
|
298
|
+
// surface via the runtime's error channel.
|
|
299
|
+
} finally {
|
|
300
|
+
setPending(false);
|
|
301
|
+
}
|
|
302
|
+
}}
|
|
303
|
+
>
|
|
304
|
+
{resolvedIcon}
|
|
305
|
+
<span>{resolvedLabel}</span>
|
|
306
|
+
</ThemedButton>
|
|
307
|
+
</span>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function formatUnavailableReason(
|
|
312
|
+
reason: string | undefined,
|
|
313
|
+
): string | undefined {
|
|
314
|
+
if (reason === "INSUFFICIENT_RESOURCES") {
|
|
315
|
+
return "Insufficient resources";
|
|
316
|
+
}
|
|
317
|
+
return reason;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Add an alpha channel to a CSS colour string. Supports `#rgb`,
|
|
322
|
+
* `#rrggbb`, and any colour the browser can paint via a fallback to
|
|
323
|
+
* `color-mix` (modern Safari/Chrome/Firefox all support this; older
|
|
324
|
+
* runtimes get the original colour without alpha which is still
|
|
325
|
+
* visible — the halo is decorative).
|
|
326
|
+
*/
|
|
327
|
+
function withAlpha(color: string, alpha: number): string {
|
|
328
|
+
const trimmed = color.trim();
|
|
329
|
+
if (trimmed.startsWith("#")) {
|
|
330
|
+
const hex = trimmed.slice(1);
|
|
331
|
+
if (hex.length === 3) {
|
|
332
|
+
// `String.prototype.slice` always returns a string (possibly
|
|
333
|
+
// empty) — never `undefined` — so duplicating each digit is
|
|
334
|
+
// safe to feed to `parseInt` without further narrowing.
|
|
335
|
+
const r = parseInt(hex.slice(0, 1).repeat(2), 16);
|
|
336
|
+
const g = parseInt(hex.slice(1, 2).repeat(2), 16);
|
|
337
|
+
const b = parseInt(hex.slice(2, 3).repeat(2), 16);
|
|
338
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
339
|
+
}
|
|
340
|
+
if (hex.length === 6) {
|
|
341
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
342
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
343
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
344
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// Best-effort fallback for non-hex colours (rgb/hsl/named): use
|
|
348
|
+
// `color-mix` with transparent. Safe to land in inline style — the
|
|
349
|
+
// halo is purely decorative and will gracefully degrade.
|
|
350
|
+
return `color-mix(in srgb, ${trimmed} ${Math.round(alpha * 100)}%, transparent)`;
|
|
351
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimally-styled primary action button.
|
|
3
|
+
*
|
|
4
|
+
* Visual styling now flows through {@link buttonStyle}: the background,
|
|
5
|
+
* border, foreground, radius, typography and elevation all derive from
|
|
6
|
+
* the active {@link useTheme}'s `intent.primary` slot. Override the
|
|
7
|
+
* variant when a non-primary call site needs a different emphasis (the
|
|
8
|
+
* underlying `<DefaultInteractionButton>` is the canonical button for
|
|
9
|
+
* interaction-bound submission).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ButtonHTMLAttributes } from "react";
|
|
13
|
+
import type { ButtonSize, ButtonVariant } from "../theme/derive.js";
|
|
14
|
+
import { ThemedButton } from "./ThemedButton.js";
|
|
15
|
+
|
|
16
|
+
export interface PrimaryButtonProps
|
|
17
|
+
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
18
|
+
/** Intent slot — defaults to `primary`. */
|
|
19
|
+
variant?: ButtonVariant;
|
|
20
|
+
/** Sizing token — defaults to `md`. */
|
|
21
|
+
size?: ButtonSize;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function PrimaryButton({
|
|
25
|
+
children,
|
|
26
|
+
disabled,
|
|
27
|
+
style,
|
|
28
|
+
variant = "primary",
|
|
29
|
+
size = "md",
|
|
30
|
+
...rest
|
|
31
|
+
}: PrimaryButtonProps) {
|
|
32
|
+
return (
|
|
33
|
+
<ThemedButton
|
|
34
|
+
type="button"
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
variant={variant}
|
|
37
|
+
size={size}
|
|
38
|
+
style={style}
|
|
39
|
+
{...rest}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</ThemedButton>
|
|
43
|
+
);
|
|
44
|
+
}
|