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.",
|
"AddNewWorkflowDescription": "Create a new automated workflow and save it to the selected workspace wiki to backup.",
|
||||||
"BelongsToWorkspace": "Belongs to workspace",
|
"BelongsToWorkspace": "Belongs to workspace",
|
||||||
"AddNewWorkflowDoneMessage": "Add successfully",
|
"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",
|
"Description": "Description",
|
||||||
"Tags": "Tags",
|
"Tags": "Tags",
|
||||||
"Title": "Title"
|
"Title": "Title",
|
||||||
|
"Delete": "Delete",
|
||||||
|
"Open": "Open"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,6 +453,7 @@
|
||||||
"Title": "标题",
|
"Title": "标题",
|
||||||
"Description": "描述",
|
"Description": "描述",
|
||||||
"Tags": "标签",
|
"Tags": "标签",
|
||||||
|
"Open": "打开",
|
||||||
"LanguageModel": {
|
"LanguageModel": {
|
||||||
"ModelNotExist": "找不到模型",
|
"ModelNotExist": "找不到模型",
|
||||||
"ModelNotExistDescription": "尝试使用你给的这个路径加载模型,但在这个位置其实没有所需要的模型",
|
"ModelNotExistDescription": "尝试使用你给的这个路径加载模型,但在这个位置其实没有所需要的模型",
|
||||||
|
|
@ -464,6 +465,8 @@
|
||||||
"AddNewWorkflow": "添加新的工作流",
|
"AddNewWorkflow": "添加新的工作流",
|
||||||
"AddNewWorkflowDescription": "创建新的自动化工作流,并保存到所选的工作区Wiki里备份。",
|
"AddNewWorkflowDescription": "创建新的自动化工作流,并保存到所选的工作区Wiki里备份。",
|
||||||
"AddNewWorkflowDoneMessage": "添加成功",
|
"AddNewWorkflowDoneMessage": "添加成功",
|
||||||
|
"DeleteWorkflow": "删除工作流",
|
||||||
|
"DeleteWorkflowDescription": "将从所属的Wiki工作区里彻底删除该工作流,是否真的要删除?",
|
||||||
"BelongsToWorkspace": "所属工作区",
|
"BelongsToWorkspace": "所属工作区",
|
||||||
"AddTagsDescription": "Wiki内的标签,回车可以添加更多"
|
"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 MenuIcon from '@mui/icons-material/Menu';
|
||||||
import MenuOpenIcon from '@mui/icons-material/MenuOpen';
|
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 type { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import is from 'typescript-styled-is';
|
import { DeleteConfirmationDialog } from './DeleteConfirmationDialog';
|
||||||
import type { IWorkflowTiddler } from './useWorkflowDataSource';
|
import type { IWorkflowTiddler } from './useWorkflowDataSource';
|
||||||
|
|
||||||
const WorkflowCard = styled(Card)<{ $backgroundImage?: string }>`
|
const WorkflowListContainer = styled(Box)`
|
||||||
${is('$backgroundImage')`
|
display: flex;
|
||||||
background-image: url(${(props: { $backgroundImage: string }) => props.$backgroundImage});
|
flex-direction: row;
|
||||||
`}
|
flex-wrap: wrap;
|
||||||
background-size: cover;
|
justify-content: flex-start;
|
||||||
background-blend-mode: darken;
|
align-items: flex-start;
|
||||||
`;
|
`;
|
||||||
|
const WorkflowCard = styled(Card)`
|
||||||
const WorkflowTitle = styled(Typography)`
|
display: flex;
|
||||||
color: white;
|
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 {
|
interface IWorkflowListItemProps {
|
||||||
|
|
@ -43,15 +53,31 @@ export function WorkflowListItem(props: IWorkflowListItemProps) {
|
||||||
}, [item, onDeleteWorkflow]);
|
}, [item, onDeleteWorkflow]);
|
||||||
const menuID = `workflow-list-item-menu-${item.id}`;
|
const menuID = `workflow-list-item-menu-${item.id}`;
|
||||||
return (
|
return (
|
||||||
<WorkflowCard $backgroundImage={item.image}>
|
<WorkflowCard>
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
|
{item.image && (
|
||||||
|
<CardMedia
|
||||||
|
component='img'
|
||||||
|
height='140'
|
||||||
|
image={item.image}
|
||||||
|
alt={`screenshot of workflow ${item.title}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<CardContent>
|
<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>
|
</CardContent>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
|
<ItemMenuCardActions>
|
||||||
|
<Button>{t('Open')}</Button>
|
||||||
<Button aria-controls={menuID} aria-haspopup='true' onClick={handleOpenItemMenu}>
|
<Button aria-controls={menuID} aria-haspopup='true' onClick={handleOpenItemMenu}>
|
||||||
{anchorElement === null ? <MenuIcon /> : <MenuOpenIcon />}
|
{anchorElement === null ? <MenuIcon /> : <MenuOpenIcon />}
|
||||||
</Button>
|
</Button>
|
||||||
|
</ItemMenuCardActions>
|
||||||
<Menu
|
<Menu
|
||||||
id={menuID}
|
id={menuID}
|
||||||
anchorEl={anchorElement}
|
anchorEl={anchorElement}
|
||||||
|
|
@ -85,9 +111,30 @@ interface IWorkflowListProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkflowList: React.FC<IWorkflowListProps> = ({ workflows, onDeleteWorkflow }) => {
|
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 (
|
return (
|
||||||
<Box>
|
<>
|
||||||
{workflows.map((workflow) => <WorkflowListItem key={workflow.id} item={workflow} onDeleteWorkflow={onDeleteWorkflow} />)}
|
<WorkflowListContainer>
|
||||||
</Box>
|
{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 { useAvailableFilterTags, useWorkflows } from './useWorkflowDataSource';
|
||||||
import { IWorkflowListItem, WorkflowList } from './WorkflowList';
|
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)`
|
const AddNewItemFloatingButton = styled(Fab)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1em;
|
bottom: 1em;
|
||||||
|
|
@ -44,7 +54,8 @@ export const WorkflowManage: React.FC = () => {
|
||||||
.filter(workflow => selectedTags.length > 0 ? selectedTags.some(tag => workflow.tags.includes(tag)) : workflow);
|
.filter(workflow => selectedTags.length > 0 ? selectedTags.some(tag => workflow.tags.includes(tag)) : workflow);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<WorkflowManageContainer>
|
||||||
|
<SearchRegionContainer>
|
||||||
<TextField
|
<TextField
|
||||||
label='Search'
|
label='Search'
|
||||||
value={search}
|
value={search}
|
||||||
|
|
@ -65,6 +76,7 @@ export const WorkflowManage: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</SearchRegionContainer>
|
||||||
<WorkflowList workflows={filteredWorkflows} onDeleteWorkflow={onDeleteWorkflow} />
|
<WorkflowList workflows={filteredWorkflows} onDeleteWorkflow={onDeleteWorkflow} />
|
||||||
<AddNewItemFloatingButton color='primary' aria-label='add' onClick={handleOpenDialog}>
|
<AddNewItemFloatingButton color='primary' aria-label='add' onClick={handleOpenDialog}>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|
@ -76,6 +88,6 @@ export const WorkflowManage: React.FC = () => {
|
||||||
availableFilterTags={availableFilterTags}
|
availableFilterTags={availableFilterTags}
|
||||||
workspacesList={workspacesList}
|
workspacesList={workspacesList}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</WorkflowManageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,13 @@ export function useWorkflowFromWiki(workspacesList: IWorkspaceWithMetadata[] | u
|
||||||
return workspacesList?.map?.((workspace, workspaceIndex) => {
|
return workspacesList?.map?.((workspace, workspaceIndex) => {
|
||||||
const workflowTiddlersInWorkspace = workflowsByWorkspace[workspaceIndex];
|
const workflowTiddlersInWorkspace = workflowsByWorkspace[workspaceIndex];
|
||||||
return workflowTiddlersInWorkspace.map((tiddler) => {
|
return workflowTiddlersInWorkspace.map((tiddler) => {
|
||||||
|
// DEBUG: console tiddler
|
||||||
|
console.log(`tiddler`, tiddler);
|
||||||
const workflowItem: IWorkflowListItem = {
|
const workflowItem: IWorkflowListItem = {
|
||||||
id: `${workspace.id}:${tiddler.title}`,
|
id: `${workspace.id}:${tiddler.title}`,
|
||||||
title: tiddler.title,
|
title: tiddler.title,
|
||||||
description: tiddler.description,
|
description: tiddler.description,
|
||||||
tags: tiddler.tags,
|
tags: typeof tiddler.tags === 'string' ? (tiddler.tags as string).split(' ') : tiddler.tags,
|
||||||
workspaceID: workspace.id,
|
workspaceID: workspace.id,
|
||||||
image: tiddler['page-cover'],
|
image: tiddler['page-cover'],
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue