* Add ellipsis icon * Reduce size of tokens * Move UpdateToast to toasts folder * Update variables.scss * Add reps for grid objects These reps act like the existing PartyRep except for Characters and Summons, as well as a new component just for Weapons. They only render the grid of objects and nothing else. Eventually PartyRep will use WeaponRep * Added RepSegment This is a Character, Weapon or Summon rep wrapped with an input and label for use in a SegmentedControl * Modify PartySegmentedControl to use RepSegments This will not work on mobile yet, where it should gracefully degrade to a normal SegmentedControl with only text * Extract URL copied and Remixed toasts into files * Extract delete team alert into a file Also, to support this: * Added `Destructive` class to Button * Added `primaryActionClassName` prop to Alert * Added an alert for when remixing teams * Began refactoring PartyDetails into several files * PartyHeader will live at the top, above the new segmented control * PartyDetails stays below, only showing remixed teams and the description * PartyDropdown handles the new ... menu * Remove duplicated code This is description and remix code that is still in `PartyDetails` * Small fixes for weapon grid * Add placeholder image for guidebooks * Add localizations * Add Guidebook type and update other types * Update gitignore Don't commit guidebook images * Indicate if a dialog is scrollable We had broken paging in the infinite scroll component. Turning off "scrolling" at the dialog levels fixes it without adding scrollbars in environments that persistently show them * Add ExtraContainer This is the purple container that will contain additional weapons and sephira guidebooks * Move ExtraWeapons to ExtraWeaponsGrid And put it in ExtraContainer * Added GuidebooksGrid and GuidebookUnit These are the display components for Guidebooks in the WeaponGrid * Visual adjustments to summon grid * Add Empty class to weapons when unit is unfilled * Implement GuidebooksGrid in WeaponGrid * Remove extra switch * Remove old dependencies and props * Implement searching for/adding guidebooks to party * Update styles * Fix dependency * Properly determine when extra container should display * Change to 1-indexing for guidebooks * Add support for removing guidebooks * Display guidebook validation error * Move read only buttons to PartyHeader Also broke up tokens and made them easier to render * Add guidebooks to DetailsObject * Remove preview when on mobile sizes
197 lines
5.3 KiB
TypeScript
197 lines
5.3 KiB
TypeScript
// Libraries
|
|
import React, { useEffect, useState } from 'react'
|
|
import { useRouter } from 'next/router'
|
|
import { subscribe, useSnapshot } from 'valtio'
|
|
import { Trans, useTranslation } from 'next-i18next'
|
|
import Link from 'next/link'
|
|
import classNames from 'classnames'
|
|
|
|
// Dependencies: Common
|
|
import Button from '~components/common/Button'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuTrigger,
|
|
DropdownMenuContent,
|
|
DropdownMenuGroup,
|
|
DropdownMenuItem,
|
|
} from '~components/common/DropdownMenuContent'
|
|
|
|
// Dependencies: Toasts
|
|
import RemixedToast from '~components/toasts/RemixedToast'
|
|
import UrlCopiedToast from '~components/toasts/UrlCopiedToast'
|
|
|
|
// Dependencies: Alerts
|
|
import DeleteTeamAlert from '~components/dialogs/DeleteTeamAlert'
|
|
import RemixTeamAlert from '~components/dialogs/RemixTeamAlert'
|
|
|
|
// Dependencies: Utils
|
|
import api from '~utils/api'
|
|
import { accountState } from '~utils/accountState'
|
|
import { appState } from '~utils/appState'
|
|
import { getLocalId } from '~utils/localId'
|
|
import { retrieveLocaleCookies } from '~utils/retrieveCookies'
|
|
import { setEditKey, storeEditKey } from '~utils/userToken'
|
|
|
|
// Dependencies: Icons
|
|
import EllipsisIcon from '~public/icons/Ellipsis.svg'
|
|
|
|
// Dependencies: Props
|
|
interface Props {
|
|
editable: boolean
|
|
deleteTeamCallback: () => void
|
|
remixTeamCallback: () => void
|
|
}
|
|
|
|
const PartyDropdown = ({
|
|
editable,
|
|
deleteTeamCallback,
|
|
remixTeamCallback,
|
|
}: Props) => {
|
|
// Localization
|
|
const { t } = useTranslation('common')
|
|
|
|
// Router
|
|
const router = useRouter()
|
|
const locale =
|
|
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
const localeData = retrieveLocaleCookies()
|
|
|
|
const [open, setOpen] = useState(false)
|
|
|
|
const [deleteAlertOpen, setDeleteAlertOpen] = useState(false)
|
|
const [remixAlertOpen, setRemixAlertOpen] = useState(false)
|
|
|
|
const [copyToastOpen, setCopyToastOpen] = useState(false)
|
|
const [remixToastOpen, setRemixToastOpen] = useState(false)
|
|
|
|
const [name, setName] = useState('')
|
|
const [originalName, setOriginalName] = useState('')
|
|
|
|
// Snapshots
|
|
const { account } = useSnapshot(accountState)
|
|
const { party: partySnapshot } = useSnapshot(appState)
|
|
|
|
// Subscribe to app state to listen for party name and
|
|
// unsubscribe when component is unmounted
|
|
const unsubscribe = subscribe(appState, () => {
|
|
const newName =
|
|
appState.party && appState.party.name ? appState.party.name : ''
|
|
setName(newName)
|
|
})
|
|
|
|
useEffect(() => () => unsubscribe(), [])
|
|
|
|
// Methods: Event handlers (Buttons)
|
|
function handleButtonClicked() {
|
|
setOpen(!open)
|
|
}
|
|
|
|
// Methods: Event handlers (Menus)
|
|
function handleOpenChange(open: boolean) {
|
|
setOpen(open)
|
|
}
|
|
|
|
function closeMenu() {
|
|
setOpen(false)
|
|
}
|
|
|
|
// Method: Actions
|
|
function copyToClipboard() {
|
|
if (router.asPath.split('/')[1] === 'p') {
|
|
navigator.clipboard.writeText(window.location.href)
|
|
setCopyToastOpen(true)
|
|
}
|
|
}
|
|
|
|
// Methods: Event handlers
|
|
|
|
// Alerts / Delete team
|
|
function openDeleteTeamAlert() {
|
|
setDeleteAlertOpen(true)
|
|
}
|
|
|
|
function handleDeleteTeamAlertChange(open: boolean) {
|
|
setDeleteAlertOpen(open)
|
|
}
|
|
|
|
// Alerts / Remix team
|
|
function openRemixTeamAlert() {
|
|
setRemixAlertOpen(true)
|
|
}
|
|
|
|
function handleRemixTeamAlertChange(open: boolean) {
|
|
setRemixAlertOpen(open)
|
|
}
|
|
|
|
// Toasts / Copy URL
|
|
function handleCopyToastOpenChanged(open: boolean) {
|
|
setCopyToastOpen(open)
|
|
}
|
|
|
|
function handleCopyToastCloseClicked() {
|
|
setCopyToastOpen(false)
|
|
}
|
|
|
|
// Toasts / Remix team
|
|
function handleRemixToastOpenChanged(open: boolean) {
|
|
setRemixToastOpen(open)
|
|
}
|
|
|
|
function handleRemixToastCloseClicked() {
|
|
setRemixToastOpen(false)
|
|
}
|
|
|
|
const editableItems = () => {
|
|
return (
|
|
<>
|
|
<DropdownMenuGroup className="MenuGroup">
|
|
<DropdownMenuItem className="MenuItem" onClick={copyToClipboard}>
|
|
<span>Copy link to team</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="MenuItem" onClick={openRemixTeamAlert}>
|
|
<span>Remix team</span>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuGroup>
|
|
<DropdownMenuGroup className="MenuGroup">
|
|
<DropdownMenuItem className="MenuItem" onClick={openDeleteTeamAlert}>
|
|
<span className="destructive">Delete team</span>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuGroup>
|
|
</>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div id="DropdownWrapper">
|
|
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
leftAccessoryIcon={<EllipsisIcon />}
|
|
className={classNames({ Active: open })}
|
|
blended={true}
|
|
onClick={handleButtonClicked}
|
|
/>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent>{editableItems()}</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
|
|
<DeleteTeamAlert
|
|
open={deleteAlertOpen}
|
|
onOpenChange={handleDeleteTeamAlertChange}
|
|
deleteCallback={deleteTeamCallback}
|
|
/>
|
|
|
|
<RemixTeamAlert
|
|
creator={editable}
|
|
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
|
|
open={remixAlertOpen}
|
|
onOpenChange={handleRemixTeamAlertChange}
|
|
remixCallback={remixTeamCallback}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default PartyDropdown
|