@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,124 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useState } from "react";
|
|
3
|
+
import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger, } from "./Drawer.js";
|
|
4
|
+
import { useChromeSuppression } from "./ChromeSuppressionContext.js";
|
|
5
|
+
import { ThemedButton } from "./ThemedButton.js";
|
|
6
|
+
import { useThemeCssVars } from "../theme/ThemeProvider.js";
|
|
7
|
+
const DEFAULT_MAX_WIDTH = "min(28rem, calc(100vw - 24px))";
|
|
8
|
+
const DEFAULT_MAX_HEIGHT = "min(65vh, calc(100vh - 96px))";
|
|
9
|
+
function placementStyle(placement) {
|
|
10
|
+
const base = {
|
|
11
|
+
position: "fixed",
|
|
12
|
+
bottom: "calc(12px + env(safe-area-inset-bottom, 0px))",
|
|
13
|
+
zIndex: 900,
|
|
14
|
+
};
|
|
15
|
+
switch (placement) {
|
|
16
|
+
case "bottom-center":
|
|
17
|
+
return {
|
|
18
|
+
...base,
|
|
19
|
+
left: "50%",
|
|
20
|
+
transform: "translateX(-50%)",
|
|
21
|
+
};
|
|
22
|
+
case "bottom-right":
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
right: "calc(12px + env(safe-area-inset-right, 0px))",
|
|
26
|
+
};
|
|
27
|
+
case "bottom-left":
|
|
28
|
+
default:
|
|
29
|
+
return {
|
|
30
|
+
...base,
|
|
31
|
+
left: "calc(12px + env(safe-area-inset-left, 0px))",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function triggerAlignment(placement) {
|
|
36
|
+
switch (placement) {
|
|
37
|
+
case "bottom-center":
|
|
38
|
+
return "center";
|
|
39
|
+
case "bottom-right":
|
|
40
|
+
return "flex-end";
|
|
41
|
+
case "bottom-left":
|
|
42
|
+
default:
|
|
43
|
+
return "flex-start";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function renderToggleLabel(label, count, open, override) {
|
|
47
|
+
if (typeof override === "function")
|
|
48
|
+
return override({ label, count, open });
|
|
49
|
+
if (override !== undefined)
|
|
50
|
+
return override;
|
|
51
|
+
return `${label} (${count})`;
|
|
52
|
+
}
|
|
53
|
+
export function HandDock({ label, count, presentation, children, }) {
|
|
54
|
+
const mode = presentation?.mode ?? "inline";
|
|
55
|
+
const [open, setOpen] = useState(presentation?.defaultOpen ?? false);
|
|
56
|
+
const contentId = useId();
|
|
57
|
+
const themeCssVars = useThemeCssVars();
|
|
58
|
+
useChromeSuppression(contentId, mode === "drawer" && open);
|
|
59
|
+
if (mode === "hidden")
|
|
60
|
+
return null;
|
|
61
|
+
if (mode === "inline")
|
|
62
|
+
return _jsx(_Fragment, { children: children });
|
|
63
|
+
const placement = presentation?.placement ?? "bottom-left";
|
|
64
|
+
const toggleLabel = renderToggleLabel(label, count, open, presentation?.toggleLabel);
|
|
65
|
+
const fallbackContentStyle = {
|
|
66
|
+
maxHeight: presentation?.maxHeight ?? DEFAULT_MAX_HEIGHT,
|
|
67
|
+
overflow: "auto",
|
|
68
|
+
padding: 12,
|
|
69
|
+
pointerEvents: "auto",
|
|
70
|
+
};
|
|
71
|
+
const triggerStyle = {
|
|
72
|
+
alignSelf: triggerAlignment(placement),
|
|
73
|
+
boxShadow: "0 10px 28px rgba(15, 23, 42, 0.22)",
|
|
74
|
+
pointerEvents: "auto",
|
|
75
|
+
visibility: open ? "hidden" : "visible",
|
|
76
|
+
};
|
|
77
|
+
const canUsePortal = typeof document !== "undefined";
|
|
78
|
+
const serverOpenContent = !canUsePortal && open ? (_jsx("div", { id: contentId, role: "region", "aria-label": label, style: fallbackContentStyle, children: children })) : null;
|
|
79
|
+
return (_jsxs(Drawer, { open: open, onOpenChange: setOpen, direction: "bottom", children: [_jsxs("div", { "data-hand-dock": label, "data-state": open ? "open" : "closed", style: {
|
|
80
|
+
...placementStyle(placement),
|
|
81
|
+
display: "flex",
|
|
82
|
+
maxWidth: presentation?.maxWidth ?? DEFAULT_MAX_WIDTH,
|
|
83
|
+
width: presentation?.maxWidth ?? DEFAULT_MAX_WIDTH,
|
|
84
|
+
flexDirection: "column",
|
|
85
|
+
alignItems: "stretch",
|
|
86
|
+
gap: 8,
|
|
87
|
+
pointerEvents: "none",
|
|
88
|
+
...presentation?.style,
|
|
89
|
+
}, children: [serverOpenContent, _jsx(DrawerTrigger, { asChild: true, children: _jsx(ThemedButton, { type: "button", variant: "secondary", size: "sm", "data-state": open ? "open" : "closed", "aria-expanded": open, "aria-controls": open ? contentId : undefined, className: "rounded-full", style: triggerStyle, children: toggleLabel }) })] }), canUsePortal ? (_jsxs(DrawerContent, { id: contentId, "aria-describedby": undefined, className: "border-border bg-background/95 shadow-2xl backdrop-blur supports-[backdrop-filter]:bg-background/90", style: {
|
|
90
|
+
...themeCssVars,
|
|
91
|
+
position: "fixed",
|
|
92
|
+
right: 0,
|
|
93
|
+
bottom: 0,
|
|
94
|
+
left: 0,
|
|
95
|
+
zIndex: 50,
|
|
96
|
+
display: "flex",
|
|
97
|
+
maxHeight: "80vh",
|
|
98
|
+
flexDirection: "column",
|
|
99
|
+
overflow: "hidden",
|
|
100
|
+
borderTop: "1px solid var(--border, #cbd5e1)",
|
|
101
|
+
borderTopLeftRadius: 16,
|
|
102
|
+
borderTopRightRadius: 16,
|
|
103
|
+
background: "var(--background, rgba(255, 255, 255, 0.96))",
|
|
104
|
+
color: "var(--foreground, #0f172a)",
|
|
105
|
+
fontFamily: "var(--font-sans)",
|
|
106
|
+
boxShadow: "0 -18px 48px rgba(15, 23, 42, 0.22)",
|
|
107
|
+
backdropFilter: "blur(8px)",
|
|
108
|
+
}, children: [_jsxs(DrawerHeader, { className: "border-border border-b px-4 pb-3 pt-3 text-left", style: {
|
|
109
|
+
display: "flex",
|
|
110
|
+
flexDirection: "column",
|
|
111
|
+
gap: 4,
|
|
112
|
+
borderBottom: "1px solid var(--border, #e2e8f0)",
|
|
113
|
+
padding: "12px 16px",
|
|
114
|
+
textAlign: "left",
|
|
115
|
+
}, children: [_jsx(DrawerTitle, { className: "text-base", style: { fontSize: 16, fontWeight: 600, lineHeight: 1.25 }, children: label }), _jsxs(DrawerDescription, { children: [count, " card", count === 1 ? "" : "s", " available."] })] }), _jsx("div", { role: "region", "aria-label": label, style: {
|
|
116
|
+
maxHeight: presentation?.maxHeight ?? DEFAULT_MAX_HEIGHT,
|
|
117
|
+
maxWidth: presentation?.maxWidth ?? DEFAULT_MAX_WIDTH,
|
|
118
|
+
width: "100%",
|
|
119
|
+
margin: "0 auto",
|
|
120
|
+
overflow: "auto",
|
|
121
|
+
padding: "12px 16px calc(16px + env(safe-area-inset-bottom, 0px))",
|
|
122
|
+
boxSizing: "border-box",
|
|
123
|
+
}, className: "mx-auto w-full overflow-y-auto px-4 pb-[calc(1rem+env(safe-area-inset-bottom,0px))] pt-3", children: children })] })) : null] }));
|
|
124
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import type { InteractionHandle, InteractionParamsShape } from "../hooks/useInteractionHandle.js";
|
|
3
|
+
import type { InteractionDescriptor, InteractionInputDescriptor } from "../types/plugin-state.js";
|
|
4
|
+
export interface InteractionFieldRenderProps<Params extends InteractionParamsShape = InteractionParamsShape, Key extends keyof Params & string = keyof Params & string> {
|
|
5
|
+
descriptor: InteractionDescriptor;
|
|
6
|
+
input: InteractionInputDescriptor & {
|
|
7
|
+
key: Key;
|
|
8
|
+
};
|
|
9
|
+
handle: InteractionHandle<Params>;
|
|
10
|
+
value: Params[Key] | undefined;
|
|
11
|
+
setValue: (value: Params[Key]) => void;
|
|
12
|
+
clearValue: () => void;
|
|
13
|
+
errors: readonly string[];
|
|
14
|
+
missing: boolean;
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
}
|
|
17
|
+
export type InteractionFieldRenderMap<Params extends InteractionParamsShape = InteractionParamsShape> = Partial<{
|
|
18
|
+
[K in keyof Params & string]: (props: InteractionFieldRenderProps<Params, K>) => ReactNode;
|
|
19
|
+
}>;
|
|
20
|
+
export interface InteractionFieldProps<Params extends InteractionParamsShape = InteractionParamsShape, Key extends keyof Params & string = keyof Params & string> {
|
|
21
|
+
descriptor: InteractionDescriptor;
|
|
22
|
+
inputKey: Key;
|
|
23
|
+
handle: InteractionHandle<Params>;
|
|
24
|
+
errors?: readonly string[];
|
|
25
|
+
missing?: boolean;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
render?: InteractionFieldRenderMap<Params>[Key];
|
|
28
|
+
}
|
|
29
|
+
export interface InteractionFormProps<Params extends InteractionParamsShape = InteractionParamsShape, DefaultedKeys extends keyof Params & string = never> {
|
|
30
|
+
descriptor: InteractionDescriptor;
|
|
31
|
+
handle: InteractionHandle<Params, DefaultedKeys>;
|
|
32
|
+
fields?: ReadonlyArray<keyof Params & string>;
|
|
33
|
+
hiddenFields?: ReadonlyArray<keyof Params & string>;
|
|
34
|
+
renderFields?: InteractionFieldRenderMap<Params>;
|
|
35
|
+
title?: ReactNode;
|
|
36
|
+
description?: ReactNode;
|
|
37
|
+
submitLabel?: ReactNode;
|
|
38
|
+
cancelLabel?: ReactNode;
|
|
39
|
+
onCancel?: () => void;
|
|
40
|
+
onSubmitSuccess?: () => void;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
accordion?: boolean;
|
|
43
|
+
defaultOpen?: boolean;
|
|
44
|
+
style?: CSSProperties;
|
|
45
|
+
}
|
|
46
|
+
export declare function InteractionForm<Params extends InteractionParamsShape = InteractionParamsShape, DefaultedKeys extends keyof Params & string = never>({ descriptor, handle, fields, hiddenFields, renderFields, title, description, submitLabel, cancelLabel, onCancel, onSubmitSuccess, disabled, accordion, defaultOpen, style, }: InteractionFormProps<Params, DefaultedKeys>): import("react/jsx-runtime").JSX.Element;
|
|
47
|
+
export declare function InteractionField<Params extends InteractionParamsShape = InteractionParamsShape, Key extends keyof Params & string = keyof Params & string>({ descriptor, inputKey, handle, errors, missing, disabled, render, }: InteractionFieldProps<Params, Key>): import("react/jsx-runtime").JSX.Element | null;
|
|
48
|
+
export declare function hasDefaultInteractionFormFields(descriptor: Pick<InteractionDescriptor, "inputs">): boolean;
|
|
49
|
+
export declare function defaultFormInputs(descriptor: Pick<InteractionDescriptor, "inputs">): InteractionInputDescriptor[];
|
|
50
|
+
//# sourceMappingURL=InteractionForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractionForm.d.ts","sourceRoot":"","sources":["../../src/components/InteractionForm.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAElB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAYf,OAAO,KAAK,EAEV,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAEV,qBAAqB,EACrB,0BAA0B,EAE3B,MAAM,0BAA0B,CAAC;AAKlC,MAAM,WAAW,2BAA2B,CAC1C,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,EAC9D,GAAG,SAAS,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM;IAEzD,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,EAAE,0BAA0B,GAAG;QAAE,GAAG,EAAE,GAAG,CAAA;KAAE,CAAC;IACjD,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC/B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,yBAAyB,CACnC,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,IAC5D,OAAO,CAAC;KACT,CAAC,IAAI,MAAM,MAAM,GAAG,MAAM,GAAG,CAC5B,KAAK,EAAE,2BAA2B,CAAC,MAAM,EAAE,CAAC,CAAC,KAC1C,SAAS;CACf,CAAC,CAAC;AAEH,MAAM,WAAW,qBAAqB,CACpC,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,EAC9D,GAAG,SAAS,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM;IAEzD,UAAU,EAAE,qBAAqB,CAAC;IAClC,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,oBAAoB,CACnC,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,EAC9D,aAAa,SAAS,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK;IAEnD,UAAU,EAAE,qBAAqB,CAAC;IAClC,MAAM,EAAE,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAID,wBAAgB,eAAe,CAC7B,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,EAC9D,aAAa,SAAS,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK,EACnD,EACA,UAAU,EACV,MAAM,EACN,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,WAAW,EACX,WAAW,EACX,WAAsB,EACtB,QAAQ,EACR,eAAe,EACf,QAAgB,EAChB,SAAgB,EAChB,WAAmB,EACnB,KAAK,GACN,EAAE,oBAAoB,CAAC,MAAM,EAAE,aAAa,CAAC,2CA2Q7C;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,SAAS,sBAAsB,GAAG,sBAAsB,EAC9D,GAAG,SAAS,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACzD,EACA,UAAU,EACV,QAAQ,EACR,MAAM,EACN,MAA2B,EAC3B,OAAe,EACf,QAAgB,EAChB,MAAM,GACP,EAAE,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,kDAoBpC;AAED,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,GAChD,OAAO,CAET;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,GAChD,0BAA0B,EAAE,CAY9B"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useMemo, useState, } from "react";
|
|
3
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
4
|
+
import { Input } from "../internal/ui/input.js";
|
|
5
|
+
import { Label } from "../internal/ui/label.js";
|
|
6
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, } from "../internal/ui/select.js";
|
|
7
|
+
import { surfaceStyle } from "../theme/derive.js";
|
|
8
|
+
import { useTheme, useThemeCssVars } from "../theme/ThemeProvider.js";
|
|
9
|
+
import { interactionLabel } from "../utils/interaction-labels.js";
|
|
10
|
+
import { useChromeSuppression } from "./ChromeSuppressionContext.js";
|
|
11
|
+
import { ThemedButton } from "./ThemedButton.js";
|
|
12
|
+
const EMPTY_FIELD_ERRORS = [];
|
|
13
|
+
export function InteractionForm({ descriptor, handle, fields, hiddenFields, renderFields, title, description, submitLabel, cancelLabel = "Cancel", onCancel, onSubmitSuccess, disabled = false, accordion = true, defaultOpen = false, style, }) {
|
|
14
|
+
const theme = useTheme();
|
|
15
|
+
const fallbackLabel = interactionLabel(descriptor);
|
|
16
|
+
const formId = useId();
|
|
17
|
+
useChromeSuppression(formId, true);
|
|
18
|
+
const [pending, setPending] = useState(false);
|
|
19
|
+
const [validation, setValidation] = useState(null);
|
|
20
|
+
const [formError, setFormError] = useState(null);
|
|
21
|
+
const [accordionOpen, setAccordionOpen] = useState(defaultOpen);
|
|
22
|
+
const hidden = useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
|
|
23
|
+
const visibleInputs = useMemo(() => {
|
|
24
|
+
const allowed = fields ? new Set(fields) : null;
|
|
25
|
+
return defaultFormInputs(descriptor).filter((input) => {
|
|
26
|
+
const key = input.key;
|
|
27
|
+
if (allowed && !allowed.has(key))
|
|
28
|
+
return false;
|
|
29
|
+
return !hidden.has(key);
|
|
30
|
+
});
|
|
31
|
+
}, [descriptor, fields, hidden]);
|
|
32
|
+
const currentValidation = validation;
|
|
33
|
+
const fieldErrors = (currentValidation?.fieldErrors ?? {});
|
|
34
|
+
const missing = new Set(currentValidation?.missing ?? []);
|
|
35
|
+
const formErrors = [
|
|
36
|
+
...(currentValidation?.formErrors ?? []),
|
|
37
|
+
...(formError ? [formError] : []),
|
|
38
|
+
];
|
|
39
|
+
const isDisabled = disabled || pending || !descriptor.available;
|
|
40
|
+
const useAccordion = accordion && visibleInputs.length > 0;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setAccordionOpen(defaultOpen);
|
|
43
|
+
}, [defaultOpen, descriptor.interactionId]);
|
|
44
|
+
const containerStyle = {
|
|
45
|
+
...surfaceStyle(theme, { tone: "card" }),
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
gap: theme.space[2],
|
|
49
|
+
padding: theme.space[3],
|
|
50
|
+
minWidth: "min(100%, 280px)",
|
|
51
|
+
boxSizing: "border-box",
|
|
52
|
+
fontFamily: theme.typography.fontFamily.body,
|
|
53
|
+
...style,
|
|
54
|
+
};
|
|
55
|
+
const submit = async (event) => {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
if (isDisabled)
|
|
58
|
+
return;
|
|
59
|
+
const nextValidation = handle.validateDraft();
|
|
60
|
+
setValidation(nextValidation);
|
|
61
|
+
setFormError(null);
|
|
62
|
+
if (!nextValidation.ok) {
|
|
63
|
+
setAccordionOpen(true);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setPending(true);
|
|
67
|
+
try {
|
|
68
|
+
await handle.submitDraft();
|
|
69
|
+
onSubmitSuccess?.();
|
|
70
|
+
setAccordionOpen(defaultOpen);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
setAccordionOpen(true);
|
|
74
|
+
setFormError(error instanceof Error
|
|
75
|
+
? error.message
|
|
76
|
+
: "Interaction submission failed");
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
setPending(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const header = (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [_jsx("strong", { style: {
|
|
83
|
+
fontFamily: theme.typography.fontFamily.display,
|
|
84
|
+
fontSize: theme.typography.fontSize.md,
|
|
85
|
+
color: theme.semantic.text.primary,
|
|
86
|
+
}, children: title ?? fallbackLabel }), description ? (_jsx("span", { style: {
|
|
87
|
+
fontSize: theme.typography.fontSize.sm,
|
|
88
|
+
color: theme.semantic.text.muted,
|
|
89
|
+
}, children: description })) : null] }));
|
|
90
|
+
const fieldsContent = visibleInputs.length > 0 ? (_jsx("div", { style: {
|
|
91
|
+
display: "flex",
|
|
92
|
+
flexDirection: "column",
|
|
93
|
+
gap: theme.space[3],
|
|
94
|
+
}, children: visibleInputs.map((input) => {
|
|
95
|
+
const key = input.key;
|
|
96
|
+
return (_jsx(InteractionField, { descriptor: descriptor, inputKey: key, handle: handle, errors: fieldErrors[key] ?? [], missing: missing.has(key), disabled: isDisabled, render: renderFields?.[key] }, input.key));
|
|
97
|
+
}) })) : (_jsx("span", { style: {
|
|
98
|
+
fontSize: theme.typography.fontSize.sm,
|
|
99
|
+
color: theme.semantic.text.muted,
|
|
100
|
+
}, children: "No visible fields are required. Board or card selection may complete this interaction." }));
|
|
101
|
+
const formErrorContent = formErrors.length > 0 ? (_jsx("div", { role: "alert", style: {
|
|
102
|
+
display: "flex",
|
|
103
|
+
flexDirection: "column",
|
|
104
|
+
gap: theme.space[1],
|
|
105
|
+
color: theme.semantic.intent.danger.solid,
|
|
106
|
+
fontSize: theme.typography.fontSize.sm,
|
|
107
|
+
}, children: formErrors.map((error) => (_jsx("span", { children: error }, error))) })) : null;
|
|
108
|
+
const actions = (_jsxs("div", { style: {
|
|
109
|
+
display: "flex",
|
|
110
|
+
gap: theme.space[2],
|
|
111
|
+
justifyContent: "flex-end",
|
|
112
|
+
}, children: [onCancel ? (_jsx(ThemedButton, { type: "button", variant: "secondary", size: "sm", disabled: pending, onClick: onCancel, className: "h-9 px-3 text-sm", children: cancelLabel })) : null, _jsx(ThemedButton, { type: "submit", variant: "primary", size: "sm", disabled: isDisabled, className: "h-9 px-3 text-sm", children: pending ? "Submitting..." : (submitLabel ?? fallbackLabel) })] }));
|
|
113
|
+
const body = (_jsxs(_Fragment, { children: [fieldsContent, formErrorContent, actions] }));
|
|
114
|
+
return (_jsx("form", { "data-interaction-form": true, "data-interaction-id": descriptor.interactionId, onSubmit: (event) => void submit(event), style: containerStyle, children: useAccordion ? (_jsx(AccordionPrimitive.Root, { type: "single", collapsible: true, value: accordionOpen ? "fields" : undefined, onValueChange: (value) => setAccordionOpen(value === "fields"), children: _jsxs(AccordionPrimitive.Item, { value: "fields", children: [_jsx(AccordionPrimitive.Header, { style: { margin: 0 }, children: _jsxs(AccordionPrimitive.Trigger, { style: {
|
|
115
|
+
alignItems: "center",
|
|
116
|
+
appearance: "none",
|
|
117
|
+
background: "transparent",
|
|
118
|
+
border: 0,
|
|
119
|
+
color: theme.semantic.text.primary,
|
|
120
|
+
cursor: "pointer",
|
|
121
|
+
display: "flex",
|
|
122
|
+
fontFamily: theme.typography.fontFamily.display,
|
|
123
|
+
fontSize: theme.typography.fontSize.md,
|
|
124
|
+
fontWeight: theme.typography.fontWeight.bold,
|
|
125
|
+
justifyContent: "space-between",
|
|
126
|
+
padding: 0,
|
|
127
|
+
textAlign: "left",
|
|
128
|
+
width: "100%",
|
|
129
|
+
}, children: [_jsx("span", { children: title ?? fallbackLabel }), _jsx("span", { "aria-hidden": true, children: accordionOpen ? "−" : "+" })] }) }), _jsx(AccordionPrimitive.Content, { children: _jsxs("div", { style: {
|
|
130
|
+
display: "flex",
|
|
131
|
+
flexDirection: "column",
|
|
132
|
+
gap: theme.space[2],
|
|
133
|
+
marginTop: theme.space[2],
|
|
134
|
+
}, children: [description ? (_jsx("span", { style: {
|
|
135
|
+
fontSize: theme.typography.fontSize.sm,
|
|
136
|
+
color: theme.semantic.text.muted,
|
|
137
|
+
}, children: description })) : null, body] }) })] }) })) : (_jsxs(_Fragment, { children: [header, body] })) }));
|
|
138
|
+
}
|
|
139
|
+
export function InteractionField({ descriptor, inputKey, handle, errors = EMPTY_FIELD_ERRORS, missing = false, disabled = false, render, }) {
|
|
140
|
+
const input = descriptor.inputs.find((candidate) => candidate.key === inputKey);
|
|
141
|
+
if (!input)
|
|
142
|
+
return null;
|
|
143
|
+
const typedInput = input;
|
|
144
|
+
const value = handle.values[inputKey];
|
|
145
|
+
const props = {
|
|
146
|
+
descriptor,
|
|
147
|
+
input: typedInput,
|
|
148
|
+
handle,
|
|
149
|
+
value,
|
|
150
|
+
setValue: (next) => handle.setInput(inputKey, next),
|
|
151
|
+
clearValue: () => handle.clearInput(inputKey),
|
|
152
|
+
errors,
|
|
153
|
+
missing,
|
|
154
|
+
disabled,
|
|
155
|
+
};
|
|
156
|
+
if (render)
|
|
157
|
+
return _jsx(_Fragment, { children: render(props) });
|
|
158
|
+
return _jsx(DefaultInteractionField, { ...props });
|
|
159
|
+
}
|
|
160
|
+
export function hasDefaultInteractionFormFields(descriptor) {
|
|
161
|
+
return defaultFormInputs(descriptor).length > 0;
|
|
162
|
+
}
|
|
163
|
+
export function defaultFormInputs(descriptor) {
|
|
164
|
+
return descriptor.inputs.filter((input) => {
|
|
165
|
+
switch (input.domain.type) {
|
|
166
|
+
case "choice":
|
|
167
|
+
case "choiceList":
|
|
168
|
+
case "resourceMap":
|
|
169
|
+
case "boundedNumber":
|
|
170
|
+
return true;
|
|
171
|
+
case "target":
|
|
172
|
+
return input.domain.selection?.mode === "many";
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function DefaultInteractionField(props) {
|
|
177
|
+
const { input } = props;
|
|
178
|
+
switch (input.domain.type) {
|
|
179
|
+
case "choice":
|
|
180
|
+
if (input.domain.selection?.mode === "many") {
|
|
181
|
+
return (_jsx(ChoiceListField, { ...props, domain: {
|
|
182
|
+
type: "choiceList",
|
|
183
|
+
choices: input.domain.choices,
|
|
184
|
+
min: input.domain.selection.min ?? 0,
|
|
185
|
+
max: input.domain.selection.max,
|
|
186
|
+
selection: input.domain.selection,
|
|
187
|
+
} }));
|
|
188
|
+
}
|
|
189
|
+
return _jsx(ChoiceField, { ...props, domain: input.domain });
|
|
190
|
+
case "choiceList":
|
|
191
|
+
return _jsx(ChoiceListField, { ...props, domain: input.domain });
|
|
192
|
+
case "resourceMap":
|
|
193
|
+
return _jsx(ResourceMapField, { ...props, domain: input.domain });
|
|
194
|
+
case "boundedNumber":
|
|
195
|
+
return _jsx(BoundedNumberField, { ...props, domain: input.domain });
|
|
196
|
+
case "target":
|
|
197
|
+
return _jsx(TargetSummaryField, { ...props, domain: input.domain });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function FieldFrame({ label, controlId, errors, missing, children, }) {
|
|
201
|
+
const theme = useTheme();
|
|
202
|
+
const messages = errors.length > 0 ? errors : missing ? ["Required"] : [];
|
|
203
|
+
return (_jsxs("div", { style: {
|
|
204
|
+
display: "flex",
|
|
205
|
+
flexDirection: "column",
|
|
206
|
+
gap: theme.space[1],
|
|
207
|
+
fontSize: theme.typography.fontSize.sm,
|
|
208
|
+
color: theme.semantic.text.primary,
|
|
209
|
+
}, children: [_jsx(Label, { htmlFor: controlId, style: {
|
|
210
|
+
color: theme.semantic.text.primary,
|
|
211
|
+
fontWeight: theme.typography.fontWeight.bold,
|
|
212
|
+
}, children: label }), children, messages.length > 0 ? (_jsx("span", { role: "alert", style: {
|
|
213
|
+
display: "flex",
|
|
214
|
+
flexDirection: "column",
|
|
215
|
+
gap: 2,
|
|
216
|
+
color: theme.semantic.intent.danger.solid,
|
|
217
|
+
fontSize: theme.typography.fontSize.xs,
|
|
218
|
+
}, children: messages.map((message) => (_jsx("span", { children: message }, message))) })) : null] }));
|
|
219
|
+
}
|
|
220
|
+
function ChoiceOptionLabel({ choice }) {
|
|
221
|
+
const theme = useTheme();
|
|
222
|
+
return (_jsxs("span", { style: {
|
|
223
|
+
display: "inline-flex",
|
|
224
|
+
alignItems: "center",
|
|
225
|
+
gap: 6,
|
|
226
|
+
minWidth: 0,
|
|
227
|
+
}, children: [_jsxs("span", { style: {
|
|
228
|
+
display: "inline-flex",
|
|
229
|
+
alignItems: "center",
|
|
230
|
+
gap: 6,
|
|
231
|
+
minWidth: 0,
|
|
232
|
+
}, children: [choice.icon ? (_jsx("span", { "aria-hidden": true, style: { fontSize: "1.1em", lineHeight: 1 }, children: choice.icon })) : null, _jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: choice.label })] }), choice.badge ? (_jsx("span", { style: {
|
|
233
|
+
borderRadius: 999,
|
|
234
|
+
background: theme.semantic.surface.inset,
|
|
235
|
+
color: theme.semantic.text.muted,
|
|
236
|
+
fontSize: theme.typography.fontSize.xs,
|
|
237
|
+
fontWeight: theme.typography.fontWeight.bold,
|
|
238
|
+
lineHeight: 1,
|
|
239
|
+
padding: "3px 6px",
|
|
240
|
+
whiteSpace: "nowrap",
|
|
241
|
+
}, children: choice.badge })) : null] }));
|
|
242
|
+
}
|
|
243
|
+
function ChoiceDescription({ choice }) {
|
|
244
|
+
const theme = useTheme();
|
|
245
|
+
const message = choice?.disabledReason ?? choice?.description;
|
|
246
|
+
if (!message)
|
|
247
|
+
return null;
|
|
248
|
+
return (_jsx("span", { style: {
|
|
249
|
+
color: choice?.disabledReason
|
|
250
|
+
? theme.semantic.intent.danger.solid
|
|
251
|
+
: theme.semantic.text.muted,
|
|
252
|
+
fontSize: theme.typography.fontSize.xs,
|
|
253
|
+
}, children: message }));
|
|
254
|
+
}
|
|
255
|
+
function ChoiceField({ input, value, setValue, errors, missing, disabled, domain, }) {
|
|
256
|
+
const theme = useTheme();
|
|
257
|
+
const themeCssVars = useThemeCssVars();
|
|
258
|
+
const choices = domain.choices ?? [];
|
|
259
|
+
const controlId = useId();
|
|
260
|
+
const selectedChoice = typeof value === "string"
|
|
261
|
+
? choices.find((choice) => choice.value === value)
|
|
262
|
+
: undefined;
|
|
263
|
+
if (choices.length > 0 && choices.length <= 3) {
|
|
264
|
+
return (_jsx(FieldFrame, { label: labelForInput(input), errors: errors, missing: missing, children: _jsx("span", { style: {
|
|
265
|
+
display: "inline-flex",
|
|
266
|
+
flexWrap: "wrap",
|
|
267
|
+
gap: theme.space[1],
|
|
268
|
+
}, children: choices.map((choice) => {
|
|
269
|
+
const selected = value === choice.value;
|
|
270
|
+
return (_jsx(ThemedButton, { type: "button", variant: selected ? "primary" : "secondary", size: "sm", disabled: disabled || choice.disabled, "aria-pressed": selected, title: choice.disabledReason ?? choice.description, onClick: () => setValue(choice.value), className: "h-8 px-3 text-sm", children: _jsx(ChoiceOptionLabel, { choice: choice }) }, choice.value));
|
|
271
|
+
}) }) }));
|
|
272
|
+
}
|
|
273
|
+
return (_jsxs(FieldFrame, { label: labelForInput(input), controlId: controlId, errors: errors, missing: missing, children: [_jsxs(Select, { disabled: disabled, value: typeof value === "string" ? value : undefined, onValueChange: (next) => setValue(next), children: [_jsx(SelectTrigger, { id: controlId, size: "sm", className: "w-full bg-white", children: _jsx("span", { "data-slot": "select-value", children: selectedChoice ? (_jsx(ChoiceOptionLabel, { choice: selectedChoice })) : (_jsx("span", { style: { color: theme.semantic.text.muted }, children: "Choose..." })) }) }), _jsx(SelectContent, { style: {
|
|
274
|
+
...themeCssVars,
|
|
275
|
+
fontFamily: theme.typography.fontFamily.body,
|
|
276
|
+
}, children: choices.map((choice) => (_jsx(SelectItem, { value: choice.value, textValue: choice.label, disabled: choice.disabled, children: _jsx(ChoiceOptionLabel, { choice: choice }) }, choice.value))) })] }), _jsx(ChoiceDescription, { choice: selectedChoice })] }));
|
|
277
|
+
}
|
|
278
|
+
function ChoiceListField({ input, value, setValue, errors, missing, disabled, domain, }) {
|
|
279
|
+
const theme = useTheme();
|
|
280
|
+
const selected = new Set(Array.isArray(value) ? value : []);
|
|
281
|
+
const min = domain.selection?.mode === "many"
|
|
282
|
+
? (domain.selection.min ?? 0)
|
|
283
|
+
: domain.min;
|
|
284
|
+
const max = (domain.selection?.mode === "many" ? domain.selection.max : domain.max) ??
|
|
285
|
+
domain.choices?.length ??
|
|
286
|
+
Number.POSITIVE_INFINITY;
|
|
287
|
+
const toggle = (choice) => {
|
|
288
|
+
const next = new Set(selected);
|
|
289
|
+
if (next.has(choice))
|
|
290
|
+
next.delete(choice);
|
|
291
|
+
else if (next.size < max)
|
|
292
|
+
next.add(choice);
|
|
293
|
+
setValue([...next]);
|
|
294
|
+
};
|
|
295
|
+
const meta = min || Number.isFinite(max)
|
|
296
|
+
? `Pick ${min ?? 0}${Number.isFinite(max) ? `-${max}` : "+"}`
|
|
297
|
+
: undefined;
|
|
298
|
+
return (_jsx(FieldFrame, { label: _jsxs("span", { style: {
|
|
299
|
+
display: "flex",
|
|
300
|
+
justifyContent: "space-between",
|
|
301
|
+
gap: theme.space[2],
|
|
302
|
+
}, children: [_jsx("span", { children: labelForInput(input) }), meta ? (_jsx("span", { style: { color: theme.semantic.text.muted }, children: meta })) : null] }), errors: errors, missing: missing, children: _jsx("span", { style: { display: "flex", flexWrap: "wrap", gap: theme.space[1] }, children: (domain.choices ?? []).map((choice) => {
|
|
303
|
+
const checked = selected.has(choice.value);
|
|
304
|
+
return (_jsx(ThemedButton, { type: "button", variant: checked ? "primary" : "secondary", size: "sm", disabled: disabled ||
|
|
305
|
+
choice.disabled ||
|
|
306
|
+
(!checked && selected.size >= max), "aria-pressed": checked, title: choice.disabledReason ?? choice.description, onClick: () => toggle(choice.value), className: "h-8 px-3 text-sm", children: _jsx(ChoiceOptionLabel, { choice: choice }) }, choice.value));
|
|
307
|
+
}) }) }));
|
|
308
|
+
}
|
|
309
|
+
function ResourceMapField({ input, value, setValue, errors, missing, disabled, domain, }) {
|
|
310
|
+
const theme = useTheme();
|
|
311
|
+
const current = isRecord(value) ? value : {};
|
|
312
|
+
const update = (resourceId, delta, min, max) => {
|
|
313
|
+
const previous = numberOrZero(current[resourceId]);
|
|
314
|
+
const next = Math.max(min, Math.min(max, previous + delta));
|
|
315
|
+
setValue({ ...current, [resourceId]: next });
|
|
316
|
+
};
|
|
317
|
+
return (_jsx(FieldFrame, { label: labelForInput(input), errors: errors, missing: missing, children: _jsx("span", { style: {
|
|
318
|
+
display: "grid",
|
|
319
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))",
|
|
320
|
+
gap: theme.space[2],
|
|
321
|
+
}, children: (domain.resources ?? []).map((resource) => {
|
|
322
|
+
const min = resource.min ?? 0;
|
|
323
|
+
const max = resource.max ?? 0;
|
|
324
|
+
const amount = numberOrZero(current[resource.resourceId]);
|
|
325
|
+
return (_jsxs("span", { style: {
|
|
326
|
+
display: "grid",
|
|
327
|
+
gridTemplateColumns: "minmax(0, 1fr) auto auto auto",
|
|
328
|
+
alignItems: "center",
|
|
329
|
+
gap: theme.space[1],
|
|
330
|
+
padding: theme.space[2],
|
|
331
|
+
borderRadius: theme.radius.md,
|
|
332
|
+
background: theme.semantic.surface.inset,
|
|
333
|
+
}, children: [_jsxs("span", { style: {
|
|
334
|
+
minWidth: 0,
|
|
335
|
+
display: "inline-flex",
|
|
336
|
+
alignItems: "center",
|
|
337
|
+
gap: theme.space[1],
|
|
338
|
+
}, children: [resource.icon ? (_jsx("span", { "aria-hidden": true, style: { fontSize: "1.1em" }, children: resource.icon })) : null, resource.label ?? humanize(input.key)] }), _jsx(StepperButton, { label: `Decrease ${resource.label ?? resource.resourceId}`, disabled: disabled || amount <= min, onClick: () => update(resource.resourceId, -1, min, max), children: "-" }), _jsx("span", { style: {
|
|
339
|
+
minWidth: "2ch",
|
|
340
|
+
textAlign: "center",
|
|
341
|
+
fontVariantNumeric: "tabular-nums",
|
|
342
|
+
}, children: amount }), _jsx(StepperButton, { label: `Increase ${resource.label ?? resource.resourceId}`, disabled: disabled || amount >= max, onClick: () => update(resource.resourceId, 1, min, max), children: "+" })] }, resource.resourceId));
|
|
343
|
+
}) }) }));
|
|
344
|
+
}
|
|
345
|
+
function BoundedNumberField({ input, value, setValue, errors, missing, disabled, domain, }) {
|
|
346
|
+
const theme = useTheme();
|
|
347
|
+
const min = domain.min ?? 0;
|
|
348
|
+
const max = domain.max ?? Number.POSITIVE_INFINITY;
|
|
349
|
+
const step = domain.step ?? 1;
|
|
350
|
+
const current = typeof value === "number" ? value : min;
|
|
351
|
+
const controlId = useId();
|
|
352
|
+
const update = (next) => setValue(Math.max(min, Math.min(max, next)));
|
|
353
|
+
return (_jsx(FieldFrame, { label: labelForInput(input), controlId: controlId, errors: errors, missing: missing, children: _jsxs("span", { style: {
|
|
354
|
+
display: "inline-flex",
|
|
355
|
+
alignItems: "center",
|
|
356
|
+
gap: theme.space[1],
|
|
357
|
+
}, children: [_jsx(StepperButton, { label: `Decrease ${input.key}`, disabled: disabled || current <= min, onClick: () => update(current - step), children: "-" }), _jsx(Input, { id: controlId, type: "number", min: min, max: Number.isFinite(max) ? max : undefined, step: step, value: current, disabled: disabled, onChange: (event) => update(Number(event.target.value)), className: "h-9 w-[8ch] px-2 text-center text-sm md:text-sm" }), _jsx(StepperButton, { label: `Increase ${input.key}`, disabled: disabled || current >= max, onClick: () => update(current + step), children: "+" })] }) }));
|
|
358
|
+
}
|
|
359
|
+
function TargetSummaryField({ input, value, errors, missing, domain, }) {
|
|
360
|
+
return (_jsx(FieldFrame, { label: labelForInput(input), errors: errors, missing: missing, children: _jsx("span", { children: Array.isArray(value)
|
|
361
|
+
? value.length > 0
|
|
362
|
+
? `${value.length} selected: ${value.join(", ")}`
|
|
363
|
+
: `Select ${targetSelectionLabel(domain)}.`
|
|
364
|
+
: typeof value === "string"
|
|
365
|
+
? value
|
|
366
|
+
: `Select a ${domain.targetKind ?? "target"} on the board.` }) }));
|
|
367
|
+
}
|
|
368
|
+
function targetSelectionLabel(domain) {
|
|
369
|
+
const target = domain.targetKind ?? "target";
|
|
370
|
+
const selection = domain.selection;
|
|
371
|
+
if (selection?.mode !== "many")
|
|
372
|
+
return `a ${target}`;
|
|
373
|
+
const min = selection.min ?? 0;
|
|
374
|
+
if (selection.max !== undefined && min === selection.max) {
|
|
375
|
+
return `${min} ${target}${min === 1 ? "" : "s"}`;
|
|
376
|
+
}
|
|
377
|
+
return `${min}${selection.max ? `-${selection.max}` : "+"} ${target}s`;
|
|
378
|
+
}
|
|
379
|
+
function StepperButton({ label, disabled, onClick, children, }) {
|
|
380
|
+
return (_jsx(ThemedButton, { type: "button", variant: "secondary", size: "sm", "aria-label": label, disabled: disabled, onClick: onClick, className: "h-8 w-8 text-sm", children: children }));
|
|
381
|
+
}
|
|
382
|
+
function labelForInput(input) {
|
|
383
|
+
if (input.domain.type === "choice") {
|
|
384
|
+
const exact = input.domain.choices?.find((choice) => choice.value === input.defaultValue);
|
|
385
|
+
if (exact?.label && input.key === exact.value)
|
|
386
|
+
return exact.label;
|
|
387
|
+
}
|
|
388
|
+
return humanize(input.key);
|
|
389
|
+
}
|
|
390
|
+
function humanize(key) {
|
|
391
|
+
return key
|
|
392
|
+
.replace(/Id$/, "")
|
|
393
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
394
|
+
.replace(/[-_]+/g, " ")
|
|
395
|
+
.replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
396
|
+
}
|
|
397
|
+
function isRecord(value) {
|
|
398
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
399
|
+
}
|
|
400
|
+
function numberOrZero(value) {
|
|
401
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
402
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disclosure that hides a list of low-salience actions behind a single
|
|
3
|
+
* "More" toggle. Sized and styled by the active theme so it looks
|
|
4
|
+
* consistent with neighbouring `<DefaultInteractionButton>` rows.
|
|
5
|
+
*
|
|
6
|
+
* Why not a popover or dropdown? Two reasons:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Layout safety.** The default panel surface lives directly above
|
|
9
|
+
* the hand strip; a floating popover would be obscured by the
|
|
10
|
+
* hand's `overflow-x: auto` clipping. An inline expansion stays
|
|
11
|
+
* inside the panel container and pushes neighbouring rows down.
|
|
12
|
+
* 2. **Discoverability.** Players miss menus that hide behind triple
|
|
13
|
+
* dots / chevrons. An expanded inline list still looks like a row
|
|
14
|
+
* of buttons (Jakob — same affordance as the always-visible row).
|
|
15
|
+
*
|
|
16
|
+
* The toggle reports its open state via `aria-expanded` and labels
|
|
17
|
+
* the disclosed region via `aria-controls` so screen readers announce
|
|
18
|
+
* "Expanded — More actions, region containing 3 buttons" naturally.
|
|
19
|
+
*/
|
|
20
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
21
|
+
export interface MoreActionsProps {
|
|
22
|
+
/**
|
|
23
|
+
* Items rendered inside the disclosure when expanded. Typically
|
|
24
|
+
* `<DefaultInteractionButton>` instances for `salience: "tertiary"`
|
|
25
|
+
* descriptors, but any `ReactNode` works (custom panel cards, etc.).
|
|
26
|
+
*/
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Toggle label. Defaults to `"More"`. The descriptor count gets
|
|
30
|
+
* appended automatically when {@link count} is supplied.
|
|
31
|
+
*/
|
|
32
|
+
label?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Number of hidden items, used to render the trailing "(N)" badge.
|
|
35
|
+
* Omit when the count is irrelevant or already implied (e.g. when
|
|
36
|
+
* the panel only has a fixed set of disclosed items).
|
|
37
|
+
*/
|
|
38
|
+
count?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Initial open state. Defaults to `false` — the disclosure is the
|
|
41
|
+
* point. Authors who want it open by default for a specific seat
|
|
42
|
+
* (e.g. tutorial mode) should pass `true`.
|
|
43
|
+
*/
|
|
44
|
+
defaultOpen?: boolean;
|
|
45
|
+
/** Additional inline style merged after the default container. */
|
|
46
|
+
style?: CSSProperties;
|
|
47
|
+
}
|
|
48
|
+
export declare function MoreActions({ children, label, count, defaultOpen, style, }: MoreActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
//# sourceMappingURL=MoreActions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MoreActions.d.ts","sourceRoot":"","sources":["../../src/components/MoreActions.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAmB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK5E,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAc,EACd,KAAK,EACL,WAAmB,EACnB,KAAK,GACN,EAAE,gBAAgB,2CAmFlB"}
|