Implement Edit team modal (#312)
* Small refactor to CharLimitedFieldset Some methods were renamed for clarity. <input> props are actually put on the input properly. * Add tabindex to Popover trigger * Add tabindex to Switch and SwitchTableField * Add tabindex to DurationInput * Add new properties * Added guidebooks to RaidGroup * Added auto_summon to Party * Conditionally render description in TableField * Improve SwitchTableField * Add support for passing in classes * Add support for passing a disabled prop * Pass description to TableField * Right-align switch * Add support for Extra color switch * Align SliderTableField input to right * Align SelectTableField input to right * Update placeholder styles * Fix empty state on DurationInput * Remove tabindex from DurationInput * Update InputTableField Allow for passing down input properties and remove fixed width * Fix dialog footer styles * Update dialog and overlay z-index * Add styles to TableField Added styles for numeric inputs, disabled inputs, and generally cleaning things up * Add guidebooks to RaidCombobox + styles * Added guidebooks to the dummy raid group * Fix background color * Make less tall * Implement EditPartyModal EditPartyModal takes functionality that was in PartyHeader and puts it in a modal dialog. This lets us add fields and reduces the complexity of other components. Translations were also added. * Remove edit functionality * Add darker shadow to Select * Properly send raid ID to server * Show Extra grids based on selected raid * Fix EX badge colors * Use child as value in normal textarea * Remove toggle ability from Extra grids * Remove edit functionality from PartyDetails
This commit is contained in:
parent
938e34f21c
commit
835cdfff6f
35 changed files with 853 additions and 581 deletions
|
|
@ -414,7 +414,8 @@ const FilterModal = (props: Props) => {
|
||||||
{originalOnlyField()}
|
{originalOnlyField()}
|
||||||
</div>
|
</div>
|
||||||
<div className="DialogFooter" ref={footerRef}>
|
<div className="DialogFooter" ref={footerRef}>
|
||||||
<div className="Buttons Spaced">
|
<div className="Left"></div>
|
||||||
|
<div className="Right Buttons Spaced">
|
||||||
<Button
|
<Button
|
||||||
blended={true}
|
blended={true}
|
||||||
text={t('modals.filters.buttons.clear')}
|
text={t('modals.filters.buttons.clear')}
|
||||||
|
|
|
||||||
|
|
@ -330,11 +330,14 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
{themeField()}
|
{themeField()}
|
||||||
</div>
|
</div>
|
||||||
<div className="DialogFooter" ref={footerRef}>
|
<div className="DialogFooter" ref={footerRef}>
|
||||||
|
<div className="Left"></div>
|
||||||
|
<div className="Right">
|
||||||
<Button
|
<Button
|
||||||
contained={true}
|
contained={true}
|
||||||
text={t('modals.settings.buttons.confirm')}
|
text={t('modals.settings.buttons.confirm')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, {
|
||||||
|
ForwardRefRenderFunction,
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
|
||||||
|
import classNames from 'classnames'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props extends React.HTMLProps<HTMLInputElement> {
|
||||||
fieldName: string
|
fieldName: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
value?: string
|
value?: string
|
||||||
|
|
@ -11,47 +18,61 @@ interface Props {
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharLimitedFieldset = React.forwardRef<HTMLInputElement, Props>(
|
const CharLimitedFieldset: ForwardRefRenderFunction<HTMLInputElement, Props> = (
|
||||||
function useFieldSet(props, ref) {
|
{
|
||||||
const fieldType = ['password', 'confirm_password'].includes(props.fieldName)
|
fieldName,
|
||||||
? 'password'
|
placeholder,
|
||||||
: 'text'
|
value,
|
||||||
|
limit,
|
||||||
const [currentCount, setCurrentCount] = useState(0)
|
error,
|
||||||
|
onBlur,
|
||||||
useEffect(() => {
|
onChange: onInputChange,
|
||||||
setCurrentCount(
|
...props
|
||||||
props.value ? props.limit - props.value.length : props.limit
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
// States
|
||||||
|
const [currentCount, setCurrentCount] = useState(
|
||||||
|
() => limit - (value || '').length
|
||||||
)
|
)
|
||||||
}, [props.limit, props.value])
|
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
// Hooks
|
||||||
setCurrentCount(props.limit - event.currentTarget.value.length)
|
useEffect(() => {
|
||||||
if (props.onChange) props.onChange(event)
|
setCurrentCount(limit - (value || '').length)
|
||||||
|
}, [limit, value])
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { value: inputValue } = event.currentTarget
|
||||||
|
setCurrentCount(limit - inputValue.length)
|
||||||
|
if (onInputChange) {
|
||||||
|
onInputChange(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rendering methods
|
||||||
return (
|
return (
|
||||||
<fieldset className="Fieldset">
|
<fieldset className="Fieldset">
|
||||||
<div className="Joined">
|
<div className={classNames({ Joined: true }, props.className)}>
|
||||||
<input
|
<input
|
||||||
|
{...props}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="Input"
|
className="Input"
|
||||||
type={fieldType}
|
type={props.type}
|
||||||
name={props.fieldName}
|
name={fieldName}
|
||||||
placeholder={props.placeholder}
|
placeholder={placeholder}
|
||||||
defaultValue={props.value || ''}
|
defaultValue={value || ''}
|
||||||
onBlur={props.onBlur}
|
onBlur={onBlur}
|
||||||
onChange={onChange}
|
onChange={handleInputChange}
|
||||||
maxLength={props.limit}
|
maxLength={limit}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
formNoValidate
|
formNoValidate
|
||||||
/>
|
/>
|
||||||
<span className="Counter">{currentCount}</span>
|
<span className="Counter">{currentCount}</span>
|
||||||
</div>
|
</div>
|
||||||
{props.error.length > 0 && <p className="InputError">{props.error}</p>}
|
{error.length > 0 && <p className="InputError">{error}</p>}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
export default CharLimitedFieldset
|
export default forwardRef(CharLimitedFieldset)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
z-index: 40;
|
z-index: 10;
|
||||||
|
|
||||||
.DialogContent {
|
.DialogContent {
|
||||||
$multiplier: 4;
|
$multiplier: 4;
|
||||||
|
|
@ -160,7 +160,8 @@
|
||||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.16);
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.16);
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.24);
|
border-top: 1px solid rgba(0, 0, 0, 0.24);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
padding: ($unit * 1.5) ($unit * $multiplier) $unit-3x;
|
padding: ($unit * 1.5) ($unit * $multiplier) $unit-3x;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
||||||
|
|
@ -178,7 +179,6 @@
|
||||||
|
|
||||||
&.Spaced {
|
&.Spaced {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ interface Props
|
||||||
}
|
}
|
||||||
|
|
||||||
const DurationInput = React.forwardRef<HTMLInputElement, Props>(
|
const DurationInput = React.forwardRef<HTMLInputElement, Props>(
|
||||||
function DurationInput({ className, value, onValueChange }, forwardedRef) {
|
function DurationInput(
|
||||||
|
{ className, value, onValueChange, ...props },
|
||||||
|
forwardedRef
|
||||||
|
) {
|
||||||
// State
|
// State
|
||||||
const [duration, setDuration] = useState('')
|
const [duration, setDuration] = useState('')
|
||||||
const [minutesSelected, setMinutesSelected] = useState(false)
|
const [minutesSelected, setMinutesSelected] = useState(false)
|
||||||
|
|
@ -202,7 +205,7 @@ const DurationInput = React.forwardRef<HTMLInputElement, Props>(
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
value={`${getSeconds()}`.padStart(2, '0')}
|
value={getSeconds() > 0 ? `${getSeconds()}`.padStart(2, '0') : ''}
|
||||||
onChange={handleSecondsChange}
|
onChange={handleSecondsChange}
|
||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--input-bound-bg-hover);
|
background-color: var(--input-bound-bg-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||||
|
color: var(--text-tertiary) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.AlignRight {
|
&.AlignRight {
|
||||||
|
|
@ -43,7 +48,7 @@
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::placeholder {
|
.Input::placeholder {
|
||||||
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||||
color: var(--text-secondary) !important;
|
color: var(--text-secondary) !important;
|
||||||
opacity: 1; /* Firefox */
|
opacity: 1; /* Firefox */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
.InputField.TableField .Input {
|
.InputField.TableField .Input[type='number'] {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: $unit-8x;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,50 +3,60 @@ import Input from '~components/common/Input'
|
||||||
import TableField from '~components/common/TableField'
|
import TableField from '~components/common/TableField'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
name: string
|
extends React.DetailedHTMLProps<
|
||||||
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
|
HTMLInputElement
|
||||||
|
> {
|
||||||
label: string
|
label: string
|
||||||
description?: string
|
description?: string
|
||||||
placeholder?: string
|
|
||||||
value?: number
|
|
||||||
className?: string
|
|
||||||
imageAlt?: string
|
imageAlt?: string
|
||||||
imageClass?: string
|
imageClass?: string
|
||||||
imageSrc?: string[]
|
imageSrc?: string[]
|
||||||
onValueChange: (value: number) => void
|
onValueChange: (value?: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputTableField = (props: Props) => {
|
const InputTableField = ({
|
||||||
const [value, setValue] = useState(0)
|
label,
|
||||||
|
description,
|
||||||
|
imageAlt,
|
||||||
|
imageClass,
|
||||||
|
imageSrc,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
|
const [inputValue, setInputValue] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.value) setValue(props.value)
|
if (props.value) setInputValue(`${props.value}`)
|
||||||
}, [props.value])
|
}, [props.value])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.onValueChange(value)
|
props.onValueChange(inputValue)
|
||||||
}, [value])
|
}, [inputValue])
|
||||||
|
|
||||||
function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
setValue(parseInt(event.currentTarget?.value))
|
setInputValue(`${parseInt(event.currentTarget?.value)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableField
|
<TableField
|
||||||
name={props.name}
|
{...props}
|
||||||
className="InputField"
|
name={props.name || ''}
|
||||||
imageAlt={props.imageAlt}
|
className={classNames({ InputField: true }, props.className)}
|
||||||
imageClass={props.imageClass}
|
imageAlt={imageAlt}
|
||||||
imageSrc={props.imageSrc}
|
imageClass={imageClass}
|
||||||
label={props.label}
|
imageSrc={imageSrc}
|
||||||
|
label={label}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
className="Bound"
|
className="Bound"
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
type="number"
|
value={inputValue ? `${inputValue}` : ''}
|
||||||
value={value ? `${value}` : ''}
|
|
||||||
step={1}
|
step={1}
|
||||||
|
tabIndex={props.tabIndex}
|
||||||
|
type={props.type}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
</TableField>
|
</TableField>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
.Overlay {
|
.Overlay {
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 30;
|
z-index: 9;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ interface Props extends ComponentProps<'div'> {
|
||||||
className?: string
|
className?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
}
|
}
|
||||||
|
triggerTabIndex?: number
|
||||||
value?: {
|
value?: {
|
||||||
element: ReactNode
|
element: ReactNode
|
||||||
rawValue: string
|
rawValue: string
|
||||||
|
|
@ -83,6 +84,7 @@ const Popover = React.forwardRef<HTMLDivElement, Props>(function Popover(
|
||||||
<PopoverPrimitive.Trigger
|
<PopoverPrimitive.Trigger
|
||||||
className={triggerClasses}
|
className={triggerClasses}
|
||||||
data-placeholder={!props.value}
|
data-placeholder={!props.value}
|
||||||
|
tabIndex={props.triggerTabIndex}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
{value}
|
{value}
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@
|
||||||
.Select {
|
.Select {
|
||||||
background: var(--dialog-bg);
|
background: var(--dialog-bg);
|
||||||
border-radius: $card-corner;
|
border-radius: $card-corner;
|
||||||
border: $hover-stroke;
|
border: 1px solid rgba(0, 0, 0, 0.24);
|
||||||
box-shadow: $hover-shadow;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);
|
||||||
padding: 0 $unit;
|
padding: 0 $unit;
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
min-width: var(--radix-select-trigger-width);
|
min-width: var(--radix-select-trigger-width);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
.SelectField.TableField .Right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ const SelectTableField = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<TableField
|
<TableField
|
||||||
name={props.name}
|
name={props.name}
|
||||||
|
className="SelectField"
|
||||||
imageAlt={props.imageAlt}
|
imageAlt={props.imageAlt}
|
||||||
imageClass={props.imageClass}
|
imageClass={props.imageClass}
|
||||||
imageSrc={props.imageSrc}
|
imageSrc={props.imageSrc}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: $unit-8x;
|
width: $unit-8x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ const Switch = (props: Props) => {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
required={required}
|
required={required}
|
||||||
value={value}
|
value={value}
|
||||||
|
tabIndex={props.tabIndex}
|
||||||
onCheckedChange={onCheckedChange}
|
onCheckedChange={onCheckedChange}
|
||||||
>
|
>
|
||||||
<RadixSwitch.Thumb className={thumbClasses} />
|
<RadixSwitch.Thumb className={thumbClasses} />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
.TableField.SwitchTableField {
|
||||||
|
&.Extra .Switch[data-state='checked'] {
|
||||||
|
background: var(--extra-purple-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Right {
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import classNames from 'classnames'
|
||||||
import Switch from '~components/common/Switch'
|
import Switch from '~components/common/Switch'
|
||||||
import TableField from '~components/common/TableField'
|
import TableField from '~components/common/TableField'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
name: string
|
name: string
|
||||||
label: string
|
label: string
|
||||||
description?: string
|
description?: string
|
||||||
|
disabled?: boolean
|
||||||
value?: boolean
|
value?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
|
tabIndex?: number
|
||||||
imageAlt?: string
|
imageAlt?: string
|
||||||
imageClass?: string
|
imageClass?: string
|
||||||
imageSrc?: string[]
|
imageSrc?: string[]
|
||||||
|
|
@ -31,10 +34,19 @@ const SwitchTableField = (props: Props) => {
|
||||||
setValue(value)
|
setValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const classes = classNames(
|
||||||
|
{
|
||||||
|
SwitchTableField: true,
|
||||||
|
Disabled: props.disabled,
|
||||||
|
},
|
||||||
|
props.className
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableField
|
<TableField
|
||||||
name={props.name}
|
name={props.name}
|
||||||
className="SwitchField"
|
description={props.description}
|
||||||
|
className={classes}
|
||||||
imageAlt={props.imageAlt}
|
imageAlt={props.imageAlt}
|
||||||
imageClass={props.imageClass}
|
imageClass={props.imageClass}
|
||||||
imageSrc={props.imageSrc}
|
imageSrc={props.imageSrc}
|
||||||
|
|
@ -43,6 +55,8 @@ const SwitchTableField = (props: Props) => {
|
||||||
<Switch
|
<Switch
|
||||||
name={props.name}
|
name={props.name}
|
||||||
checked={value}
|
checked={value}
|
||||||
|
disabled={props.disabled}
|
||||||
|
tabIndex={props.tabIndex}
|
||||||
onCheckedChange={onValueChange}
|
onCheckedChange={onValueChange}
|
||||||
/>
|
/>
|
||||||
</TableField>
|
</TableField>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: $unit-2x;
|
gap: $unit-2x;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
|
min-height: $unit-6x;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: $unit-half 0;
|
padding: $unit-half 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -17,7 +18,30 @@
|
||||||
color: var(--accent-blue);
|
color: var(--accent-blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Numeric .Right > .Input,
|
||||||
|
&.Numeric .Right > .Duration {
|
||||||
|
text-align: right;
|
||||||
|
max-width: $unit-12x;
|
||||||
|
width: $unit-12x;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Numeric .Right > .Duration {
|
||||||
|
justify-content: flex-end;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Disabled {
|
||||||
|
&:hover .Left .Info h3 {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Left .Info h3 {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.Left {
|
.Left {
|
||||||
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: $unit;
|
gap: $unit;
|
||||||
|
|
@ -59,7 +83,6 @@
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: $font-small;
|
font-size: $font-small;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
max-width: 300px;
|
|
||||||
|
|
||||||
&.jp {
|
&.jp {
|
||||||
max-width: 270px;
|
max-width: 270px;
|
||||||
|
|
@ -71,6 +94,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
gap: $unit-2x;
|
gap: $unit-2x;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const TableField = (props: Props) => {
|
||||||
<div className="Left">
|
<div className="Left">
|
||||||
<div className="Info">
|
<div className="Info">
|
||||||
<h3>{props.label}</h3>
|
<h3>{props.label}</h3>
|
||||||
<p>{props.description}</p>
|
{props.description && <p>{props.description}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="Image">{image()}</div>
|
<div className="Image">{image()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,10 @@ import classNames from 'classnames'
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
grid: GridArray<GridWeapon>
|
grid: GridArray<GridWeapon>
|
||||||
enabled: boolean
|
|
||||||
editable: boolean
|
editable: boolean
|
||||||
found?: boolean
|
found?: boolean
|
||||||
offset: number
|
offset: number
|
||||||
removeWeapon: (id: string) => void
|
removeWeapon: (id: string) => void
|
||||||
updateExtra: (enabled: boolean) => void
|
|
||||||
updateObject: (object: SearchableObject, position: number) => void
|
updateObject: (object: SearchableObject, position: number) => void
|
||||||
updateUncap: (id: string, position: number, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
@ -26,12 +24,9 @@ const EXTRA_WEAPONS_COUNT = 3
|
||||||
|
|
||||||
const ExtraWeaponsGrid = ({
|
const ExtraWeaponsGrid = ({
|
||||||
grid,
|
grid,
|
||||||
enabled,
|
|
||||||
editable,
|
editable,
|
||||||
found,
|
|
||||||
offset,
|
offset,
|
||||||
removeWeapon,
|
removeWeapon,
|
||||||
updateExtra,
|
|
||||||
updateObject,
|
updateObject,
|
||||||
updateUncap,
|
updateUncap,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
|
@ -40,16 +35,9 @@ const ExtraWeaponsGrid = ({
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
ExtraWeapons: true,
|
ExtraWeapons: true,
|
||||||
ContainerItem: true,
|
ContainerItem: true,
|
||||||
Disabled: !enabled,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onCheckedChange(checked: boolean) {
|
const extraWeapons = (
|
||||||
updateExtra(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
const disabledElement = <></>
|
|
||||||
|
|
||||||
const enabledElement = (
|
|
||||||
<ul id="ExtraWeaponGrid">
|
<ul id="ExtraWeaponGrid">
|
||||||
{Array.from(Array(EXTRA_WEAPONS_COUNT)).map((x, i) => {
|
{Array.from(Array(EXTRA_WEAPONS_COUNT)).map((x, i) => {
|
||||||
const itemClasses = classNames({
|
const itemClasses = classNames({
|
||||||
|
|
@ -77,17 +65,8 @@ const ExtraWeaponsGrid = ({
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<div className="Header">
|
<div className="Header">
|
||||||
<h3>{t('extra_weapons')}</h3>
|
<h3>{t('extra_weapons')}</h3>
|
||||||
{editable ? (
|
|
||||||
<Switch
|
|
||||||
name="ExtraWeapons"
|
|
||||||
checked={enabled}
|
|
||||||
onCheckedChange={onCheckedChange}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{enabled ? enabledElement : ''}
|
{extraWeapons}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import './index.scss'
|
||||||
interface Props {
|
interface Props {
|
||||||
grid: GuidebookList
|
grid: GuidebookList
|
||||||
editable: boolean
|
editable: boolean
|
||||||
offset: number
|
|
||||||
removeGuidebook: (position: number) => void
|
removeGuidebook: (position: number) => void
|
||||||
updateObject: (object: SearchableObject, position: number) => void
|
updateObject: (object: SearchableObject, position: number) => void
|
||||||
}
|
}
|
||||||
|
|
@ -28,28 +27,12 @@ const GuidebooksGrid = ({
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
const [enabled, setEnabled] = useState(false)
|
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
Guidebooks: true,
|
Guidebooks: true,
|
||||||
ContainerItem: true,
|
ContainerItem: true,
|
||||||
Disabled: !enabled,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
const guidebooks = (
|
||||||
console.log('Grid updated')
|
|
||||||
if (hasGuidebooks()) setEnabled(true)
|
|
||||||
}, [grid])
|
|
||||||
|
|
||||||
function hasGuidebooks() {
|
|
||||||
return grid && (grid[0] || grid[1] || grid[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCheckedChange(checked: boolean) {
|
|
||||||
setEnabled(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
const enabledElement = (
|
|
||||||
<ul id="GuidebooksGrid">
|
<ul id="GuidebooksGrid">
|
||||||
{Array.from(Array(EXTRA_WEAPONS_COUNT)).map((x, i) => {
|
{Array.from(Array(EXTRA_WEAPONS_COUNT)).map((x, i) => {
|
||||||
const itemClasses = classNames({
|
const itemClasses = classNames({
|
||||||
|
|
@ -75,21 +58,12 @@ const GuidebooksGrid = ({
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<div className="Header">
|
<div className="Header">
|
||||||
<h3>{t('sephira_guidebooks')}</h3>
|
<h3>{t('sephira_guidebooks')}</h3>
|
||||||
{editable ? (
|
|
||||||
<Switch
|
|
||||||
name="Guidebooks"
|
|
||||||
checked={enabled}
|
|
||||||
onCheckedChange={onCheckedChange}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{enabled ? enabledElement : ''}
|
{guidebooks}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
return editable || (enabled && !editable) ? guidebookElement : <div />
|
return guidebookElement
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GuidebooksGrid
|
export default GuidebooksGrid
|
||||||
|
|
|
||||||
56
components/party/EditPartyModal/index.scss
Normal file
56
components/party/EditPartyModal/index.scss
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
.EditTeam.DialogContent {
|
||||||
|
min-height: 80vh;
|
||||||
|
|
||||||
|
.Container.Scrollable {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: $unit-2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Fields {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: $unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtraNotice {
|
||||||
|
background: var(--extra-purple-bg);
|
||||||
|
border-radius: $input-corner;
|
||||||
|
color: var(--extra-purple-text);
|
||||||
|
font-weight: $medium;
|
||||||
|
padding: $unit-2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DescriptionField {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: inherit;
|
||||||
|
gap: $unit;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.Left {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.Input {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Image {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
477
components/party/EditPartyModal/index.tsx
Normal file
477
components/party/EditPartyModal/index.tsx
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogClose,
|
||||||
|
DialogTitle,
|
||||||
|
} from '~components/common/Dialog'
|
||||||
|
import DialogContent from '~components/common/DialogContent'
|
||||||
|
import Button from '~components/common/Button'
|
||||||
|
import CharLimitedFieldset from '~components/common/CharLimitedFieldset'
|
||||||
|
import DurationInput from '~components/common/DurationInput'
|
||||||
|
import InputTableField from '~components/common/InputTableField'
|
||||||
|
import RaidCombobox from '~components/raids/RaidCombobox'
|
||||||
|
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 type { DetailsObject } from 'types'
|
||||||
|
import type { DialogProps } from '@radix-ui/react-dialog'
|
||||||
|
|
||||||
|
import CheckIcon from '~public/icons/Check.svg'
|
||||||
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props extends DialogProps {
|
||||||
|
party?: Party
|
||||||
|
updateCallback: (details: DetailsObject) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
|
||||||
|
// Set up router
|
||||||
|
const router = useRouter()
|
||||||
|
const locale = router.locale
|
||||||
|
|
||||||
|
// Set up translation
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
|
// Refs
|
||||||
|
const headerRef = React.createRef<HTMLDivElement>()
|
||||||
|
const footerRef = React.createRef<HTMLDivElement>()
|
||||||
|
const descriptionInput = useRef<HTMLTextAreaElement>(null)
|
||||||
|
|
||||||
|
// States: Component
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [errors, setErrors] = useState<{ [key: string]: string }>({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
const [currentSegment, setCurrentSegment] = useState(0)
|
||||||
|
|
||||||
|
// States: Data
|
||||||
|
const [name, setName] = useState('')
|
||||||
|
const [raid, setRaid] = useState<Raid>()
|
||||||
|
const [extra, setExtra] = useState(false)
|
||||||
|
const [chargeAttack, setChargeAttack] = useState(true)
|
||||||
|
const [fullAuto, setFullAuto] = useState(false)
|
||||||
|
const [autoGuard, setAutoGuard] = useState(false)
|
||||||
|
const [autoSummon, setAutoSummon] = useState(false)
|
||||||
|
|
||||||
|
const [buttonCount, setButtonCount] = useState<number | undefined>(undefined)
|
||||||
|
const [chainCount, setChainCount] = useState<number | undefined>(undefined)
|
||||||
|
const [turnCount, setTurnCount] = useState<number | undefined>(undefined)
|
||||||
|
const [clearTime, setClearTime] = useState(0)
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
useEffect(() => {
|
||||||
|
if (!party) return
|
||||||
|
|
||||||
|
setName(party.name)
|
||||||
|
setRaid(party.raid)
|
||||||
|
setAutoGuard(party.auto_guard)
|
||||||
|
setAutoSummon(party.auto_summon)
|
||||||
|
setFullAuto(party.full_auto)
|
||||||
|
setChargeAttack(party.charge_attack)
|
||||||
|
setClearTime(party.clear_time)
|
||||||
|
if (party.turn_count) setTurnCount(party.turn_count)
|
||||||
|
if (party.button_count) setButtonCount(party.button_count)
|
||||||
|
if (party.chain_count) setChainCount(party.chain_count)
|
||||||
|
}, [party])
|
||||||
|
|
||||||
|
// Methods: Event handlers (Dialog)
|
||||||
|
function openChange() {
|
||||||
|
if (open) {
|
||||||
|
setOpen(false)
|
||||||
|
if (props.onOpenChange) props.onOpenChange(false)
|
||||||
|
} else {
|
||||||
|
setOpen(true)
|
||||||
|
if (props.onOpenChange) props.onOpenChange(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEscapeKeyDown(event: KeyboardEvent) {
|
||||||
|
event.preventDefault()
|
||||||
|
openChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenAutoFocus(event: Event) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Event handlers (Fields)
|
||||||
|
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const { name, value } = event.target
|
||||||
|
setName(value)
|
||||||
|
|
||||||
|
let newErrors = errors
|
||||||
|
setErrors(newErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChargeAttackChanged(checked: boolean) {
|
||||||
|
setChargeAttack(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFullAutoChanged(checked: boolean) {
|
||||||
|
setFullAuto(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAutoGuardChanged(checked: boolean) {
|
||||||
|
setAutoGuard(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAutoSummonChanged(checked: boolean) {
|
||||||
|
setAutoSummon(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleExtraChanged(checked: boolean) {
|
||||||
|
setExtra(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClearTimeChanged(value: number) {
|
||||||
|
if (!isNaN(value)) setClearTime(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTurnCountChanged(value?: string) {
|
||||||
|
if (!value) return
|
||||||
|
const numericalValue = parseInt(value)
|
||||||
|
if (!isNaN(numericalValue)) setTurnCount(numericalValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleButtonCountChanged(value?: string) {
|
||||||
|
if (!value) return
|
||||||
|
const numericalValue = parseInt(value)
|
||||||
|
if (!isNaN(numericalValue)) setButtonCount(numericalValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChainCountChanged(value?: string) {
|
||||||
|
if (!value) return
|
||||||
|
const numericalValue = parseInt(value)
|
||||||
|
if (!isNaN(numericalValue)) setChainCount(numericalValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTextAreaChanged(
|
||||||
|
event: React.ChangeEvent<HTMLTextAreaElement>
|
||||||
|
) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const { name, value } = event.target
|
||||||
|
let newErrors = errors
|
||||||
|
|
||||||
|
setErrors(newErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
function receiveRaid(raid?: Raid) {
|
||||||
|
if (raid) {
|
||||||
|
setRaid(raid)
|
||||||
|
|
||||||
|
if (raid.group.extra) setExtra(true)
|
||||||
|
else setExtra(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Data methods
|
||||||
|
function updateDetails(event: React.MouseEvent) {
|
||||||
|
const descriptionValue = descriptionInput.current?.value
|
||||||
|
const details: DetailsObject = {
|
||||||
|
fullAuto: fullAuto,
|
||||||
|
autoGuard: autoGuard,
|
||||||
|
autoSummon: autoSummon,
|
||||||
|
chargeAttack: chargeAttack,
|
||||||
|
clearTime: clearTime,
|
||||||
|
buttonCount: buttonCount,
|
||||||
|
turnCount: turnCount,
|
||||||
|
chainCount: chainCount,
|
||||||
|
name: name,
|
||||||
|
description: descriptionValue,
|
||||||
|
raid: raid,
|
||||||
|
extra: extra,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCallback(details)
|
||||||
|
openChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Rendering methods
|
||||||
|
const segmentedControl = () => {
|
||||||
|
return (
|
||||||
|
<SegmentedControl blended={true}>
|
||||||
|
<Segment
|
||||||
|
groupName="edit_nav"
|
||||||
|
name="core"
|
||||||
|
selected={currentSegment === 0}
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setCurrentSegment(0)}
|
||||||
|
>
|
||||||
|
{t('modals.edit_team.segments.basic_info')}
|
||||||
|
</Segment>
|
||||||
|
<Segment
|
||||||
|
groupName="edit_nav"
|
||||||
|
name="properties"
|
||||||
|
selected={currentSegment === 1}
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setCurrentSegment(1)}
|
||||||
|
>
|
||||||
|
{t('modals.edit_team.segments.properties')}
|
||||||
|
</Segment>
|
||||||
|
</SegmentedControl>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameField = () => {
|
||||||
|
return (
|
||||||
|
<CharLimitedFieldset
|
||||||
|
className="Bound"
|
||||||
|
fieldName="name"
|
||||||
|
placeholder="Name your team"
|
||||||
|
value={name}
|
||||||
|
limit={50}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
error={errors.name}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const raidField = () => {
|
||||||
|
return (
|
||||||
|
<RaidCombobox
|
||||||
|
showAllRaidsOption={false}
|
||||||
|
currentRaid={raid}
|
||||||
|
onChange={receiveRaid}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraNotice = () => {
|
||||||
|
if (extra) {
|
||||||
|
return (
|
||||||
|
<div className="ExtraNotice">
|
||||||
|
<span className="ExtraNoticeText">
|
||||||
|
{raid && raid.group.guidebooks
|
||||||
|
? t('modals.edit_team.extra_notice_guidebooks')
|
||||||
|
: t('modals.edit_team.extra_notice')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const descriptionField = () => {
|
||||||
|
return (
|
||||||
|
<div className="DescriptionField">
|
||||||
|
<textarea
|
||||||
|
className="Input Bound"
|
||||||
|
name="description"
|
||||||
|
placeholder={
|
||||||
|
'Write your notes here\n\n\nWatch out for the 50% trigger!\nMake sure to click Fediel’s 3 first\nGood luck with RNG!'
|
||||||
|
}
|
||||||
|
onChange={handleTextAreaChanged}
|
||||||
|
ref={descriptionInput}
|
||||||
|
>
|
||||||
|
{party ? party.description : ''}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const chargeAttackField = () => {
|
||||||
|
return (
|
||||||
|
<SwitchTableField
|
||||||
|
name="charge_attack"
|
||||||
|
label={t('modals.edit_team.labels.charge_attack')}
|
||||||
|
value={chargeAttack}
|
||||||
|
onValueChange={handleChargeAttackChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullAutoField = () => {
|
||||||
|
return (
|
||||||
|
<SwitchTableField
|
||||||
|
name="full_auto"
|
||||||
|
label={t('modals.edit_team.labels.full_auto')}
|
||||||
|
value={fullAuto}
|
||||||
|
onValueChange={handleFullAutoChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoGuardField = () => {
|
||||||
|
return (
|
||||||
|
<SwitchTableField
|
||||||
|
name="auto_guard"
|
||||||
|
label={t('modals.edit_team.labels.auto_guard')}
|
||||||
|
value={autoGuard}
|
||||||
|
onValueChange={handleAutoGuardChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoSummonField = () => {
|
||||||
|
return (
|
||||||
|
<SwitchTableField
|
||||||
|
name="auto_summon"
|
||||||
|
label={t('modals.edit_team.labels.auto_summon')}
|
||||||
|
value={autoSummon}
|
||||||
|
onValueChange={handleAutoSummonChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraField = () => {
|
||||||
|
return (
|
||||||
|
<SwitchTableField
|
||||||
|
name="extra"
|
||||||
|
className="Extra"
|
||||||
|
label={t('modals.edit_team.labels.extra')}
|
||||||
|
description={t('modals.edit_team.descriptions.extra')}
|
||||||
|
value={extra}
|
||||||
|
disabled={true}
|
||||||
|
onValueChange={handleExtraChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearTimeField = () => {
|
||||||
|
return (
|
||||||
|
<TableField
|
||||||
|
className="Numeric"
|
||||||
|
name="clear_time"
|
||||||
|
label={t('modals.edit_team.labels.clear_time')}
|
||||||
|
>
|
||||||
|
<DurationInput
|
||||||
|
name="clear_time"
|
||||||
|
className="Bound"
|
||||||
|
value={clearTime}
|
||||||
|
onValueChange={(value: number) => handleClearTimeChanged(value)}
|
||||||
|
/>
|
||||||
|
</TableField>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnCountField = () => {
|
||||||
|
return (
|
||||||
|
<InputTableField
|
||||||
|
name="turn_count"
|
||||||
|
className="Numeric"
|
||||||
|
label={t('modals.edit_team.labels.turn_count')}
|
||||||
|
placeholder="0"
|
||||||
|
type="number"
|
||||||
|
value={turnCount}
|
||||||
|
onValueChange={handleTurnCountChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonCountField = () => {
|
||||||
|
return (
|
||||||
|
<InputTableField
|
||||||
|
name="button_count"
|
||||||
|
className="Numeric"
|
||||||
|
label={t('modals.edit_team.labels.button_count')}
|
||||||
|
placeholder="0"
|
||||||
|
type="number"
|
||||||
|
value={buttonCount}
|
||||||
|
onValueChange={handleButtonCountChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const chainCountField = () => {
|
||||||
|
return (
|
||||||
|
<InputTableField
|
||||||
|
name="chain_count"
|
||||||
|
className="Numeric"
|
||||||
|
label={t('modals.edit_team.labels.chain_count')}
|
||||||
|
placeholder="0"
|
||||||
|
type="number"
|
||||||
|
value={chainCount}
|
||||||
|
onValueChange={handleChainCountChanged}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const infoPage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{nameField()}
|
||||||
|
{raidField()}
|
||||||
|
{extraNotice()}
|
||||||
|
{descriptionField()}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const propertiesPage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{chargeAttackField()}
|
||||||
|
{fullAutoField()}
|
||||||
|
{autoSummonField()}
|
||||||
|
{autoGuardField()}
|
||||||
|
{extraField()}
|
||||||
|
{clearTimeField()}
|
||||||
|
{turnCountField()}
|
||||||
|
{buttonCountField()}
|
||||||
|
{chainCountField()}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={openChange}>
|
||||||
|
<DialogTrigger asChild>{props.children}</DialogTrigger>
|
||||||
|
<DialogContent
|
||||||
|
className="EditTeam"
|
||||||
|
headerref={headerRef}
|
||||||
|
footerref={footerRef}
|
||||||
|
onEscapeKeyDown={onEscapeKeyDown}
|
||||||
|
onOpenAutoFocus={onOpenAutoFocus}
|
||||||
|
>
|
||||||
|
<div className="DialogHeader" ref={headerRef}>
|
||||||
|
<div className="DialogTop">
|
||||||
|
<DialogTitle className="DialogTitle">
|
||||||
|
{t('modals.edit_team.title')}
|
||||||
|
</DialogTitle>
|
||||||
|
</div>
|
||||||
|
<DialogClose className="DialogClose" asChild>
|
||||||
|
<span>
|
||||||
|
<CrossIcon />
|
||||||
|
</span>
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Content">
|
||||||
|
{segmentedControl()}
|
||||||
|
<div className="Fields">
|
||||||
|
{currentSegment === 0 && infoPage()}
|
||||||
|
{currentSegment === 1 && propertiesPage()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="DialogFooter" ref={footerRef}>
|
||||||
|
<div className="Left"></div>
|
||||||
|
<div className="Right Buttons Spaced">
|
||||||
|
<Button
|
||||||
|
contained={true}
|
||||||
|
text={t('buttons.cancel')}
|
||||||
|
onClick={openChange}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
contained={true}
|
||||||
|
rightAccessoryIcon={<CheckIcon />}
|
||||||
|
text={t('modals.edit_team.buttons.confirm')}
|
||||||
|
onClick={updateDetails}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPartyModal
|
||||||
|
|
@ -119,6 +119,23 @@ const Party = (props: Props) => {
|
||||||
.then((response) => storeParty(response.data.party))
|
.then((response) => storeParty(response.data.party))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateParty(details: DetailsObject) {
|
||||||
|
const payload = formatDetailsObject(details)
|
||||||
|
|
||||||
|
if (props.team && props.team.id) {
|
||||||
|
return await api.endpoints.parties
|
||||||
|
.update(props.team.id, payload)
|
||||||
|
.then((response) => storeParty(response.data.party))
|
||||||
|
.catch((error) => {
|
||||||
|
const data = error.response.data
|
||||||
|
if (data.errors && Object.keys(data.errors).includes('guidebooks')) {
|
||||||
|
const message = t('errors.validation.guidebooks')
|
||||||
|
setErrorMessage(message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Methods: Updating the party's details
|
// Methods: Updating the party's details
|
||||||
async function updateDetails(details: DetailsObject) {
|
async function updateDetails(details: DetailsObject) {
|
||||||
if (!props.team) return await createParty(details)
|
if (!props.team) return await createParty(details)
|
||||||
|
|
@ -131,10 +148,10 @@ const Party = (props: Props) => {
|
||||||
const mappings: { [key: string]: string } = {
|
const mappings: { [key: string]: string } = {
|
||||||
name: 'name',
|
name: 'name',
|
||||||
description: 'description',
|
description: 'description',
|
||||||
raid: 'raid_id',
|
|
||||||
chargeAttack: 'charge_attack',
|
chargeAttack: 'charge_attack',
|
||||||
fullAuto: 'full_auto',
|
fullAuto: 'full_auto',
|
||||||
autoGuard: 'auto_guard',
|
autoGuard: 'auto_guard',
|
||||||
|
autoSummon: 'auto_summon',
|
||||||
clearTime: 'clear_time',
|
clearTime: 'clear_time',
|
||||||
buttonCount: 'button_count',
|
buttonCount: 'button_count',
|
||||||
chainCount: 'chain_count',
|
chainCount: 'chain_count',
|
||||||
|
|
@ -152,6 +169,8 @@ const Party = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (details.raid) payload.raid_id = details.raid.id
|
||||||
|
|
||||||
if (Object.keys(payload).length >= 1) {
|
if (Object.keys(payload).length >= 1) {
|
||||||
return { party: payload }
|
return { party: payload }
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -159,23 +178,6 @@ const Party = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateParty(details: DetailsObject) {
|
|
||||||
const payload = formatDetailsObject(details)
|
|
||||||
|
|
||||||
if (props.team && props.team.id) {
|
|
||||||
return await api.endpoints.parties
|
|
||||||
.update(props.team.id, payload)
|
|
||||||
.then((response) => storeParty(response.data.party))
|
|
||||||
.catch((error) => {
|
|
||||||
const data = error.response.data
|
|
||||||
if (data.errors && Object.keys(data.errors).includes('guidebooks')) {
|
|
||||||
const message = t('errors.validation.guidebooks')
|
|
||||||
setErrorMessage(message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelAlert() {
|
function cancelAlert() {
|
||||||
setErrorMessage('')
|
setErrorMessage('')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import clonedeep from 'lodash.clonedeep'
|
import clonedeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
|
|
@ -9,23 +8,13 @@ import LiteYouTubeEmbed from 'react-lite-youtube-embed'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import reactStringReplace from 'react-string-replace'
|
import reactStringReplace from 'react-string-replace'
|
||||||
|
|
||||||
import Button from '~components/common/Button'
|
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/GridRepCollection'
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/GridRep'
|
||||||
import Tooltip from '~components/common/Tooltip'
|
|
||||||
import TextFieldset from '~components/common/TextFieldset'
|
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
import { appState, initialAppState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
import { formatTimeAgo } from '~utils/timeAgo'
|
|
||||||
import { youtube } from '~utils/youtube'
|
import { youtube } from '~utils/youtube'
|
||||||
|
|
||||||
import CheckIcon from '~public/icons/Check.svg'
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
|
||||||
import EllipsisIcon from '~public/icons/Ellipsis.svg'
|
|
||||||
import EditIcon from '~public/icons/Edit.svg'
|
|
||||||
import RemixIcon from '~public/icons/Remix.svg'
|
|
||||||
|
|
||||||
import type { DetailsObject } from 'types'
|
import type { DetailsObject } from 'types'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
@ -45,10 +34,7 @@ const PartyDetails = (props: Props) => {
|
||||||
const youtubeUrlRegex =
|
const youtubeUrlRegex =
|
||||||
/(?:https:\/\/www\.youtube\.com\/watch\?v=|https:\/\/youtu\.be\/)([\w-]+)/g
|
/(?:https:\/\/www\.youtube\.com\/watch\?v=|https:\/\/youtu\.be\/)([\w-]+)/g
|
||||||
|
|
||||||
const descriptionInput = React.createRef<HTMLTextAreaElement>()
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [alertOpen, setAlertOpen] = useState(false)
|
|
||||||
|
|
||||||
const [remixes, setRemixes] = useState<Party[]>([])
|
const [remixes, setRemixes] = useState<Party[]>([])
|
||||||
const [embeddedDescription, setEmbeddedDescription] =
|
const [embeddedDescription, setEmbeddedDescription] =
|
||||||
|
|
@ -60,17 +46,6 @@ const PartyDetails = (props: Props) => {
|
||||||
Visible: !open,
|
Visible: !open,
|
||||||
})
|
})
|
||||||
|
|
||||||
const editableClasses = classNames({
|
|
||||||
PartyDetails: true,
|
|
||||||
Editable: true,
|
|
||||||
Visible: open,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [errors, setErrors] = useState<{ [key: string]: string }>({
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Extract the video IDs from the description
|
// Extract the video IDs from the description
|
||||||
if (appState.party.description) {
|
if (appState.party.description) {
|
||||||
|
|
@ -104,46 +79,12 @@ const PartyDetails = (props: Props) => {
|
||||||
}
|
}
|
||||||
}, [appState.party.description])
|
}, [appState.party.description])
|
||||||
|
|
||||||
function handleTextAreaChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
const { name, value } = event.target
|
|
||||||
let newErrors = errors
|
|
||||||
|
|
||||||
setErrors(newErrors)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchYoutubeData(videoId: string) {
|
async function fetchYoutubeData(videoId: string) {
|
||||||
return await youtube
|
return await youtube
|
||||||
.getVideoById(videoId, { maxResults: 1 })
|
.getVideoById(videoId, { maxResults: 1 })
|
||||||
.then((data) => data.items[0].snippet.localized.title)
|
.then((data) => data.items[0].snippet.localized.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleDetails() {
|
|
||||||
// Enabling this code will make live updates not work,
|
|
||||||
// but I'm not sure why it's here, so we're not going to remove it.
|
|
||||||
|
|
||||||
// if (name !== party.name) {
|
|
||||||
// const resetName = party.name ? party.name : ''
|
|
||||||
// setName(resetName)
|
|
||||||
// if (nameInput.current) nameInput.current.value = resetName
|
|
||||||
// }
|
|
||||||
setOpen(!open)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDetails(event: React.MouseEvent) {
|
|
||||||
const details: DetailsObject = {
|
|
||||||
description: descriptionInput.current?.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
props.updateCallback(details)
|
|
||||||
toggleDetails()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClick() {
|
|
||||||
setAlertOpen(!alertOpen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods: Navigation
|
// Methods: Navigation
|
||||||
function goTo(shortcode?: string) {
|
function goTo(shortcode?: string) {
|
||||||
if (shortcode) router.push(`/p/${shortcode}`)
|
if (shortcode) router.push(`/p/${shortcode}`)
|
||||||
|
|
@ -232,46 +173,6 @@ const PartyDetails = (props: Props) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const editable = () => {
|
|
||||||
return (
|
|
||||||
<section className={editableClasses}>
|
|
||||||
<TextFieldset
|
|
||||||
fieldName="name"
|
|
||||||
placeholder={
|
|
||||||
'Write your notes here\n\n\nWatch out for the 50% trigger!\nMake sure to click Fediel’s 3 first\nGood luck with RNG!'
|
|
||||||
}
|
|
||||||
value={props.party?.description}
|
|
||||||
onChange={handleTextAreaChange}
|
|
||||||
error={errors.description}
|
|
||||||
ref={descriptionInput}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="bottom">
|
|
||||||
<div className="left">
|
|
||||||
{router.pathname !== '/new' ? (
|
|
||||||
<Button
|
|
||||||
leftAccessoryIcon={<CrossIcon />}
|
|
||||||
className="Blended medium destructive"
|
|
||||||
onClick={handleClick}
|
|
||||||
text={t('buttons.delete')}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="right">
|
|
||||||
<Button text={t('buttons.cancel')} onClick={toggleDetails} />
|
|
||||||
<Button
|
|
||||||
leftAccessoryIcon={<CheckIcon className="Check" />}
|
|
||||||
text={t('buttons.save_info')}
|
|
||||||
onClick={updateDetails}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const readOnly = () => {
|
const readOnly = () => {
|
||||||
return (
|
return (
|
||||||
<section className={readOnlyClasses}>
|
<section className={readOnlyClasses}>
|
||||||
|
|
@ -291,10 +192,7 @@ const PartyDetails = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="DetailsWrapper">
|
<section className="DetailsWrapper">{readOnly()}</section>
|
||||||
{readOnly()}
|
|
||||||
{editable()}
|
|
||||||
</section>
|
|
||||||
{remixes && remixes.length > 0 ? remixSection() : ''}
|
{remixes && remixes.length > 0 ? remixSection() : ''}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState, ChangeEvent, KeyboardEvent } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
@ -6,21 +6,16 @@ import { useTranslation } from 'next-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import Button from '~components/common/Button'
|
import Button from '~components/common/Button'
|
||||||
import CharLimitedFieldset from '~components/common/CharLimitedFieldset'
|
|
||||||
import DurationInput from '~components/common/DurationInput'
|
|
||||||
import Input from '~components/common/Input'
|
|
||||||
import RaidCombobox from '~components/raids/RaidCombobox'
|
|
||||||
import Switch from '~components/common/Switch'
|
|
||||||
import Tooltip from '~components/common/Tooltip'
|
import Tooltip from '~components/common/Tooltip'
|
||||||
import Token from '~components/common/Token'
|
import Token from '~components/common/Token'
|
||||||
|
|
||||||
|
import EditPartyModal from '~components/party/EditPartyModal'
|
||||||
import PartyDropdown from '~components/party/PartyDropdown'
|
import PartyDropdown from '~components/party/PartyDropdown'
|
||||||
|
|
||||||
import { accountState } from '~utils/accountState'
|
import { accountState } from '~utils/accountState'
|
||||||
import { appState, initialAppState } from '~utils/appState'
|
import { appState, initialAppState } from '~utils/appState'
|
||||||
import { formatTimeAgo } from '~utils/timeAgo'
|
import { formatTimeAgo } from '~utils/timeAgo'
|
||||||
|
|
||||||
import CheckIcon from '~public/icons/Check.svg'
|
|
||||||
import EditIcon from '~public/icons/Edit.svg'
|
import EditIcon from '~public/icons/Edit.svg'
|
||||||
import RemixIcon from '~public/icons/Remix.svg'
|
import RemixIcon from '~public/icons/Remix.svg'
|
||||||
import SaveIcon from '~public/icons/Save.svg'
|
import SaveIcon from '~public/icons/Save.svg'
|
||||||
|
|
@ -41,7 +36,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PartyHeader = (props: Props) => {
|
const PartyHeader = (props: Props) => {
|
||||||
const { party, raids } = useSnapshot(appState)
|
const { party } = useSnapshot(appState)
|
||||||
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -49,12 +44,7 @@ const PartyHeader = (props: Props) => {
|
||||||
|
|
||||||
const { party: partySnapshot } = useSnapshot(appState)
|
const { party: partySnapshot } = useSnapshot(appState)
|
||||||
|
|
||||||
const nameInput = React.createRef<HTMLInputElement>()
|
|
||||||
const descriptionInput = React.createRef<HTMLTextAreaElement>()
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
const [alertOpen, setAlertOpen] = useState(false)
|
|
||||||
|
|
||||||
const [chargeAttack, setChargeAttack] = useState(true)
|
const [chargeAttack, setChargeAttack] = useState(true)
|
||||||
const [fullAuto, setFullAuto] = useState(false)
|
const [fullAuto, setFullAuto] = useState(false)
|
||||||
|
|
@ -65,18 +55,9 @@ const PartyHeader = (props: Props) => {
|
||||||
const [turnCount, setTurnCount] = useState<number | undefined>(undefined)
|
const [turnCount, setTurnCount] = useState<number | undefined>(undefined)
|
||||||
const [clearTime, setClearTime] = useState(0)
|
const [clearTime, setClearTime] = useState(0)
|
||||||
|
|
||||||
const [raidSlug, setRaidSlug] = useState('')
|
const classes = classNames({
|
||||||
|
|
||||||
const readOnlyClasses = classNames({
|
|
||||||
PartyDetails: true,
|
PartyDetails: true,
|
||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
Visible: !open,
|
|
||||||
})
|
|
||||||
|
|
||||||
const editableClasses = classNames({
|
|
||||||
PartyDetails: true,
|
|
||||||
Editable: true,
|
|
||||||
Visible: open,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const userClass = classNames({
|
const userClass = classNames({
|
||||||
|
|
@ -93,11 +74,6 @@ const PartyHeader = (props: Props) => {
|
||||||
light: party && party.element == 6,
|
light: party && party.element == 6,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<{ [key: string]: string }>({
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.party) {
|
if (props.party) {
|
||||||
setName(props.party.name)
|
setName(props.party.name)
|
||||||
|
|
@ -130,112 +106,6 @@ const PartyHeader = (props: Props) => {
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
const { name, value } = event.target
|
|
||||||
setName(value)
|
|
||||||
|
|
||||||
let newErrors = errors
|
|
||||||
setErrors(newErrors)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleChargeAttackChanged(checked: boolean) {
|
|
||||||
setChargeAttack(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFullAutoChanged(checked: boolean) {
|
|
||||||
setFullAuto(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAutoGuardChanged(checked: boolean) {
|
|
||||||
setAutoGuard(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClearTimeInput(value: number) {
|
|
||||||
if (!isNaN(value)) setClearTime(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTurnCountInput(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const value = parseInt(event.currentTarget.value)
|
|
||||||
if (!isNaN(value)) setTurnCount(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleButtonCountInput(event: ChangeEvent<HTMLInputElement>) {
|
|
||||||
const value = parseInt(event.currentTarget.value)
|
|
||||||
if (!isNaN(value)) setButtonCount(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleChainCountInput(event: ChangeEvent<HTMLInputElement>) {
|
|
||||||
const value = parseInt(event.currentTarget.value)
|
|
||||||
if (!isNaN(value)) setChainCount(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleInputKeyDown(event: KeyboardEvent<HTMLInputElement>) {
|
|
||||||
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
|
|
||||||
// Allow the key to be processed normally
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current value
|
|
||||||
const input = event.currentTarget
|
|
||||||
let value = event.currentTarget.value
|
|
||||||
|
|
||||||
// Check if the key that was pressed is the backspace key
|
|
||||||
if (event.key === 'Backspace') {
|
|
||||||
// Remove the colon if the value is "12:"
|
|
||||||
if (value.length === 4) {
|
|
||||||
value = value.slice(0, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the backspace key to be processed normally
|
|
||||||
input.value = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the key that was pressed is the tab key
|
|
||||||
if (event.key === 'Tab') {
|
|
||||||
// Allow the tab key to be processed normally
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the character that was entered and check if it is numeric
|
|
||||||
const char = parseInt(event.key)
|
|
||||||
const isNumber = !isNaN(char)
|
|
||||||
|
|
||||||
// Check if the character should be accepted or rejected
|
|
||||||
const numberValue = parseInt(`${value}${char}`)
|
|
||||||
const minValue = parseInt(event.currentTarget.min)
|
|
||||||
const maxValue = parseInt(event.currentTarget.max)
|
|
||||||
|
|
||||||
if (!isNumber || numberValue < minValue || numberValue > maxValue) {
|
|
||||||
// Reject the character if it isn't a number,
|
|
||||||
// or if it exceeds the min and max values
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleDetails() {
|
|
||||||
// Enabling this code will make live updates not work,
|
|
||||||
// but I'm not sure why it's here, so we're not going to remove it.
|
|
||||||
|
|
||||||
// if (name !== party.name) {
|
|
||||||
// const resetName = party.name ? party.name : ''
|
|
||||||
// setName(resetName)
|
|
||||||
// if (nameInput.current) nameInput.current.value = resetName
|
|
||||||
// }
|
|
||||||
setOpen(!open)
|
|
||||||
}
|
|
||||||
|
|
||||||
function receiveRaid(raid?: Raid) {
|
|
||||||
if (raid) setRaidSlug(raid?.slug)
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchValue(value: boolean) {
|
|
||||||
if (value) return 'on'
|
|
||||||
else return 'off'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions: Favorites
|
// Actions: Favorites
|
||||||
function toggleFavorite() {
|
function toggleFavorite() {
|
||||||
if (appState.party.favorited) unsaveFavorite()
|
if (appState.party.favorited) unsaveFavorite()
|
||||||
|
|
@ -258,28 +128,6 @@ const PartyHeader = (props: Props) => {
|
||||||
else console.error('Failed to unsave team: No party ID')
|
else console.error('Failed to unsave team: No party ID')
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDetails(event: React.MouseEvent) {
|
|
||||||
const descriptionValue = descriptionInput.current?.value
|
|
||||||
const allRaids = appState.raidGroups.flatMap((group) => group.raids)
|
|
||||||
const raid = allRaids.find((raid) => raid.slug === raidSlug)
|
|
||||||
|
|
||||||
const details: DetailsObject = {
|
|
||||||
fullAuto: fullAuto,
|
|
||||||
autoGuard: autoGuard,
|
|
||||||
chargeAttack: chargeAttack,
|
|
||||||
clearTime: clearTime,
|
|
||||||
buttonCount: buttonCount,
|
|
||||||
turnCount: turnCount,
|
|
||||||
chainCount: chainCount,
|
|
||||||
name: name,
|
|
||||||
description: descriptionValue,
|
|
||||||
raid: raid,
|
|
||||||
}
|
|
||||||
|
|
||||||
props.updateCallback(details)
|
|
||||||
toggleDetails()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods: Navigation
|
// Methods: Navigation
|
||||||
function goTo(shortcode?: string) {
|
function goTo(shortcode?: string) {
|
||||||
if (shortcode) router.push(`/p/${shortcode}`)
|
if (shortcode) router.push(`/p/${shortcode}`)
|
||||||
|
|
@ -487,145 +335,6 @@ const PartyHeader = (props: Props) => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const editable = () => {
|
|
||||||
return (
|
|
||||||
<section className={editableClasses}>
|
|
||||||
<CharLimitedFieldset
|
|
||||||
fieldName="name"
|
|
||||||
placeholder="Name your team"
|
|
||||||
value={props.party?.name}
|
|
||||||
limit={50}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
error={errors.name}
|
|
||||||
ref={nameInput}
|
|
||||||
/>
|
|
||||||
<RaidCombobox
|
|
||||||
showAllRaidsOption={false}
|
|
||||||
currentRaid={props.party?.raid ? props.party?.raid : undefined}
|
|
||||||
onChange={receiveRaid}
|
|
||||||
/>
|
|
||||||
<ul className="SwitchToggleGroup DetailToggleGroup">
|
|
||||||
<li className="Ougi ToggleSection">
|
|
||||||
<label htmlFor="ougi">
|
|
||||||
<span>{t('party.details.labels.charge_attack')}</span>
|
|
||||||
<div>
|
|
||||||
<Switch
|
|
||||||
name="charge_attack"
|
|
||||||
onCheckedChange={handleChargeAttackChanged}
|
|
||||||
value={switchValue(chargeAttack)}
|
|
||||||
checked={chargeAttack}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="FullAuto ToggleSection">
|
|
||||||
<label htmlFor="full_auto">
|
|
||||||
<span>{t('party.details.labels.full_auto')}</span>
|
|
||||||
<div>
|
|
||||||
<Switch
|
|
||||||
onCheckedChange={handleFullAutoChanged}
|
|
||||||
name="full_auto"
|
|
||||||
value={switchValue(fullAuto)}
|
|
||||||
checked={fullAuto}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="AutoGuard ToggleSection">
|
|
||||||
<label htmlFor="auto_guard">
|
|
||||||
<span>{t('party.details.labels.auto_guard')}</span>
|
|
||||||
<div>
|
|
||||||
<Switch
|
|
||||||
onCheckedChange={handleAutoGuardChanged}
|
|
||||||
name="auto_guard"
|
|
||||||
value={switchValue(autoGuard)}
|
|
||||||
disabled={!fullAuto}
|
|
||||||
checked={autoGuard}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul className="InputToggleGroup DetailToggleGroup">
|
|
||||||
<li className="InputSection">
|
|
||||||
<label htmlFor="auto_guard">
|
|
||||||
<span>{t('party.details.labels.button_chain')}</span>
|
|
||||||
<div className="Input Bound">
|
|
||||||
<Input
|
|
||||||
name="buttons"
|
|
||||||
type="number"
|
|
||||||
placeholder="0"
|
|
||||||
value={`${buttonCount}`}
|
|
||||||
min="0"
|
|
||||||
max="99"
|
|
||||||
onChange={handleButtonCountInput}
|
|
||||||
onKeyDown={handleInputKeyDown}
|
|
||||||
/>
|
|
||||||
<span>b</span>
|
|
||||||
<Input
|
|
||||||
name="chains"
|
|
||||||
type="number"
|
|
||||||
placeholder="0"
|
|
||||||
min="0"
|
|
||||||
max="99"
|
|
||||||
value={`${chainCount}`}
|
|
||||||
onChange={handleChainCountInput}
|
|
||||||
onKeyDown={handleInputKeyDown}
|
|
||||||
/>
|
|
||||||
<span>c</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="InputSection">
|
|
||||||
<label htmlFor="auto_guard">
|
|
||||||
<span>{t('party.details.labels.turn_count')}</span>
|
|
||||||
<Input
|
|
||||||
name="turn_count"
|
|
||||||
className="AlignRight Bound"
|
|
||||||
type="number"
|
|
||||||
step="1"
|
|
||||||
min="1"
|
|
||||||
max="999"
|
|
||||||
placeholder="0"
|
|
||||||
value={`${turnCount}`}
|
|
||||||
onChange={handleTurnCountInput}
|
|
||||||
onKeyDown={handleInputKeyDown}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="InputSection">
|
|
||||||
<label htmlFor="auto_guard">
|
|
||||||
<span>{t('party.details.labels.clear_time')}</span>
|
|
||||||
<div>
|
|
||||||
<DurationInput
|
|
||||||
name="clear_time"
|
|
||||||
className="Bound"
|
|
||||||
placeholder="00:00"
|
|
||||||
value={clearTime}
|
|
||||||
onValueChange={(value: number) => handleClearTimeInput(value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div className="bottom">
|
|
||||||
<div className="right">
|
|
||||||
<Button text={t('buttons.cancel')} onClick={toggleDetails} />
|
|
||||||
<Button
|
|
||||||
leftAccessoryIcon={<CheckIcon className="Check" />}
|
|
||||||
text={t('buttons.save_info')}
|
|
||||||
onClick={updateDetails}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const readOnly = () => {
|
|
||||||
return <section className={readOnlyClasses}>{renderTokens()}</section>
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -666,11 +375,15 @@ const PartyHeader = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
{party.editable ? (
|
{party.editable ? (
|
||||||
<div className="Right">
|
<div className="Right">
|
||||||
|
<EditPartyModal
|
||||||
|
party={props.party}
|
||||||
|
updateCallback={props.updateCallback}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
leftAccessoryIcon={<EditIcon />}
|
leftAccessoryIcon={<EditIcon />}
|
||||||
text={t('buttons.show_info')}
|
text={t('buttons.show_info')}
|
||||||
onClick={toggleDetails}
|
|
||||||
/>
|
/>
|
||||||
|
</EditPartyModal>
|
||||||
<PartyDropdown
|
<PartyDropdown
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
deleteTeamCallback={props.deleteCallback}
|
deleteTeamCallback={props.deleteCallback}
|
||||||
|
|
@ -684,8 +397,7 @@ const PartyHeader = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{readOnly()}
|
<section className={classes}>{renderTokens()}</section>
|
||||||
{editable()}
|
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
.Raids {
|
.Raids {
|
||||||
border-bottom-left-radius: $card-corner;
|
border-bottom-left-radius: $card-corner;
|
||||||
border-bottom-right-radius: $card-corner;
|
border-bottom-right-radius: $card-corner;
|
||||||
height: 50vh;
|
height: 36vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
padding: 0 $unit;
|
padding: 0 $unit;
|
||||||
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.DetailsWrapper .PartyDetails.Editable .Raid.SelectTrigger {
|
.DetailsWrapper .PartyDetails.Editable .Raid.SelectTrigger,
|
||||||
|
.EditTeam .Raid.SelectTrigger {
|
||||||
|
background: var(--input-bound-bg);
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 11px;
|
padding-bottom: 11px;
|
||||||
|
|
@ -139,9 +141,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ExtraIndicator {
|
.ExtraIndicator {
|
||||||
background: var(--extra-purple-bg);
|
background: var(--extra-purple-secondary);
|
||||||
border-radius: $full-corner;
|
border-radius: $full-corner;
|
||||||
color: var(--extra-purple-text);
|
color: $grey-100;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-weight: $bold;
|
font-weight: $bold;
|
||||||
font-size: $font-tiny;
|
font-size: $font-tiny;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ interface Props {
|
||||||
currentRaid?: Raid
|
currentRaid?: Raid
|
||||||
defaultRaid?: Raid
|
defaultRaid?: Raid
|
||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
|
tabIndex?: number
|
||||||
onChange?: (raid?: Raid) => void
|
onChange?: (raid?: Raid) => void
|
||||||
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +47,7 @@ const untitledGroup: RaidGroup = {
|
||||||
section: 0,
|
section: 0,
|
||||||
order: 0,
|
order: 0,
|
||||||
extra: false,
|
extra: false,
|
||||||
|
guidebooks: false,
|
||||||
raids: [],
|
raids: [],
|
||||||
difficulty: 0,
|
difficulty: 0,
|
||||||
hl: false,
|
hl: false,
|
||||||
|
|
@ -114,7 +116,6 @@ const RaidCombobox = (props: Props) => {
|
||||||
// Set current raid and section when the current raid changes
|
// Set current raid and section when the current raid changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.currentRaid) {
|
if (props.currentRaid) {
|
||||||
console.log('We are here with a raid')
|
|
||||||
setCurrentRaid(props.currentRaid)
|
setCurrentRaid(props.currentRaid)
|
||||||
setCurrentSection(props.currentRaid.group.section)
|
setCurrentSection(props.currentRaid.group.section)
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +184,6 @@ const RaidCombobox = (props: Props) => {
|
||||||
const { top: itemTop } = node.getBoundingClientRect()
|
const { top: itemTop } = node.getBoundingClientRect()
|
||||||
|
|
||||||
listRef.current.scrollTop = itemTop - listTop
|
listRef.current.scrollTop = itemTop - listTop
|
||||||
console.log('Focusing node')
|
|
||||||
node.focus()
|
node.focus()
|
||||||
setScrolled(true)
|
setScrolled(true)
|
||||||
}
|
}
|
||||||
|
|
@ -534,6 +534,7 @@ const RaidCombobox = (props: Props) => {
|
||||||
onOpenChange={toggleOpen}
|
onOpenChange={toggleOpen}
|
||||||
placeholder={t('raids.placeholder')}
|
placeholder={t('raids.placeholder')}
|
||||||
trigger={{ className: 'Raid' }}
|
trigger={{ className: 'Raid' }}
|
||||||
|
triggerTabIndex={props.tabIndex}
|
||||||
value={renderTriggerContent()}
|
value={renderTriggerContent()}
|
||||||
>
|
>
|
||||||
<Command className="Raid Combobox">
|
<Command className="Raid Combobox">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.ExtraIndicator {
|
.ExtraIndicator {
|
||||||
background: var(--extra-purple-primary);
|
background: var(--extra-purple-secondary);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,6 +15,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Selected .ExtraIndicator {
|
||||||
|
background: var(--extra-purple-secondary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.Text {
|
.Text {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -377,23 +377,24 @@ const WeaponGrid = (props: Props) => {
|
||||||
|
|
||||||
const extraElement = (
|
const extraElement = (
|
||||||
<ExtraContainer>
|
<ExtraContainer>
|
||||||
|
{appState.party.raid && appState.party.raid.group.extra && (
|
||||||
<ExtraWeaponsGrid
|
<ExtraWeaponsGrid
|
||||||
grid={appState.grid.weapons.allWeapons}
|
grid={appState.grid.weapons.allWeapons}
|
||||||
enabled={appState.party.extra}
|
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
offset={numWeapons}
|
offset={numWeapons}
|
||||||
removeWeapon={removeWeapon}
|
removeWeapon={removeWeapon}
|
||||||
updateExtra={props.updateExtra}
|
|
||||||
updateObject={receiveWeaponFromSearch}
|
updateObject={receiveWeaponFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{appState.party.raid && appState.party.raid.group.guidebooks && (
|
||||||
<GuidebooksGrid
|
<GuidebooksGrid
|
||||||
grid={appState.party.guidebooks}
|
grid={appState.party.guidebooks}
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
offset={numWeapons}
|
|
||||||
removeGuidebook={removeGuidebook}
|
removeGuidebook={removeGuidebook}
|
||||||
updateObject={receiveGuidebookFromSearch}
|
updateObject={receiveGuidebookFromSearch}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</ExtraContainer>
|
</ExtraContainer>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,39 @@
|
||||||
"confirm": "Confirm"
|
"confirm": "Confirm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"edit_team": {
|
||||||
|
"title": "Edit team details",
|
||||||
|
"segments": {
|
||||||
|
"basic_info": "Basic info",
|
||||||
|
"properties": "Battle settings"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"add_field": "Add field",
|
||||||
|
"confirm": "Save details"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": "Name",
|
||||||
|
"description": "Description",
|
||||||
|
"raid": "Battle",
|
||||||
|
"extra": "Additional weapons",
|
||||||
|
"charge_attack": "Charge Attack",
|
||||||
|
"full_auto": "Full Auto",
|
||||||
|
"auto_guard": "Auto Guard",
|
||||||
|
"auto_summon": "Auto Summon",
|
||||||
|
"turn_count": "Number of turns",
|
||||||
|
"button_count": "Number of buttons pressed",
|
||||||
|
"chain_count": "Number of chains",
|
||||||
|
"clear_time": "Clear time"
|
||||||
|
},
|
||||||
|
"descriptions": {
|
||||||
|
"extra": "Additional weapons are controlled by the selected battle"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"name": "Name your team"
|
||||||
|
},
|
||||||
|
"extra_notice": "You can add additional weapons to this team",
|
||||||
|
"extra_notice_guidebooks": "You can add additional weapons and Sephira Guidebooks to this team"
|
||||||
|
},
|
||||||
"delete_team": {
|
"delete_team": {
|
||||||
"title": "Delete team",
|
"title": "Delete team",
|
||||||
"description": "Are you sure you want to permanently delete this team?",
|
"description": "Are you sure you want to permanently delete this team?",
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,35 @@
|
||||||
"confirm": "入れ替える"
|
"confirm": "入れ替える"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"edit_team": {
|
||||||
|
"title": "編成の詳細を編集",
|
||||||
|
"segments": {
|
||||||
|
"basic_info": "基本情報",
|
||||||
|
"properties": "バトル設定・結果"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"add_field": "フィールドを追加",
|
||||||
|
"confirm": "詳細を保存する"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": "編成名",
|
||||||
|
"description": "説明",
|
||||||
|
"raid": "バトル",
|
||||||
|
"charge_attack": "奥義",
|
||||||
|
"full_auto": "フルオート",
|
||||||
|
"auto_guard": "オートガード",
|
||||||
|
"auto_summon": "オート召喚",
|
||||||
|
"turn_count": "経過ターン",
|
||||||
|
"button_count": "ポチ数",
|
||||||
|
"chain_count": "チェイン数",
|
||||||
|
"clear_time": "討伐時間"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"name": "編成に名前を付ける"
|
||||||
|
},
|
||||||
|
"extra_notice": "Additional Weapons枠に武器を装備することはできます",
|
||||||
|
"extra_notice_guidebooks": "Additional Weaponsまたはセフィラ導本を装備することはできます"
|
||||||
|
},
|
||||||
"delete_team": {
|
"delete_team": {
|
||||||
"title": "編成を削除しますか",
|
"title": "編成を削除しますか",
|
||||||
"description": "編成を削除する操作は取り消せません。",
|
"description": "編成を削除する操作は取り消せません。",
|
||||||
|
|
|
||||||
1
types/Party.d.ts
vendored
1
types/Party.d.ts
vendored
|
|
@ -20,6 +20,7 @@ interface Party {
|
||||||
raid: Raid
|
raid: Raid
|
||||||
full_auto: boolean
|
full_auto: boolean
|
||||||
auto_guard: boolean
|
auto_guard: boolean
|
||||||
|
auto_summon: boolean
|
||||||
charge_attack: boolean
|
charge_attack: boolean
|
||||||
clear_time: number
|
clear_time: number
|
||||||
button_count?: number
|
button_count?: number
|
||||||
|
|
|
||||||
1
types/RaidGroup.d.ts
vendored
1
types/RaidGroup.d.ts
vendored
|
|
@ -10,5 +10,6 @@ interface RaidGroup {
|
||||||
section: number
|
section: number
|
||||||
order: number
|
order: number
|
||||||
extra: boolean
|
extra: boolean
|
||||||
|
guidebooks: boolean
|
||||||
hl: boolean
|
hl: boolean
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
types/index.d.ts
vendored
1
types/index.d.ts
vendored
|
|
@ -29,6 +29,7 @@ export type DetailsObject = {
|
||||||
[key: string]: boolean | number | string | string[] | Raid | undefined
|
[key: string]: boolean | number | string | string[] | Raid | undefined
|
||||||
fullAuto?: boolean
|
fullAuto?: boolean
|
||||||
autoGuard?: boolean
|
autoGuard?: boolean
|
||||||
|
autoSummon?: boolean
|
||||||
chargeAttack?: boolean
|
chargeAttack?: boolean
|
||||||
clearTime?: number
|
clearTime?: number
|
||||||
buttonCount?: number
|
buttonCount?: number
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue