mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 09:23:00 +02:00
#77 - Grouping implementation
This commit is contained in:
@@ -15,6 +15,7 @@ import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Settings } from '../pagesView/models/Settings';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { parseJSON } from 'date-fns';
|
||||
|
||||
|
||||
export class Dashboard {
|
||||
@@ -181,6 +182,8 @@ export class Dashboard {
|
||||
fmModified: file.mtime,
|
||||
fmFilePath: file.filePath,
|
||||
fmFileName: file.fileName,
|
||||
fmDraft: article?.data.draft ? "Draft" : "Published",
|
||||
fmYear: article?.data[dateField] ? parseJSON(article?.data[dateField]).getFullYear() : null,
|
||||
// Make sure these are always set
|
||||
title: article?.data.title,
|
||||
slug: article?.data.slug,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const groupBy = (array: any[], key: string) => {
|
||||
return array.reduce((result, currentValue) => {
|
||||
(result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
|
||||
return result;
|
||||
}, {});
|
||||
};
|
||||
@@ -9,6 +9,7 @@ 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;
|
||||
@@ -22,7 +23,7 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
|
||||
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<string | null>(null);
|
||||
const [ group, setGroup ] = React.useState<GroupOption>(GroupOption.none);
|
||||
const { pageItems } = usePages(pages, tab, sorting, folder, search, tag, category);
|
||||
useDarkMode();
|
||||
|
||||
@@ -56,13 +57,13 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
|
||||
switchFolder={(folderName: string | null) => setFolder(folderName)}
|
||||
switchTag={(tagId: string | null) => setTag(tagId)}
|
||||
switchCategory={(categoryId: string | null) => setCategory(categoryId)}
|
||||
switchGroup={(groupName: string | null) => setGroup(groupName)}
|
||||
switchGroup={(groupId: GroupOption) => setGroup(groupId)}
|
||||
onSearch={(value: string | null) => setSearch(value)}
|
||||
settings={settings}
|
||||
/>
|
||||
|
||||
<div className="flex-grow max-w-7xl mx-auto py-6 px-4">
|
||||
{ loading ? <Spinner /> : <Overview pages={pageItems} 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} /> }
|
||||
</div>
|
||||
|
||||
<SponsorMsg />
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IGroupingProps {
|
||||
crntGroup: string | null;
|
||||
switchGroup: (group: string | null) => void;
|
||||
group: GroupOption;
|
||||
switchGroup: (group: GroupOption) => void;
|
||||
}
|
||||
|
||||
export const Grouping: React.FunctionComponent<IGroupingProps> = (props: React.PropsWithChildren<IGroupingProps>) => {
|
||||
return (
|
||||
<>
|
||||
export const groupOptions = [
|
||||
{ name: "None", id: GroupOption.none },
|
||||
{ name: "Year", id: GroupOption.Year },
|
||||
{ name: "Draft/Published", id: GroupOption.Draft },
|
||||
];
|
||||
|
||||
</>
|
||||
export const Grouping: React.FunctionComponent<IGroupingProps> = ({group, switchGroup}: React.PropsWithChildren<IGroupingProps>) => {
|
||||
const crntGroup = groupOptions.find(x => x.id === group);
|
||||
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Group by`} title={crntGroup?.name || ""} />
|
||||
|
||||
<MenuItems>
|
||||
{groupOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === crntGroup?.id}
|
||||
onClick={switchGroup} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { Startup } from '../Startup';
|
||||
import { Button } from '../Button';
|
||||
import { Navigation } from '../Navigation';
|
||||
import { Grouping } from '.';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
|
||||
export interface IHeaderProps {
|
||||
settings: Settings;
|
||||
@@ -42,8 +43,8 @@ export interface IHeaderProps {
|
||||
switchCategory: (category: string | null) => void;
|
||||
|
||||
// Grouping
|
||||
crntGroup: string | null;
|
||||
switchGroup: (groupId: string | null) => void;
|
||||
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>) => {
|
||||
@@ -73,7 +74,7 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, curre
|
||||
|
||||
<Filter label={`Category filter`} activeItem={crntCategory} items={settings.categories} onClick={switchCategory} />
|
||||
|
||||
<Grouping crntGroup={crntGroup} switchGroup={switchGroup} />
|
||||
<Grouping group={crntGroup} switchGroup={switchGroup} />
|
||||
|
||||
<Sorting currentSorting={currentSorting} switchSorting={switchSorting} />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { ChevronRightIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
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 { Item } from './Item';
|
||||
@@ -7,11 +11,13 @@ import { List } from './List';
|
||||
|
||||
export interface IOverviewProps {
|
||||
pages: Page[];
|
||||
grouping: GroupOption;
|
||||
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings}: React.PropsWithChildren<IOverviewProps>) => {
|
||||
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings, grouping}: React.PropsWithChildren<IOverviewProps>) => {
|
||||
const [ open, setOpen ] = React.useState(true);
|
||||
|
||||
if (!pages || !pages.length) {
|
||||
return (
|
||||
@@ -32,6 +38,46 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settin
|
||||
);
|
||||
}
|
||||
|
||||
if (grouping !== GroupOption.none) {
|
||||
const groupedPages = groupBy(pages, grouping === GroupOption.Year ? 'fmYear' : 'fmDraft');
|
||||
let groupKeys = Object.keys(groupedPages);
|
||||
|
||||
if (grouping === GroupOption.Year) {
|
||||
groupKeys = groupKeys.sort((a, b) => { return parseInt(b) - parseInt(a) });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
groupKeys.map((groupId, idx) => (
|
||||
<Disclosure key={groupId} as={`div`} className={`w-full`} defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className={`mb-4 ${idx !== 0 ? "mt-8" : ""}`}>
|
||||
<h2 className={`text-2xl font-bold flex items-center`}>
|
||||
<ChevronRightIcon
|
||||
className={`w-8 h-8 mr-1 ${open ? "transform rotate-90" : ""}`}
|
||||
/>
|
||||
{GroupOption[grouping]}: {groupId} ({groupedPages[groupId].length})
|
||||
</h2>
|
||||
</Disclosure.Button>
|
||||
|
||||
<Disclosure.Panel>
|
||||
<List>
|
||||
{groupedPages[groupId].map((page: Page) => (
|
||||
<Item key={page.slug} {...page} />
|
||||
))}
|
||||
</List>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
))
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<List>
|
||||
{pages.map(page => (
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
export enum GroupOption {
|
||||
none = 1,
|
||||
Year,
|
||||
Draft
|
||||
}
|
||||
@@ -5,6 +5,8 @@ export interface Page {
|
||||
fmFilePath: string;
|
||||
fmFileName: string;
|
||||
fmModified: number;
|
||||
fmDraft: "Draft" | "Published",
|
||||
fmYear: number | null;
|
||||
|
||||
title: string;
|
||||
slug: string;
|
||||
|
||||
Reference in New Issue
Block a user