add basic layout for Pattern Library.
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| import { invoke } from '@tauri-apps/api/core'; | ||||
| import dayjs from 'dayjs'; | ||||
| import { atom } from 'jotai'; | ||||
| import { atomFamily } from 'jotai/utils'; | ||||
| import { atomFamily, atomWithRefresh } from 'jotai/utils'; | ||||
| import { get, reduce } from 'lodash-es'; | ||||
| import { v4 } from 'uuid'; | ||||
|  | ||||
| @@ -121,7 +121,7 @@ export function totalDuration(pattern: Pattern): number { | ||||
| } | ||||
|  | ||||
| export const PatternsAtom = atomFamily((keyword: string) => | ||||
|   atom(async () => { | ||||
|   atomWithRefresh(async () => { | ||||
|     try { | ||||
|       const patterns = await invoke<Pattern[]>('list_patterns', { keyword }); | ||||
|       return patterns; | ||||
|   | ||||
| @@ -1,10 +1,27 @@ | ||||
| @layer pages { | ||||
|   .pattern_list { | ||||
|   .patterns { | ||||
|     min-width: 0; | ||||
|     min-height: 0; | ||||
|     overflow: hidden; | ||||
|     flex: 1; | ||||
|     border-radius: calc(var(--border-radius) * 2); | ||||
|     background-color: var(--color-surface-container); | ||||
|     padding: calc(var(--spacing) * 2); | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: stretch; | ||||
|     gap: calc(var(--spacing) * 2); | ||||
|     .search_row { | ||||
|       display: flex; | ||||
|       flex-direction: row; | ||||
|       align-items: center; | ||||
|       gap: calc(var(--spacing)); | ||||
|     } | ||||
|   } | ||||
|   .pattern_list { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: stretch; | ||||
|     gap: calc(var(--spacing) * 2); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,51 @@ | ||||
| import { FC } from 'react'; | ||||
| import { Icon } from '@iconify/react/dist/iconify.js'; | ||||
| import cx from 'clsx'; | ||||
| import { useAtomValue } from 'jotai'; | ||||
| import { FC, useState } from 'react'; | ||||
| import { useDebounce } from 'react-use'; | ||||
| import { ScrollArea } from '../../components/ScrollArea'; | ||||
| import { PatternsAtom } from '../../context/Patterns'; | ||||
| import styles from './Patterns.module.css'; | ||||
|  | ||||
| const Patterns: FC = () => { | ||||
|   const [rawKeyword, setRawKeyword] = useState<string>(''); | ||||
|   const [keyword, setKeyword] = useState<string | null>(null); | ||||
|   const patterns = useAtomValue(PatternsAtom(keyword)); | ||||
|  | ||||
|   useDebounce( | ||||
|     () => { | ||||
|       if (rawKeyword.trim().length > 0) { | ||||
|         setKeyword(rawKeyword.trim()); | ||||
|       } else { | ||||
|         setKeyword(null); | ||||
|       } | ||||
|     }, | ||||
|     1500, | ||||
|     [rawKeyword], | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <div className={styles.pattern_list}> | ||||
|       <ScrollArea enableY></ScrollArea> | ||||
|     <div className={styles.patterns}> | ||||
|       <div className={styles.search_row}> | ||||
|         <div className={cx('input_wrapper', 'extendable')}> | ||||
|           <Icon icon="material-symbols-light:search" /> | ||||
|           <input | ||||
|             type="text" | ||||
|             placeholder="Search patterns..." | ||||
|             value={rawKeyword} | ||||
|             onChange={(evt) => setRawKeyword(evt.currentTarget.value)} | ||||
|           /> | ||||
|         </div> | ||||
|         <button className="tonal secondary"> | ||||
|           <Icon icon="material-symbols-light:add" /> | ||||
|           New Pattern | ||||
|         </button> | ||||
|       </div> | ||||
|       <ScrollArea enableY> | ||||
|         <div className={styles.pattern_list}> | ||||
|           {patterns.length === 0 && <div className="empty_prompt">No pattern found.</div>} | ||||
|         </div> | ||||
|       </ScrollArea> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user