-
{name ? name : t('no_title')}
+
+
+ {name ? name : t('no_title')}
+
+ {party.remix && party.sourceParty ? (
+
+ }
+ onClick={() => goTo(party.sourceParty?.shortcode)}
+ />
+
+ ) : (
+ ''
+ )}
+
{renderUserBlock()}
{party.raid ? linkedRaidBlock(party.raid) : ''}
@@ -654,6 +750,7 @@ const PartyDetails = (props: Props) => {
{readOnly}
{editable}
+ {remixes && remixes.length > 0 ? remixSection() : ''}
{deleteAlert()}
)
diff --git a/components/Tooltip/index.scss b/components/Tooltip/index.scss
new file mode 100644
index 00000000..3a4b325d
--- /dev/null
+++ b/components/Tooltip/index.scss
@@ -0,0 +1,8 @@
+.Tooltip {
+ background: var(--dialog-bg);
+ border-radius: $card-corner;
+ line-height: 1.3;
+ padding: $unit * 1.5;
+ z-index: 35;
+ max-width: 200px;
+}
diff --git a/components/Tooltip/index.tsx b/components/Tooltip/index.tsx
new file mode 100644
index 00000000..a894886c
--- /dev/null
+++ b/components/Tooltip/index.tsx
@@ -0,0 +1,39 @@
+import React, { PropsWithChildren } from 'react'
+import classNames from 'classnames'
+
+import * as TooltipPrimitive from '@radix-ui/react-tooltip'
+
+import './index.scss'
+interface Props extends TooltipPrimitive.TooltipContentProps {
+ content: React.ReactNode
+ open?: boolean
+ onOpenChange?: (open: boolean) => void
+}
+
+export default function Tooltip({
+ children,
+ content,
+ open,
+ onOpenChange,
+ ...props
+}: PropsWithChildren
) {
+ const classes = classNames(props.className, {
+ Tooltip: true,
+ })
+
+ return (
+
+ {children}
+
+ {content}
+ {/* */}
+
+
+ )
+}
diff --git a/package-lock.json b/package-lock.json
index c7f624c5..754af5dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-toast": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.0.1",
+ "@radix-ui/react-tooltip": "^1.0.3",
"@svgr/webpack": "^6.2.0",
"axios": "^0.25.0",
"classnames": "^2.3.1",
@@ -2623,6 +2624,52 @@
"react-dom": "^16.8 || ^17.0 || ^18.0"
}
},
+ "node_modules/@radix-ui/react-tooltip": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.3.tgz",
+ "integrity": "sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.0",
+ "@radix-ui/react-compose-refs": "1.0.0",
+ "@radix-ui/react-context": "1.0.0",
+ "@radix-ui/react-dismissable-layer": "1.0.2",
+ "@radix-ui/react-id": "1.0.0",
+ "@radix-ui/react-popper": "1.1.0",
+ "@radix-ui/react-portal": "1.0.1",
+ "@radix-ui/react-presence": "1.0.0",
+ "@radix-ui/react-primitive": "1.0.1",
+ "@radix-ui/react-slot": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.0",
+ "@radix-ui/react-visually-hidden": "1.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
+ "integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@floating-ui/react-dom": "0.7.2",
+ "@radix-ui/react-arrow": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.0",
+ "@radix-ui/react-context": "1.0.0",
+ "@radix-ui/react-primitive": "1.0.1",
+ "@radix-ui/react-use-callback-ref": "1.0.0",
+ "@radix-ui/react-use-layout-effect": "1.0.0",
+ "@radix-ui/react-use-rect": "1.0.0",
+ "@radix-ui/react-use-size": "1.0.0",
+ "@radix-ui/rect": "1.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ }
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz",
@@ -9156,6 +9203,46 @@
"@radix-ui/react-use-controllable-state": "1.0.0"
}
},
+ "@radix-ui/react-tooltip": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.3.tgz",
+ "integrity": "sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ==",
+ "requires": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.0",
+ "@radix-ui/react-compose-refs": "1.0.0",
+ "@radix-ui/react-context": "1.0.0",
+ "@radix-ui/react-dismissable-layer": "1.0.2",
+ "@radix-ui/react-id": "1.0.0",
+ "@radix-ui/react-popper": "1.1.0",
+ "@radix-ui/react-portal": "1.0.1",
+ "@radix-ui/react-presence": "1.0.0",
+ "@radix-ui/react-primitive": "1.0.1",
+ "@radix-ui/react-slot": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.0",
+ "@radix-ui/react-visually-hidden": "1.0.1"
+ },
+ "dependencies": {
+ "@radix-ui/react-popper": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
+ "integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
+ "requires": {
+ "@babel/runtime": "^7.13.10",
+ "@floating-ui/react-dom": "0.7.2",
+ "@radix-ui/react-arrow": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.0",
+ "@radix-ui/react-context": "1.0.0",
+ "@radix-ui/react-primitive": "1.0.1",
+ "@radix-ui/react-use-callback-ref": "1.0.0",
+ "@radix-ui/react-use-layout-effect": "1.0.0",
+ "@radix-ui/react-use-rect": "1.0.0",
+ "@radix-ui/react-use-size": "1.0.0",
+ "@radix-ui/rect": "1.0.0"
+ }
+ }
+ }
+ },
"@radix-ui/react-use-callback-ref": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz",
diff --git a/package.json b/package.json
index 2f33a3b2..ac4d8332 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-toast": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.0.1",
+ "@radix-ui/react-tooltip": "^1.0.3",
"@svgr/webpack": "^6.2.0",
"axios": "^0.25.0",
"classnames": "^2.3.1",
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 1bf22868..498851d9 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -11,6 +11,7 @@ import setUserToken from '~utils/setUserToken'
import '../styles/globals.scss'
import { ToastProvider, Viewport } from '@radix-ui/react-toast'
+import { TooltipProvider } from '@radix-ui/react-tooltip'
function MyApp({ Component, pageProps }: AppProps) {
const accountCookie = getCookie('account')
@@ -45,10 +46,12 @@ function MyApp({ Component, pageProps }: AppProps) {
return (
-
-
-
-
+
+
+
+
+
+
)
diff --git a/pages/new/index.tsx b/pages/new/index.tsx
index 650448bb..b3c2c225 100644
--- a/pages/new/index.tsx
+++ b/pages/new/index.tsx
@@ -1,7 +1,9 @@
import React, { useEffect } from 'react'
+import { useRouter } from 'next/router'
import Head from 'next/head'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
+import clonedeep from 'lodash.clonedeep'
import Party from '~components/Party'
@@ -9,7 +11,7 @@ import api from '~utils/api'
import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids'
import setUserToken from '~utils/setUserToken'
-import { appState } from '~utils/appState'
+import { appState, initialAppState } from '~utils/appState'
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
import { printError } from '~utils/reportError'
@@ -29,6 +31,9 @@ const NewRoute: React.FC = (props: Props) => {
// Import translations
const { t } = useTranslation('common')
+ // Set up router
+ const router = useRouter()
+
function callback(path: string) {
// This is scuffed, how do we do this natively?
window.history.replaceState(null, `Grid Tool`, `${path}`)
@@ -38,6 +43,16 @@ const NewRoute: React.FC = (props: Props) => {
persistStaticData()
}, [persistStaticData])
+ useEffect(() => {
+ // Clean state
+ const resetState = clonedeep(initialAppState)
+ Object.keys(resetState).forEach((key) => {
+ appState[key] = resetState[key]
+ })
+ // Set party to be editable
+ appState.party.editable = true
+ }, [])
+
function persistStaticData() {
appState.raids = props.raids
appState.jobs = props.jobs
@@ -47,7 +62,7 @@ const NewRoute: React.FC = (props: Props) => {
}
return (
-
+
{/* HTML */}
{t('page.titles.new')}
diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx
index 8ae2af4a..b7e16f22 100644
--- a/pages/p/[party].tsx
+++ b/pages/p/[party].tsx
@@ -70,7 +70,7 @@ const PartyRoute: React.FC = (props: Props) => {
}
return (
-
+
+
+
+
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 1754ed94..0f7ee421 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -39,6 +39,9 @@
"show_info": "Edit info",
"hide_info": "Hide info",
"save_info": "Save info",
+ "remix": "Remix",
+ "save": "Save",
+ "saved": "Saved",
"menu": "Menu",
"new": "New",
"wiki": "View more on gbf.wiki"
@@ -384,6 +387,14 @@
"no_skill": "No skill"
}
},
+ "toasts": {
+ "copied": "This party's URL was copied to your clipboard"
+ },
+ "tooltips": {
+ "remix": "Make a copy of this team",
+ "save": "Save this team to your account",
+ "source": "Go to original team"
+ },
"extra_weapons": "Additional Weapons",
"equipped": "Equipped",
"coming_soon": "Coming Soon",
@@ -394,5 +405,6 @@
"no_user": "Anonymous",
"no_job": "No class",
"no_value": "No value",
+ "remixes": "Remixes",
"level": "Level"
}
diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json
index 305251b1..4bcfc3cf 100644
--- a/public/locales/ja/common.json
+++ b/public/locales/ja/common.json
@@ -39,6 +39,9 @@
"show_info": "詳細を編集",
"save_info": "詳細を保存",
"hide_info": "詳細を非表示",
+ "remix": "リミックス",
+ "save": "保存する",
+ "saved": "保存",
"menu": "メニュー",
"new": "作成",
"wiki": "gbf.wikiで詳しく見る"
@@ -385,6 +388,14 @@
"no_skill": "設定されていません"
}
},
+ "toasts": {
+ "copied": "この編成のURLはクリップボードにコピーされました"
+ },
+ "tooltips": {
+ "remix": "この編成をコピーする",
+ "save": "この編成をアカウントに保存する",
+ "source": "オリジナルの編成へ"
+ },
"equipped": "装備した",
"extra_weapons": "Additional Weapons",
"coming_soon": "開発中",
@@ -395,5 +406,6 @@
"no_user": "無名",
"no_job": "ジョブなし",
"no_value": "値なし",
+ "remixes": "リミックスされた編成",
"level": "レベル"
}
diff --git a/styles/variables.scss b/styles/variables.scss
index 91aa81e1..4190fa4f 100644
--- a/styles/variables.scss
+++ b/styles/variables.scss
@@ -153,8 +153,8 @@ $dialog--bg--dark: $grey-25;
// Color Definitions: Menu
$menu--bg--light: $grey-100;
$menu--bg--dark: $grey-10;
-$menu--text--light: $grey-90;
-$menu--text--dark: $grey-50;
+$menu--text--light: $grey-50;
+$menu--text--dark: $grey-60;
$menu--separator--light: $grey-90;
$menu--separator--dark: $grey-05;
$menu--item--bg--light--hover: $grey-85;
diff --git a/types/Party.d.ts b/types/Party.d.ts
index 536d01e5..eebea134 100644
--- a/types/Party.d.ts
+++ b/types/Party.d.ts
@@ -18,6 +18,7 @@ interface Party {
button_count?: number
turn_count?: number
chain_count?: number
+ source_party?: Party
job: Job
job_skills: JobSkillObject
accessory: JobAccessory
@@ -28,6 +29,8 @@ interface Party {
weapons: Array
summons: Array
user: User
+ remix: boolean
+ remixes: Party[]
created_at: string
updated_at: string
}
diff --git a/utils/api.tsx b/utils/api.tsx
index d6625940..3912b61e 100644
--- a/utils/api.tsx
+++ b/utils/api.tsx
@@ -120,6 +120,11 @@ class Api {
return axios.get(resourceUrl, params)
}
+ remix(shortcode: string, params?: {}) {
+ const resourceUrl = `${this.url}/parties/${shortcode}/remix`
+ return axios.post(resourceUrl, params)
+ }
+
savedTeams(params: {}) {
const resourceUrl = `${this.url}/parties/favorites`
return axios.get(resourceUrl, params)
diff --git a/utils/appState.tsx b/utils/appState.tsx
index 61380ddd..816ba19a 100644
--- a/utils/appState.tsx
+++ b/utils/appState.tsx
@@ -36,6 +36,7 @@ interface AppState {
party: {
id: string | undefined
+ shortcode: string | undefined
editable: boolean
detailsVisible: boolean
name: string | undefined
@@ -55,6 +56,9 @@ interface AppState {
extra: boolean
user: User | undefined
favorited: boolean
+ remix: boolean
+ remixes: Party[]
+ sourceParty?: Party
created_at: string
updated_at: string
}
@@ -87,6 +91,7 @@ interface AppState {
export const initialAppState: AppState = {
party: {
id: undefined,
+ shortcode: '',
editable: false,
detailsVisible: false,
name: undefined,
@@ -111,6 +116,9 @@ export const initialAppState: AppState = {
extra: false,
user: undefined,
favorited: false,
+ remix: false,
+ remixes: [],
+ sourceParty: undefined,
created_at: '',
updated_at: '',
},