generated from template/vite-react-template
	Compare commits
	
		
			1 Commits
		
	
	
		
			508ec96029
			...
			a99d9c2322
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 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`, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
		Reference in New Issue
	
	Block a user