mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Webatrice: card import wizard (#4397)
This commit is contained in:
parent
dde0f568d9
commit
36e5a399d5
41 changed files with 1479 additions and 35 deletions
4
webclient/src/components/Card/Card.css
Normal file
4
webclient/src/components/Card/Card.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
20
webclient/src/components/Card/Card.tsx
Normal file
20
webclient/src/components/Card/Card.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { CardDTO } from 'services';
|
||||
|
||||
import './Card.css';
|
||||
|
||||
interface CardProps {
|
||||
card: CardDTO;
|
||||
}
|
||||
|
||||
const Card = ({ card }: CardProps) => {
|
||||
const src = `https://api.scryfall.com/cards/${card?.identifiers?.scryfallId}?format=image`;
|
||||
|
||||
return card && (
|
||||
<img className="card" src={src} alt={card?.name} />
|
||||
);
|
||||
}
|
||||
|
||||
export default Card;
|
||||
45
webclient/src/components/CardDetails/CardDetails.css
Normal file
45
webclient/src/components/CardDetails/CardDetails.css
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
.cardDetails {
|
||||
padding: 10px;
|
||||
width: calc(400px * .716);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.cardDetails-card {
|
||||
height: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.cardDetails-attribute {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.cardDetails-attributes {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.cardDetails-attribute__label {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.cardDetails-attribute__value {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cardDetails-text {
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
background: rgba(0, 0, 0, .15);
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.cardDetails-text__flavor {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.cardDetails-text__current:not(:empty) + .cardDetails-text__flavor {
|
||||
margin-top: 10px;
|
||||
}
|
||||
130
webclient/src/components/CardDetails/CardDetails.tsx
Normal file
130
webclient/src/components/CardDetails/CardDetails.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { CardDTO } from 'services';
|
||||
|
||||
import Card from '../Card/Card';
|
||||
|
||||
import './CardDetails.css';
|
||||
|
||||
interface CardProps {
|
||||
card: CardDTO;
|
||||
}
|
||||
|
||||
// @TODO: add missing fields (loyalty, hand, etc)
|
||||
|
||||
const CardDetails = ({ card }: CardProps) => {
|
||||
return (
|
||||
<div className='cardDetails'>
|
||||
<div className='cardDetails-card'>
|
||||
<Card card={card} />
|
||||
</div>
|
||||
|
||||
{
|
||||
card && (
|
||||
<div>
|
||||
<div className='cardDetails-attributes'>
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Name:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.name}</span>
|
||||
</div>
|
||||
|
||||
{
|
||||
(!card.power && !card.toughness) ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>P/T:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.power || 0}/{card.toughness || 0}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.manaCost ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Cost:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.manaCost.replace(/\{|\}/g, '')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.convertedManaCost ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>CMC:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.convertedManaCost}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.colorIdentity?.length ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Identity:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.colorIdentity.join('')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.colors?.length ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Color(s):</span>
|
||||
<span className='cardDetails-attribute__value'>{card.colors.join('')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.types?.length ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Main Type:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.types.join(', ')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.type ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Type:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.type}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.side ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Side:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.side}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!card.layout ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Layout:</span>
|
||||
<span className='cardDetails-attribute__value'>{card.layout}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='cardDetails-text'>
|
||||
<div className='cardDetails-text__current'>
|
||||
{card.text?.trim()}
|
||||
</div>
|
||||
|
||||
<div className='cardDetails-text__flavor'>
|
||||
{card.flavorText?.trim()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CardDetails;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.dialog-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import React from "react";
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import { CardImportForm } from 'forms';
|
||||
|
||||
import './CardImportDialog.css';
|
||||
|
||||
const CardImportDialog = ({ classes, handleClose, isOpen }: any) => {
|
||||
const handleOnClose = () => {
|
||||
handleClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog onClose={handleOnClose} open={isOpen}>
|
||||
<DialogTitle disableTypography className="dialog-title">
|
||||
<Typography variant="h6">Import Cards</Typography>
|
||||
|
||||
{handleOnClose ? (
|
||||
<IconButton onClick={handleOnClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<CardImportForm onSubmit={handleOnClose}></CardImportForm>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardImportDialog;
|
||||
|
|
@ -19,6 +19,8 @@ import { Room, RouteEnum, User } from "types";
|
|||
import "./Header.css";
|
||||
import logo from "./logo.png";
|
||||
|
||||
import CardImportDialog from '../CardImportDialog/CardImportDialog';
|
||||
|
||||
class Header extends Component<HeaderProps> {
|
||||
state: HeaderState;
|
||||
options: string[] = [
|
||||
|
|
@ -29,12 +31,17 @@ class Header extends Component<HeaderProps> {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { anchorEl: null };
|
||||
this.state = {
|
||||
anchorEl: null,
|
||||
showCardImportDialog: false,
|
||||
};
|
||||
|
||||
this.handleMenuOpen = this.handleMenuOpen.bind(this);
|
||||
this.handleMenuItemClick = this.handleMenuItemClick.bind(this);
|
||||
this.handleMenuClose = this.handleMenuClose.bind(this);
|
||||
this.leaveRoom = this.leaveRoom.bind(this);
|
||||
this.openImportCardWizard = this.openImportCardWizard.bind(this);
|
||||
this.closeImportCardWizard = this.closeImportCardWizard.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
|
@ -65,9 +72,18 @@ class Header extends Component<HeaderProps> {
|
|||
RoomsService.leaveRoom(roomId);
|
||||
};
|
||||
|
||||
openImportCardWizard() {
|
||||
this.setState({ showCardImportDialog: true });
|
||||
this.handleMenuClose();
|
||||
}
|
||||
|
||||
closeImportCardWizard() {
|
||||
this.setState({ showCardImportDialog: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { joinedRooms, state, user } = this.props;
|
||||
const { anchorEl } = this.state;
|
||||
const { anchorEl, showCardImportDialog } = this.state;
|
||||
|
||||
let options = [ ...this.options ];
|
||||
|
||||
|
|
@ -156,6 +172,10 @@ class Header extends Component<HeaderProps> {
|
|||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
|
||||
<MenuItem key='Import Cards' onClick={(event) => this.openImportCardWizard()}>
|
||||
Import Cards
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -163,6 +183,11 @@ class Header extends Component<HeaderProps> {
|
|||
</div>
|
||||
) }
|
||||
</Toolbar>
|
||||
|
||||
<CardImportDialog
|
||||
isOpen={showCardImportDialog}
|
||||
handleClose={this.closeImportCardWizard}
|
||||
></CardImportDialog>
|
||||
</AppBar>
|
||||
)
|
||||
}
|
||||
|
|
@ -177,7 +202,8 @@ interface HeaderProps {
|
|||
}
|
||||
|
||||
interface HeaderState {
|
||||
anchorEl: Element
|
||||
anchorEl: Element;
|
||||
showCardImportDialog: boolean;
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
|
|
|||
4
webclient/src/components/Message/CardCallout.css
Normal file
4
webclient/src/components/Message/CardCallout.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.callout {
|
||||
font-weight: bold;
|
||||
color: green;
|
||||
}
|
||||
87
webclient/src/components/Message/CardCallout.tsx
Normal file
87
webclient/src/components/Message/CardCallout.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Popover from '@material-ui/core/Popover';
|
||||
|
||||
import { CardDTO, TokenDTO } from 'services';
|
||||
|
||||
import CardDetails from '../CardDetails/CardDetails';
|
||||
import TokenDetails from '../TokenDetails/TokenDetails';
|
||||
|
||||
import './CardCallout.css';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
popover: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
popoverContent: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const CardCallout = ({ name }) => {
|
||||
const classes = useStyles();
|
||||
const [card, setCard] = useState<CardDTO>(null);
|
||||
const [token, setToken] = useState<TokenDTO>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<Element>(null);
|
||||
|
||||
useMemo(async () => {
|
||||
const card = await CardDTO.get(name);
|
||||
if (card) {
|
||||
return setCard(card)
|
||||
}
|
||||
|
||||
const token = await TokenDTO.get(name);
|
||||
if (token) {
|
||||
return setToken(token);
|
||||
}
|
||||
}, [name]);
|
||||
|
||||
const handlePopoverOpen = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handlePopoverClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
return (
|
||||
<span className='callout'>
|
||||
<span
|
||||
onMouseEnter={handlePopoverOpen}
|
||||
onMouseLeave={handlePopoverClose}
|
||||
>{card?.name || token?.name?.value || name}</span>
|
||||
|
||||
{
|
||||
(card || token) && (
|
||||
<Popover
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handlePopoverClose}
|
||||
className={classes.popover}
|
||||
classes={{
|
||||
paper: classes.popoverContent,
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<div className="callout-card">
|
||||
{ card && ( <CardDetails card={card} /> ) }
|
||||
{ token && ( <TokenDetails token={token} /> ) }
|
||||
</div>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardCallout;
|
||||
3
webclient/src/components/Message/Message.css
Normal file
3
webclient/src/components/Message/Message.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.link {
|
||||
color: blue;
|
||||
}
|
||||
105
webclient/src/components/Message/Message.tsx
Normal file
105
webclient/src/components/Message/Message.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { NavLink, generatePath } from "react-router-dom";
|
||||
|
||||
import {
|
||||
RouteEnum,
|
||||
URL_REGEX,
|
||||
MESSAGE_SENDER_REGEX,
|
||||
MENTION_REGEX,
|
||||
CARD_CALLOUT_REGEX,
|
||||
CALLOUT_BOUNDARY_REGEX,
|
||||
} from 'types';
|
||||
|
||||
import CardCallout from './CardCallout';
|
||||
import './Message.css';
|
||||
|
||||
const Message = ({ message: { message, messageType, timeOf, timeReceived } }) => (
|
||||
<div className='message'>
|
||||
<div className='message__detail'>
|
||||
<ParsedMessage message={message} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ParsedMessage = ({ message }) => {
|
||||
const [messageChunks, setMessageChunks] = useState(null);
|
||||
const [name, setName] = useState(null);
|
||||
|
||||
useMemo(() => {
|
||||
const name = message.match(MESSAGE_SENDER_REGEX);
|
||||
|
||||
if (name) {
|
||||
setName(name[1]);
|
||||
}
|
||||
|
||||
setMessageChunks(parseMessage(message));
|
||||
}, [message]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ name && ( <strong><PlayerLink name={name} />:</strong> ) }
|
||||
{ messageChunks }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PlayerLink = ({ name, label = name }) => (
|
||||
<NavLink className="link" to={generatePath(RouteEnum.PLAYER, { name })}>
|
||||
{label}
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
function parseMessage(message) {
|
||||
return message.replace(MESSAGE_SENDER_REGEX, '')
|
||||
.split(CARD_CALLOUT_REGEX)
|
||||
.filter(chunk => !!chunk)
|
||||
.map(parseChunks);
|
||||
}
|
||||
|
||||
function parseChunks(chunk, index) {
|
||||
if (chunk.match(CARD_CALLOUT_REGEX)) {
|
||||
const name = chunk.replace(CALLOUT_BOUNDARY_REGEX, '').trim();
|
||||
return ( <CardCallout name={name} key={index}></CardCallout> );
|
||||
}
|
||||
|
||||
if (chunk.match(URL_REGEX)) {
|
||||
return parseUrlChunk(chunk);
|
||||
}
|
||||
|
||||
if (chunk.match(MENTION_REGEX)) {
|
||||
return parseMentionChunk(chunk);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
function parseUrlChunk(chunk) {
|
||||
return chunk.split(URL_REGEX)
|
||||
.filter(urlChunk => !!urlChunk)
|
||||
.map((urlChunk, index) => {
|
||||
if (urlChunk.match(URL_REGEX)) {
|
||||
return ( <a className='link' href={urlChunk} key={index} target='_blank' rel='noopener noreferrer'>{urlChunk}</a> );
|
||||
}
|
||||
|
||||
return urlChunk;
|
||||
});
|
||||
}
|
||||
|
||||
function parseMentionChunk(chunk) {
|
||||
return chunk.split(MENTION_REGEX)
|
||||
.filter(mentionChunk => !!mentionChunk)
|
||||
.map((mentionChunk, index) => {
|
||||
const mention = mentionChunk.match(MENTION_REGEX);
|
||||
|
||||
if (mention) {
|
||||
const name = mention[0].substr(1);
|
||||
return ( <PlayerLink name={name} label={mention} key={index} /> );
|
||||
}
|
||||
|
||||
return mentionChunk;
|
||||
});
|
||||
}
|
||||
|
||||
export default Message;
|
||||
4
webclient/src/components/Token/Token.css
Normal file
4
webclient/src/components/Token/Token.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.token {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
19
webclient/src/components/Token/Token.tsx
Normal file
19
webclient/src/components/Token/Token.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { TokenDTO } from 'services';
|
||||
|
||||
import './Token.css';
|
||||
|
||||
interface TokenProps {
|
||||
token: TokenDTO;
|
||||
}
|
||||
|
||||
const Token = ({ token }: TokenProps) => {
|
||||
const set = Array.isArray(token?.set) ? token?.set[0] : token?.set;
|
||||
return token && (
|
||||
<img className="token" src={set?.picURL} alt={token?.name?.value} />
|
||||
);
|
||||
}
|
||||
|
||||
export default Token;
|
||||
46
webclient/src/components/TokenDetails/TokenDetails.css
Normal file
46
webclient/src/components/TokenDetails/TokenDetails.css
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
.tokenDetails {
|
||||
padding: 10px;
|
||||
width: calc(400px * .716);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.tokenDetails-token {
|
||||
height: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tokenDetails-attribute {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.tokenDetails-attributes {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tokenDetails-attribute__label {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tokenDetails-attribute__value {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tokenDetails-text {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
padding: 5px;
|
||||
background: rgba(0, 0, 0, .15);
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.tokenDetails-text__flavor {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tokenDetails-text__current:not(:empty) + .tokenDetails-text__flavor {
|
||||
margin-top: 10px;
|
||||
}
|
||||
86
webclient/src/components/TokenDetails/TokenDetails.tsx
Normal file
86
webclient/src/components/TokenDetails/TokenDetails.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { TokenDTO } from 'services';
|
||||
|
||||
import Token from '../Token/Token';
|
||||
|
||||
import './TokenDetails.css';
|
||||
|
||||
interface TokenProps {
|
||||
token: TokenDTO;
|
||||
}
|
||||
|
||||
const TokenDetails = ({ token }: TokenProps) => {
|
||||
const props = token?.prop?.value;
|
||||
|
||||
return (
|
||||
<div className='tokenDetails'>
|
||||
<div className='tokenDetails-token'>
|
||||
<Token token={token} />
|
||||
</div>
|
||||
|
||||
{
|
||||
token && (
|
||||
<div>
|
||||
<div className='tokenDetails-attributes'>
|
||||
<div className='tokenDetails-attribute'>
|
||||
<span className='tokenDetails-attribute__label'>Name:</span>
|
||||
<span className='tokenDetails-attribute__value'>{token.name?.value}</span>
|
||||
</div>
|
||||
|
||||
{
|
||||
(!props.pt?.value) ? null : (
|
||||
<div className='tokenDetails-attribute'>
|
||||
<span className='tokenDetails-attribute__label'>P/T:</span>
|
||||
<span className='tokenDetails-attribute__value'>{props.pt.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!props.colors?.value ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Color(s):</span>
|
||||
<span className='cardDetails-attribute__value'>{props.colors.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!props.maintype?.value ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Main Type:</span>
|
||||
<span className='cardDetails-attribute__value'>{props.maintype.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!props.type?.value ? null : (
|
||||
<div className='cardDetails-attribute'>
|
||||
<span className='cardDetails-attribute__label'>Type:</span>
|
||||
<span className='cardDetails-attribute__value'>{props.type.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
!token.text?.value ? null : (
|
||||
<div className='tokenDetails-text'>
|
||||
<div className='tokenDetails-text__current'>
|
||||
{token.text.value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TokenDetails;
|
||||
|
|
@ -32,4 +32,4 @@ const Row = ({ data, index, style }) => (
|
|||
</div>
|
||||
);
|
||||
|
||||
export default VirtualList;
|
||||
export default VirtualList;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
// Common components
|
||||
export { default as Card } from './Card/Card';
|
||||
export { default as CardDetails } from './CardDetails/CardDetails';
|
||||
export { default as Header } from './Header/Header';
|
||||
export { default as InputField } from './InputField/InputField';
|
||||
export { default as InputAction } from './InputAction/InputAction';
|
||||
export { default as Message } from './Message/Message';
|
||||
export { default as VirtualList } from './VirtualList/VirtualList';
|
||||
export { default as UserDisplay} from './UserDisplay/UserDisplay';
|
||||
export { default as ThreePaneLayout } from './ThreePaneLayout/ThreePaneLayout';
|
||||
|
|
@ -13,3 +16,5 @@ export { default as ScrollToBottomOnChanges } from './ScrollToBottomOnChanges/Sc
|
|||
export { default as AuthGuard } from './Guard/AuthGuard';
|
||||
export { default as ModGuard} from './Guard/ModGuard';
|
||||
|
||||
// Dialogs
|
||||
export { default as CardImportDialog} from './CardImportDialog/CardImportDialog';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue