Merge branch 'staging' into transformers

This commit is contained in:
Justin Edmund 2023-07-06 22:35:56 -07:00
commit 43ccb464b1
40 changed files with 666 additions and 289 deletions

View file

@ -26,7 +26,7 @@ const AboutHead = ({ page }: Props) => {
name="description"
content={t(`page.descriptions.${currentPage}`)}
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
{/* OpenGraph */}

View file

@ -13,6 +13,14 @@
color: inherit;
z-index: 10;
@include breakpoint(phone) {
place-items: flex-end;
overflow-y: hidden;
&.filter {
}
}
.dialogContent {
$multiplier: 4;
@ -51,11 +59,11 @@
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
min-width: inherit;
min-height: 90vh;
min-height: inherit;
transform: initial;
left: 0;
right: 0;
top: 5vh;
top: $unit-10x;
height: auto;
width: 100%;
}
@ -101,110 +109,6 @@
overflow-y: auto;
}
}
&.Conflict {
$weapon-diameter: 14rem;
.Content {
display: flex;
flex-direction: column;
gap: $unit-4x;
padding: $unit-4x $unit-4x $unit-2x $unit-4x;
& > p {
font-size: $font-regular;
line-height: 1.4;
strong {
font-weight: $bold;
}
&:lang(ja) {
line-height: 1.4;
}
}
}
.weapon,
.character {
display: flex;
flex-direction: column;
gap: $unit;
text-align: center;
width: $weapon-diameter;
font-weight: $medium;
img {
border-radius: 1rem;
width: $weapon-diameter;
height: auto;
}
span {
line-height: 1.3;
}
}
.Diagram {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: flex-start;
&.CharacterDiagram {
align-items: center;
}
ul {
align-items: center;
display: flex;
flex-direction: column;
gap: $unit-2x;
}
.wrapper {
display: flex;
justify-content: center;
width: 100%;
}
.arrow {
align-items: center;
color: $grey-55;
display: flex;
font-size: 4rem;
text-align: center;
height: $weapon-diameter;
justify-content: center;
}
}
footer {
display: flex;
flex-direction: row;
gap: $unit;
.Button {
font-size: $font-regular;
padding: ($unit * 1.5) ($unit * 2);
width: 100%;
&.btn-disabled {
background: $grey-90;
color: $grey-70;
cursor: not-allowed;
}
&:not(.btn-disabled) {
background: $grey-90;
color: $grey-50;
&:hover {
background: $grey-80;
}
}
}
}
}
}
@keyframes openModalDesktop {
@ -221,11 +125,20 @@
@keyframes slideUp {
0% {
transform: translate(0%, 100%);
transform: translateY(400px);
animation-timing-function: ease-out;
}
60% {
transform: translateY(-30px);
animation-timing-function: ease-in;
}
80% {
transform: translateY(10px);
animation-timing-function: ease-out;
}
100% {
transform: translate(0, 0%);
transform: translateY(0px);
animation-timing-function: ease-in;
}
}
}

View file

@ -11,6 +11,7 @@ interface Props
React.DialogHTMLAttributes<HTMLDivElement>,
HTMLDivElement
> {
wrapperClassName?: string
headerref?: React.RefObject<HTMLDivElement>
footerref?: React.RefObject<HTMLDivElement>
scrollable?: boolean
@ -127,7 +128,16 @@ const DialogContent = React.forwardRef<HTMLDivElement, Props>(function Dialog(
return (
<DialogPrimitive.Portal>
<dialog className={styles.dialog}>
<dialog
className={classNames(
{
[styles.dialog]: true,
},
props.wrapperClassName
?.split(' ')
.map((className) => styles[className])
)}
>
<DialogPrimitive.Content
{...props}
className={classes}

View file

@ -16,4 +16,11 @@
display: flex;
gap: $unit;
}
@include breakpoint(phone) {
position: fixed;
bottom: $unit-14x;
left: 0;
right: 0;
}
}

View file

@ -36,6 +36,14 @@
outline: none;
}
p.empty:first-child::before {
color: var(--text-tertiary);
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
&.bound {
background-color: var(--input-bound-bg);
@ -61,6 +69,45 @@
font-style: italic;
}
ul {
padding: 0 $unit-2x;
list-style-type: disc;
}
ol {
padding: 0 $unit-2x;
list-style-type: decimal;
}
h1 {
font-size: $font-xlarge;
font-weight: $medium;
margin: $unit-2x 0;
text-align: left;
}
h2 {
font-size: $font-large;
font-weight: $medium;
margin: $unit-2x 0;
text-align: left;
}
h3 {
font-size: $font-regular;
font-weight: $medium;
margin: $unit-2x 0;
text-align: left;
}
mark {
border-radius: $item-corner-small;
background: var(--highlight-bg);
color: var(--highlight-text);
font-weight: $normal;
padding: 1px $unit-fourth;
}
iframe {
background: var(--input-bound-bg);
border-radius: $card-corner;
@ -78,15 +125,18 @@
.mention {
border-radius: $item-corner-small;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px rgba(0, 0, 0, 0.25);
background: var(--card-bg);
0 1px 0px var(--null-shadow);
background: var(--null-bg);
color: var(--text-primary);
font-weight: $medium;
font-size: 15px;
padding: 1px $unit-half;
transition: all 0.1s ease-out;
&:hover {
background: var(--card-bg-hover);
background: var(--null-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--null-shadow-hover);
text-decoration: none;
cursor: pointer;
}
@ -98,6 +148,7 @@
color: var(--fire-text);
&:hover {
background: var(--fire-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--fire-shadow-hover);
color: var(--fire-text-hover);
@ -111,6 +162,7 @@
color: var(--water-text);
&:hover {
background: var(--water-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--water-shadow-hover);
color: var(--water-text-hover);
@ -124,6 +176,7 @@
color: var(--earth-text);
&:hover {
background: var(--earth-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--earth-shadow-hover);
color: var(--earth-text-hover);
@ -150,6 +203,7 @@
color: var(--dark-text);
&:hover {
background: var(--dark-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--dark-shadow-hover);
color: var(--dark-text-hover);
@ -163,6 +217,7 @@
color: var(--light-text);
&:hover {
background: var(--light-bg-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 0px var(--light-shadow-hover);
color: var(--light-text-hover);
@ -180,26 +235,6 @@
padding: $unit;
z-index: 10;
button {
background: var(--toolbar-item-bg);
border-radius: $bubble-menu-item-corner;
color: var(--toolbar-item-text);
font-weight: $medium;
font-size: $font-small;
padding: $unit-half $unit;
&:hover {
background: var(--toolbar-item-bg-hover);
color: var(--toolbar-item-text-hover);
cursor: pointer;
}
&.active {
background: var(--toolbar-item-bg-active);
color: var(--toolbar-item-text-active);
}
}
.divider {
background: var(--toolbar-divider-bg);
border-radius: $full-corner;

View file

@ -1,15 +1,32 @@
import { ComponentProps, useCallback } from 'react'
import { useRouter } from 'next/router'
import { ComponentProps, useCallback, useEffect } from 'react'
import { useEditor, EditorContent } from '@tiptap/react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link'
import Highlight from '@tiptap/extension-highlight'
import Placeholder from '@tiptap/extension-placeholder'
import Typography from '@tiptap/extension-typography'
import Youtube from '@tiptap/extension-youtube'
import CustomMention from '~extensions/CustomMention'
import classNames from 'classnames'
import { mentionSuggestionOptions } from '~utils/mentionSuggestions'
import type { JSONContent } from '@tiptap/core'
import ToolbarButton from '~components/common/ToolbarButton'
import BoldIcon from 'remixicon-react/BoldIcon'
import ItalicIcon from 'remixicon-react/ItalicIcon'
import StrikethroughIcon from 'remixicon-react/StrikethroughIcon'
import UnorderedListIcon from 'remixicon-react/ListUnorderedIcon'
import OrderedListIcon from '~public/icons/remix/list-ordered-2.svg'
import PaintbrushIcon from 'remixicon-react/PaintBrushLineIcon'
import H1Icon from 'remixicon-react/H1Icon'
import H2Icon from 'remixicon-react/H2Icon'
import H3Icon from 'remixicon-react/H3Icon'
import LinkIcon from 'remixicon-react/LinkIcon'
import YoutubeIcon from 'remixicon-react/YoutubeLineIcon'
import styles from './index.module.scss'
interface Props extends ComponentProps<'div'> {
@ -27,9 +44,68 @@ const Editor = ({
onUpdate,
...props
}: Props) => {
// Hooks: Router
const router = useRouter()
const locale = router.locale || 'en'
const { t } = useTranslation('common')
useEffect(() => {
editor?.commands.setContent(formatContent(content))
}, [content])
// Setup: Editor
const editor = useEditor({
content: formatContent(content),
editable: editable,
editorProps: {
attributes: {
class: classNames(
{
[styles.editor]: true,
[styles.bound]: bound,
},
className?.split(' ').map((c) => styles[c])
),
},
},
extensions: [
StarterKit.configure({
heading: {
levels: [1, 2, 3],
},
}),
Link,
Highlight,
Placeholder.configure({
emptyEditorClass: styles.empty,
placeholder: t('modals.edit_team.placeholders.description'),
}),
Typography,
CustomMention.configure({
renderLabel({ options, node }) {
return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}`
},
suggestion: mentionSuggestionOptions,
HTMLAttributes: {
class: classNames({
[styles.mention]: true,
}),
},
}),
Youtube.configure({
inline: false,
modestBranding: true,
interfaceLanguage: locale,
}),
],
onUpdate: ({ editor }) => {
const json = editor.getJSON()
if (onUpdate) onUpdate(json)
},
})
// Methods: Convenience
function isJSON(content?: string) {
if (!content) return false
@ -59,46 +135,7 @@ const Editor = ({
}
}
const editor = useEditor({
content: formatContent(content),
editable: editable,
editorProps: {
attributes: {
class: classNames(
{
[styles.editor]: true,
[styles.bound]: bound,
},
className?.split(' ').map((c) => styles[c])
),
},
},
extensions: [
StarterKit,
Link,
CustomMention.configure({
renderLabel({ options, node }) {
return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}`
},
suggestion: mentionSuggestionOptions,
HTMLAttributes: {
class: classNames({
[styles.mention]: true,
}),
},
}),
Youtube.configure({
inline: false,
modestBranding: true,
interfaceLanguage: locale,
}),
],
onUpdate: ({ editor }) => {
const json = editor.getJSON()
if (onUpdate) onUpdate(json)
},
})
// Methods: Actions
const setLink = useCallback(() => {
const previousUrl = editor?.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
@ -131,6 +168,7 @@ const Editor = ({
}
}
// Methods: Rendering
if (!editor) {
return null
}
@ -139,32 +177,84 @@ const Editor = ({
<section className={styles.wrapper}>
{editor && editable === true && (
<nav className={styles.toolbar}>
<button
<ToolbarButton
editor={editor}
action="bold"
icon={<BoldIcon />}
onClick={() => editor.chain().focus().toggleBold().run()}
className={editor.isActive('bold') ? styles.active : ''}
>
bold
</button>
<button
/>
<ToolbarButton
editor={editor}
action="italic"
icon={<ItalicIcon />}
onClick={() => editor.chain().focus().toggleItalic().run()}
className={editor.isActive('italic') ? styles.active : ''}
>
italic
</button>
<button
/>
<ToolbarButton
editor={editor}
action="strike"
icon={<StrikethroughIcon />}
onClick={() => editor.chain().focus().toggleStrike().run()}
className={editor.isActive('strike') ? styles.active : ''}
>
strike
</button>
/>
<ToolbarButton
editor={editor}
action="highlight"
icon={<PaintbrushIcon />}
onClick={() => editor.chain().focus().toggleHighlight().run()}
/>
<div className={styles.divider} />
<button
<ToolbarButton
editor={editor}
action="heading"
level={1}
icon={<H1Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
/>
<ToolbarButton
editor={editor}
action="heading"
level={2}
icon={<H2Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 2 }).run()
}
/>
<ToolbarButton
editor={editor}
action="heading"
level={3}
icon={<H3Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 3 }).run()
}
/>
<div className={styles.divider} />
<ToolbarButton
editor={editor}
action="bulletList"
icon={<UnorderedListIcon />}
onClick={() => editor.chain().focus().toggleBulletList().run()}
/>
<ToolbarButton
editor={editor}
action="orderedList"
icon={<OrderedListIcon />}
onClick={() => editor.chain().focus().toggleOrderedList().run()}
/>
<div className={styles.divider} />
<ToolbarButton
editor={editor}
action="link"
icon={<LinkIcon />}
onClick={setLink}
className={editor.isActive('link') ? styles.active : ''}
>
+ link
</button>
<button onClick={addYoutubeVideo}>+ youtube</button>
/>
<ToolbarButton
editor={editor}
action="youtube"
icon={<YoutubeIcon />}
onClick={addYoutubeVideo}
/>
</nav>
)}
<EditorContent editor={editor} />

View file

@ -21,12 +21,6 @@
&.flush {
padding: 0;
}
.Arrow {
fill: var(--dialog-bg);
filter: drop-shadow(0px 1px 1px rgb(0 0 0 / 0.18));
margin-top: -1px;
}
}
.trigger {

View file

@ -0,0 +1,5 @@
.arrow {
fill: var(--dialog-bg);
filter: drop-shadow(0px 1px 1px rgb(0 0 0 / 0.18));
margin-top: -1px;
}

View file

@ -63,6 +63,10 @@
&.table {
min-width: $unit * 30;
@include breakpoint(phone) {
width: 100%;
}
}
&.hidden {

View file

@ -14,6 +14,10 @@
border-radius: 9999px;
height: 3px;
}
&.table {
flex-grow: 1;
}
}
.range {

View file

@ -54,6 +54,7 @@ const SliderTableField = (props: Props) => {
max={props.max}
step={props.step}
value={[props.value ? props.value : 0]}
className="table"
onValueChange={handleValueChange}
onValueCommit={handleValueCommit}
/>

View file

@ -36,6 +36,14 @@
}
}
&.switch {
@include breakpoint(phone) {
align-items: center;
flex-direction: row;
justify-content: space-between;
}
}
.left {
align-items: center;
display: flex;

View file

@ -0,0 +1,36 @@
.button {
background: var(--toolbar-item-bg);
border-radius: $bubble-menu-item-corner;
color: var(--toolbar-item-text);
display: flex;
align-items: center;
justify-content: center;
font-weight: $medium;
font-size: $font-small;
padding: $unit-half;
&:hover {
background: var(--toolbar-item-bg-hover);
color: var(--toolbar-item-text-hover);
cursor: pointer;
svg {
fill: var(--text-primary);
}
}
&.active {
background: var(--toolbar-item-bg-active);
color: var(--toolbar-item-text-active);
svg {
fill: white;
}
}
svg {
fill: var(--text-tertiary);
height: $unit-2x;
width: $unit-2x;
}
}

View file

@ -0,0 +1,41 @@
import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
import { Editor } from '@tiptap/react'
import Tooltip from '~components/common/Tooltip'
import styles from './index.module.scss'
interface Props {
editor: Editor
action: string
level?: number
icon: React.ReactNode
onClick: () => void
}
const ToolbarIcon = ({ editor, action, level, icon, onClick }: Props) => {
const { t } = useTranslation('common')
const classes = classNames({
[styles.button]: true,
[styles.active]: level
? editor.isActive(action, { level: level })
: editor.isActive(action),
})
return (
<Tooltip
content={
level
? t(`toolbar.tooltips.${action}`, { level: level })
: t(`toolbar.tooltips.${action}`)
}
>
<button onClick={onClick} className={classes}>
{icon}
</button>
</Tooltip>
)
}
export default ToolbarIcon

View file

@ -39,5 +39,6 @@
@include breakpoint(phone) {
gap: $unit-4x;
margin-bottom: $unit * 24;
}
}

View file

@ -402,7 +402,8 @@ const FilterModal = (props: Props) => {
<Dialog open={open} onOpenChange={openChange}>
<DialogTrigger asChild>{props.children}</DialogTrigger>
<DialogContent
className="Filter"
className="filter"
wrapperClassName="filter"
headerref={headerRef}
footerref={footerRef}
onEscapeKeyDown={onEscapeKeyDown}

View file

@ -11,7 +11,7 @@ const NewHead = () => {
{/* HTML */}
<title>{t('page.titles.new')}</title>
<meta name="description" content={t('page.descriptions.new')} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
{/* OpenGraph */}

View file

@ -20,7 +20,7 @@ const ProfileHead = ({ user }: Props) => {
username: user.username,
})}
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
{/* OpenGraph */}

View file

@ -9,7 +9,7 @@ const SavedHead = () => {
return (
<Head>
<title>{t('page.titles.saved')}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
<meta property="og:title" content={t('page.titles.saved')} />

View file

@ -11,7 +11,6 @@ const TeamsHead = () => {
{/* HTML */}
<title>{t('page.titles.discover')}</title>
<meta name="description" content={t('page.descriptions.discover')} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
{/* OpenGraph */}

View file

@ -297,7 +297,8 @@ const EditPartyModal = ({
// Methods: Modification checking
function hasBeenModified() {
const nameChanged =
name !== party.name && !(name === '' && party.name === undefined)
name !== party.name &&
!(name === '' && (party.name === undefined || party.name === null))
const descriptionChanged =
description !== party.description &&
!(description === '' && party.description === undefined)
@ -433,7 +434,7 @@ const EditPartyModal = ({
const nameField = (
<Input
name="name"
placeholder="Name your team"
placeholder={t('modals.edit_team.placeholders.name')}
autoFocus={true}
value={name}
maxLength={50}
@ -466,22 +467,12 @@ const EditPartyModal = ({
}
}
const descriptionField = (
<Textarea
className="editParty"
bound={true}
placeholder={t('modals.edit_team.placeholders.description')}
value={description}
onInput={handleTextAreaChanged}
ref={descriptionInput}
/>
)
const editorField = (
<Editor
bound={true}
content={description}
content={props.party?.description}
editable={true}
key={props.party?.shortcode}
onUpdate={handleEditorUpdate}
/>
)

View file

@ -46,6 +46,7 @@ const Party = (props: Props) => {
// Set up states
const { party } = useSnapshot(appState)
const [updatedParty, setUpdatedParty] = useState<Party | undefined>()
const [editable, setEditable] = useState(false)
const [refresh, setRefresh] = useState(false)
const [errorMessage, setErrorMessage] = useState('')
@ -57,7 +58,10 @@ const Party = (props: Props) => {
useEffect(() => {
const resetState = clonedeep(initialAppState)
appState.grid = resetState.grid
if (props.team) storeParty(props.team)
if (props.team) {
storeParty(props.team)
setUpdatedParty(props.team)
}
}, [])
// Subscribe to app state to listen for account changes and
@ -108,9 +112,11 @@ const Party = (props: Props) => {
let payload = {}
if (details) payload = formatDetailsObject(details)
return await api.endpoints.parties
.create(payload)
.then((response) => storeParty(response.data.party))
return await api.endpoints.parties.create(payload).then((response) => {
storeParty(response.data.party)
setUpdatedParty(response.data.party)
return Promise.resolve(response.data.party)
})
}
async function updateParty(details: DetailsObject) {
@ -119,7 +125,11 @@ const Party = (props: Props) => {
if (props.team && props.team.id) {
return await api.endpoints.parties
.update(props.team.id, payload)
.then((response) => storeParty(response.data.party))
.then((response) => {
storeParty(response.data.party)
setUpdatedParty(response.data.party)
return Promise.resolve(response.data.party)
})
.catch((error) => {
const data = error.response.data
if (data.errors && Object.keys(data.errors).includes('guidebooks')) {
@ -438,7 +448,7 @@ const Party = (props: Props) => {
{errorAlert()}
<PartyHeader
party={props.team}
party={updatedParty}
new={props.new || false}
editable={props.new ? true : party.editable}
raidGroups={props.raidGroups}
@ -452,7 +462,7 @@ const Party = (props: Props) => {
<section id="Party">{currentGrid()}</section>
<PartyFooter
party={props.team}
party={updatedParty}
new={props.new || false}
editable={party.editable}
raidGroups={props.raidGroups}

View file

@ -208,10 +208,13 @@ const PartyFooter = (props: Props) => {
const descriptionSection = (
<>
{partySnapshot &&
partySnapshot.description &&
partySnapshot.description.length > 0 && (
<Editor content={appState.party.description} />
{props.party &&
props.party.description &&
props.party.description.length > 0 && (
<Editor
content={props.party.description}
key={props.party?.shortcode}
/>
)}
{(!partySnapshot || !partySnapshot.description) && (
<section className={styles.noDescription}>

View file

@ -32,7 +32,7 @@ const PartyHead = ({ party, meta }: Props) => {
raidName: party.raid ? party.raid.name[locale] : '',
})}
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/images/favicon.png" />
{/* OpenGraph */}

View file

@ -261,9 +261,7 @@ const SummonGrid = (props: Props) => {
try {
if (stage != previousTranscendenceStages[position])
await api.endpoints.grid_summons
.update(id, payload)
.then((response) => {
await api.updateTranscendence('summon', id, stage).then((response) => {
storeGridSummon(response.data.grid_summon)
})
} catch (error) {

View file

@ -1,4 +1,4 @@
.Fragment {
.fragment {
$degrees: 72deg;
$origWidth: 29px;
@ -28,11 +28,11 @@
cursor: pointer;
}
&.Visible {
&.visible {
opacity: 1;
}
&.Stage1 {
&.stage1 {
top: 3px;
left: 18px;
@ -41,7 +41,7 @@
// }
}
&.Stage2 {
&.stage2 {
top: 10px;
left: 27px;
transform: rotate($degrees);
@ -51,7 +51,7 @@
// }
}
&.Stage3 {
&.stage3 {
top: 21px;
left: 24px;
transform: rotate($degrees * 2);
@ -61,7 +61,7 @@
// }
}
&.Stage4 {
&.stage4 {
top: 21px;
left: 12px;
transform: rotate($degrees * 3);
@ -71,7 +71,7 @@
// }
}
&.Stage5 {
&.stage5 {
top: 10px;
left: 8px;
transform: rotate($degrees * 4);

View file

@ -1,5 +1,5 @@
import React from 'react'
import classnames from 'classnames'
import classNames from 'classnames'
import styles from './index.module.scss'
@ -18,14 +18,14 @@ const TranscendenceFragment = ({
onClick,
onHover,
}: Props) => {
const classes = classnames({
Fragment: true,
Visible: visible,
Stage1: stage === 1,
Stage2: stage === 2,
Stage3: stage === 3,
Stage4: stage === 4,
Stage5: stage === 5,
const classes = classNames({
[styles.fragment]: true,
[styles.visible]: visible,
[styles.stage1]: stage === 1,
[styles.stage2]: stage === 2,
[styles.stage3]: stage === 3,
[styles.stage4]: stage === 4,
[styles.stage5]: stage === 5,
})
function handleClick() {

View file

@ -1,11 +1,20 @@
.Transcendence.Popover {
align-items: center;
.transcendence {
display: flex;
flex-direction: column;
gap: $unit-half;
display: flex;
align-items: center;
justify-content: center;
width: $unit-10x;
height: $unit-10x;
justify-content: center;
animation: scaleIn $duration-zoom ease-out;
background: var(--dialog-bg);
border-radius: $card-corner;
border: 0.5px solid rgba(0, 0, 0, 0.18);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.24);
outline: none;
padding: $unit;
transform-origin: var(--radix-popover-content-transform-origin);
z-index: 32;
&.open {
@ -18,7 +27,50 @@
font-weight: $medium;
}
.Pending {
.pending {
color: $yellow;
}
}
@keyframes scaleIn {
0% {
opacity: 0;
transform: scale(0);
}
20% {
opacity: 0.2;
transform: scale(0.4);
}
40% {
opacity: 0.4;
transform: scale(0.8);
}
60% {
opacity: 0.6;
transform: scale(1);
}
65% {
opacity: 0.65;
transform: scale(1.1);
}
70% {
opacity: 0.7;
transform: scale(1);
}
75% {
opacity: 0.75;
transform: scale(0.98);
}
80% {
opacity: 0.8;
transform: scale(1.02);
}
90% {
opacity: 0.9;
transform: scale(0.96);
}
100% {
opacity: 1;
transform: scale(1);
}
}

View file

@ -2,8 +2,8 @@ import React, { PropsWithChildren, useEffect, useState } from 'react'
import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
import { Popover } from '@radix-ui/react-popover'
import {
Popover,
PopoverAnchor,
PopoverContent,
} from '~components/common/PopoverContent'
@ -40,12 +40,8 @@ const TranscendencePopover = ({
const popoverRef = React.createRef<HTMLDivElement>()
const classes = classNames({
Transcendence: true,
})
const levelClasses = classNames({
Pending: stage != currentStage,
[styles.pending]: stage != currentStage,
})
useEffect(() => {
@ -77,16 +73,20 @@ const TranscendencePopover = ({
return (
<Popover open={open} onOpenChange={onOpenChange}>
<PopoverAnchor>{children}</PopoverAnchor>
<PopoverContent className={classes} ref={popoverRef} tabIndex={tabIndex}>
<PopoverContent
className={styles.transcendence}
ref={popoverRef}
tabIndex={tabIndex}
>
<TranscendenceStar
className="Interactive Base"
className="interactive base"
editable={true}
interactive={true}
stage={stage}
onFragmentClick={handleFragmentClicked}
onFragmentHover={handleFragmentHovered}
/>
<h4>
<h4 className="name">
<span>{t('level')}&nbsp;</span>
<span className={levelClasses}>{baseLevel + 10 * currentStage}</span>
</h4>

View file

@ -46,9 +46,12 @@ const TranscendenceStar = ({
[styles.stage5]: stage === 5,
})
const baseImageClasses = classnames(className, {
const baseImageClasses = classnames(
{
[styles.figure]: true,
})
},
className?.split(' ').map((c) => styles[c])
)
useEffect(() => {
setVisibleStage(stage)
@ -87,7 +90,7 @@ const TranscendenceStar = ({
onMouseLeave={interactive ? handleMouseLeave : () => {}}
tabIndex={tabIndex}
>
<div className="Fragments">
<div className={styles.fragments}>
{[...Array(NUM_FRAGMENTS)].map((e, i) => {
const loopStage = i + 1
return interactive ? (

49
package-lock.json generated
View file

@ -20,8 +20,11 @@
"@radix-ui/react-tooltip": "^1.0.3",
"@svgr/webpack": "^6.2.0",
"@tiptap/extension-bubble-menu": "^2.0.3",
"@tiptap/extension-highlight": "^2.0.3",
"@tiptap/extension-link": "^2.0.3",
"@tiptap/extension-mention": "^2.0.3",
"@tiptap/extension-placeholder": "^2.0.3",
"@tiptap/extension-typography": "^2.0.3",
"@tiptap/extension-youtube": "^2.0.3",
"@tiptap/pm": "^2.0.3",
"@tiptap/react": "^2.0.3",
@ -56,6 +59,7 @@
"react-lite-youtube-embed": "^2.3.52",
"react-scroll": "^1.8.5",
"react-string-replace": "^1.1.0",
"remixicon-react": "^1.0.0",
"resolve-url-loader": "^5.0.0",
"sanitize-html": "^2.8.1",
"sass": "^1.61.0",
@ -7229,6 +7233,18 @@
"@tiptap/core": "^2.0.0"
}
},
"node_modules/@tiptap/extension-highlight": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.3.tgz",
"integrity": "sha512-NrtibY8cZkIjZMQuHRrKd4php+plOvAoSo8g3uVFu275I/Ixt5HqJ53R4voCXs8W8BOBRs2HS2QX8Cjh79XhtA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.0.0"
}
},
"node_modules/@tiptap/extension-history": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.3.tgz",
@ -7333,6 +7349,19 @@
"@tiptap/core": "^2.0.0"
}
},
"node_modules/@tiptap/extension-placeholder": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz",
"integrity": "sha512-Z42jo0termRAf0S0L8oxrts94IWX5waU4isS2CUw8xCUigYyCFslkhQXkWATO1qRbjNFLKN2C9qvCgGf4UeBrw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.0.0",
"@tiptap/pm": "^2.0.0"
}
},
"node_modules/@tiptap/extension-strike": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.3.tgz",
@ -7357,6 +7386,18 @@
"@tiptap/core": "^2.0.0"
}
},
"node_modules/@tiptap/extension-typography": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.0.3.tgz",
"integrity": "sha512-5U91O2dffYOvwenWG+zT1N/pnt+RppSlocxs1KaNWFLlI2fgzDTyUyjzygIHGmskStqay2MuvmPnfVABoC+1Gw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.0.0"
}
},
"node_modules/@tiptap/extension-youtube": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@tiptap/extension-youtube/-/extension-youtube-2.0.3.tgz",
@ -19306,6 +19347,14 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/remixicon-react": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/remixicon-react/-/remixicon-react-1.0.0.tgz",
"integrity": "sha512-KOXlc8EdKdujr2f/2idyFSQRjUB8p0HNiWZYBBzRsTRlTXFuSAFfnGq9culNjhCGmc92Jbtfr9OP0MXWvTMdsQ==",
"peerDependencies": {
"react": ">=0.14.0"
}
},
"node_modules/renderkid": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",

View file

@ -27,8 +27,11 @@
"@radix-ui/react-tooltip": "^1.0.3",
"@svgr/webpack": "^6.2.0",
"@tiptap/extension-bubble-menu": "^2.0.3",
"@tiptap/extension-highlight": "^2.0.3",
"@tiptap/extension-link": "^2.0.3",
"@tiptap/extension-mention": "^2.0.3",
"@tiptap/extension-placeholder": "^2.0.3",
"@tiptap/extension-typography": "^2.0.3",
"@tiptap/extension-youtube": "^2.0.3",
"@tiptap/pm": "^2.0.3",
"@tiptap/react": "^2.0.3",
@ -63,6 +66,7 @@
"react-lite-youtube-embed": "^2.3.52",
"react-scroll": "^1.8.5",
"react-string-replace": "^1.1.0",
"remixicon-react": "^1.0.0",
"resolve-url-loader": "^5.0.0",
"sanitize-html": "^2.8.1",
"sass": "^1.61.0",

View file

@ -1,4 +1,5 @@
import { appWithTranslation } from 'next-i18next'
import Head from 'next/head'
import Link from 'next/link'
import { useTranslation } from 'next-i18next'
import { get } from 'local-storage'
@ -131,6 +132,13 @@ function MyApp({ Component, pageProps }: AppProps) {
}
return (
<>
<Head>
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0"
/>
</Head>
<ThemeProvider>
<ToastProvider swipeDirection="right">
<TooltipProvider>
@ -145,6 +153,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</TooltipProvider>
</ToastProvider>
</ThemeProvider>
</>
)
}

View file

@ -90,8 +90,8 @@ const PartyRoute: React.FC<Props> = ({
break
}
if (router.asPath !== '/new' && router.asPath !== '/')
router.replace(path, undefined, { shallow: true })
// if (router.asPath !== '/new' && router.asPath !== '/')
// router.replace(path, undefined, { shallow: true })
}
// Set the initial data from props

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.75024 3.5H4.71733L3.25 3.89317V5.44582L4.25002 5.17782L4.25018 8.5H3V10H7V8.5H5.75024V3.5ZM10 4H21V6H10V4ZM10 11H21V13H10V11ZM10 18H21V20H10V18ZM2.875 15.625C2.875 14.4514 3.82639 13.5 5 13.5C6.17361 13.5 7.125 14.4514 7.125 15.625C7.125 16.1106 6.96183 16.5587 6.68747 16.9167L6.68271 16.9229L5.31587 18.5H7V20H3.00012L2.99959 18.8786L5.4717 16.035C5.5673 15.9252 5.625 15.7821 5.625 15.625C5.625 15.2798 5.34518 15 5 15C4.67378 15 4.40573 15.2501 4.37747 15.5688L4.3651 15.875H2.875V15.625Z"></path></svg>

After

Width:  |  Height:  |  Size: 579 B

View file

@ -557,6 +557,19 @@
"tokens": {
"remix": "Remixed"
},
"toolbar": {
"tooltips": {
"bold": "Bold",
"italic": "Italic",
"strike": "Strikethrough",
"highlight": "Highlight",
"link": "Add a link",
"youtube": "Add a Youtube video",
"heading": "Heading {{level}}",
"bulletList": "Bullet list",
"orderedList": "Numbered list"
}
},
"tooltips": {
"copy_url": "Copy the URL to this team",
"new": "Create a new team",

View file

@ -555,6 +555,19 @@
"tokens": {
"remix": "リミックスされた"
},
"toolbar": {
"tooltips": {
"bold": "太字",
"italic": "斜体",
"strike": "取り消し線",
"highlight": "ハイライト",
"link": "リンクを挿入",
"youtube": "Youtube動画を埋め込む",
"heading": "見出し {{level}}",
"bulletList": "箇条書き",
"orderedList": "番号リスト"
}
},
"tooltips": {
"copy_url": "この編成のURLをコピーする",
"new": "新しい編成を作成する",

View file

@ -63,6 +63,12 @@
--toolbar-item-text-hover: #{$toolbar--item--text--light--hover};
--toolbar-item-text-active: #{$toolbar--item--text--light--active};
// Light - Highlights
--highlight-bg: #{$highlight--bg--light};
--highlight-bg-hover: #{$highlight--bg--light--hover};
--highlight-text: #{$highlight--text--light};
--highlight-text-hover: #{$highlight--text--light--hover};
// Light - Placeholders
--placeholder-bound-bg: #{$placeholder--bound--bg--light};
--placeholder-bound-bg-hover: #{$placeholder--bound--bg--light--hover};
@ -153,6 +159,14 @@
--grid-border-color: #{$grid--border--color--light};
// Light - Element theming
--null-bg: #{$null--bg--light};
--null-bg-hover: #{$null--bg--hover--light};
--null-text: #{$null--text--light};
--null-raid-text: #{$null--text--raid--light};
--null-text-hover: #{$null--text--hover--light};
--null-shadow: #{$null--shadow--light};
--null-shadow-hover: #{$null--shadow--light--hover};
--wind-bg: #{$wind--bg--light};
--wind-bg-hover: #{$wind--bg--hover--light};
--wind-text: #{$wind--text--light};
@ -271,6 +285,12 @@
--toolbar-item-text-hover: #{$toolbar--item--text--dark--hover};
--toolbar-item-text-active: #{$toolbar--item--text--dark--active};
// Dark - Highlights
--highlight-bg: #{$highlight--bg--dark};
--highlight-bg-hover: #{$highlight--bg--dark--hover};
--highlight-text: #{$highlight--text--dark};
--highlight-text-hover: #{$highlight--text--dark--hover};
// Dark - Placeholders
--placeholder-bound-bg: #{$placeholder--bound--bg--dark};
--placeholder-bound-bg-hover: #{$placeholder--bound--bg--dark--hover};
@ -361,6 +381,14 @@
--grid-border-color: #{$grid--border--color--dark};
// Dark - Element theming
--null-bg: #{$null--bg--dark};
--null-bg-hover: #{$null--bg--hover--dark};
--null-text: #{$null--text--dark};
--null-raid-text: #{$null--text--raid--dark};
--null-text-hover: #{$null--text--hover--dark};
--null-shadow: #{$null--shadow--dark};
--null-shadow-hover: #{$null--shadow--dark--hover};
--wind-bg: #{$wind--bg--dark};
--wind-bg-hover: #{$wind--bg--hover--dark};
--wind-text: #{$wind--text--dark};

View file

@ -79,6 +79,13 @@ $orange-75: #ffb461;
$orange-80: #facea7;
$orange-90: #ffebd9;
// Yellow -- Highlights
$yellow-10: #4d3703;
$yellow-30: #956d11;
$yellow-50: #c8a657;
$yellow-70: #fedc8d;
$yellow-90: #ffed4c;
// Colors -- Interface
$blue: #275dc5;
$red: #ff6161;
@ -96,6 +103,7 @@ $accent--yellow--light: #c89d39;
$accent--yellow--dark: #f9cc64;
$yellow-text-10: #a39200;
$yellow-text-20: #ffed4c;
$highlight-yellow: #ffed4c55;
$accent--yellow--00: #463805;
$accent--yellow--20: #7f6a00;
@ -383,6 +391,19 @@ $toolbar--item--text--dark--hover: $grey-90;
$toolbar--item--text--light--active: $grey-100;
$toolbar--item--text--dark--active: $grey-00;
// Color Definitions: Highlights
$highlight--bg--light: $yellow-70;
$highlight--bg--dark: $yellow-50;
$highlight--bg--light--hover: $yellow-50;
$highlight--bg--dark--hover: $yellow-70;
$highlight--text--light: $yellow-30;
$highlight--text--dark: $yellow-10;
$highlight--text--light--hover: $yellow-10;
$highlight--text--dark--hover: $yellow-30;
// Color Definitions: Element Toggle
$toggle--bg--light: $grey-90;
$toggle--bg--dark: $grey-15;
@ -443,6 +464,28 @@ $wind--shadow--dark: fade-out($wind-text-20, 0.3);
$wind--shadow--light--hover: fade-out($wind-text-00, 0.3);
$wind--shadow--dark--hover: fade-out($wind-text-00, 0.3);
// Color Definitions: Element / Null
$null--bg--light: $grey-75;
$null--bg--dark: $grey-40;
$null--bg--hover--light: $grey-70;
$null--bg--hover--dark: $grey-30;
$null--text--light: $grey-40;
$null--text--dark: $grey-90;
$null--text--raid--light: $grey-40;
$null--text--raid--dark: $grey-90;
$null--text--hover--light: $grey-20;
$null--text--hover--dark: $grey-90;
$null--shadow--light: fade-out($grey-60, 0.3);
$null--shadow--dark: fade-out($grey-25, 0.3);
$null--shadow--light--hover: fade-out($grey-50, 0.3);
$null--shadow--dark--hover: fade-out($grey-10, 0.3);
// Color Definitions: Element / Fire
$fire--bg--light: $fire-bg-10;
$fire--bg--dark: $fire-bg-10;

View file

@ -180,6 +180,17 @@ class Api {
})
}
updateTranscendence(resource: 'character'|'summon', id: string, value: number) {
const pluralized = resource + 's'
const resourceUrl = `${this.url}/${pluralized}/update_uncap`
return axios.post(resourceUrl, {
[resource]: {
id: id,
transcendence_step: value
}
})
}
userInfo(id: string) {
const resourceUrl = `${this.url}/users/info/${id}`
return axios.get(resourceUrl)