From 76aadfbc1547015587e8e2952e7f8235c679d0cd Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 19 Apr 2023 00:27:39 -0700 Subject: [PATCH] Added GuidebooksGrid and GuidebookUnit These are the display components for Guidebooks in the WeaponGrid --- components/extra/GuidebookUnit/index.scss | 109 +++++++++++ components/extra/GuidebookUnit/index.tsx | 201 +++++++++++++++++++++ components/extra/GuidebooksGrid/index.scss | 45 +++++ components/extra/GuidebooksGrid/index.tsx | 95 ++++++++++ 4 files changed, 450 insertions(+) create mode 100644 components/extra/GuidebookUnit/index.scss create mode 100644 components/extra/GuidebookUnit/index.tsx create mode 100644 components/extra/GuidebooksGrid/index.scss create mode 100644 components/extra/GuidebooksGrid/index.tsx diff --git a/components/extra/GuidebookUnit/index.scss b/components/extra/GuidebookUnit/index.scss new file mode 100644 index 00000000..9c6f489d --- /dev/null +++ b/components/extra/GuidebookUnit/index.scss @@ -0,0 +1,109 @@ +.GuidebookUnit { + align-items: center; + display: flex; + flex-direction: column; + gap: $unit-half; + position: relative; + width: 100%; + height: auto; + z-index: 0; + + @include breakpoint(tablet) { + min-height: auto; + } + + .Button { + pointer-events: none; + opacity: 0; + z-index: 10; + } + + &:hover .Button, + .Button.Clicked { + pointer-events: initial; + opacity: 1; + } + + &.editable .GuidebookImage:hover { + border: $hover-stroke; + box-shadow: $hover-shadow; + cursor: pointer; + transform: $scale-wide; + } + + &.empty { + min-height: auto; + } + + &.filled h3 { + display: block; + } + + &.filled ul { + display: flex; + } + + & h3, + & ul { + display: none; + } + + h3 { + color: var(--text-primary); + font-size: $font-button; + font-weight: $normal; + line-height: 1.1; + margin: 0; + text-align: center; + } + + .GuidebookImage { + background: var(--extra-purple-card-bg); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: $unit; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: calc($unit / 4); + overflow: hidden; + position: relative; + transition: $duration-zoom all ease-in-out; + + img { + position: relative; + width: 100%; + z-index: 2; + + &.Placeholder { + opacity: 0; + } + } + + .icon { + position: absolute; + height: $unit * 3; + width: $unit * 3; + z-index: 1; + + svg { + transition: $duration-color-fade fill ease-in-out; + fill: var(--extra-purple-secondary); + } + } + } + + .GuidebookName { + font-size: $font-name; + line-height: 1.2; + + @include breakpoint(phone) { + font-size: $font-tiny; + } + } + + .GuidebookDescription { + font-size: $font-small; + line-height: 1.2; + text-align: center; + } +} diff --git a/components/extra/GuidebookUnit/index.tsx b/components/extra/GuidebookUnit/index.tsx new file mode 100644 index 00000000..bb1fc38f --- /dev/null +++ b/components/extra/GuidebookUnit/index.tsx @@ -0,0 +1,201 @@ +import React, { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { Trans, useTranslation } from 'next-i18next' +import classNames from 'classnames' + +import Alert from '~components/common/Alert' +import SearchModal from '~components/search/SearchModal' +import { + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, +} from '~components/common/ContextMenu' +import ContextMenuItem from '~components/common/ContextMenuItem' +import Button from '~components/common/Button' + +import type { SearchableObject } from '~types' + +import PlusIcon from '~public/icons/Add.svg' +import SettingsIcon from '~public/icons/Settings.svg' +import './index.scss' + +interface Props { + guidebook: Guidebook | undefined + position: number + editable: boolean + removeGuidebook: (id: string) => void + updateObject: (object: SearchableObject, position: number) => void +} + +const GuidebookUnit = ({ + guidebook, + position, + editable, + removeGuidebook: sendGuidebookToRemove, + updateObject, +}: Props) => { + // Translations and locale + const { t } = useTranslation('common') + const router = useRouter() + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + + // State: UI + const [searchModalOpen, setSearchModalOpen] = useState(false) + const [contextMenuOpen, setContextMenuOpen] = useState(false) + const [alertOpen, setAlertOpen] = useState(false) + + // State: Other + const [imageUrl, setImageUrl] = useState('') + + // Classes + const classes = classNames({ + GuidebookUnit: true, + editable: editable, + filled: guidebook !== undefined, + empty: guidebook == undefined, + }) + + const buttonClasses = classNames({ + Options: true, + Clicked: contextMenuOpen, + }) + + // Hooks + useEffect(() => { + generateImageUrl() + }, [guidebook]) + + // Methods: Open layer + function openSearchModal() { + if (editable) setSearchModalOpen(true) + } + + function openRemoveGuidebookAlert() { + setAlertOpen(true) + } + + // Methods: Handle button clicked + function handleButtonClicked() { + setContextMenuOpen(!contextMenuOpen) + } + + // Methods: Handle open change + function handleContextMenuOpenChange(open: boolean) { + if (!open) setContextMenuOpen(false) + } + + function handleSearchModalOpenChange(open: boolean) { + setSearchModalOpen(open) + } + + // Methods: Mutate data + function removeGuidebook() { + if (guidebook) sendGuidebookToRemove(guidebook.id) + setAlertOpen(false) + } + + // Methods: Image string generation + function generateImageUrl() { + let imgSrc = guidebook + ? `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/guidebooks/book_${guidebook.granblue_id}.png` + : '' + + setImageUrl(imgSrc) + } + + const placeholderImageUrl = '/images/placeholders/placeholder-guidebook.png' + + // Methods: Layer element rendering + const contextMenu = () => { + if (editable && guidebook) { + return ( + <> + + +