mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
feat: beautify item card
This commit is contained in:
parent
491044747f
commit
2f1b6c3be3
6 changed files with 141 additions and 45 deletions
|
|
@ -461,9 +461,13 @@
|
|||
"AddNewWorkflowDescription": "Create a new automated workflow and save it to the selected workspace wiki to backup.",
|
||||
"BelongsToWorkspace": "Belongs to workspace",
|
||||
"AddNewWorkflowDoneMessage": "Add successfully",
|
||||
"AddTagsDescription": "in-wiki tags, press Enter to add more"
|
||||
"AddTagsDescription": "in-wiki tags, press Enter to add more",
|
||||
"DeleteWorkflow": "Delete Workflow",
|
||||
"DeleteWorkflowDescription": "The workflow will be completely deleted from the Wiki workspace it belongs to, should it really be deleted?"
|
||||
},
|
||||
"Description": "Description",
|
||||
"Tags": "Tags",
|
||||
"Title": "Title"
|
||||
"Title": "Title",
|
||||
"Delete": "Delete",
|
||||
"Open": "Open"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -453,6 +453,7 @@
|
|||
"Title": "标题",
|
||||
"Description": "描述",
|
||||
"Tags": "标签",
|
||||
"Open": "打开",
|
||||
"LanguageModel": {
|
||||
"ModelNotExist": "找不到模型",
|
||||
"ModelNotExistDescription": "尝试使用你给的这个路径加载模型,但在这个位置其实没有所需要的模型",
|
||||
|
|
@ -464,6 +465,8 @@
|
|||
"AddNewWorkflow": "添加新的工作流",
|
||||
"AddNewWorkflowDescription": "创建新的自动化工作流,并保存到所选的工作区Wiki里备份。",
|
||||
"AddNewWorkflowDoneMessage": "添加成功",
|
||||
"DeleteWorkflow": "删除工作流",
|
||||
"DeleteWorkflowDescription": "将从所属的Wiki工作区里彻底删除该工作流,是否真的要删除?",
|
||||
"BelongsToWorkspace": "所属工作区",
|
||||
"AddTagsDescription": "Wiki内的标签,回车可以添加更多"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface DeleteConfirmationDialogProps {
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void;
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
export const DeleteConfirmationDialog = ({
|
||||
open,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}: DeleteConfirmationDialogProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Dialog open={open} onClose={onCancel}>
|
||||
<DialogTitle>{t('Workflow.DeleteWorkflow')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>{t('Workflow.DeleteWorkflowDescription')}</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>{t('No')}</Button>
|
||||
<Button onClick={onConfirm}>{t('Yes')} {t('Delete')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,24 +1,34 @@
|
|||
/* eslint-disable unicorn/no-null */
|
||||
/* eslint-disable unicorn/no-null, @typescript-eslint/strict-boolean-expressions, unicorn/no-useless-undefined */
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import MenuOpenIcon from '@mui/icons-material/MenuOpen';
|
||||
import { Box, Button, Card, CardActionArea, CardContent, Fade, Menu, MenuItem, Typography } from '@mui/material';
|
||||
import { Box, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Chip, Fade, Menu, MenuItem, Stack, Typography } from '@mui/material';
|
||||
import type { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import is from 'typescript-styled-is';
|
||||
import { DeleteConfirmationDialog } from './DeleteConfirmationDialog';
|
||||
import type { IWorkflowTiddler } from './useWorkflowDataSource';
|
||||
|
||||
const WorkflowCard = styled(Card)<{ $backgroundImage?: string }>`
|
||||
${is('$backgroundImage')`
|
||||
background-image: url(${(props: { $backgroundImage: string }) => props.$backgroundImage});
|
||||
`}
|
||||
background-size: cover;
|
||||
background-blend-mode: darken;
|
||||
const WorkflowListContainer = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
const WorkflowTitle = styled(Typography)`
|
||||
color: white;
|
||||
const WorkflowCard = styled(Card)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
justify-content: space-between;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
const ItemMenuCardActions = styled(CardActions)`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
interface IWorkflowListItemProps {
|
||||
|
|
@ -43,15 +53,31 @@ export function WorkflowListItem(props: IWorkflowListItemProps) {
|
|||
}, [item, onDeleteWorkflow]);
|
||||
const menuID = `workflow-list-item-menu-${item.id}`;
|
||||
return (
|
||||
<WorkflowCard $backgroundImage={item.image}>
|
||||
<WorkflowCard>
|
||||
<CardActionArea>
|
||||
{item.image && (
|
||||
<CardMedia
|
||||
component='img'
|
||||
height='140'
|
||||
image={item.image}
|
||||
alt={`screenshot of workflow ${item.title}`}
|
||||
/>
|
||||
)}
|
||||
<CardContent>
|
||||
<WorkflowTitle variant='h5'>{item.title}</WorkflowTitle>
|
||||
<Typography gutterBottom variant='h5' component='div'>{item.title}</Typography>
|
||||
{item.tags && (
|
||||
<Stack direction='row' spacing={1} flexWrap='wrap'>
|
||||
{item.tags.map(tag => <Chip key={tag} label={tag} style={{ marginBottom: '0.3em' }} clickable />)}
|
||||
</Stack>
|
||||
)}
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Button aria-controls={menuID} aria-haspopup='true' onClick={handleOpenItemMenu}>
|
||||
{anchorElement === null ? <MenuIcon /> : <MenuOpenIcon />}
|
||||
</Button>
|
||||
<ItemMenuCardActions>
|
||||
<Button>{t('Open')}</Button>
|
||||
<Button aria-controls={menuID} aria-haspopup='true' onClick={handleOpenItemMenu}>
|
||||
{anchorElement === null ? <MenuIcon /> : <MenuOpenIcon />}
|
||||
</Button>
|
||||
</ItemMenuCardActions>
|
||||
<Menu
|
||||
id={menuID}
|
||||
anchorEl={anchorElement}
|
||||
|
|
@ -85,9 +111,30 @@ interface IWorkflowListProps {
|
|||
}
|
||||
|
||||
export const WorkflowList: React.FC<IWorkflowListProps> = ({ workflows, onDeleteWorkflow }) => {
|
||||
const [itemToDelete, setDeleteItem] = useState<IWorkflowListItem | undefined>();
|
||||
const handleDeleteConfirmed = useCallback(() => {
|
||||
if (itemToDelete) {
|
||||
onDeleteWorkflow(itemToDelete);
|
||||
setDeleteItem(undefined);
|
||||
}
|
||||
}, [itemToDelete, onDeleteWorkflow]);
|
||||
const handleDeleteWithConfirmation = useCallback((item: IWorkflowListItem) => {
|
||||
setDeleteItem(item);
|
||||
}, []);
|
||||
const handleDeleteCancel = useCallback(() => {
|
||||
setDeleteItem(undefined);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{workflows.map((workflow) => <WorkflowListItem key={workflow.id} item={workflow} onDeleteWorkflow={onDeleteWorkflow} />)}
|
||||
</Box>
|
||||
<>
|
||||
<WorkflowListContainer>
|
||||
{workflows.map((workflow) => <WorkflowListItem key={workflow.id} item={workflow} onDeleteWorkflow={handleDeleteWithConfirmation} />)}
|
||||
</WorkflowListContainer>
|
||||
<DeleteConfirmationDialog
|
||||
open={itemToDelete !== undefined}
|
||||
onCancel={handleDeleteCancel}
|
||||
onConfirm={handleDeleteConfirmed}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,16 @@ import { AddItemDialog } from './AddItemDialog';
|
|||
import { useAvailableFilterTags, useWorkflows } from './useWorkflowDataSource';
|
||||
import { IWorkflowListItem, WorkflowList } from './WorkflowList';
|
||||
|
||||
const WorkflowManageContainer = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1em;
|
||||
`;
|
||||
const SearchRegionContainer = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
const AddNewItemFloatingButton = styled(Fab)`
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
|
|
@ -44,27 +54,29 @@ export const WorkflowManage: React.FC = () => {
|
|||
.filter(workflow => selectedTags.length > 0 ? selectedTags.some(tag => workflow.tags.includes(tag)) : workflow);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
label='Search'
|
||||
value={search}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<Stack direction='row' spacing={1}>
|
||||
{availableFilterTags.map(tag => (
|
||||
<Chip
|
||||
key={tag}
|
||||
label={tag}
|
||||
clickable
|
||||
color={selectedTags.includes(tag) ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
handleTagClick(tag);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<WorkflowManageContainer>
|
||||
<SearchRegionContainer>
|
||||
<TextField
|
||||
label='Search'
|
||||
value={search}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<Stack direction='row' spacing={1}>
|
||||
{availableFilterTags.map(tag => (
|
||||
<Chip
|
||||
key={tag}
|
||||
label={tag}
|
||||
clickable
|
||||
color={selectedTags.includes(tag) ? 'primary' : 'default'}
|
||||
onClick={() => {
|
||||
handleTagClick(tag);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</SearchRegionContainer>
|
||||
<WorkflowList workflows={filteredWorkflows} onDeleteWorkflow={onDeleteWorkflow} />
|
||||
<AddNewItemFloatingButton color='primary' aria-label='add' onClick={handleOpenDialog}>
|
||||
<AddIcon />
|
||||
|
|
@ -76,6 +88,6 @@ export const WorkflowManage: React.FC = () => {
|
|||
availableFilterTags={availableFilterTags}
|
||||
workspacesList={workspacesList}
|
||||
/>
|
||||
</Box>
|
||||
</WorkflowManageContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -80,11 +80,13 @@ export function useWorkflowFromWiki(workspacesList: IWorkspaceWithMetadata[] | u
|
|||
return workspacesList?.map?.((workspace, workspaceIndex) => {
|
||||
const workflowTiddlersInWorkspace = workflowsByWorkspace[workspaceIndex];
|
||||
return workflowTiddlersInWorkspace.map((tiddler) => {
|
||||
// DEBUG: console tiddler
|
||||
console.log(`tiddler`, tiddler);
|
||||
const workflowItem: IWorkflowListItem = {
|
||||
id: `${workspace.id}:${tiddler.title}`,
|
||||
title: tiddler.title,
|
||||
description: tiddler.description,
|
||||
tags: tiddler.tags,
|
||||
tags: typeof tiddler.tags === 'string' ? (tiddler.tags as string).split(' ') : tiddler.tags,
|
||||
workspaceID: workspace.id,
|
||||
image: tiddler['page-cover'],
|
||||
metadata: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue