Implement party visibility (#369)
Parties can now be set to be private or unlisted. Private parties cannot be shared with anyone while Unlisted parties can be seen by those with the link. We implemented a dialog to change visibility, notices to let users know if a party isn't public, and icons on the GridRep so users can see at a glance which of their parties has different visibility on their profile.   
This commit is contained in:
parent
aabd7de207
commit
9e6c9a2108
23 changed files with 789 additions and 40 deletions
|
|
@ -270,7 +270,7 @@ const CharacterModal = ({
|
|||
<Alert
|
||||
message={
|
||||
<span>
|
||||
<Trans i18nKey="alerts.unsaved_changes.object">
|
||||
<Trans i18nKey="alert.unsaved_changes.object">
|
||||
You will lose all changes to{' '}
|
||||
<strong>{{ objectName: gridCharacter.object.name[locale] }}</strong>{' '}
|
||||
if you continue.
|
||||
|
|
@ -281,9 +281,9 @@ const CharacterModal = ({
|
|||
</span>
|
||||
}
|
||||
open={alertOpen}
|
||||
primaryActionText="Close"
|
||||
primaryActionText={t('alert.unsaved_changes.buttons.confirm')}
|
||||
primaryAction={close}
|
||||
cancelActionText="Nevermind"
|
||||
cancelActionText={t('alert.unsaved_changes.buttons.cancel')}
|
||||
cancelAction={() => setAlertOpen(false)}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
min-width: 20vw;
|
||||
max-width: 30vw;
|
||||
max-width: 32vw;
|
||||
padding: $unit * 4;
|
||||
|
||||
@include breakpoint(tablet) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&.no-shrink {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.blended {
|
||||
background: transparent;
|
||||
}
|
||||
|
|
@ -304,6 +308,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.notice {
|
||||
background-color: var(--notice-button-bg);
|
||||
color: var(--notice-button-text);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--notice-button-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.destructive {
|
||||
background: $error;
|
||||
color: white;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import classNames from 'classnames'
|
||||
|
|
@ -20,7 +19,6 @@ import SegmentedControl from '~components/common/SegmentedControl'
|
|||
import Segment from '~components/common/Segment'
|
||||
import SwitchTableField from '~components/common/SwitchTableField'
|
||||
import TableField from '~components/common/TableField'
|
||||
import Textarea from '~components/common/Textarea'
|
||||
|
||||
import capitalizeFirstLetter from '~utils/capitalizeFirstLetter'
|
||||
import type { DetailsObject } from 'types'
|
||||
|
|
@ -384,7 +382,7 @@ const EditPartyModal = ({
|
|||
<Alert
|
||||
message={
|
||||
<span>
|
||||
<Trans i18nKey="alerts.unsaved_changes.party">
|
||||
<Trans i18nKey="alert.unsaved_changes.party">
|
||||
You will lose all changes to your party{' '}
|
||||
<strong>
|
||||
{{
|
||||
|
|
@ -399,9 +397,9 @@ const EditPartyModal = ({
|
|||
</span>
|
||||
}
|
||||
open={alertOpen}
|
||||
primaryActionText="Close"
|
||||
primaryActionText={t('alert.unsaved_changes.buttons.confirm')}
|
||||
primaryAction={close}
|
||||
cancelActionText="Nevermind"
|
||||
cancelActionText={t('alert.unsaved_changes.buttons.cancel')}
|
||||
cancelAction={() => setAlertOpen(false)}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ const Party = (props: Props) => {
|
|||
if (details.guidebook1_id) payload.guidebook1_id = details.guidebook1_id
|
||||
if (details.guidebook2_id) payload.guidebook2_id = details.guidebook2_id
|
||||
if (details.guidebook3_id) payload.guidebook3_id = details.guidebook3_id
|
||||
if (details.visibility) payload.visibility = details.visibility
|
||||
if (getLocalId()) payload.local_id = getLocalId()
|
||||
|
||||
if (Object.keys(payload).length >= 1) return { party: payload }
|
||||
|
|
@ -292,6 +293,7 @@ const Party = (props: Props) => {
|
|||
appState.party.favorited = team.favorited
|
||||
appState.party.remix = team.remix
|
||||
appState.party.remixes = team.remixes
|
||||
appState.party.visibility = team.visibility
|
||||
appState.party.sourceParty = team.source_party
|
||||
appState.party.created_at = team.created_at
|
||||
appState.party.updated_at = team.updated_at
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Libraries
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
|
@ -33,12 +33,14 @@ interface Props {
|
|||
editable: boolean
|
||||
deleteTeamCallback: () => void
|
||||
remixTeamCallback: () => void
|
||||
teamVisibilityCallback: () => void
|
||||
}
|
||||
|
||||
const PartyDropdown = ({
|
||||
editable,
|
||||
deleteTeamCallback,
|
||||
remixTeamCallback,
|
||||
teamVisibilityCallback,
|
||||
}: Props) => {
|
||||
// Localization
|
||||
const { t } = useTranslation('common')
|
||||
|
|
@ -81,6 +83,11 @@ const PartyDropdown = ({
|
|||
|
||||
// Methods: Event handlers
|
||||
|
||||
// Dialogs / Visibility
|
||||
function visibilityCallback() {
|
||||
teamVisibilityCallback()
|
||||
}
|
||||
|
||||
// Alerts / Delete team
|
||||
function openDeleteTeamAlert() {
|
||||
setDeleteAlertOpen(true)
|
||||
|
|
@ -125,6 +132,9 @@ const PartyDropdown = ({
|
|||
const items = (
|
||||
<>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem onClick={visibilityCallback}>
|
||||
<span>{t('dropdown.party.visibility')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={copyToClipboard}>
|
||||
<span>{t('dropdown.party.copy')}</span>
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,58 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
align-items: center;
|
||||
background: var(--notice-bg);
|
||||
border-radius: $card-corner;
|
||||
display: flex;
|
||||
gap: $unit-2x;
|
||||
font-size: $font-regular;
|
||||
padding: $unit-4x;
|
||||
overflow: hidden;
|
||||
|
||||
@include breakpoint(small-tablet) {
|
||||
flex-direction: column;
|
||||
gap: $unit;
|
||||
padding: $unit-2x;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--notice-text);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-items: center;
|
||||
background-color: var(--notice-button-bg);
|
||||
border-radius: $full-corner;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: $unit-6x;
|
||||
width: $unit-6x;
|
||||
flex-shrink: 0;
|
||||
|
||||
svg {
|
||||
fill: var(--notice-text);
|
||||
width: $unit-3x;
|
||||
height: $unit-3x;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: $unit;
|
||||
|
||||
@include breakpoint(small-tablet) {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@ import { formatTimeAgo } from '~utils/timeAgo'
|
|||
|
||||
import RemixTeamAlert from '~components/dialogs/RemixTeamAlert'
|
||||
import RemixedToast from '~components/toasts/RemixedToast'
|
||||
import PartyVisibilityDialog from '~components/party/PartyVisibilityDialog'
|
||||
import UrlCopiedToast from '~components/toasts/UrlCopiedToast'
|
||||
|
||||
import EditIcon from '~public/icons/Edit.svg'
|
||||
import RemixIcon from '~public/icons/Remix.svg'
|
||||
import SaveIcon from '~public/icons/Save.svg'
|
||||
import PrivateIcon from '~public/icons/Private.svg'
|
||||
import UnlistedIcon from '~public/icons/Unlisted.svg'
|
||||
|
||||
import type { DetailsObject } from 'types'
|
||||
|
||||
|
|
@ -50,8 +54,10 @@ const PartyHeader = (props: Props) => {
|
|||
|
||||
// State: Component
|
||||
const [detailsOpen, setDetailsOpen] = useState(false)
|
||||
const [copyToastOpen, setCopyToastOpen] = useState(false)
|
||||
const [remixAlertOpen, setRemixAlertOpen] = useState(false)
|
||||
const [remixToastOpen, setRemixToastOpen] = useState(false)
|
||||
const [visibilityDialogOpen, setVisibilityDialogOpen] = useState(false)
|
||||
|
||||
const userClass = classNames({
|
||||
[styles.user]: true,
|
||||
|
|
@ -122,12 +128,29 @@ const PartyHeader = (props: Props) => {
|
|||
setDetailsOpen(open)
|
||||
}
|
||||
|
||||
// Dialogs: Visibility
|
||||
function visibilityDialogCallback() {
|
||||
setVisibilityDialogOpen(true)
|
||||
}
|
||||
|
||||
function handleVisibilityDialogChange(open: boolean) {
|
||||
setVisibilityDialogOpen(open)
|
||||
}
|
||||
|
||||
// Actions: Remix team
|
||||
function remixTeamCallback() {
|
||||
setRemixToastOpen(true)
|
||||
props.remixCallback()
|
||||
}
|
||||
|
||||
// Actions: Copy URL
|
||||
function copyToClipboard() {
|
||||
if (router.asPath.split('/')[1] === 'p') {
|
||||
navigator.clipboard.writeText(window.location.href)
|
||||
setCopyToastOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Alerts: Remix team
|
||||
function openRemixTeamAlert() {
|
||||
setRemixAlertOpen(true)
|
||||
|
|
@ -146,6 +169,15 @@ const PartyHeader = (props: Props) => {
|
|||
setRemixToastOpen(false)
|
||||
}
|
||||
|
||||
// Toasts / Copy URL
|
||||
function handleCopyToastOpenChanged(open: boolean) {
|
||||
setCopyToastOpen(!open)
|
||||
}
|
||||
|
||||
function handleCopyToastCloseClicked() {
|
||||
setCopyToastOpen(false)
|
||||
}
|
||||
|
||||
// Rendering
|
||||
|
||||
const userBlock = (username?: string, picture?: string, element?: string) => {
|
||||
|
|
@ -298,6 +330,50 @@ const PartyHeader = (props: Props) => {
|
|||
)
|
||||
}
|
||||
|
||||
// Render: Notice
|
||||
const unlistedNotice = (
|
||||
<div className={styles.notice}>
|
||||
<div className={styles.icon}>
|
||||
<UnlistedIcon />
|
||||
</div>
|
||||
<p>{t('party.notices.unlisted')}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Button
|
||||
bound={true}
|
||||
className="notice no-shrink"
|
||||
key="copy_link"
|
||||
text={t('party.notices.buttons.copy_link')}
|
||||
onClick={copyToClipboard}
|
||||
/>
|
||||
<Button
|
||||
bound={true}
|
||||
className="notice no-shrink"
|
||||
key="change_visibility"
|
||||
text={t('party.notices.buttons.change_visibility')}
|
||||
onClick={() => handleVisibilityDialogChange(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const privateNotice = (
|
||||
<div className={styles.notice}>
|
||||
<div className={styles.icon}>
|
||||
<PrivateIcon />
|
||||
</div>
|
||||
<p>{t('party.notices.private')}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Button
|
||||
bound={true}
|
||||
className="notice"
|
||||
key="change_visibility"
|
||||
text={t('party.notices.buttons.change_visibility')}
|
||||
onClick={() => handleVisibilityDialogChange(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
// Render: Buttons
|
||||
const saveButton = () => {
|
||||
return (
|
||||
|
|
@ -358,6 +434,8 @@ const PartyHeader = (props: Props) => {
|
|||
return (
|
||||
<>
|
||||
<header className={styles.wrapper}>
|
||||
{party.visibility == 2 && unlistedNotice}
|
||||
{party.visibility == 3 && privateNotice}
|
||||
<section className={styles.info}>
|
||||
<div className={styles.left}>
|
||||
<div className={styles.header}>
|
||||
|
|
@ -399,6 +477,7 @@ const PartyHeader = (props: Props) => {
|
|||
editable={props.editable}
|
||||
deleteTeamCallback={props.deleteCallback}
|
||||
remixTeamCallback={props.remixCallback}
|
||||
teamVisibilityCallback={visibilityDialogCallback}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -412,6 +491,13 @@ const PartyHeader = (props: Props) => {
|
|||
<section className={styles.tokens}>{renderTokens()}</section>
|
||||
</header>
|
||||
|
||||
<PartyVisibilityDialog
|
||||
open={visibilityDialogOpen}
|
||||
value={party.visibility as 1 | 2 | 3}
|
||||
onOpenChange={handleVisibilityDialogChange}
|
||||
updateParty={props.updateCallback}
|
||||
/>
|
||||
|
||||
<RemixTeamAlert
|
||||
creator={props.editable}
|
||||
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
|
||||
|
|
@ -426,6 +512,12 @@ const PartyHeader = (props: Props) => {
|
|||
onOpenChange={handleRemixToastOpenChanged}
|
||||
onCloseClick={handleRemixToastCloseClicked}
|
||||
/>
|
||||
|
||||
<UrlCopiedToast
|
||||
open={copyToastOpen}
|
||||
onOpenChange={handleCopyToastOpenChanged}
|
||||
onCloseClick={handleCopyToastCloseClicked}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
83
components/party/PartyVisibilityDialog/index.module.scss
Normal file
83
components/party/PartyVisibilityDialog/index.module.scss
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-4x;
|
||||
padding: 0 $unit-4x $unit-2x;
|
||||
|
||||
.description {
|
||||
color: var(--text-primary);
|
||||
font-size: $font-regular;
|
||||
}
|
||||
|
||||
.radioGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
|
||||
.radioSet {
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
|
||||
.radioItem {
|
||||
align-items: center;
|
||||
background: var(--radio-button-bg);
|
||||
border-radius: $full-corner;
|
||||
border: none;
|
||||
display: flex;
|
||||
border: 2px solid transparent;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
height: $unit-4x;
|
||||
width: $unit-4x;
|
||||
min-height: $unit-4x;
|
||||
min-width: $unit-4x;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--radio-active-bg);
|
||||
|
||||
&:hover {
|
||||
outline-color: var(--radio-active-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
[data-state='checked'] {
|
||||
background-color: var(--radio-active-bg);
|
||||
border-radius: $full-corner;
|
||||
display: block;
|
||||
height: $unit-2x;
|
||||
width: $unit-2x;
|
||||
}
|
||||
|
||||
&[data-state='checked']:hover [data-state='checked'] {
|
||||
background-color: var(--radio-active-bg-hover);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--radio-button-bg-hover);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-half;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: var(--text-primary);
|
||||
font-size: $font-regular;
|
||||
font-weight: $bold;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text-tertiary);
|
||||
font-size: $font-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
303
components/party/PartyVisibilityDialog/index.tsx
Normal file
303
components/party/PartyVisibilityDialog/index.tsx
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
||||
import * as RadioGroup from '@radix-ui/react-radio-group'
|
||||
import Alert from '~components/common/Alert'
|
||||
import Button from '~components/common/Button'
|
||||
import { Dialog, DialogTrigger } from '~components/common/Dialog'
|
||||
import DialogHeader from '~components/common/DialogHeader'
|
||||
import DialogFooter from '~components/common/DialogFooter'
|
||||
import DialogContent from '~components/common/DialogContent'
|
||||
|
||||
import type { DetailsObject } from 'types'
|
||||
import type { DialogProps } from '@radix-ui/react-dialog'
|
||||
|
||||
import { appState } from '~utils/appState'
|
||||
|
||||
import styles from './index.module.scss'
|
||||
|
||||
interface Props extends DialogProps {
|
||||
open: boolean
|
||||
value: 1 | 2 | 3
|
||||
onOpenChange?: (open: boolean) => void
|
||||
updateParty: (details: DetailsObject) => Promise<any>
|
||||
}
|
||||
|
||||
const EditPartyModal = ({
|
||||
open,
|
||||
value,
|
||||
updateParty,
|
||||
onOpenChange,
|
||||
...props
|
||||
}: Props) => {
|
||||
// Set up translation
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up reactive state
|
||||
const { party } = useSnapshot(appState)
|
||||
|
||||
// Refs
|
||||
const headerRef = React.createRef<HTMLDivElement>()
|
||||
const topContainerRef = React.createRef<HTMLDivElement>()
|
||||
const footerRef = React.createRef<HTMLDivElement>()
|
||||
const radioItemRef = [
|
||||
React.createRef<HTMLButtonElement>(),
|
||||
React.createRef<HTMLButtonElement>(),
|
||||
React.createRef<HTMLButtonElement>(),
|
||||
]
|
||||
|
||||
// States: Component
|
||||
const [alertOpen, setAlertOpen] = useState(false)
|
||||
const [errors, setErrors] = useState<{ [key: string]: string }>({
|
||||
name: '',
|
||||
description: '',
|
||||
})
|
||||
|
||||
// States: Data
|
||||
const [visibility, setVisibility] = useState(1)
|
||||
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
setVisibility(party.visibility)
|
||||
}, [value])
|
||||
|
||||
// Methods: Event handlers (Dialog)
|
||||
function handleOpenChange() {
|
||||
if (hasBeenModified() && open) {
|
||||
setAlertOpen(true)
|
||||
} else if (!hasBeenModified() && open) {
|
||||
close()
|
||||
} else {
|
||||
if (onOpenChange) onOpenChange(true)
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
setAlertOpen(false)
|
||||
setVisibility(party.visibility)
|
||||
if (onOpenChange) onOpenChange(false)
|
||||
}
|
||||
|
||||
function onEscapeKeyDown(event: KeyboardEvent) {
|
||||
event.preventDefault()
|
||||
handleOpenChange()
|
||||
}
|
||||
|
||||
function onOpenAutoFocus(event: Event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
// Methods: Event handlers (Fields)
|
||||
function handleValueChange(value: string) {
|
||||
const newVisibility = parseInt(value)
|
||||
setVisibility(newVisibility)
|
||||
}
|
||||
|
||||
// Handlers
|
||||
function handleScroll(event: React.UIEvent<HTMLDivElement, UIEvent>) {
|
||||
const scrollTop = event.currentTarget.scrollTop
|
||||
const scrollHeight = event.currentTarget.scrollHeight
|
||||
const clientHeight = event.currentTarget.clientHeight
|
||||
|
||||
if (topContainerRef && topContainerRef.current)
|
||||
manipulateHeaderShadow(topContainerRef.current, scrollTop)
|
||||
|
||||
if (footerRef && footerRef.current)
|
||||
manipulateFooterShadow(
|
||||
footerRef.current,
|
||||
scrollTop,
|
||||
scrollHeight,
|
||||
clientHeight
|
||||
)
|
||||
}
|
||||
|
||||
function manipulateHeaderShadow(header: HTMLDivElement, scrollTop: number) {
|
||||
const boxShadowBase = '0 2px 8px'
|
||||
const maxValue = 50
|
||||
|
||||
if (scrollTop >= 0) {
|
||||
const input = scrollTop > maxValue ? maxValue : scrollTop
|
||||
|
||||
const boxShadowOpacity = mapRange(input, 0, maxValue, 0.0, 0.16)
|
||||
const borderOpacity = mapRange(input, 0, maxValue, 0.0, 0.24)
|
||||
|
||||
header.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})`
|
||||
header.style.borderBottomColor = `rgba(0, 0, 0, ${borderOpacity})`
|
||||
}
|
||||
}
|
||||
|
||||
function manipulateFooterShadow(
|
||||
footer: HTMLDivElement,
|
||||
scrollTop: number,
|
||||
scrollHeight: number,
|
||||
clientHeight: number
|
||||
) {
|
||||
const boxShadowBase = '0 -2px 8px'
|
||||
const minValue = scrollHeight - 200
|
||||
const currentScroll = scrollTop + clientHeight
|
||||
|
||||
if (currentScroll >= minValue) {
|
||||
const input = currentScroll < minValue ? minValue : currentScroll
|
||||
|
||||
const boxShadowOpacity = mapRange(
|
||||
input,
|
||||
minValue,
|
||||
scrollHeight,
|
||||
0.16,
|
||||
0.0
|
||||
)
|
||||
const borderOpacity = mapRange(input, minValue, scrollHeight, 0.24, 0.0)
|
||||
|
||||
footer.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})`
|
||||
footer.style.borderTopColor = `rgba(0, 0, 0, ${borderOpacity})`
|
||||
}
|
||||
}
|
||||
|
||||
const calculateFooterShadow = debounce(() => {
|
||||
const boxShadowBase = '0 -2px 8px'
|
||||
const scrollable = document.querySelector(`.${styles.scrollable}`)
|
||||
const footer = footerRef
|
||||
|
||||
if (footer && footer.current) {
|
||||
if (scrollable) {
|
||||
if (scrollable.clientHeight >= scrollable.scrollHeight) {
|
||||
footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0)`
|
||||
footer.current.style.borderTopColor = `rgba(0, 0, 0, 0)`
|
||||
} else {
|
||||
footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0.16)`
|
||||
footer.current.style.borderTopColor = `rgba(0, 0, 0, 0.24)`
|
||||
}
|
||||
} else {
|
||||
footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0)`
|
||||
footer.current.style.borderTopColor = `rgba(0, 0, 0, 0)`
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', calculateFooterShadow)
|
||||
calculateFooterShadow()
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', calculateFooterShadow)
|
||||
}
|
||||
}, [calculateFooterShadow])
|
||||
|
||||
function mapRange(
|
||||
value: number,
|
||||
low1: number,
|
||||
high1: number,
|
||||
low2: number,
|
||||
high2: number
|
||||
) {
|
||||
return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1)
|
||||
}
|
||||
|
||||
// Methods: Modification checking
|
||||
function hasBeenModified() {
|
||||
return visibility !== party.visibility
|
||||
}
|
||||
|
||||
// Methods: Data methods
|
||||
async function updateDetails(event: React.MouseEvent) {
|
||||
const details: DetailsObject = {
|
||||
visibility: visibility,
|
||||
}
|
||||
|
||||
await updateParty(details)
|
||||
if (onOpenChange) onOpenChange(false)
|
||||
}
|
||||
|
||||
// Methods: Rendering methods
|
||||
function renderRadioItem(value: string, label: string) {
|
||||
return (
|
||||
<div className={styles.radioSet}>
|
||||
<RadioGroup.Item
|
||||
className={styles.radioItem}
|
||||
value={value}
|
||||
id={label}
|
||||
tabIndex={parseInt(value)}
|
||||
ref={radioItemRef[parseInt(value)]}
|
||||
>
|
||||
<RadioGroup.Indicator className={styles.radioIndicator} />
|
||||
</RadioGroup.Item>
|
||||
<label htmlFor={label}>
|
||||
<h4>{t(`modals.team_visibility.options.${label}`)}</h4>
|
||||
<p>{t(`modals.team_visibility.descriptions.${label}`)}</p>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const confirmationAlert = (
|
||||
<Alert
|
||||
message={t('modals.team_visibility.alerts.unsaved_changes.message')}
|
||||
open={alertOpen}
|
||||
primaryActionText={t(
|
||||
'modals.team_visibility.alerts.unsaved_changes.buttons.confirm'
|
||||
)}
|
||||
primaryAction={close}
|
||||
cancelActionText={t(
|
||||
'modals.team_visibility.alerts.unsaved_changes.buttons.cancel'
|
||||
)}
|
||||
cancelAction={() => setAlertOpen(false)}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{confirmationAlert}
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogTrigger asChild>{props.children}</DialogTrigger>
|
||||
<DialogContent
|
||||
className="changeVisibility"
|
||||
onEscapeKeyDown={onEscapeKeyDown}
|
||||
onOpenAutoFocus={onOpenAutoFocus}
|
||||
>
|
||||
<DialogHeader
|
||||
title={t('modals.team_visibility.title')}
|
||||
ref={headerRef}
|
||||
/>
|
||||
|
||||
<div className={styles.content}>
|
||||
<p className={styles.description}>
|
||||
{t('modals.team_visibility.description')}
|
||||
</p>
|
||||
<RadioGroup.Root
|
||||
className={styles.radioGroup}
|
||||
defaultValue={`${visibility}`}
|
||||
aria-label={t('modals.team_visibility.label')}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
{renderRadioItem('1', 'public')}
|
||||
{renderRadioItem('2', 'unlisted')}
|
||||
{renderRadioItem('3', 'private')}
|
||||
</RadioGroup.Root>
|
||||
</div>
|
||||
|
||||
<DialogFooter
|
||||
ref={footerRef}
|
||||
rightElements={[
|
||||
<Button
|
||||
bound={true}
|
||||
onClick={() => onOpenChange && onOpenChange(false)}
|
||||
key="cancel"
|
||||
text={t('buttons.cancel')}
|
||||
/>,
|
||||
<Button
|
||||
bound={true}
|
||||
key="confirm"
|
||||
onClick={updateDetails}
|
||||
text={t('modals.team_visibility.buttons.confirm')}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditPartyModal
|
||||
|
|
@ -241,6 +241,18 @@
|
|||
gap: calc($unit / 2);
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: $unit * 2.5;
|
||||
height: $unit * 2.5;
|
||||
|
||||
svg {
|
||||
fill: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ import { accountState } from '~utils/accountState'
|
|||
import { formatTimeAgo } from '~utils/timeAgo'
|
||||
|
||||
import Button from '~components/common/Button'
|
||||
import Tooltip from '~components/common/Tooltip'
|
||||
|
||||
import SaveIcon from '~public/icons/Save.svg'
|
||||
import PrivateIcon from '~public/icons/Private.svg'
|
||||
import UnlistedIcon from '~public/icons/Unlisted.svg'
|
||||
import ShieldIcon from '~public/icons/Shield.svg'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
|
|
@ -472,6 +475,48 @@ const GridRep = ({ party, loading, onClick, onSave }: Props) => {
|
|||
</div>
|
||||
)
|
||||
|
||||
const favoriteButton = (
|
||||
<Link href="#">
|
||||
<Button
|
||||
className={classNames({
|
||||
save: true,
|
||||
saved: party.favorited,
|
||||
})}
|
||||
leftAccessoryIcon={<SaveIcon className="stroke" />}
|
||||
active={party.favorited}
|
||||
bound={true}
|
||||
size="small"
|
||||
onClick={sendSaveData}
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
|
||||
function buttonArea() {
|
||||
if (
|
||||
account.authorized &&
|
||||
((party.user && account.user && account.user.id !== party.user.id) ||
|
||||
!party.user)
|
||||
) {
|
||||
return favoriteButton
|
||||
} else if (party.visibility === 2) {
|
||||
return (
|
||||
<Tooltip content={t('party.tooltips.unlisted')}>
|
||||
<span className={styles.icon}>
|
||||
<UnlistedIcon />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)
|
||||
} else if (party.visibility === 3) {
|
||||
return (
|
||||
<Tooltip content={t('party.tooltips.private')}>
|
||||
<span className={styles.icon}>
|
||||
<PrivateIcon />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const detailsWithUsername = (
|
||||
<div className={styles.details}>
|
||||
<div className={styles.top}>
|
||||
|
|
@ -493,25 +538,7 @@ const GridRep = ({ party, loading, onClick, onSave }: Props) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{account.authorized &&
|
||||
((party.user && account.user && account.user.id !== party.user.id) ||
|
||||
!party.user) ? (
|
||||
<Link href="#">
|
||||
<Button
|
||||
className={classNames({
|
||||
save: true,
|
||||
saved: party.favorited,
|
||||
})}
|
||||
leftAccessoryIcon={<SaveIcon className="stroke" />}
|
||||
active={party.favorited}
|
||||
bound={true}
|
||||
size="small"
|
||||
onClick={sendSaveData}
|
||||
/>
|
||||
</Link>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{buttonArea()}
|
||||
</div>
|
||||
<div className={styles.attributed}>
|
||||
{attribution()}
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ const WeaponModal = ({
|
|||
<Alert
|
||||
message={
|
||||
<span>
|
||||
<Trans i18nKey="alerts.unsaved_changes.object">
|
||||
<Trans i18nKey="alert.unsaved_changes.object">
|
||||
You will lose all changes to{' '}
|
||||
<strong>{{ objectName: gridWeapon.object.name[locale] }}</strong> if
|
||||
you continue.
|
||||
|
|
@ -455,9 +455,9 @@ const WeaponModal = ({
|
|||
</span>
|
||||
}
|
||||
open={alertOpen}
|
||||
primaryActionText="Close"
|
||||
primaryActionText={t('alert.unsaved_changes.buttons.confirm')}
|
||||
primaryAction={close}
|
||||
cancelActionText="Nevermind"
|
||||
cancelActionText={t('alert.unsaved_changes.buttons.cancel')}
|
||||
cancelAction={() => setAlertOpen(false)}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
3
public/icons/Private.svg
Normal file
3
public/icons/Private.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.625 4.27273C3.625 2.46525 5.13604 1 7 1C8.86396 1 10.375 2.46525 10.375 4.27273V6.45455H10.4056C11.01 6.45455 11.5 6.94454 11.5 7.54897V11.9056C11.5 12.51 11.01 13 10.4056 13H3.59443C2.98999 13 2.5 12.51 2.5 11.9056V7.54897C2.5 6.94454 2.98999 6.45455 3.59443 6.45455H3.625V4.27273ZM5.3125 6.45455H8.6875V4.27273C8.6875 3.4678 8.30444 2.63636 7 2.63636C5.69557 2.63636 5.3125 3.45827 5.3125 4.27273V6.45455Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 532 B |
4
public/icons/Public.svg
Normal file
4
public/icons/Public.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.04515 9.5C8.42586 9.5 9.54515 8.38071 9.54515 7C9.54515 5.61929 8.42586 4.5 7.04515 4.5C5.66444 4.5 4.54515 5.61929 4.54515 7C4.54515 8.38071 5.66444 9.5 7.04515 9.5ZM7.04515 8.5C7.87358 8.5 8.54515 7.82843 8.54515 7C8.54515 6.17157 7.87358 5.5 7.04515 5.5C6.21672 5.5 5.54515 6.17157 5.54515 7C5.54515 7.82843 6.21672 8.5 7.04515 8.5Z" />
|
||||
<path d="M13.2118 6.44776C10.1648 1.67656 4.08822 1.61674 0.814821 6.43618C0.611758 6.73515 0.607725 7.12607 0.803991 7.42954C3.9948 12.3633 10.0268 12.435 13.2119 7.41171C13.3986 7.11736 13.3994 6.7415 13.2118 6.44776Z" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 719 B |
3
public/icons/Unlisted.svg
Normal file
3
public/icons/Unlisted.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.9358 6.38178C14.0712 6.14109 13.9858 5.83624 13.7451 5.70088C13.5044 5.56552 13.1996 5.6509 13.0642 5.89159C11.5427 8.59686 9.24574 9.87242 7.00066 9.86173C4.753 9.85102 2.45409 8.5506 0.934206 5.88876C0.79728 5.64896 0.49188 5.56556 0.252076 5.70248C0.012272 5.83941 -0.0711275 6.14481 0.0657981 6.38461C0.666775 7.43713 1.38868 8.31027 2.19043 8.99743C2.16108 9.0253 2.13467 9.0572 2.11204 9.09296L1.57636 9.93929C1.42867 10.1726 1.4981 10.4815 1.73143 10.6292C1.96476 10.7769 2.27364 10.7074 2.42132 10.4741L2.957 9.62777C2.96384 9.61698 2.9702 9.60602 2.97611 9.59493C3.33838 9.83773 3.71284 10.0462 4.09629 10.2199C4.08317 10.2433 4.07177 10.268 4.0623 10.2941L3.71972 11.2353C3.62528 11.4948 3.75907 11.7817 4.01856 11.8761C4.27805 11.9706 4.56497 11.8368 4.65942 11.5773L5.00199 10.6361C5.01037 10.613 5.01695 10.5898 5.02181 10.5665C5.50693 10.7115 6.00176 10.8035 6.5004 10.8416C6.50014 10.8483 6.5 10.855 6.5 10.8618V11.8634C6.5 12.1395 6.72386 12.3634 7 12.3634C7.27614 12.3634 7.5 12.1395 7.5 11.8634V10.8618L7.49975 10.8458C7.99944 10.8117 8.49534 10.7233 8.9815 10.5811C8.98592 10.5995 8.99142 10.6179 8.99805 10.6361L9.34063 11.5773C9.43507 11.8368 9.72199 11.9706 9.98148 11.8761C10.241 11.7817 10.3748 11.4948 10.2803 11.2353L9.93775 10.2941C9.93055 10.2743 9.92222 10.2552 9.91288 10.2369C10.2967 10.0644 10.6715 9.85639 11.0341 9.6131L11.043 9.62777L11.5787 10.4741C11.7264 10.7074 12.0353 10.7769 12.2686 10.6292C12.5019 10.4815 12.5714 10.1726 12.4237 9.93929L11.888 9.09296C11.8689 9.06272 11.847 9.03523 11.823 9.01061C12.6198 8.32285 13.3376 7.44546 13.9358 6.38178Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
|
@ -12,11 +12,15 @@
|
|||
},
|
||||
"alert": {
|
||||
"confirm_logout": "Are you sure you want to log out?",
|
||||
"incompatible_weapon": "You've selected a weapon that can't be added to the Additional Weapon slots.",
|
||||
"unsaved_changes": {
|
||||
"party": "You will lose all changes to your party <strong>{objectName}</strong> if you continue.<br/><br/>Are you sure you want to continue without saving?",
|
||||
"object": "You will lose all changes to <strong>{objectName}</strong> if you continue.<br/><br/>Are you sure you want to continue without saving?"
|
||||
},
|
||||
"incompatible_weapon": "You've selected a weapon that can't be added to the Additional Weapon slots."
|
||||
"party": "You will lose all changes to your party <strong>{{objectName}}</strong> if you continue.<br/><br/>Are you sure you want to continue without saving?",
|
||||
"object": "You will lose all changes to <strong>{{objectName}}</strong> if you continue.<br/><br/>Are you sure you want to continue without saving?",
|
||||
"buttons": {
|
||||
"confirm": "Continue without saving",
|
||||
"cancel": "Nevermind"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ax": {
|
||||
"no_skill": "No AX Skill",
|
||||
|
|
@ -65,6 +69,7 @@
|
|||
},
|
||||
"dropdown": {
|
||||
"party": {
|
||||
"visibility": "Change team visibility",
|
||||
"copy": "Copy link to team",
|
||||
"delete": "Delete team",
|
||||
"remix": "Remix team"
|
||||
|
|
@ -397,6 +402,33 @@
|
|||
"remove": "Remove summon"
|
||||
}
|
||||
},
|
||||
"team_visibility": {
|
||||
"title": "Change team visibility",
|
||||
"label": "Team visibility",
|
||||
"description": "Change who can see this team and where it shows up on the site",
|
||||
"alerts": {
|
||||
"unsaved_changes": {
|
||||
"message": "Are you sure you want to continue without changing your team's visibility?",
|
||||
"buttons": {
|
||||
"confirm": "Continue without saving",
|
||||
"cancel": "Nevermind"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"public": "Public",
|
||||
"unlisted": "Unlisted",
|
||||
"private": "Private"
|
||||
},
|
||||
"descriptions": {
|
||||
"public": "Visible to everyone and appears on the Teams page",
|
||||
"unlisted": "Only visible to people with the link and does not appear on the Teams page",
|
||||
"private": "Only visible to you and does not appear on the Teams page"
|
||||
},
|
||||
"buttons": {
|
||||
"confirm": "Change visibility"
|
||||
}
|
||||
},
|
||||
"weapon": {
|
||||
"title": "Modify Weapon",
|
||||
"buttons": {
|
||||
|
|
@ -459,6 +491,18 @@
|
|||
"with_count_one": "{{count}} turn",
|
||||
"with_count_other": "{{count}} turns"
|
||||
}
|
||||
},
|
||||
"notices": {
|
||||
"unlisted": "This party is unlisted. Only people with the link with can see it.",
|
||||
"private": "This party is private. Only you can see it.",
|
||||
"buttons": {
|
||||
"copy_link": "Copy link",
|
||||
"change_visibility": "Change visibility"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"unlisted": "This party is unlisted",
|
||||
"private": "This party is private"
|
||||
}
|
||||
},
|
||||
"proficiencies": {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,15 @@
|
|||
},
|
||||
"alert": {
|
||||
"confirm_logout": "ログアウトしますか",
|
||||
"unsaved_changes_party": "<strong>「{objectName}」</strong>という編成の変更は保存していません。<br/><br/>変更を保存せずに続けますか?",
|
||||
"unsaved_changes_object": "<strong>「{objectName}」</strong>の変更は保存していません。<br/><br/>変更を保存せずに続けますか?",
|
||||
"incompatible_weapon": "Additional Weaponsに装備できない武器を入れました。"
|
||||
"incompatible_weapon": "Additional Weaponsに装備できない武器を入れました。",
|
||||
"unsaved_changes": {
|
||||
"party": "<strong>「{{objectName}}」</strong>という編成の変更は保存していません。<br/><br/>変更を保存せずに続けますか?",
|
||||
"object": "<strong>「{{objectName}}」</strong>の変更は保存していません。<br/><br/>変更を保存せずに続けますか?",
|
||||
"buttons": {
|
||||
"confirm": "変更せずに続ける",
|
||||
"cancel": "キャンセル"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ax": {
|
||||
"no_skill": "EXスキルなし",
|
||||
|
|
@ -63,6 +69,7 @@
|
|||
},
|
||||
"dropdown": {
|
||||
"party": {
|
||||
"visibility": "編成のプライバシー設定を変更",
|
||||
"copy": "編成のリンクをコピー",
|
||||
"delete": "編成を削除",
|
||||
"remix": "編成をリミックス"
|
||||
|
|
@ -395,6 +402,33 @@
|
|||
"remove": "召喚石を削除する"
|
||||
}
|
||||
},
|
||||
"team_visibility": {
|
||||
"title": "編成のプライバシー設定を変更",
|
||||
"label": "プライバシー設定",
|
||||
"description": "この編成は誰が共有できるかと編成一覧に表示されるかを変更できます",
|
||||
"alerts": {
|
||||
"unsaved_changes": {
|
||||
"message": "編成のプライバシー設定を変更せずに続けますか?",
|
||||
"buttons": {
|
||||
"confirm": "変更せずに続ける",
|
||||
"cancel": "キャンセル"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"public": "公開",
|
||||
"unlisted": "限定公開",
|
||||
"private": "非公開"
|
||||
},
|
||||
"descriptions": {
|
||||
"public": "誰でも自由に共有可能。編成一覧に表示します。",
|
||||
"unlisted": "リンクが知っている人のみが共有可能。編成一覧に表示しません。",
|
||||
"private": "自分自身だけが可視。編成一覧に表示しません。"
|
||||
},
|
||||
"buttons": {
|
||||
"confirm": "プライバシー設定を変更"
|
||||
}
|
||||
},
|
||||
"weapon": {
|
||||
"title": "武器変更",
|
||||
"buttons": {
|
||||
|
|
@ -453,10 +487,23 @@
|
|||
"minutes": "分",
|
||||
"seconds": "秒"
|
||||
},
|
||||
|
||||
"turns": {
|
||||
"with_count_one": "{{count}}ターン",
|
||||
"with_count_other": "{{count}}ターン"
|
||||
}
|
||||
},
|
||||
"notices": {
|
||||
"unlisted": "この編成は限定公開でリンクが知っている人のみが共有可能",
|
||||
"private": "この編成は未公開で自分自身だけが可視",
|
||||
"buttons": {
|
||||
"copy_link": "リンクをコピー",
|
||||
"change_visibility": "プライバシー設定を変更"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"unlisted": "この編成は限定公開",
|
||||
"private": "この編成は未公開"
|
||||
}
|
||||
},
|
||||
"proficiencies": {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@
|
|||
--notice-bg: #{$notice--bg--light};
|
||||
--notice-text: #{$notice--text--light};
|
||||
|
||||
--notice-button-bg: #{$notice--button--bg--light};
|
||||
--notice-button-bg-hover: #{$notice--button--bg--light--hover};
|
||||
--notice-button-text: #{$notice--button--text--light};
|
||||
--notice-button-text-hover: #{$notice--button--text--light--hover};
|
||||
|
||||
// Light - Buttons
|
||||
--button-bg: #{$button--bg--light};
|
||||
--button-bg-hover: #{$button--bg--light--hover};
|
||||
|
|
@ -112,6 +117,13 @@
|
|||
--slider-thumb-shadow: #{$slider--thumb--shadow--light};
|
||||
--slider-thumb-shadow-hover: #{$slider--thumb--shadow--light--hover};
|
||||
|
||||
// Light - Radio Buttons
|
||||
--radio-button-bg: #{$radio--bg--light};
|
||||
--radio-button-bg-hover: #{$radio--bg--light--hover};
|
||||
|
||||
--radio-active-bg: #{$radio--active--bg--light};
|
||||
--radio-active-bg-hover: #{$radio--active--bg--light--hover};
|
||||
|
||||
// Light - About
|
||||
--link-item-bg: #{$link--item--bg--light};
|
||||
--link-item-image-color: #{$link--item--bg--image--light};
|
||||
|
|
@ -313,6 +325,11 @@
|
|||
--notice-bg: #{$notice--bg--dark};
|
||||
--notice-text: #{$notice--text--dark};
|
||||
|
||||
--notice-button-bg: #{$notice--button--bg--dark};
|
||||
--notice-button-bg-hover: #{$notice--button--bg--dark--hover};
|
||||
--notice-button-text: #{$notice--button--text--dark};
|
||||
--notice-button-text-hover: #{$notice--button--text--dark--hover};
|
||||
|
||||
// Dark - Buttons
|
||||
--button-bg: #{$button--bg--dark};
|
||||
--button-bg-hover: #{$button--bg--dark--hover};
|
||||
|
|
@ -344,6 +361,13 @@
|
|||
--slider-thumb-shadow: #{$slider--thumb--shadow--dark};
|
||||
--slider-thumb-shadow-hover: #{$slider--thumb--shadow--dark--hover};
|
||||
|
||||
// Dark - Radio Buttons
|
||||
--radio-button-bg: #{$radio--bg--dark};
|
||||
--radio-button-bg-hover: #{$radio--bg--dark--hover};
|
||||
|
||||
--radio-active-bg: #{$radio--active--bg--dark};
|
||||
--radio-active-bg-hover: #{$radio--active--bg--dark--hover};
|
||||
|
||||
// Dark - About
|
||||
--link-item-bg: #{$link--item--bg--dark};
|
||||
--link-item-image-color: #{$link--item--bg--image--dark};
|
||||
|
|
|
|||
|
|
@ -107,10 +107,13 @@ $yellow-text-20: #ffed4c;
|
|||
$highlight-yellow: #ffed4c55;
|
||||
|
||||
$accent--yellow--00: #463805;
|
||||
$accent--yellow--20: #7f6a00;
|
||||
$accent--yellow--10: #6c5a01;
|
||||
$accent--yellow--20: #776300;
|
||||
$accent--yellow--40: #a39200;
|
||||
$accent--yellow--60: #c89d39;
|
||||
$accent--yellow--70: #d1aa4f;
|
||||
$accent--yellow--80: #deb351;
|
||||
$accent--yellow--90: #e6bd5e;
|
||||
$accent--yellow--100: #f9cc64;
|
||||
|
||||
$selected--item--bg--dark: #f9cc645d;
|
||||
|
|
@ -222,6 +225,18 @@ $notice--bg--dark: $accent--yellow--00;
|
|||
$notice--text--light: $accent--yellow--20;
|
||||
$notice--text--dark: $accent--yellow--100;
|
||||
|
||||
$notice--button--bg--light: $accent--yellow--80;
|
||||
$notice--button--bg--dark: $accent--yellow--20;
|
||||
|
||||
$notice--button--bg--light--hover: $accent--yellow--70;
|
||||
$notice--button--bg--dark--hover: $accent--yellow--10;
|
||||
|
||||
$notice--button--text--light: $accent--yellow--10;
|
||||
$notice--button--text--dark: $accent--yellow--90;
|
||||
|
||||
$notice--button--text--light--hover: $accent--yellow--00;
|
||||
$notice--button--text--dark--hover: $accent--yellow--100;
|
||||
|
||||
// Color Definitions: Button
|
||||
$button--bg--light: $grey-80;
|
||||
$button--bg--light--hover: $grey-100;
|
||||
|
|
@ -440,6 +455,19 @@ $icon--secondary--color--dark: $grey-50;
|
|||
$icon--secondary--hover--light: $grey-50;
|
||||
$icon--secondary--hover--dark: $grey-70;
|
||||
|
||||
// Color Definitions: Radio Buttons
|
||||
$radio--bg--light: $grey-75;
|
||||
$radio--bg--dark: $grey-10;
|
||||
|
||||
$radio--bg--light--hover: $grey-70;
|
||||
$radio--bg--dark--hover: $grey-00;
|
||||
|
||||
$radio--active--bg--light: $accent--blue--light;
|
||||
$radio--active--bg--dark: $accent--blue--dark;
|
||||
|
||||
$radio--active--bg--light--hover: $accent--blue--light--focus;
|
||||
$radio--active--bg--dark--hover: $accent--blue--dark--focus;
|
||||
|
||||
// Color Definitions: Tag
|
||||
$tag--bg--light: $grey-60;
|
||||
$tag--bg--dark: $grey-00;
|
||||
|
|
|
|||
1
types/Party.d.ts
vendored
1
types/Party.d.ts
vendored
|
|
@ -43,6 +43,7 @@ interface Party {
|
|||
local_id?: string
|
||||
remix: boolean
|
||||
remixes: Party[]
|
||||
visibility: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
|
|
|||
1
types/index.d.ts
vendored
1
types/index.d.ts
vendored
|
|
@ -41,6 +41,7 @@ export type DetailsObject = {
|
|||
job?: Job
|
||||
extra?: boolean
|
||||
guidebooks?: string[]
|
||||
visibility?: number
|
||||
}
|
||||
|
||||
export type ExtendedMastery = {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ interface AppState {
|
|||
favorited: boolean
|
||||
remix: boolean
|
||||
remixes: Party[]
|
||||
visibility: number
|
||||
sourceParty?: Party
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
|
@ -128,6 +129,7 @@ export const initialAppState: AppState = {
|
|||
favorited: false,
|
||||
remix: false,
|
||||
remixes: [],
|
||||
visibility: 1,
|
||||
sourceParty: undefined,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
|
|
|
|||
Loading…
Reference in a new issue