mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 17:32:58 +02:00
Support filtering, documentation updates
This commit is contained in:
@@ -2,5 +2,6 @@ export enum DashboardMessage {
|
||||
getData = 'getData',
|
||||
openFile = 'openFile',
|
||||
getTheme = 'getTheme',
|
||||
createContent = 'createContent'
|
||||
createContent = 'createContent',
|
||||
updateSetting = 'updateSetting',
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IButtonProps {
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const Button: React.FunctionComponent<IButtonProps> = ({onClick, disabled, children}: React.PropsWithChildren<IButtonProps>) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -5,68 +5,22 @@ import { Overview } from './Overview';
|
||||
import { Header } from './Header';
|
||||
import { Tab } from '../constants/Tab';
|
||||
import { SortOption } from '../constants/SortOption';
|
||||
import Fuse from 'fuse.js';
|
||||
import { Page } from '../models/Page';
|
||||
import useDarkMode from '../../hooks/useDarkMode';
|
||||
import usePages from '../hooks/usePages';
|
||||
|
||||
export interface IDashboardProps {}
|
||||
|
||||
// TODO: Filter by tag / category
|
||||
|
||||
const fuseOptions: Fuse.IFuseOptions<Page> = {
|
||||
keys: [
|
||||
"title",
|
||||
"slug",
|
||||
"description",
|
||||
"fmFileName"
|
||||
]
|
||||
};
|
||||
|
||||
export const Dashboard: React.FunctionComponent<IDashboardProps> = ({}: React.PropsWithChildren<IDashboardProps>) => {
|
||||
const { loading, pages, settings } = useMessages();
|
||||
const [ tab, setTab ] = React.useState(Tab.All);
|
||||
const [ sorting, setSorting ] = React.useState(SortOption.LastModified);
|
||||
const [ group, setGroup ] = React.useState<string | null>(null);
|
||||
const [ search, setSearch ] = React.useState<string | null>(null);
|
||||
const [ pageItems, setPageItems ] = React.useState<Page[]>([]);
|
||||
const [ tag, setTag ] = React.useState<string | null>(null);
|
||||
const [ category, setCategory ] = React.useState<string | null>(null);
|
||||
const { pageItems } = usePages(pages, tab, sorting, group, search, tag, category);
|
||||
useDarkMode();
|
||||
|
||||
React.useEffect(() => {
|
||||
// Check if search needs to be performed
|
||||
let searchedPages = pages;
|
||||
if (search) {
|
||||
const fuse = new Fuse(pages, fuseOptions);
|
||||
const results = fuse.search(search);
|
||||
searchedPages = results.map(page => page.item);
|
||||
}
|
||||
|
||||
// Filter the pages
|
||||
let pagesToShow = searchedPages;
|
||||
if (tab === Tab.Published) {
|
||||
pagesToShow = searchedPages.filter(page => !page.draft);
|
||||
} else if (tab === Tab.Draft) {
|
||||
pagesToShow = searchedPages.filter(page => !!page.draft);
|
||||
} else {
|
||||
pagesToShow = searchedPages;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (group) {
|
||||
pagesSorted = pagesSorted.filter(page => page.fmGroup === group);
|
||||
}
|
||||
|
||||
setPageItems(pagesSorted);
|
||||
}, [ pages, tab, sorting, group, search ]);
|
||||
|
||||
const pageGroups = [...new Set(pages.map(page => page.fmGroup))];
|
||||
|
||||
return (
|
||||
@@ -77,9 +31,13 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({}: React.Pr
|
||||
groups={pageGroups}
|
||||
crntGroup={group}
|
||||
totalPages={pageItems.length}
|
||||
crntTag={tag}
|
||||
crntCategory={category}
|
||||
switchTab={(tabId: Tab) => setTab(tabId)}
|
||||
switchSorting={(sortId: SortOption) => setSorting(sortId)}
|
||||
switchGroup={(groupId: string | null) => setGroup(groupId)}
|
||||
switchTag={(tagId: string | null) => setTag(tagId)}
|
||||
switchCategory={(categoryId: string | null) => setCategory(categoryId)}
|
||||
onSearch={(value: string | null) => setSearch(value)}
|
||||
settings={settings}
|
||||
/>
|
||||
|
||||
@@ -6,11 +6,23 @@ export interface IDateFieldProps {
|
||||
}
|
||||
|
||||
export const DateField: React.FunctionComponent<IDateFieldProps> = ({value}: React.PropsWithChildren<IDateFieldProps>) => {
|
||||
const [ dateValue, setDateValue ] = React.useState<string>("");
|
||||
|
||||
const parsedValue = typeof value === 'string' ? parseJSON(value) : value;
|
||||
const dateString = format(parsedValue, 'yyyy-MM-dd');
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
const parsedValue = typeof value === 'string' ? parseJSON(value) : value;
|
||||
const dateString = format(parsedValue, 'yyyy-MM-dd');
|
||||
setDateValue(dateString);
|
||||
} catch (e) {
|
||||
// Date is invalid
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
if (!dateValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`text-vulcan-100 dark:text-whisper-900 text-xs`}>{dateString}</span>
|
||||
<span className={`text-vulcan-100 dark:text-whisper-900 text-xs`}>{dateValue}</span>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { MenuButton } from './MenuButton';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItems } from './MenuItems';
|
||||
|
||||
export interface IFilterProps {
|
||||
label: string;
|
||||
items: string[];
|
||||
activeItem: string | null;
|
||||
onClick: (item: string | null) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_VALUE = "No filter";
|
||||
|
||||
export const Filter: React.FunctionComponent<IFilterProps> = ({label, activeItem, items, onClick}: React.PropsWithChildren<IFilterProps>) => {
|
||||
|
||||
console.log(items);
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={label} title={activeItem || DEFAULT_VALUE} />
|
||||
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
title={DEFAULT_VALUE}
|
||||
value={null}
|
||||
isCurrent={!!activeItem}
|
||||
onClick={() => onClick(null)} />
|
||||
|
||||
{items.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === activeItem}
|
||||
onClick={() => onClick(option)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,9 @@ import { Menu, Transition } from '@headlessui/react';
|
||||
import { ChevronDownIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { MenuButton } from './MenuButton';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItems } from './MenuItems';
|
||||
|
||||
export interface IGroupingProps {
|
||||
groups: string[];
|
||||
@@ -20,45 +22,24 @@ export const Grouping: React.FunctionComponent<IGroupingProps> = ({groups, crntG
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<div>
|
||||
<span className={`text-gray-500 dark:text-whisper-700 mr-2 font-medium`}>Showing:</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">
|
||||
{crntGroup || DEFAULT_TYPE}
|
||||
<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"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<MenuButton label={`Showing`} title={crntGroup || DEFAULT_TYPE} />
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="origin-top-right absolute right-0 z-10 mt-2 w-40 rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm">
|
||||
<div className="py-1">
|
||||
<MenuItem
|
||||
title={DEFAULT_TYPE}
|
||||
value={null}
|
||||
isCurrent={!crntGroup}
|
||||
onClick={switchGroup} />
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
title={DEFAULT_TYPE}
|
||||
value={null}
|
||||
isCurrent={!crntGroup}
|
||||
onClick={switchGroup} />
|
||||
|
||||
{groups.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === crntGroup}
|
||||
onClick={switchGroup} />
|
||||
))}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
{groups.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === crntGroup}
|
||||
onClick={switchGroup} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,9 @@ import { MessageHelper } from '../../helpers/MessageHelper';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import { Searchbox } from './Searchbox';
|
||||
import { Settings } from '../models/Settings';
|
||||
import { Startup } from './Startup';
|
||||
import { Button } from './Button';
|
||||
import { Filter } from './Filter';
|
||||
|
||||
export interface IHeaderProps {
|
||||
settings: Settings;
|
||||
@@ -28,9 +31,17 @@ export interface IHeaderProps {
|
||||
|
||||
// Searching
|
||||
onSearch: (value: string | null) => void;
|
||||
|
||||
// Tags
|
||||
crntTag: string | null;
|
||||
switchTag: (tag: string | null) => void;
|
||||
|
||||
// Categories
|
||||
crntCategory: string | null;
|
||||
switchCategory: (category: string | null) => void;
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntGroup, groups, switchGroup, onSearch, settings}: React.PropsWithChildren<IHeaderProps>) => {
|
||||
export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntGroup, groups, switchGroup, onSearch, settings, switchTag, crntTag, switchCategory, crntCategory}: React.PropsWithChildren<IHeaderProps>) => {
|
||||
|
||||
const createContent = () => {
|
||||
MessageHelper.sendMessage(DashboardMessage.createContent);
|
||||
@@ -40,15 +51,12 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, curre
|
||||
<div className={`mb-6 sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
|
||||
<div className={`px-4 mb-2 flex items-center justify-between`}>
|
||||
<Searchbox onSearch={onSearch} />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500"
|
||||
onClick={createContent}
|
||||
disabled={!settings.initialized}
|
||||
>
|
||||
Create content
|
||||
</button>
|
||||
|
||||
<div className={`flex items-center space-x-4`}>
|
||||
<Startup settings={settings} />
|
||||
|
||||
<Button onClick={createContent} disabled={!settings.initialized}>Create content</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 flex items-center border-b border-gray-200 dark:border-whisper-600">
|
||||
@@ -56,6 +64,10 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, curre
|
||||
|
||||
<Grouping crntGroup={crntGroup} groups={groups} switchGroup={switchGroup} />
|
||||
|
||||
<Filter label={`Tag filter`} activeItem={crntTag} items={settings.tags} onClick={switchTag} />
|
||||
|
||||
<Filter label={`Category filter`} activeItem={crntCategory} items={settings.categories} onClick={switchCategory} />
|
||||
|
||||
<Sorting currentSorting={currentSorting} switchSorting={switchSorting} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { ChevronDownIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IMenuButtonProps {
|
||||
label: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const MenuButton: React.FunctionComponent<IMenuButtonProps> = ({label, title}: React.PropsWithChildren<IMenuButtonProps>) => {
|
||||
return (
|
||||
<div>
|
||||
<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">
|
||||
{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"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ export const MenuItem: React.FunctionComponent<IMenuItemProps> = ({title, value,
|
||||
<Menu.Item>
|
||||
<button
|
||||
onClick={() => onClick(value)}
|
||||
className={`${!isCurrent ? `text-vulcan-500 dark:text-whisper-500` : `text-gray-500 dark:text-whisper-900`} block px-4 py-2 text-sm font-medium w-full text-left hover:text-gray-700 dark:hover:text-whisper-600`}
|
||||
className={`${!isCurrent ? `text-vulcan-500 dark:text-whisper-500` : `text-gray-500 dark:text-whisper-900`} block px-4 py-2 text-sm font-medium w-full text-left hover:bg-gray-100 hover:text-gray-700 dark:hover:text-whisper-600 dark:hover:bg-vulcan-100`}
|
||||
>
|
||||
{title}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export interface IMenuItemsProps {}
|
||||
|
||||
export const MenuItems: React.FunctionComponent<IMenuItemsProps> = ({children}: React.PropsWithChildren<IMenuItemsProps>) => {
|
||||
return (
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="origin-top-right absolute right-0 z-10 mt-2 w-40 rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto">
|
||||
<div className="py-1">
|
||||
{children}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -4,6 +4,8 @@ import { SortOption } from '../constants/SortOption';
|
||||
import { ChevronDownIcon } from '@heroicons/react/solid';
|
||||
import { Fragment } from 'react';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItems } from './MenuItems';
|
||||
import { MenuButton } from './MenuButton';
|
||||
|
||||
export interface ISortingProps {
|
||||
currentSorting: SortOption;
|
||||
@@ -24,39 +26,18 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({currentSorting,
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<div>
|
||||
<span className={`text-gray-500 dark:text-whisper-700 mr-2 font-medium`}>Sort by:</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">
|
||||
{crntSort?.name}
|
||||
<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"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<MenuButton label={`Sort by`} title={crntSort?.name || ""} />
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="origin-top-right absolute right-0 z-10 mt-2 w-40 rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm">
|
||||
<div className="py-1">
|
||||
{sortOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === currentSorting}
|
||||
onClick={switchSorting} />
|
||||
))}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
<MenuItems>
|
||||
{sortOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === currentSorting}
|
||||
onClick={switchSorting} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as React from 'react';
|
||||
import { SETTINGS_DASHBOARD_OPENONSTART } from '../../constants';
|
||||
import { MessageHelper } from '../../helpers/MessageHelper';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import { Settings } from '../models/Settings';
|
||||
|
||||
export interface IStartupProps {
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export const Startup: React.FunctionComponent<IStartupProps> = ({settings}: React.PropsWithChildren<IStartupProps>) => {
|
||||
const [isChecked, setIsChecked] = React.useState(false);
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setIsChecked(e.target.checked);
|
||||
MessageHelper.sendMessage(DashboardMessage.updateSetting, { name: SETTINGS_DASHBOARD_OPENONSTART, value: e.target.checked });
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(`openOnStart`, settings.openOnStart);
|
||||
setIsChecked(!!settings.openOnStart);
|
||||
}, [settings?.openOnStart]);
|
||||
|
||||
return (
|
||||
<div className={`relative flex items-start`}>
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
id="startup"
|
||||
aria-describedby="startup-description"
|
||||
name="startup"
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
onChange={onChange}
|
||||
className="focus:outline-none focus:ring-teal-500 h-4 w-4 text-teal-600 border-gray-300 dark:border-vulcan-50 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-2 text-sm">
|
||||
<label id="startup-description" htmlFor="startup" className="font-medium text-vulcan-50 dark:text-whisper-900">
|
||||
Open on startup?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { SortOption } from '../constants/SortOption';
|
||||
import { Tab } from '../constants/Tab';
|
||||
import { Page } from '../models/Page';
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
const fuseOptions: Fuse.IFuseOptions<Page> = {
|
||||
keys: [
|
||||
"title",
|
||||
"slug",
|
||||
"description",
|
||||
"fmFileName"
|
||||
]
|
||||
};
|
||||
|
||||
export default function usePages(pages: Page[], tab: Tab, sorting: SortOption, group: string | null, search: string | null, tag: string | null, category: string | null) {
|
||||
const [ pageItems, setPageItems ] = useState<Page[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if search needs to be performed
|
||||
let searchedPages = pages;
|
||||
if (search) {
|
||||
const fuse = new Fuse(pages, fuseOptions);
|
||||
const results = fuse.search(search);
|
||||
searchedPages = results.map(page => page.item);
|
||||
}
|
||||
|
||||
// Filter the pages
|
||||
let pagesToShow = searchedPages;
|
||||
if (tab === Tab.Published) {
|
||||
pagesToShow = searchedPages.filter(page => !page.draft);
|
||||
} else if (tab === Tab.Draft) {
|
||||
pagesToShow = searchedPages.filter(page => !!page.draft);
|
||||
} else {
|
||||
pagesToShow = searchedPages;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (group) {
|
||||
pagesSorted = pagesSorted.filter(page => page.fmGroup === group);
|
||||
}
|
||||
|
||||
// Filter by tag
|
||||
if (tag) {
|
||||
pagesSorted = pagesSorted.filter(page => page.tags && page.tags.includes(tag));
|
||||
}
|
||||
|
||||
// Filter by category
|
||||
if (category) {
|
||||
pagesSorted = pagesSorted.filter(page => page.categories && page.categories.includes(category));
|
||||
}
|
||||
|
||||
setPageItems(pagesSorted);
|
||||
}, [ pages, tab, sorting, group, search, tag, category ]);
|
||||
|
||||
return {
|
||||
pageItems
|
||||
};
|
||||
}
|
||||
@@ -3,4 +3,7 @@ import { ContentFolder } from './../../models/ContentFolder';
|
||||
export interface Settings {
|
||||
folders: ContentFolder[];
|
||||
initialized: boolean
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
openOnStart: boolean | null;
|
||||
}
|
||||
Reference in New Issue
Block a user