generated from template/vite-react-template
update mark
This commit is contained in:
parent
508ec96029
commit
a99d9c2322
@ -17,6 +17,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kevisual/query-mark": "workspace:*",
|
"@kevisual/query-mark": "workspace:*",
|
||||||
|
"@kevisual/components": "workspace:*",
|
||||||
"@kevisual/router": "0.0.9",
|
"@kevisual/router": "0.0.9",
|
||||||
"@kevisual/store": "workspace:*",
|
"@kevisual/store": "workspace:*",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
@ -28,6 +29,7 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
"react-i18next": "^15.4.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
|
3
src/Module.tsx
Normal file
3
src/Module.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { App as Manager } from './manager/Manager';
|
||||||
|
|
||||||
|
export { Manager };
|
@ -2,7 +2,7 @@ import { useManagerStore } from './store';
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useShallow } from 'zustand/shallow';
|
import { useShallow } from 'zustand/shallow';
|
||||||
import { ManagerProvider } from './Provider';
|
import { ManagerProvider } from './Provider';
|
||||||
import { ChevronDown, ChevronLeft, Edit, Plus, Search, Trash, Menu as MenuIcon, MenuSquare } from 'lucide-react';
|
import { ChevronDown, ChevronLeft, X, Edit, Plus, Search, Trash, Menu as MenuIcon, MenuSquare } from 'lucide-react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { EditMark as EditMarkComponent } from './edit/Edit';
|
import { EditMark as EditMarkComponent } from './edit/Edit';
|
||||||
@ -16,17 +16,19 @@ type ManagerProps = {
|
|||||||
showAdd?: boolean;
|
showAdd?: boolean;
|
||||||
onClick?: (data?: any) => void;
|
onClick?: (data?: any) => void;
|
||||||
markType?: MarkType;
|
markType?: MarkType;
|
||||||
|
showSelect?: boolean;
|
||||||
};
|
};
|
||||||
export const Manager = (props: ManagerProps) => {
|
export const Manager = (props: ManagerProps) => {
|
||||||
const { showSearch = true, showAdd = false, onClick } = props;
|
const { showSearch = true, showAdd = false, onClick, showSelect = true } = props;
|
||||||
|
|
||||||
const { control } = useForm({ defaultValues: { search: '' } });
|
const { control } = useForm({ defaultValues: { search: '' } });
|
||||||
const { list, init, setCurrentMarkId, currentMarkId, deleteMark, getMark, setMarkData, pagination, setPagination, getList, search, setSearch } =
|
const { list, init, setCurrentMarkId, currentMarkId, markData, deleteMark, getMark, setMarkData, pagination, setPagination, getList, search, setSearch } =
|
||||||
useManagerStore(
|
useManagerStore(
|
||||||
useShallow((state) => {
|
useShallow((state) => {
|
||||||
return {
|
return {
|
||||||
list: state.list,
|
list: state.list,
|
||||||
init: state.init,
|
init: state.init,
|
||||||
|
markData: state.markData,
|
||||||
currentMarkId: state.currentMarkId,
|
currentMarkId: state.currentMarkId,
|
||||||
setCurrentMarkId: state.setCurrentMarkId,
|
setCurrentMarkId: state.setCurrentMarkId,
|
||||||
deleteMark: state.deleteMark,
|
deleteMark: state.deleteMark,
|
||||||
@ -87,8 +89,9 @@ export const Manager = (props: ManagerProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
console.log('list', list.length, pagination.total);
|
console.log('list', list.length, pagination.total);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full p-4 relative'>
|
<div className='w-full h-full p-4 bg-white border-r border-r-gray-200 relative'>
|
||||||
<div className='flex px-4 mb-4 justify-between items-center absolute top-0 left-0 h-[56px] w-full'>
|
<div className='flex px-4 mb-4 justify-between items-center absolute top-0 left-0 h-[56px] w-full'>
|
||||||
<div className='flex ml-12 items-center space-x-2 '>
|
<div className='flex ml-12 items-center space-x-2 '>
|
||||||
<Controller
|
<Controller
|
||||||
@ -122,34 +125,38 @@ export const Manager = (props: ManagerProps) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex items-center space-x-2'}>
|
<div className={'flex items-center space-x-2'}>
|
||||||
<IconButton onClick={handleClick}>
|
{showSelect && (
|
||||||
<MenuIcon className='w-4 h-4' />
|
<>
|
||||||
</IconButton>
|
<IconButton onClick={handleClick}>
|
||||||
<Menu
|
<MenuIcon className='w-4 h-4' />
|
||||||
anchorEl={anchorEl}
|
</IconButton>
|
||||||
open={open}
|
<Menu
|
||||||
onClose={handleClose}
|
anchorEl={anchorEl}
|
||||||
onClick={(e) => {
|
open={open}
|
||||||
console.log('e', e);
|
onClose={handleClose}
|
||||||
}}>
|
onClick={(e) => {
|
||||||
{['md', 'mdx', 'wallnote', 'excalidraw'].map((option) => (
|
console.log('e', e);
|
||||||
<MenuItem
|
|
||||||
key={option}
|
|
||||||
value={option}
|
|
||||||
onClick={() => {
|
|
||||||
handleMenuItemClick(option);
|
|
||||||
}}>
|
}}>
|
||||||
{option}
|
{['md', 'mdx', 'wallnote', 'excalidraw'].map((option) => (
|
||||||
</MenuItem>
|
<MenuItem
|
||||||
))}
|
key={option}
|
||||||
</Menu>
|
value={option}
|
||||||
|
onClick={() => {
|
||||||
|
handleMenuItemClick(option);
|
||||||
|
}}>
|
||||||
|
{option}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200',
|
'text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200',
|
||||||
showAdd ? '' : 'hidden',
|
showAdd ? '' : 'hidden',
|
||||||
)}>
|
)}>
|
||||||
<Plus
|
<Plus
|
||||||
className={clsx('w-4 h-4 ', currentMarkId ? 'rotate-12' : 'rotate-0')}
|
className={clsx('w-4 h-4 ')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentMarkId('');
|
setCurrentMarkId('');
|
||||||
|
|
||||||
@ -157,7 +164,7 @@ export const Manager = (props: ManagerProps) => {
|
|||||||
id: '',
|
id: '',
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
markType: 'md' as any,
|
markType: props.markType || ('md' as any),
|
||||||
summary: '',
|
summary: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
link: '',
|
link: '',
|
||||||
@ -165,6 +172,16 @@ export const Manager = (props: ManagerProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
{markData && (
|
||||||
|
<button
|
||||||
|
className='text-blue-500 cursor-pointer hover:underline flex items-center p-2 rounded-md hover:bg-blue-100 transition duration-200'
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentMarkId('');
|
||||||
|
setMarkData(undefined);
|
||||||
|
}}>
|
||||||
|
<X className='w-4 h-4 ' />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-[56px] overflow-auto scrollbar' style={{ height: 'calc(100% - 56px)' }}>
|
<div className='mt-[56px] overflow-auto scrollbar' style={{ height: 'calc(100% - 56px)' }}>
|
||||||
@ -259,11 +276,25 @@ export const EditMark = () => {
|
|||||||
}
|
}
|
||||||
return <div className='w-full h-full'></div>;
|
return <div className='w-full h-full'></div>;
|
||||||
};
|
};
|
||||||
export const LayoutMain = (props: { children?: React.ReactNode }) => {
|
export const LayoutMain = (props: { children?: React.ReactNode; expandChildren?: React.ReactNode }) => {
|
||||||
const [openMenu, setOpenMenu] = useState(false);
|
const [openMenu, setOpenMenu] = useState(false);
|
||||||
|
const getDocumentHeight = () => {
|
||||||
|
return document.documentElement.scrollHeight;
|
||||||
|
};
|
||||||
|
const markData = useManagerStore((state) => state.markData);
|
||||||
|
const isEdit = !!markData;
|
||||||
|
const hasExpandChildren = !!props.expandChildren;
|
||||||
|
const style = useMemo(() => {
|
||||||
|
if (!hasExpandChildren || openMenu) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
top: getDocumentHeight() / 2 + 10,
|
||||||
|
};
|
||||||
|
}, [getDocumentHeight, hasExpandChildren, openMenu]);
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full flex'>
|
<div className='w-full h-full flex'>
|
||||||
<div className='absolute top-4 left-4 z-10'>
|
<div className={clsx('absolute top-4 z-10', openMenu ? 'left-4' : '-left-4')} style={style}>
|
||||||
<Button
|
<Button
|
||||||
variant='contained'
|
variant='contained'
|
||||||
color={openMenu ? 'info' : 'primary'}
|
color={openMenu ? 'info' : 'primary'}
|
||||||
@ -278,9 +309,12 @@ export const LayoutMain = (props: { children?: React.ReactNode }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={clsx('h-full w-full sm:w-1/3', openMenu ? '' : 'hidden')}>{props.children}</div>
|
<div className={clsx('h-full w-full sm:w-1/3', openMenu ? '' : 'hidden')}>{props.children}</div>
|
||||||
<div className={clsx('h-full hidden sm:block sm:w-2/3', openMenu ? '' : 'hidden')}>
|
{(!props.expandChildren || isEdit) && (
|
||||||
<EditMark />
|
<div className={clsx('h-full hidden sm:block sm:w-2/3', openMenu ? '' : 'hidden')}>
|
||||||
</div>
|
<EditMark />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.expandChildren && <div className='h-full grow'>{props.expandChildren}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -305,12 +339,19 @@ export type AppProps = {
|
|||||||
* 管理器id, 存储到store的id
|
* 管理器id, 存储到store的id
|
||||||
*/
|
*/
|
||||||
managerId?: string;
|
managerId?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
showSelect?: boolean;
|
||||||
};
|
};
|
||||||
export const App = (props: AppProps) => {
|
export const App = (props: AppProps) => {
|
||||||
return (
|
return (
|
||||||
<ManagerProvider id={props.managerId}>
|
<ManagerProvider id={props.managerId}>
|
||||||
<LayoutMain>
|
<LayoutMain expandChildren={props.children}>
|
||||||
<Manager markType={props.markType} showSearch={props.showSearch} showAdd={props.showAdd} onClick={props.onClick} />
|
<Manager
|
||||||
|
markType={props.markType}
|
||||||
|
showSearch={props.showSearch}
|
||||||
|
showAdd={props.showAdd}
|
||||||
|
onClick={props.onClick}
|
||||||
|
showSelect={props.showSelect}></Manager>
|
||||||
</LayoutMain>
|
</LayoutMain>
|
||||||
</ManagerProvider>
|
</ManagerProvider>
|
||||||
);
|
);
|
||||||
|
@ -91,9 +91,20 @@ export const EditMark = () => {
|
|||||||
defaultValue={mark?.thumbnail || ''}
|
defaultValue={mark?.thumbnail || ''}
|
||||||
render={({ field }) => <TextField {...field} label={t('thumbnail')} variant='outlined' fullWidth margin='normal' />}
|
render={({ field }) => <TextField {...field} label={t('thumbnail')} variant='outlined' fullWidth margin='normal' />}
|
||||||
/>
|
/>
|
||||||
<Button type='submit' variant='contained' color='primary'>
|
<div className='flex gap-2'>
|
||||||
{t('save')}
|
<Button type='submit' variant='contained' color='primary'>
|
||||||
</Button>
|
{t('save')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='contained'
|
||||||
|
color='secondary'
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentMarkId('');
|
||||||
|
setMarkData(undefined);
|
||||||
|
}}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -61,7 +61,7 @@ export const createManagerStore: StateCreator<ManagerStore, [], [], any> = (set,
|
|||||||
},
|
},
|
||||||
updateMark: async (mark: Mark) => {
|
updateMark: async (mark: Mark) => {
|
||||||
const queryMark = get().queryMark;
|
const queryMark = get().queryMark;
|
||||||
const res = await queryMark.updateMark(mark.id, mark);
|
const res = await queryMark.updateMark(mark);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const oldList = state.list;
|
const oldList = state.list;
|
||||||
|
30
vite.lib.config.ts
Normal file
30
vite.lib.config.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import path from 'path';
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import pkgs from './package.json' with { type: 'json' };
|
||||||
|
const version = pkgs.version || '0.0.1';
|
||||||
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const basename = isDev ? '/' : pkgs?.basename || '/';
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
base: basename,
|
||||||
|
define: {
|
||||||
|
BASE_NAME: JSON.stringify(basename),
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
target: 'esnext',
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, './src/pages/App.tsx'),
|
||||||
|
name: 'Mark',
|
||||||
|
fileName: (format) => `mark.${format}.js`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user