diff --git a/package.json b/package.json index 7d39a42..a833e95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modaq", - "version": "1.25.0", + "version": "1.26.0", "description": "Quiz Bowl Reader using TypeScript, React, and MobX", "repository": { "type": "git", diff --git a/src/components/FormattedText.tsx b/src/components/FormattedText.tsx index 383cd78..ba05434 100644 --- a/src/components/FormattedText.tsx +++ b/src/components/FormattedText.tsx @@ -72,6 +72,7 @@ const useStyles = memoizeFunction( mergeStyleSets({ text: { display: "inline", + textDecorationSkipInk: "none", }, pronunciationGuide: { // Don't override the color if it's disabled; the container has that responsibility diff --git a/src/components/GameBar.tsx b/src/components/GameBar.tsx index 774dd82..b2125e2 100644 --- a/src/components/GameBar.tsx +++ b/src/components/GameBar.tsx @@ -95,6 +95,14 @@ export const GameBar = observer(function GameBar(): JSX.Element { uiState.dialogState.showReorderPlayersDialog(game.players); }, [uiState, game]); + const renameTeamHandler = React.useCallback(() => { + if (game.players.length === 0) { + return; + } + + uiState.dialogState.showRenameTeamDialog(game.players[0].teamName); + }, [uiState, game]); + const items: ICommandBarItemProps[] = appState.uiState.hideNewGame ? [] : [ @@ -147,6 +155,7 @@ export const GameBar = observer(function GameBar(): JSX.Element { addPlayerHandler, protestBonusHandler, reorderPlayersHandler, + renameTeamHandler, addQuestionsHandler ); items.push({ @@ -219,6 +228,7 @@ function getActionSubMenuItems( addPlayerHandler: () => void, protestBonusHandler: () => void, reorderPlayersHandler: () => void, + renameTeamHandler: () => void, addQuestionsHandler: () => void ): ICommandBarItemProps[] { const items: ICommandBarItemProps[] = []; @@ -230,7 +240,8 @@ function getActionSubMenuItems( game, uiState, addPlayerHandler, - reorderPlayersHandler + reorderPlayersHandler, + renameTeamHandler ); items.push(playerManagementSection); @@ -420,10 +431,11 @@ function getPlayerManagementSubMenuItems( game: GameState, uiState: UIState, addPlayerHandler: () => void, - reorderPlayersHandler: () => void + reorderPlayersHandler: () => void, + renameTeamHandler: () => void ): ICommandBarItemProps { const teamNames: string[] = game.teamNames; - const swapActivePlayerMenus: ICommandBarItemProps[] = []; + const playerActionsMenus: ICommandBarItemProps[] = []; for (const teamName of teamNames) { const players: Player[] = game.getPlayers(teamName); const activePlayers: Set = game.getActivePlayers(teamName, uiState.cycleIndex); @@ -502,7 +514,7 @@ function getPlayerManagementSubMenuItems( }); } - swapActivePlayerMenus.push({ + playerActionsMenus.push({ key: `active_${teamName}`, itemType: ContextualMenuItemType.Section, sectionProps: { @@ -514,13 +526,13 @@ function getPlayerManagementSubMenuItems( } // TODO: This should be under a section for player management (add player, subs) - const swapPlayerItem: ICommandBarItemProps = { - key: "swapPlayer", - text: "Substitute/Remove", + const playerActionsItem: ICommandBarItemProps = { + key: "player", + text: "Player", subMenuProps: { - items: swapActivePlayerMenus, + items: playerActionsMenus, }, - disabled: swapActivePlayerMenus.length === 0, + disabled: playerActionsMenus.length === 0, // This needs its own submenu, with all the starters, then all the possible subs // We should disable this if there are no subs available }; @@ -549,13 +561,20 @@ function getPlayerManagementSubMenuItems( disabled: appState.game.cycles.length === 0, }; + const renameTeamItem: ICommandBarItemProps = { + key: "renameTeam", + text: "Rename team...", + onClick: renameTeamHandler, + disabled: appState.game.cycles.length === 0, + }; + return { - key: "playerManagement", + key: "teamManagement", itemType: ContextualMenuItemType.Section, sectionProps: { bottomDivider: true, - title: "Player Management", - items: [swapPlayerItem, addPlayerItem, reorderPlayersItem], + title: "Team Management", + items: [playerActionsItem, addPlayerItem, reorderPlayersItem, renameTeamItem], }, }; } @@ -766,7 +785,7 @@ function onSwapPlayerClick( const additionalHint: string = Math.abs(cycleIndex - halftimeIndex) <= 1 - ? ` If you want to substitue a player at halftime, do it on question ${Math.floor(halftimeIndex + 1)}.` + ? ` If you want to substitute a player at halftime, do it on question ${Math.floor(halftimeIndex + 1)}.` : ""; item.data.appState.uiState.dialogState.showOKCancelMessageDialog( "Substitute Player", diff --git a/src/components/ModalDialogContainer.tsx b/src/components/ModalDialogContainer.tsx index 87f4718..ef91914 100644 --- a/src/components/ModalDialogContainer.tsx +++ b/src/components/ModalDialogContainer.tsx @@ -17,6 +17,7 @@ import { ScoresheetDialog } from "./dialogs/ScoresheetDialog"; import { StateContext } from "../contexts/StateContext"; import { AppState } from "../state/AppState"; import { ModalVisibilityStatus } from "../state/ModalVisibilityStatus"; +import { RenameTeamDialog } from "./dialogs/RenameTeamDialog"; export const ModalDialogContainer = observer(function ModalDialogContainer() { // The Protest dialogs aren't here because they require extra information @@ -38,6 +39,7 @@ export const ModalDialogContainer = observer(function ModalDialogContainer() { + diff --git a/src/components/dialogs/AddPlayerDialog.tsx b/src/components/dialogs/AddPlayerDialog.tsx index bf5863e..21b2158 100644 --- a/src/components/dialogs/AddPlayerDialog.tsx +++ b/src/components/dialogs/AddPlayerDialog.tsx @@ -1,57 +1,20 @@ import * as React from "react"; import { observer } from "mobx-react-lite"; -import { - Dropdown, - TextField, - IDropdownOption, - IDialogContentProps, - DialogType, - IModalProps, - ContextualMenu, - Dialog, - DialogFooter, - PrimaryButton, - DefaultButton, -} from "@fluentui/react"; +import { Dropdown, TextField, IDropdownOption, DialogFooter, PrimaryButton, DefaultButton } from "@fluentui/react"; import * as AddPlayerDialogController from "../../components/dialogs/AddPlayerDialogController"; import { IPlayer } from "../../state/TeamState"; import { AppState } from "../../state/AppState"; import { StateContext } from "../../contexts/StateContext"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Add Player", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; // TODO: Look into making a DefaultDialog, which handles the footers and default props export const AddPlayerDialog = observer(function AddPlayerDialog(): JSX.Element { - const appState: AppState = React.useContext(StateContext); - return ( - + ); }); diff --git a/src/components/dialogs/AddQuestionsDialog.tsx b/src/components/dialogs/AddQuestionsDialog.tsx index ed73ee1..26c8c01 100644 --- a/src/components/dialogs/AddQuestionsDialog.tsx +++ b/src/components/dialogs/AddQuestionsDialog.tsx @@ -1,53 +1,20 @@ import * as React from "react"; import { observer } from "mobx-react-lite"; -import { - IDialogContentProps, - DialogType, - IModalProps, - ContextualMenu, - Dialog, - DialogFooter, - PrimaryButton, - DefaultButton, -} from "@fluentui/react"; +import { DialogFooter, PrimaryButton, DefaultButton } from "@fluentui/react"; import * as AddQuestionsDialogController from "./AddQuestionsDialogController"; import { AppState } from "../../state/AppState"; import { PacketLoader } from "../PacketLoader"; import { StateContext } from "../../contexts/StateContext"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Add Questions", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; // TODO: Look into making a DefaultDialog, which handles the footers and default props export const AddQuestionsDialog = observer(function AddQuestionsDialog(): JSX.Element { - const appState: AppState = React.useContext(StateContext); return ( - + ); }); diff --git a/src/components/dialogs/BonusProtestDialog.tsx b/src/components/dialogs/BonusProtestDialog.tsx index 1b98b71..0ec38b6 100644 --- a/src/components/dialogs/BonusProtestDialog.tsx +++ b/src/components/dialogs/BonusProtestDialog.tsx @@ -36,10 +36,10 @@ export const BonusProtestDialog = observer(function BonusProtestDialog(props: IB diff --git a/src/components/dialogs/CustomizeGameFormatDialog.tsx b/src/components/dialogs/CustomizeGameFormatDialog.tsx index 286f98c..b10146b 100644 --- a/src/components/dialogs/CustomizeGameFormatDialog.tsx +++ b/src/components/dialogs/CustomizeGameFormatDialog.tsx @@ -3,9 +3,6 @@ import { observer } from "mobx-react-lite"; import { IDialogContentProps, DialogType, - IModalProps, - ContextualMenu, - Dialog, DialogFooter, PrimaryButton, DefaultButton, @@ -28,6 +25,7 @@ import { StateContext } from "../../contexts/StateContext"; import { GameFormatPicker } from "../GameFormatPicker"; import { SheetType } from "../../state/SheetState"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; +import { ModalDialog } from "./ModalDialog"; const content: IDialogContentProps = { type: DialogType.normal, @@ -43,24 +41,6 @@ const content: IDialogContentProps = { }, }; -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - styles: { - main: { - // To have max width respected normally, we'd need to pass in an IDialogStyleProps, but it ridiculously - // requires you to pass in an entire theme to modify the max width. We could also use a modal, but that - // requires building much of what Dialogs offer easily (close buttons, footer for buttons) - minWidth: "30vw !important", - }, - }, - topOffsetFixed: true, -}; - const pivotStyles: Partial = { root: { marginBottom: 10, @@ -76,10 +56,10 @@ export const CustomizeGameFormatDialog = observer(function CustomizeGameFormatDi const cancelHandler = React.useCallback(() => CustomizeGameFormatDialogController.cancel(appState), [appState]); return ( - + ); }); diff --git a/src/components/dialogs/ExportToSheetsDialog.tsx b/src/components/dialogs/ExportToSheetsDialog.tsx index 76134d9..4f78cc3 100644 --- a/src/components/dialogs/ExportToSheetsDialog.tsx +++ b/src/components/dialogs/ExportToSheetsDialog.tsx @@ -2,11 +2,6 @@ import * as React from "react"; import { observer } from "mobx-react-lite"; import { TextField, - IDialogContentProps, - DialogType, - IModalProps, - ContextualMenu, - Dialog, DialogFooter, PrimaryButton, DefaultButton, @@ -28,29 +23,7 @@ import { AppState } from "../../state/AppState"; import { StateContext } from "../../contexts/StateContext"; import { RoundSelector } from "../RoundSelector"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Export to Sheets", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; const warningIconStyles: IIconStyles = { root: { @@ -110,16 +83,15 @@ export const ExportToSheetsDialog = observer(function ExportToSheetsDialog(): JS } return ( - + ); }); diff --git a/src/components/dialogs/FontDialog.tsx b/src/components/dialogs/FontDialog.tsx index 5231040..bb44657 100644 --- a/src/components/dialogs/FontDialog.tsx +++ b/src/components/dialogs/FontDialog.tsx @@ -1,14 +1,9 @@ import React from "react"; import { observer } from "mobx-react-lite"; import { - Dialog, DialogFooter, PrimaryButton, DefaultButton, - ContextualMenu, - DialogType, - IDialogContentProps, - IModalProps, SpinButton, Dropdown, IDropdownOption, @@ -24,29 +19,7 @@ import { AppState } from "../../state/AppState"; import { StateContext } from "../../contexts/StateContext"; import { FontDialogState } from "../../state/FontDialogState"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Font", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; const defaultFont = "Segoe UI"; @@ -73,13 +46,10 @@ const maximumFontSize = 40; const stackTokens: Partial = { childrenGap: 10 }; export const FontDialog = observer(function FontDialog(): JSX.Element { - const appState: AppState = React.useContext(StateContext); - return ( - + ); }); @@ -155,6 +125,11 @@ const FontDialogBody = observer(function FontDialogBody(): JSX.Element { text: "Burgundy", data: "#770077", }, + { + key: "black", + text: "Black", + data: "#000000", + }, { key: "darkGray", text: "Dark Gray", diff --git a/src/components/dialogs/HelpDialog.tsx b/src/components/dialogs/HelpDialog.tsx index 93a5b8b..75b2305 100644 --- a/src/components/dialogs/HelpDialog.tsx +++ b/src/components/dialogs/HelpDialog.tsx @@ -1,54 +1,19 @@ import React from "react"; import { observer } from "mobx-react-lite"; -import { - Dialog, - DialogFooter, - PrimaryButton, - ContextualMenu, - DialogType, - IDialogContentProps, - IModalProps, - Label, - Link, - Stack, - StackItem, -} from "@fluentui/react"; +import { DialogFooter, PrimaryButton, Label, Link, Stack, StackItem } from "@fluentui/react"; import { AppState } from "../../state/AppState"; import { StateContext } from "../../contexts/StateContext"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Help", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; export const HelpDialog = observer(function HelpDialog(): JSX.Element { const appState: AppState = React.useContext(StateContext); const closeHandler = React.useCallback(() => hideDialog(appState), [appState]); return ( - + ); }); diff --git a/src/components/dialogs/ImportGameDialog.tsx b/src/components/dialogs/ImportGameDialog.tsx index 37390a2..307d3d2 100644 --- a/src/components/dialogs/ImportGameDialog.tsx +++ b/src/components/dialogs/ImportGameDialog.tsx @@ -1,15 +1,10 @@ import React from "react"; import { observer } from "mobx-react-lite"; import { - Dialog, DialogFooter, PrimaryButton, DefaultButton, Label, - ContextualMenu, - DialogType, - IDialogContentProps, - IModalProps, Stack, ILabelStyles, StackItem, @@ -29,29 +24,7 @@ import { Cycle, ICycle } from "../../state/Cycle"; import { IPendingNewGame, PendingGameType } from "../../state/IPendingNewGame"; import { StateContext } from "../../contexts/StateContext"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const content: IDialogContentProps = { - type: DialogType.normal, - title: "Import Game", - closeButtonAriaLabel: "Close", - showCloseButton: true, - styles: { - innerContent: { - display: "flex", - flexDirection: "column", - }, - }, -}; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; const stackTokens: IStackTokens = { childrenGap: 10 }; @@ -61,18 +34,13 @@ export const ImportGameDialog = observer(function ImportGameDialog(): JSX.Elemen const submitHandler = React.useCallback(() => onSubmit(appState), [appState]); return ( - + ); }); diff --git a/src/components/dialogs/MessageDialog.tsx b/src/components/dialogs/MessageDialog.tsx index 5913f79..727fd5e 100644 --- a/src/components/dialogs/MessageDialog.tsx +++ b/src/components/dialogs/MessageDialog.tsx @@ -1,31 +1,12 @@ import React from "react"; import { observer } from "mobx-react-lite"; -import { - Dialog, - DialogFooter, - PrimaryButton, - ContextualMenu, - DialogType, - IDialogContentProps, - IModalProps, - Label, - DefaultButton, -} from "@fluentui/react"; +import { DialogFooter, PrimaryButton, Label, DefaultButton } from "@fluentui/react"; import { AppState } from "../../state/AppState"; import { StateContext } from "../../contexts/StateContext"; import { IMessageDialogState, MessageDialogType } from "../../state/IMessageDialogState"; import { ModalVisibilityStatus } from "../../state/ModalVisibilityStatus"; - -const modalProps: IModalProps = { - isBlocking: false, - dragOptions: { - moveMenuItemText: "Move", - closeMenuItemText: "Close", - menu: ContextualMenu, - }, - topOffsetFixed: true, -}; +import { ModalDialog } from "./ModalDialog"; export const MessageDialog = observer(function MessageDialog(): JSX.Element { const appState: AppState = React.useContext(StateContext); @@ -45,13 +26,6 @@ export const MessageDialog = observer(function MessageDialog(): JSX.Element { hideDialog(appState); }; - const dialogContent: IDialogContentProps = { - type: DialogType.normal, - title: messageDialog.title, - closeButtonAriaLabel: "Close", - showCloseButton: true, - }; - const noButton: JSX.Element | undefined = messageDialog.type === MessageDialogType.YesNocCancel ? (