From 5284f38c002e5bf88e308e208456d41d4126b30e Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 22 Aug 2023 00:10:30 +0800 Subject: ... --- FrontEnd/src/pages/timeline/TimelineCard.tsx | 12 +-- FrontEnd/src/pages/timeline/TimelineMember.css | 20 +++- FrontEnd/src/pages/timeline/TimelineMember.tsx | 105 +++++++++------------ FrontEnd/src/views/common/SearchInput.tsx | 93 +++++++----------- FrontEnd/src/views/common/list/ListContainer.css | 4 + FrontEnd/src/views/common/list/ListContainer.tsx | 23 +++++ .../src/views/common/list/ListItemContainer.css | 3 + .../src/views/common/list/ListItemContainer.tsx | 23 +++++ FrontEnd/src/views/common/list/index.ts | 4 + 9 files changed, 156 insertions(+), 131 deletions(-) create mode 100644 FrontEnd/src/views/common/list/ListContainer.css create mode 100644 FrontEnd/src/views/common/list/ListContainer.tsx create mode 100644 FrontEnd/src/views/common/list/ListItemContainer.css create mode 100644 FrontEnd/src/views/common/list/ListItemContainer.tsx create mode 100644 FrontEnd/src/views/common/list/index.ts (limited to 'FrontEnd/src') diff --git a/FrontEnd/src/pages/timeline/TimelineCard.tsx b/FrontEnd/src/pages/timeline/TimelineCard.tsx index d002a1b9..8557e5dd 100644 --- a/FrontEnd/src/pages/timeline/TimelineCard.tsx +++ b/FrontEnd/src/pages/timeline/TimelineCard.tsx @@ -8,7 +8,7 @@ import { HttpTimelineInfo } from "@/http/timeline"; import { getHttpBookmarkClient } from "@/http/bookmark"; import { useMobile } from "@/views/common/common"; -import { useDialog } from "@/views/common/dialog"; +import { Dialog, useDialog } from "@/views/common/dialog"; import UserAvatar from "@/views/common/user/UserAvatar"; import PopupMenu from "@/views/common/menu/PopupMenu"; import FullPageDialog from "@/views/common/dialog/FullPageDialog"; @@ -16,7 +16,7 @@ import Card from "@/views/common/Card"; import TimelineDeleteDialog from "./TimelineDeleteDialog"; import ConnectionStatusBadge from "./ConnectionStatusBadge"; import CollapseButton from "./CollapseButton"; -import { TimelineMemberDialog } from "./TimelineMember"; +import TimelineMember from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import IconButton from "@/views/common/button/IconButton"; @@ -144,11 +144,9 @@ export default function TimelineCard(props: TimelinePageCardProps) { ) : (
{content}
)} - + + + div { - padding: 0.5em; +.timeline-member-avatar { + height: 50px; + width: 50px; + border-radius: 50%; +} + +.timeline-member-info { + margin-left: 1em; + margin-right: auto; +} + +.timeline-member-user-search { + margin-top: 1em; } diff --git a/FrontEnd/src/pages/timeline/TimelineMember.tsx b/FrontEnd/src/pages/timeline/TimelineMember.tsx index 6c5d29ba..4c1600f5 100644 --- a/FrontEnd/src/pages/timeline/TimelineMember.tsx +++ b/FrontEnd/src/pages/timeline/TimelineMember.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import * as React from "react"; import { useTranslation } from "react-i18next"; import { convertI18nText, I18nText } from "@/common"; @@ -11,45 +10,50 @@ import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; import SearchInput from "@/views/common/SearchInput"; import UserAvatar from "@/views/common/user/UserAvatar"; import Button from "@/views/common/button/Button"; -import Dialog from "@/views/common/dialog/Dialog"; +import { ListContainer, ListItemContainer } from "@/views/common/list"; import "./TimelineMember.css"; -const TimelineMemberItem: React.FC<{ +function TimelineMemberItem({ + user, + add, + onAction, +}: { user: HttpUser; add?: boolean; onAction?: (username: string) => void; -}> = ({ user, add, onAction }) => { +}) { return ( -
-
-
- -
-
-
{user.nickname}
- {"@" + user.username} -
- {onAction ? ( -
-
- ) : null} + + +
+
{user.nickname}
+ + {"@" + user.username} +
-
+ {onAction ? ( +
+
+ ) : null} + ); -}; +} -const TimelineMemberUserSearch: React.FC<{ +function TimelineMemberUserSearch({ + timeline, + onChange, +}: { timeline: HttpTimelineInfo; onChange: () => void; -}> = ({ timeline, onChange }) => { +}) { const { t } = useTranslation(); const [userSearchText, setUserSearchText] = useState(""); @@ -64,9 +68,9 @@ const TimelineMemberUserSearch: React.FC<{ >({ type: "init" }); return ( - <> +
{ setUserSearchText(v); @@ -88,8 +92,8 @@ const TimelineMemberUserSearch: React.FC<{ users = users.filter( (user) => timeline.members.findIndex( - (m) => m.username === user.username - ) === -1 && timeline.owner.username !== user.username + (m) => m.username === user.username, + ) === -1 && timeline.owner.username !== user.username, ); setUserSearchState({ type: "users", data: users }); }, @@ -98,7 +102,7 @@ const TimelineMemberUserSearch: React.FC<{ type: "error", data: { type: "custom", value: String(e) }, }); - } + }, ); }} /> @@ -109,7 +113,7 @@ const TimelineMemberUserSearch: React.FC<{ return
{t("timeline.member.noUserAvailableToAdd")}
; } else { return ( -
+
{users.map((user) => ( { setUserSearchText(""); @@ -141,22 +145,22 @@ const TimelineMemberUserSearch: React.FC<{ ); } })()} - +
); -}; +} -export interface TimelineMemberProps { +interface TimelineMemberProps { timeline: HttpTimelineInfo; onChange: () => void; } -const TimelineMember: React.FC = (props) => { +export default function TimelineMember(props: TimelineMemberProps) { const { timeline, onChange } = props; const members = [timeline.owner, ...timeline.members]; return (
-
+ {members.map((member, index) => ( = (props) => { .memberDelete( timeline.owner.username, timeline.nameV2, - member.username + member.username, ) .then(onChange); } @@ -176,27 +180,10 @@ const TimelineMember: React.FC = (props) => { } /> ))} -
+ {timeline.manageable ? ( ) : null}
); -}; - -export default TimelineMember; - -export interface TimelineMemberDialogProps extends TimelineMemberProps { - open: boolean; - onClose: () => void; } - -export const TimelineMemberDialog: React.FC = ( - props -) => { - return ( - - - - ); -}; diff --git a/FrontEnd/src/views/common/SearchInput.tsx b/FrontEnd/src/views/common/SearchInput.tsx index 9d644ab7..e3216b86 100644 --- a/FrontEnd/src/views/common/SearchInput.tsx +++ b/FrontEnd/src/views/common/SearchInput.tsx @@ -1,79 +1,50 @@ -import { useCallback } from "react"; -import * as React from "react"; -import classnames from "classnames"; -import { useTranslation } from "react-i18next"; +import classNames from "classnames"; +import { useC, Text } from "./common"; import LoadingButton from "./button/LoadingButton"; import "./SearchInput.css"; -export interface SearchInputProps { +interface SearchInputProps { value: string; onChange: (value: string) => void; onButtonClick: () => void; - className?: string; loading?: boolean; - buttonText?: string; - placeholder?: string; - additionalButton?: React.ReactNode; - alwaysOneline?: boolean; + className?: string; + buttonText?: Text; } -const SearchInput: React.FC = (props) => { - const { onChange, onButtonClick, alwaysOneline } = props; - - const { t } = useTranslation(); - - const onInputChange = useCallback( - (event: React.ChangeEvent): void => { - onChange(event.currentTarget.value); - }, - [onChange] - ); - - const onInputKeyPress = useCallback( - (event: React.KeyboardEvent): void => { - if (event.key === "Enter") { - onButtonClick(); - event.preventDefault(); - } - }, - [onButtonClick] - ); +export default function SearchInput({ + value, + onChange, + onButtonClick, + loading, + className, + buttonText, +}: SearchInputProps) { + const c = useC(); return ( -
+
{ + const { value } = event.currentTarget; + onChange(value); + }} + onKeyDown={(event) => { + if (event.key === "Enter") { + onButtonClick(); + event.preventDefault(); + } + }} /> - {props.additionalButton ? ( -
- {props.additionalButton} -
- ) : null} -
- - {props.buttonText ?? t("search")} - -
+ + + {c(buttonText ?? "search")} +
); -}; - -export default SearchInput; +} diff --git a/FrontEnd/src/views/common/list/ListContainer.css b/FrontEnd/src/views/common/list/ListContainer.css new file mode 100644 index 00000000..679f139d --- /dev/null +++ b/FrontEnd/src/views/common/list/ListContainer.css @@ -0,0 +1,4 @@ +.cru-list-container { + border: 1px solid var(--cru-button-primary-normal-color); + border-radius: 5px; +} diff --git a/FrontEnd/src/views/common/list/ListContainer.tsx b/FrontEnd/src/views/common/list/ListContainer.tsx new file mode 100644 index 00000000..aa00d12c --- /dev/null +++ b/FrontEnd/src/views/common/list/ListContainer.tsx @@ -0,0 +1,23 @@ +import { ComponentPropsWithoutRef, forwardRef, Ref } from "react"; +import classNames from "classnames"; + +import "./ListContainer.css" + +function _ListContainer( + { className, children, ...otherProps }: ComponentPropsWithoutRef<"div">, + ref: Ref, +) { + return ( +
+ {children} +
+ ); +} + +const ListContainer = forwardRef(_ListContainer); + +export default ListContainer; diff --git a/FrontEnd/src/views/common/list/ListItemContainer.css b/FrontEnd/src/views/common/list/ListItemContainer.css new file mode 100644 index 00000000..4c08a8d1 --- /dev/null +++ b/FrontEnd/src/views/common/list/ListItemContainer.css @@ -0,0 +1,3 @@ +.cru-list-item-container { + border: 1px solid var(--cru-button-primary-normal-color); +} diff --git a/FrontEnd/src/views/common/list/ListItemContainer.tsx b/FrontEnd/src/views/common/list/ListItemContainer.tsx new file mode 100644 index 00000000..315cbd6e --- /dev/null +++ b/FrontEnd/src/views/common/list/ListItemContainer.tsx @@ -0,0 +1,23 @@ +import { ComponentPropsWithoutRef, forwardRef, Ref } from "react"; +import classNames from "classnames"; + +import "./ListItemContainer.css"; + +function _ListItemContainer( + { className, children, ...otherProps }: ComponentPropsWithoutRef<"div">, + ref: Ref, +) { + return ( +
+ {children} +
+ ); +} + +const ListItemContainer = forwardRef(_ListItemContainer); + +export default ListItemContainer; diff --git a/FrontEnd/src/views/common/list/index.ts b/FrontEnd/src/views/common/list/index.ts new file mode 100644 index 00000000..e183f7da --- /dev/null +++ b/FrontEnd/src/views/common/list/index.ts @@ -0,0 +1,4 @@ +import ListContainer from "./ListContainer"; +import ListItemContainer from "./ListItemContainer"; + +export { ListContainer, ListItemContainer }; -- cgit v1.2.3