mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
2
.vscode/recoil.code-snippets
vendored
2
.vscode/recoil.code-snippets
vendored
@@ -20,7 +20,7 @@
|
||||
"export const ${1:CollectionData}Selector = selector({",
|
||||
" key: '${1:CollectionData}Selector',",
|
||||
" get: ({get}) => {",
|
||||
" return get(${2:CollectionIdState});",
|
||||
" return get(${1:CollectionData}Atom);",
|
||||
" }",
|
||||
"});"
|
||||
],
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -19,5 +19,9 @@
|
||||
}
|
||||
],
|
||||
"eliostruyf.writingstyleguide.terms.isDisabled": true,
|
||||
"eliostruyf.writingstyleguide.biasFree.isDisabled": true
|
||||
"eliostruyf.writingstyleguide.biasFree.isDisabled": true,
|
||||
"exportall.config.folderListener": [
|
||||
"/src/pagesView/state/atom",
|
||||
"/src/pagesView/state/selectors"
|
||||
]
|
||||
}
|
||||
@@ -3,13 +3,10 @@ import { Spinner } from './Spinner';
|
||||
import useMessages from '../hooks/useMessages';
|
||||
import { Overview } from './Overview';
|
||||
import { Header } from './Header';
|
||||
import { Tab } from '../constants/Tab';
|
||||
import { SortOption } from '../constants/SortOption';
|
||||
import useDarkMode from '../../hooks/useDarkMode';
|
||||
import usePages from '../hooks/usePages';
|
||||
import { SponsorMsg } from './SponsorMsg';
|
||||
import { WelcomeScreen } from './WelcomeScreen';
|
||||
import { GroupOption } from '../constants/GroupOption';
|
||||
|
||||
export interface IDashboardProps {
|
||||
showWelcome: boolean;
|
||||
@@ -17,14 +14,7 @@ export interface IDashboardProps {
|
||||
|
||||
export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome}: React.PropsWithChildren<IDashboardProps>) => {
|
||||
const { loading, pages, settings } = useMessages();
|
||||
const [ tab, setTab ] = React.useState(Tab.All);
|
||||
const [ sorting, setSorting ] = React.useState(SortOption.LastModified);
|
||||
const [ folder, setFolder ] = React.useState<string | null>(null);
|
||||
const [ search, setSearch ] = React.useState<string | null>(null);
|
||||
const [ tag, setTag ] = React.useState<string | null>(null);
|
||||
const [ category, setCategory ] = React.useState<string | null>(null);
|
||||
const [ group, setGroup ] = React.useState<GroupOption>(GroupOption.none);
|
||||
const { pageItems } = usePages(pages, tab, sorting, folder, search, tag, category);
|
||||
const { pageItems } = usePages(pages);
|
||||
useDarkMode();
|
||||
|
||||
const pageFolders = [...new Set(pages.map(page => page.fmFolder))];
|
||||
@@ -44,26 +34,13 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
|
||||
return (
|
||||
<main className={`h-full w-full`}>
|
||||
<div className="flex flex-col h-full overflow-auto">
|
||||
<Header currentTab={tab}
|
||||
currentSorting={sorting}
|
||||
folders={pageFolders}
|
||||
crntFolder={folder}
|
||||
totalPages={pageItems.length}
|
||||
crntTag={tag}
|
||||
crntCategory={category}
|
||||
crntGroup={group}
|
||||
switchTab={(tabId: Tab) => setTab(tabId)}
|
||||
switchSorting={(sortId: SortOption) => setSorting(sortId)}
|
||||
switchFolder={(folderName: string | null) => setFolder(folderName)}
|
||||
switchTag={(tagId: string | null) => setTag(tagId)}
|
||||
switchCategory={(categoryId: string | null) => setCategory(categoryId)}
|
||||
switchGroup={(groupId: GroupOption) => setGroup(groupId)}
|
||||
onSearch={(value: string | null) => setSearch(value)}
|
||||
settings={settings}
|
||||
/>
|
||||
<Header
|
||||
folders={pageFolders}
|
||||
totalPages={pageItems.length}
|
||||
settings={settings} />
|
||||
|
||||
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
|
||||
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} grouping={group} /> }
|
||||
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
|
||||
</div>
|
||||
|
||||
<SponsorMsg />
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { FolderAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IFoldersProps {
|
||||
folders: string[];
|
||||
crntFolder: string | null;
|
||||
switchFolder: (group: string | null) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_TYPE = "All types";
|
||||
|
||||
export const Folders: React.FunctionComponent<IFoldersProps> = ({folders, crntFolder, switchFolder}: React.PropsWithChildren<IFoldersProps>) => {
|
||||
export const Folders: React.FunctionComponent<IFoldersProps> = ({folders}: React.PropsWithChildren<IFoldersProps>) => {
|
||||
const [ crntFolder, setCrntFolder ] = useRecoilState(FolderAtom);
|
||||
|
||||
if (folders.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -25,7 +27,7 @@ export const Folders: React.FunctionComponent<IFoldersProps> = ({folders, crntFo
|
||||
title={DEFAULT_TYPE}
|
||||
value={null}
|
||||
isCurrent={!crntFolder}
|
||||
onClick={switchFolder} />
|
||||
onClick={(value) => setCrntFolder(value)} />
|
||||
|
||||
{folders.map((option) => (
|
||||
<MenuItem
|
||||
@@ -33,7 +35,7 @@ export const Folders: React.FunctionComponent<IFoldersProps> = ({folders, crntFo
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === crntFolder}
|
||||
onClick={switchFolder} />
|
||||
onClick={(value) => setCrntFolder(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { GroupingAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IGroupingProps {
|
||||
group: GroupOption;
|
||||
switchGroup: (group: GroupOption) => void;
|
||||
}
|
||||
export interface IGroupingProps {}
|
||||
|
||||
export const groupOptions = [
|
||||
{ name: "None", id: GroupOption.none },
|
||||
@@ -14,7 +13,9 @@ export const groupOptions = [
|
||||
{ name: "Draft/Published", id: GroupOption.Draft },
|
||||
];
|
||||
|
||||
export const Grouping: React.FunctionComponent<IGroupingProps> = ({group, switchGroup}: React.PropsWithChildren<IGroupingProps>) => {
|
||||
export const Grouping: React.FunctionComponent<IGroupingProps> = ({}: React.PropsWithChildren<IGroupingProps>) => {
|
||||
const [ group, setGroup ] = useRecoilState(GroupingAtom);
|
||||
|
||||
const crntGroup = groupOptions.find(x => x.id === group);
|
||||
|
||||
return (
|
||||
@@ -29,7 +30,7 @@ export const Grouping: React.FunctionComponent<IGroupingProps> = ({group, switch
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === crntGroup?.id}
|
||||
onClick={switchGroup} />
|
||||
onClick={(value) => setGroup(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
|
||||
@@ -4,51 +4,29 @@ import { Searchbox } from './Searchbox';
|
||||
import { Filter } from './Filter';
|
||||
import { Folders } from './Folders';
|
||||
import { Settings } from '../../models';
|
||||
import { Tab } from '../../constants/Tab';
|
||||
import { SortOption } from '../../constants/SortOption';
|
||||
import { MessageHelper } from '../../../helpers/MessageHelper';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Startup } from '../Startup';
|
||||
import { Button } from '../Button';
|
||||
import { Navigation } from '../Navigation';
|
||||
import { Grouping } from '.';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { ViewSwitch } from './ViewSwitch';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { CategoryAtom, TagAtom } from '../../state';
|
||||
|
||||
export interface IHeaderProps {
|
||||
settings: Settings;
|
||||
|
||||
// Navigation
|
||||
currentTab: Tab;
|
||||
totalPages: number;
|
||||
switchTab: (tabId: Tab) => void;
|
||||
|
||||
// Sorting
|
||||
currentSorting: SortOption;
|
||||
switchSorting: (sortId: SortOption) => void;
|
||||
|
||||
// Grouping
|
||||
// Page folders
|
||||
folders: string[];
|
||||
crntFolder: string | null;
|
||||
switchFolder: (folderName: string | null) => void;
|
||||
|
||||
// Searching
|
||||
onSearch: (value: string | null) => void;
|
||||
|
||||
// Tags
|
||||
crntTag: string | null;
|
||||
switchTag: (tag: string | null) => void;
|
||||
|
||||
// Categories
|
||||
crntCategory: string | null;
|
||||
switchCategory: (category: string | null) => void;
|
||||
|
||||
// Grouping
|
||||
crntGroup: GroupOption;
|
||||
switchGroup: (groupId: GroupOption) => void;
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntFolder, folders, switchFolder, onSearch, settings, switchTag, crntTag, switchCategory, crntCategory, crntGroup, switchGroup}: React.PropsWithChildren<IHeaderProps>) => {
|
||||
export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folders, settings }: React.PropsWithChildren<IHeaderProps>) => {
|
||||
const [ crntTag, setCrntTag ] = useRecoilState(TagAtom);
|
||||
const [ crntCategory, setCrntCategory ] = useRecoilState(CategoryAtom);
|
||||
|
||||
const createContent = () => {
|
||||
MessageHelper.sendMessage(DashboardMessage.createContent);
|
||||
@@ -57,7 +35,7 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, curre
|
||||
return (
|
||||
<div className={`w-full max-w-7xl mx-auto sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
|
||||
<div className={`px-4 my-2 flex items-center justify-between`}>
|
||||
<Searchbox onSearch={onSearch} />
|
||||
<Searchbox />
|
||||
|
||||
<div className={`flex items-center space-x-4`}>
|
||||
<Startup settings={settings} />
|
||||
@@ -68,19 +46,19 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, curre
|
||||
|
||||
<div className="px-4 flex flex-col lg:flex-row items-center border-b border-gray-200 dark:border-whisper-600">
|
||||
<div className={`w-full lg:w-auto`}>
|
||||
<Navigation currentTab={currentTab} totalPages={totalPages} switchTab={switchTab} />
|
||||
<Navigation totalPages={totalPages} />
|
||||
</div>
|
||||
|
||||
<div className={`my-4 lg:my-0 w-full flex items-center justify-end space-x-4 lg:space-x-6 xl:space-x-8 order-first lg:order-last`}>
|
||||
<Folders crntFolder={crntFolder} folders={folders} switchFolder={switchFolder} />
|
||||
<Folders folders={folders} />
|
||||
|
||||
<Filter label={`Tag filter`} activeItem={crntTag} items={settings.tags} onClick={switchTag} />
|
||||
<Filter label={`Tag filter`} activeItem={crntTag} items={settings.tags} onClick={(value) => setCrntTag(value)} />
|
||||
|
||||
<Filter label={`Category filter`} activeItem={crntCategory} items={settings.categories} onClick={switchCategory} />
|
||||
<Filter label={`Category filter`} activeItem={crntCategory} items={settings.categories} onClick={(value) => setCrntCategory(value)} />
|
||||
|
||||
<Grouping group={crntGroup} switchGroup={switchGroup} />
|
||||
<Grouping />
|
||||
|
||||
<Sorting currentSorting={currentSorting} switchSorting={switchSorting} />
|
||||
<Sorting />
|
||||
|
||||
<ViewSwitch />
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { FilterIcon, SearchIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useDebounce } from '../../../hooks/useDebounce';
|
||||
import { SearchAtom } from '../../state';
|
||||
|
||||
export interface ISearchboxProps {
|
||||
onSearch: (searchText: string) => void;
|
||||
}
|
||||
export interface ISearchboxProps {}
|
||||
|
||||
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}: React.PropsWithChildren<ISearchboxProps>) => {
|
||||
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({}: React.PropsWithChildren<ISearchboxProps>) => {
|
||||
const [ value, setValue ] = React.useState('');
|
||||
const [ , setDebounceValue ] = useRecoilState(SearchAtom);
|
||||
const debounceSearch = useDebounce<string>(value, 500);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -15,7 +16,7 @@ export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}:
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
onSearch(debounceSearch);
|
||||
setDebounceValue(debounceSearch);
|
||||
}, [debounceSearch]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { SortOption } from '../../constants/SortOption';
|
||||
import { SearchSelector, SortingAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface ISortingProps {
|
||||
currentSorting: SortOption;
|
||||
|
||||
switchSorting: (sortId: SortOption) => void;
|
||||
}
|
||||
export interface ISortingProps {}
|
||||
|
||||
export const sortOptions = [
|
||||
{ name: "Last modified", id: SortOption.LastModified },
|
||||
@@ -15,14 +13,16 @@ export const sortOptions = [
|
||||
{ name: "By filename (desc)", id: SortOption.FileNameDesc },
|
||||
];
|
||||
|
||||
export const Sorting: React.FunctionComponent<ISortingProps> = ({currentSorting, switchSorting}: React.PropsWithChildren<ISortingProps>) => {
|
||||
export const Sorting: React.FunctionComponent<ISortingProps> = ({}: React.PropsWithChildren<ISortingProps>) => {
|
||||
const [ crntSorting, setCrntSorting ] = useRecoilState(SortingAtom);
|
||||
const searchValue = useRecoilValue(SearchSelector);
|
||||
|
||||
const crntSort = sortOptions.find(x => x.id === currentSorting);
|
||||
const crntSort = sortOptions.find(x => x.id === crntSorting);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Sort by`} title={crntSort?.name || ""} />
|
||||
<MenuButton label={`Sort by`} title={crntSort?.name || ""} disabled={!!searchValue} />
|
||||
|
||||
<MenuItems>
|
||||
{sortOptions.map((option) => (
|
||||
@@ -30,8 +30,8 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({currentSorting,
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === currentSorting}
|
||||
onClick={switchSorting} />
|
||||
isCurrent={option.id === crntSorting}
|
||||
onClick={(value) => setCrntSorting(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
|
||||
@@ -12,13 +12,6 @@ export interface IItemProps extends Page {}
|
||||
|
||||
export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, title, draft, description, preview }: React.PropsWithChildren<IItemProps>) => {
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
|
||||
let className = '';
|
||||
if (view === ViewType.Grid) {
|
||||
className = `grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`;
|
||||
} else if (view === ViewType.List) {
|
||||
className = `divide-y divide-vulcan-200`;
|
||||
}
|
||||
|
||||
const openFile = () => {
|
||||
MessageHelper.sendMessage(DashboardMessage.openFile, fmFilePath);
|
||||
|
||||
@@ -5,13 +5,14 @@ import * as React from 'react';
|
||||
export interface IMenuButtonProps {
|
||||
label: string;
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const MenuButton: React.FunctionComponent<IMenuButtonProps> = ({label, title}: React.PropsWithChildren<IMenuButtonProps>) => {
|
||||
export const MenuButton: React.FunctionComponent<IMenuButtonProps> = ({label, title, disabled}: React.PropsWithChildren<IMenuButtonProps>) => {
|
||||
return (
|
||||
<div>
|
||||
<div className={`${disabled ? 'opacity-50' : ''}`}>
|
||||
<span className={`text-gray-500 dark:text-whisper-700 mr-2 font-medium`}>{label}:</span>
|
||||
<Menu.Button className="group inline-flex justify-center text-sm font-medium text-vulcan-500 hover:text-vulcan-600 dark:text-whisper-500 dark:hover:text-whisper-600">
|
||||
<Menu.Button disabled={disabled} className="group inline-flex justify-center text-sm font-medium text-vulcan-500 hover:text-vulcan-600 dark:text-whisper-500 dark:hover:text-whisper-600">
|
||||
{title}
|
||||
<ChevronDownIcon
|
||||
className="flex-shrink-0 -mr-1 ml-1 h-5 w-5 text-gray-400 group-hover:text-gray-500 dark:text-whisper-600 dark:group-hover:text-whisper-700"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { Tab } from '../constants/Tab';
|
||||
import { TabAtom } from '../state';
|
||||
|
||||
export interface INavigationProps {
|
||||
currentTab: Tab;
|
||||
totalPages: number;
|
||||
switchTab: (tabId: Tab) => void;
|
||||
}
|
||||
|
||||
export const tabs = [
|
||||
@@ -13,18 +13,19 @@ export const tabs = [
|
||||
{ name: 'In draft', id: Tab.Draft }
|
||||
];
|
||||
|
||||
export const Navigation: React.FunctionComponent<INavigationProps> = ({currentTab, totalPages, switchTab}: React.PropsWithChildren<INavigationProps>) => {
|
||||
export const Navigation: React.FunctionComponent<INavigationProps> = ({totalPages}: React.PropsWithChildren<INavigationProps>) => {
|
||||
const [ crntTab, setCrntTab ] = useRecoilState(TabAtom);
|
||||
|
||||
return (
|
||||
<nav className="flex-1 -mb-px flex space-x-6 xl:space-x-8" aria-label="Tabs">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.name}
|
||||
className={`${tab.id === currentTab ? `border-teal-900 dark:border-teal-300 text-teal-900 dark:text-teal-300` : `border-transparent text-gray-500 dark:text-whisper-600 hover:text-gray-700 dark:hover:text-whisper-700 hover:border-gray-300 dark:hover:border-whisper-500`} whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm`}
|
||||
aria-current={tab.id === currentTab ? 'page' : undefined}
|
||||
onClick={() => switchTab(tab.id)}
|
||||
className={`${tab.id === crntTab ? `border-teal-900 dark:border-teal-300 text-teal-900 dark:text-teal-300` : `border-transparent text-gray-500 dark:text-whisper-600 hover:text-gray-700 dark:hover:text-whisper-700 hover:border-gray-300 dark:hover:border-whisper-500`} whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm`}
|
||||
aria-current={tab.id === crntTab ? 'page' : undefined}
|
||||
onClick={() => setCrntTab(tab.id)}
|
||||
>
|
||||
{tab.name}{(tab.id === currentTab && totalPages) ? ` (${totalPages})` : ''}
|
||||
{tab.name}{(tab.id === crntTab && totalPages) ? ` (${totalPages})` : ''}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { ChevronRightIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { groupBy } from '../../helpers/GroupBy';
|
||||
import { FrontMatterIcon } from '../../viewpanel/components/Icons/FrontMatterIcon';
|
||||
import { GroupOption } from '../constants/GroupOption';
|
||||
import { Page } from '../models/Page';
|
||||
import { Settings } from '../models/Settings';
|
||||
import { GroupingSelector } from '../state';
|
||||
import { Item } from './Item';
|
||||
import { List } from './List';
|
||||
|
||||
export interface IOverviewProps {
|
||||
pages: Page[];
|
||||
grouping: GroupOption;
|
||||
|
||||
pages: Page[];
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings, grouping}: React.PropsWithChildren<IOverviewProps>) => {
|
||||
const [ open, setOpen ] = React.useState(true);
|
||||
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings}: React.PropsWithChildren<IOverviewProps>) => {
|
||||
const grouping = useRecoilValue(GroupingSelector);
|
||||
|
||||
if (!pages || !pages.length) {
|
||||
return (
|
||||
@@ -65,7 +65,7 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settin
|
||||
<Disclosure.Panel>
|
||||
<List>
|
||||
{groupedPages[groupId].map((page: Page) => (
|
||||
<Item key={page.slug} {...page} />
|
||||
<Item key={`${page.slug}-${idx}`} {...page} />
|
||||
))}
|
||||
</List>
|
||||
</Disclosure.Panel>
|
||||
@@ -80,8 +80,8 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settin
|
||||
|
||||
return (
|
||||
<List>
|
||||
{pages.map(page => (
|
||||
<Item key={page.slug} {...page} />
|
||||
{pages.map((page, idx) => (
|
||||
<Item key={`${page.slug}-${idx}`} {...page} />
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
|
||||
@@ -3,18 +3,25 @@ import { SortOption } from '../constants/SortOption';
|
||||
import { Tab } from '../constants/Tab';
|
||||
import { Page } from '../models/Page';
|
||||
import Fuse from 'fuse.js';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { CategorySelector, FolderSelector, SearchSelector, SortingSelector, TabSelector, TagSelector } from '../state';
|
||||
|
||||
const fuseOptions: Fuse.IFuseOptions<Page> = {
|
||||
keys: [
|
||||
"title",
|
||||
"slug",
|
||||
"description",
|
||||
"fmFileName"
|
||||
{ name: 'title', weight: 0.8 },
|
||||
{ name: 'slug', weight: 0.8 },
|
||||
{ name: 'description', weight: 0.5 }
|
||||
]
|
||||
};
|
||||
|
||||
export default function usePages(pages: Page[], tab: Tab, sorting: SortOption, folder: string | null, search: string | null, tag: string | null, category: string | null) {
|
||||
export default function usePages(pages: Page[]) {
|
||||
const [ pageItems, setPageItems ] = useState<Page[]>([]);
|
||||
const tab = useRecoilValue(TabSelector);
|
||||
const sorting = useRecoilValue(SortingSelector);
|
||||
const folder = useRecoilValue(FolderSelector);
|
||||
const search = useRecoilValue(SearchSelector);
|
||||
const tag = useRecoilValue(TagSelector);
|
||||
const category = useRecoilValue(CategorySelector);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if search needs to be performed
|
||||
@@ -26,7 +33,7 @@ export default function usePages(pages: Page[], tab: Tab, sorting: SortOption, f
|
||||
}
|
||||
|
||||
// Filter the pages
|
||||
let pagesToShow = searchedPages;
|
||||
let pagesToShow: Page[] = Object.assign([], searchedPages);
|
||||
if (tab === Tab.Published) {
|
||||
pagesToShow = searchedPages.filter(page => !page.draft);
|
||||
} else if (tab === Tab.Draft) {
|
||||
@@ -36,13 +43,15 @@ export default function usePages(pages: Page[], tab: Tab, sorting: SortOption, f
|
||||
}
|
||||
|
||||
// Sort the pages
|
||||
let pagesSorted = pagesToShow;
|
||||
if (sorting === SortOption.FileNameAsc) {
|
||||
pagesSorted = pagesToShow.sort((a, b) => a.fmFileName.toLowerCase().localeCompare(b.fmFileName.toLowerCase()));
|
||||
} else if (sorting === SortOption.FileNameDesc) {
|
||||
pagesSorted = pagesToShow.sort((a, b) => b.fmFileName.toLowerCase().localeCompare(a.fmFileName.toLowerCase()));
|
||||
} else {
|
||||
pagesSorted = pagesToShow.sort((a, b) => b.fmModified - a.fmModified);
|
||||
let pagesSorted: Page[] = Object.assign([], pagesToShow);
|
||||
if (!search) {
|
||||
if (sorting === SortOption.FileNameAsc) {
|
||||
pagesSorted = pagesToShow.sort((a, b) => a.fmFileName.toLowerCase().localeCompare(b.fmFileName.toLowerCase()));
|
||||
} else if (sorting === SortOption.FileNameDesc) {
|
||||
pagesSorted = pagesToShow.sort((a, b) => b.fmFileName.toLowerCase().localeCompare(a.fmFileName.toLowerCase()));
|
||||
} else {
|
||||
pagesSorted = pagesToShow.sort((a, b) => b.fmModified - a.fmModified);
|
||||
}
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
|
||||
6
src/pagesView/state/atom/CategoryAtom.ts
Normal file
6
src/pagesView/state/atom/CategoryAtom.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const CategoryAtom = atom<string | null>({
|
||||
key: 'CategoryAtom',
|
||||
default: ""
|
||||
});
|
||||
6
src/pagesView/state/atom/FolderAtom.ts
Normal file
6
src/pagesView/state/atom/FolderAtom.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const FolderAtom = atom<string | null>({
|
||||
key: 'FolderAtom',
|
||||
default: null
|
||||
});
|
||||
7
src/pagesView/state/atom/GroupingAtom.ts
Normal file
7
src/pagesView/state/atom/GroupingAtom.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { atom } from 'recoil';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
|
||||
export const GroupingAtom = atom<GroupOption>({
|
||||
key: 'GroupingAtom',
|
||||
default: GroupOption.none
|
||||
});
|
||||
6
src/pagesView/state/atom/SearchAtom.ts
Normal file
6
src/pagesView/state/atom/SearchAtom.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const SearchAtom = atom<string>({
|
||||
key: 'SearchAtom',
|
||||
default: ""
|
||||
});
|
||||
7
src/pagesView/state/atom/SortingAtom.ts
Normal file
7
src/pagesView/state/atom/SortingAtom.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { atom } from 'recoil';
|
||||
import { SortOption } from '../../constants/SortOption';
|
||||
|
||||
export const SortingAtom = atom<SortOption>({
|
||||
key: 'SortingAtom',
|
||||
default: SortOption.LastModified
|
||||
});
|
||||
7
src/pagesView/state/atom/TabAtom.ts
Normal file
7
src/pagesView/state/atom/TabAtom.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { atom } from 'recoil';
|
||||
import { Tab } from '../../constants/Tab';
|
||||
|
||||
export const TabAtom = atom<Tab>({
|
||||
key: 'TabAtom',
|
||||
default: Tab.All
|
||||
});
|
||||
6
src/pagesView/state/atom/TagAtom.ts
Normal file
6
src/pagesView/state/atom/TagAtom.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const TagAtom = atom<string | null>({
|
||||
key: 'TagAtom',
|
||||
default: ""
|
||||
});
|
||||
@@ -1 +1,8 @@
|
||||
export * from './CategoryAtom';
|
||||
export * from './FolderAtom';
|
||||
export * from './GroupingAtom';
|
||||
export * from './SearchAtom';
|
||||
export * from './SortingAtom';
|
||||
export * from './TabAtom';
|
||||
export * from './TagAtom';
|
||||
export * from './ViewAtom';
|
||||
|
||||
9
src/pagesView/state/selectors/CategorySelector.ts
Normal file
9
src/pagesView/state/selectors/CategorySelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { CategoryAtom } from '..';
|
||||
|
||||
export const CategorySelector = selector({
|
||||
key: 'CategorySelector',
|
||||
get: ({get}) => {
|
||||
return get(CategoryAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/FolderSelector.ts
Normal file
9
src/pagesView/state/selectors/FolderSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { FolderAtom } from '..';
|
||||
|
||||
export const FolderSelector = selector({
|
||||
key: 'FolderSelector',
|
||||
get: ({get}) => {
|
||||
return get(FolderAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/GroupingSelector.ts
Normal file
9
src/pagesView/state/selectors/GroupingSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { GroupingAtom } from '..';
|
||||
|
||||
export const GroupingSelector = selector({
|
||||
key: 'GroupingSelector',
|
||||
get: ({get}) => {
|
||||
return get(GroupingAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/SearchSelector.ts
Normal file
9
src/pagesView/state/selectors/SearchSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { SearchAtom } from '..';
|
||||
|
||||
export const SearchSelector = selector({
|
||||
key: 'SearchSelector',
|
||||
get: ({get}) => {
|
||||
return get(SearchAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/SortingSelector.ts
Normal file
9
src/pagesView/state/selectors/SortingSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { SortingAtom } from '..';
|
||||
|
||||
export const SortingSelector = selector({
|
||||
key: 'SortingSelector',
|
||||
get: ({get}) => {
|
||||
return get(SortingAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/TabSelector.ts
Normal file
9
src/pagesView/state/selectors/TabSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { TabAtom } from '..';
|
||||
|
||||
export const TabSelector = selector({
|
||||
key: 'TabSelector',
|
||||
get: ({get}) => {
|
||||
return get(TabAtom);
|
||||
}
|
||||
});
|
||||
9
src/pagesView/state/selectors/TagSelector.ts
Normal file
9
src/pagesView/state/selectors/TagSelector.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { selector } from 'recoil';
|
||||
import { TagAtom } from '..';
|
||||
|
||||
export const TagSelector = selector({
|
||||
key: 'TagSelector',
|
||||
get: ({get}) => {
|
||||
return get(TagAtom);
|
||||
}
|
||||
});
|
||||
@@ -1 +1,8 @@
|
||||
export * from './CategorySelector';
|
||||
export * from './FolderSelector';
|
||||
export * from './GroupingSelector';
|
||||
export * from './SearchSelector';
|
||||
export * from './SortingSelector';
|
||||
export * from './TabSelector';
|
||||
export * from './TagSelector';
|
||||
export * from './ViewSelector';
|
||||
|
||||
Reference in New Issue
Block a user