Skip to content

Commit

Permalink
webサーバー部分をcherry-pickしてvite風に修正。どこからも参照されていないコンポーネントがあるなどまだ未完成
Browse files Browse the repository at this point in the history
  • Loading branch information
kizahasi committed Feb 3, 2025
1 parent 2bcaecb commit 3b51de9
Show file tree
Hide file tree
Showing 31 changed files with 1,729 additions and 59 deletions.
Binary file added apps/web-server/public/assets/warning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/web-server/public/licenses.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ assets ディレクトリ内のいくつかのファイルは、次の素材を

<http://byronknoll.blogspot.com/2011/03/vector-playing-cards.html>

### 警告アイコンの画像

<https://publicdomainvectors.org/en/free-clipart/Warning-notification-sign-vector-image/8154.html>

## ライブラリのライセンス一覧

[licenses-npm-package.txt](./licenses-npm-package.txt) をご覧ください。なお、これらば Web サーバーから参照されているライブラリのライセンスの一覧であり、API サーバーなどは含まれていません。
Expand Down
135 changes: 135 additions & 0 deletions apps/web-server/src/components/models/card/Card/CardView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import {
State,
boardTemplate,
deckPieceCardTemplate,
deckTemplateCardTemplate,
} from '@flocon-trpg/core';
import classNames from 'classnames';
import { produce } from 'immer';
import React from 'react';
import { useGetCardBackImage } from '../../room/Room/subcomponents/hooks/useGetCardBackImage';
import { CardDescription } from './subcomponents/components/CardDescriptionView/CardDescriptionView';
import { CardImageView } from './subcomponents/components/CardImageView/CardImageView';
import { CardName } from './subcomponents/components/CardNameView/CardNameView';
import { Table } from '@/components/ui/Table/Table';
import { flex, flexColumn } from '@/styles/className';

type DeckTemplateCardState = State<typeof deckTemplateCardTemplate>;
type DeckPieceCardState = State<typeof deckPieceCardTemplate>;
type BoardState = State<typeof boardTemplate>;
type CardGroups = BoardState['cardGroups'];

export type DeckTemplateCardViewProps = {
card: DeckTemplateCardState;
/** nullishの場合は読み取り専用モードになります。 */
onChange?: (newValue: DeckTemplateCardState) => void;
};

export const DeckTemplateEditorView: React.FC<DeckTemplateCardViewProps> = ({ card, onChange }) => {
return (
<div className={classNames(flex, flexColumn)}>
<CardImageView
type="template"
image={card.face}
onChange={image => {
const newCard = produce(card, card => {
card.face = image;
});
onChange && onChange(newCard);
}}
/>
<CardImageView
type="template"
image={card.back}
onChange={image => {
const newCard = produce(card, card => {
card.back = image;
});
onChange && onChange(newCard);
}}
/>

<Table>
<CardName
name={card.name}
onChange={newValue => {
const newCard = produce(card, card => {
card.name = newValue;
});
onChange && onChange(newCard);
}}
/>
<CardDescription
description={card.description}
onChange={newValue => {
const newCard = produce(card, card => {
card.description = newValue;
});
onChange && onChange(newCard);
}}
/>
</Table>
</div>
);
};

export type DeckPieceCardViewProps = {
card: DeckPieceCardState;
cardGroups: CardGroups;
/** nullishの場合は読み取り専用モードになります。 */
onChange?: (newValue: DeckPieceCardState) => void;
};

export const DeckPieceCardView: React.FC<DeckPieceCardViewProps> = ({
card,
cardGroups,
onChange,
}) => {
const back = useGetCardBackImage(card, cardGroups);
return (
<div className={classNames(flex, flexColumn)}>
<div>
{
'もしカードが表になっていないときは、そのカードの表面の画像を変更することはできません。'
}
</div>
<CardImageView
type="piece"
image={card.face}
onChange={image => {
const newCard = produce(card, card => {
card.face = image;
});
onChange && onChange(newCard);
}}
/>
<CardImageView
type="piece"
image={back.value}
// TODO: 画像を変更するボタンを追加するか、変更する方法の説明を追加する
onChange={null}
/>

<Table>
<CardName
name={card.name}
onChange={newValue => {
const newCard = produce(card, card => {
card.name = newValue;
});
onChange && onChange(newCard);
}}
/>
<CardDescription
description={card.description}
onChange={newValue => {
const newCard = produce(card, card => {
card.description = newValue;
});
onChange && onChange(newCard);
}}
/>
</Table>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { CollaborativeInput } from '@/components/ui/CollaborativeInput/CollaborativeInput';
import { HelpMessageTooltip } from '@/components/ui/HelpMessageTooltip/HelpMessageTooltip';
import { TableRow } from '@/components/ui/Table/Table';

export const CardDescription: React.FC<{
description: string | undefined;
onChange?: (newValue: string) => void;
}> = ({ description, onChange }) => {
return (
<TableRow
label={
<HelpMessageTooltip title="カードの説明文を入力できます。省略可。">
{'説明文'}
</HelpMessageTooltip>
}
>
<CollaborativeInput
bufferDuration="default"
value={description ?? ''}
disabled={onChange == null}
multiline
onChange={e => {
onChange && onChange(e);
}}
/>
</TableRow>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { State, cardImageValue, deckPieceCardTemplate } from '@flocon-trpg/core';
import classNames from 'classnames';
import React from 'react';
import { z } from 'zod';
import { FileView } from '@/components/models/file/FileView/FileView';
import { ImageView } from '@/components/models/file/ImageView/ImageView';
import { flex, flexColumn } from '@/styles/className';
import { FilePathModule } from '@/utils/file/filePath';

type CardImage = z.TypeOf<typeof cardImageValue>;

export type Props =
| {
type: 'template';
image: CardImage | undefined;
/** nullish のときは読み取り専用モードになります。 */
onChange: ((newValue: CardImage | undefined) => void) | null;
}
| {
type: 'piece';
image: CardImage | undefined;
/** nullish のときは読み取り専用モードになります。 */
onChange: ((newValue: CardImage) => void) | null;
};

export const CardImageView: React.FC<Props> = props => {
const onChange = props.onChange;
return (
<div className={classNames(flex, flexColumn)}>
{props.image == null ? (
<div style={{ width: 150, height: 150 }}>
{props.type === 'piece' ? '(未公開)' : '(なし)'}
</div>
) : (
<ImageView filePath={props.image.filePath} size={150} link />
)}
<FileView
showImage={false}
maxWidthOfLink={100}
uploaderFileBrowserHeight={null}
defaultFileTypeFilter="image"
hideClear
onPathChange={
props.onChange == null
? null
: newValue => {
if (props.onChange == null) {
throw new Error(
'props.onChange is null. This should not happen.',
);
}
const newState: CardImage | undefined =
newValue == null
? undefined
: {
$v: 1,
$r: 1,
type: 'FilePath',
filePath: FilePathModule.toOtState(newValue),
};
if (newState == null) {
if (props.type === 'template') {
props.onChange(undefined);
}
return;
}
props.onChange(newState);
}
}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { CollaborativeInput } from '@/components/ui/CollaborativeInput/CollaborativeInput';
import { HelpMessageTooltip } from '@/components/ui/HelpMessageTooltip/HelpMessageTooltip';
import { TableRow } from '@/components/ui/Table/Table';

export const CardName: React.FC<{
name: string | undefined;
onChange?: (newValue: string) => void;
}> = ({ name, onChange }) => {
return (
<TableRow
label={
<HelpMessageTooltip title="カードの名前を設定できます(例: 「ハートの3」)。省略可。">
{'名前'}
</HelpMessageTooltip>
}
>
<CollaborativeInput
bufferDuration="default"
value={name ?? ''}
disabled={onChange == null}
onChange={e => {
onChange && onChange(e);
}}
/>
</TableRow>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/** @jsxImportSource @emotion/react */
import {
State,
boardTemplate,
deckPieceCardTemplate,
deckTemplateCardTemplate,
} from '@flocon-trpg/core';
import { recordToArray } from '@flocon-trpg/utils';
import React from 'react';
import { DeckPieceCardView, DeckTemplateEditorView } from '../Card/CardView';
type DeckTemplateCardsState = Record<string, State<typeof deckTemplateCardTemplate> | undefined>;
type DeckPieceCardsState = Record<string, State<typeof deckPieceCardTemplate> | undefined>;
type BoardState = State<typeof boardTemplate>;
type CardGroups = BoardState['cardGroups'];

export type DeckTemplateCardListViewProps = {
cards: DeckTemplateCardsState;
onChange: (newValue: DeckTemplateCardsState) => void;
};

export const DeckTemplateCardListView: React.FC<DeckTemplateCardListViewProps> = ({
cards,
onChange,
}) => {
const onChangeRef = React.useRef(onChange);
const cardsArray = React.useMemo(() => {
return recordToArray(cards).map(card => (
<DeckTemplateEditorView
key={card.key}
card={card.value}
onChange={newValue => {
onChangeRef.current({ ...cards, [card.key]: newValue });
}}
/>
));
}, [cards]);

return <div>{cardsArray}</div>;
};

export type DeckPieceCardListViewProps = {
cards: DeckPieceCardsState;
cardGroups: CardGroups;
onChange: (newValue: DeckPieceCardsState) => void;
};

export const DeckPieceCardListView: React.FC<DeckPieceCardListViewProps> = ({
cards,
cardGroups,
onChange,
}) => {
const onChangeRef = React.useRef(onChange);
const cardsArray = React.useMemo(() => {
return recordToArray(cards).map(card => (
<DeckPieceCardView
key={card.key}
card={card.value}
cardGroups={cardGroups}
onChange={newValue => {
onChangeRef.current({ ...cards, [card.key]: newValue });
}}
/>
));
}, [cardGroups, cards]);

return <div>{cardsArray}</div>;
};
Loading

0 comments on commit 3b51de9

Please sign in to comment.