Dashboard updates to support content creation + search

This commit is contained in:
Elio Struyf
2021-08-25 21:39:26 +02:00
parent 722c0d6888
commit 6cce35de6c
29 changed files with 509 additions and 161 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
export enum DashboardCommand {
loading = "loading",
data = "data"
pages = "pages",
settings = "settings"
}
+2
View File
@@ -1,4 +1,6 @@
export enum DashboardMessage {
getData = 'getData',
openFile = 'openFile',
getTheme = 'getTheme',
createContent = 'createContent'
}
+64 -27
View File
@@ -5,50 +5,87 @@ 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';
export interface IDashboardProps {}
export const Dashboard: React.FunctionComponent<IDashboardProps> = (props: React.PropsWithChildren<IDashboardProps>) => {
const { loading, pages } = useMessages();
// 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[]>([]);
useDarkMode();
let pagesToShow = pages;
if (tab === Tab.Published) {
pagesToShow = pages.filter(page => !page.draft);
} else if (tab === Tab.Draft) {
pagesToShow = pages.filter(page => !!page.draft);
} else {
pagesToShow = pages;
}
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);
}
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);
}
// 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;
}
// Show draft/published
// Filter by draft
// Filter by folder (if multiple)
// TODO: Sort by last modified
// 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 (
<main className="h-full w-full">
<main className={`h-full w-full`}>
<div className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<Header currentTab={tab}
currentSorting={sorting}
groups={pageGroups}
crntGroup={group}
totalPages={pageItems.length}
switchTab={(tabId: Tab) => setTab(tabId)}
switchSorting={(sortId: SortOption) => setSorting(sortId)}
switchGroup={(groupId: string | null) => setGroup(groupId)}
onSearch={(value: string | null) => setSearch(value)}
settings={settings}
/>
<Overview pages={pagesSorted} />
</div>
{ loading ? <Spinner /> : null }
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
</div>
</main>
);
};
+1 -1
View File
@@ -11,6 +11,6 @@ export const DateField: React.FunctionComponent<IDateFieldProps> = ({value}: Rea
const dateString = format(parsedValue, 'yyyy-MM-dd');
return (
<span className={`text-vulcan-100 text-xs`}>{dateString}</span>
<span className={`text-vulcan-100 dark:text-whisper-900 text-xs`}>{dateString}</span>
);
};
+65
View File
@@ -0,0 +1,65 @@
import { Menu, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/solid';
import * as React from 'react';
import { Fragment } from 'react';
import { MenuItem } from './MenuItem';
export interface IGroupingProps {
groups: string[];
crntGroup: string | null;
switchGroup: (group: string | null) => void;
}
const DEFAULT_TYPE = "All types";
export const Grouping: React.FunctionComponent<IGroupingProps> = ({groups, crntGroup, switchGroup}: React.PropsWithChildren<IGroupingProps>) => {
if (groups.length <= 1) {
return null;
}
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>
<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} />
{groups.map((option) => (
<MenuItem
key={option}
title={option}
value={option}
isCurrent={option === crntGroup}
onClick={switchGroup} />
))}
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
);
};
+44 -78
View File
@@ -1,96 +1,62 @@
import { Menu, Transition } from '@headlessui/react';
import * as React from 'react';
import { Tab } from '../constants/Tab';
import { ChevronDownIcon } from '@heroicons/react/solid';
import { Fragment } from 'react';
import { SortOption } from '../constants/SortOption';
import { Navigation } from './Navigation';
import { Sorting } from './Sorting';
import { Grouping } from './Grouping';
import { MessageHelper } from '../../helpers/MessageHelper';
import { DashboardMessage } from '../DashboardMessage';
import { Searchbox } from './Searchbox';
import { Settings } from '../models/Settings';
export interface IHeaderProps {
settings: Settings;
// Navigation
currentTab: Tab;
currentSorting: SortOption;
totalPages: number;
switchTab: (tabId: Tab) => void;
// Sorting
currentSorting: SortOption;
switchSorting: (sortId: SortOption) => void;
// Grouping
groups: string[];
crntGroup: string | null;
switchGroup: (group: string | null) => void;
// Searching
onSearch: (value: string | null) => void;
}
function classNames(...classes: any[]) {
return classes.filter(Boolean).join(' ')
}
export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntGroup, groups, switchGroup, onSearch, settings}: React.PropsWithChildren<IHeaderProps>) => {
export const tabs = [
{ name: 'All articles', id: Tab.All},
{ name: 'Published', id: Tab.Published },
{ name: 'In draft', id: Tab.Draft }
];
export const sortOptions = [
{ name: "Last modified", id: SortOption.LastModified },
{ name: "By filename (asc)", id: SortOption.FileNameAsc },
{ name: "By filename (desc)", id: SortOption.FileNameDesc },
];
export const Header: React.FunctionComponent<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab}: React.PropsWithChildren<IHeaderProps>) => {
const createContent = () => {
MessageHelper.sendMessage(DashboardMessage.createContent);
};
return (
<div className="px-4 flex items-center border-b border-gray-200 mb-8 sticky top-0 z-50 bg-gray-50 shadow-sm">
<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={classNames(
tab.id === currentTab
? 'border-teal-900 text-teal-900'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
'whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm'
)}
aria-current={tab.id === currentTab ? 'page' : undefined}
onClick={() => switchTab(tab.id)}
>
{tab.name}
</button>
))}
</nav>
<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>
<div className="flex items-center ml-6">
<Menu as="div" className="relative z-10 inline-block text-left">
<div>
<Menu.Button className="group inline-flex justify-center text-sm font-medium text-gray-500 hover:text-gray-700">
Sort
<ChevronDownIcon
className="flex-shrink-0 -mr-1 ml-1 h-5 w-5 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
</Menu.Button>
</div>
<div className="px-4 flex items-center border-b border-gray-200 dark:border-whisper-600">
<Navigation currentTab={currentTab} totalPages={totalPages} switchTab={switchTab} />
<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 ring-1 ring-vulcan-400 ring-opacity-5 focus:outline-none text-sm">
<div className="py-1">
{sortOptions.map((option) => (
<Menu.Item key={option.id}>
<button
onClick={() => switchSorting(option.id)}
className={classNames(
option.id === currentSorting ? 'text-vulcan-500' : 'text-gray-500',
'block px-4 py-2 text-sm font-medium w-full text-left hover:text-gray-700'
)}
>
{option.name}
</button>
</Menu.Item>
))}
</div>
</Menu.Items>
</Transition>
</Menu>
<Grouping crntGroup={crntGroup} groups={groups} switchGroup={switchGroup} />
<Sorting currentSorting={currentSorting} switchSorting={switchSorting} />
</div>
</div>
);
+8 -5
View File
@@ -1,5 +1,6 @@
import * as React from 'react';
import { MessageHelper } from '../../helpers/MessageHelper';
import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon';
import { DashboardMessage } from '../DashboardMessage';
import { Page } from '../models/Page';
import { DateField } from './DateField';
@@ -15,19 +16,21 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
return (
<li className="relative">
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full rounded-lg bg-gray-50 text-vulcan-500 text-left overflow-hidden shadow-md hover:shadow-xl`}
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100`}
onClick={openFile}>
<div className="relative h-36 w-full overflow-hidden">
<div className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200">
{
preview ? (
<img src={`${preview}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
) : (
<img src={`https://images.unsplash.com/photo-1598620617148-c9e8ddee6711?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80`} alt={title} className="absolute inset-0 h-full w-full object-cover group-hover:opacity-95" loading="lazy" />
<div className={`flex items-center justify-center bg-whisper-500 dark:bg-vulcan-200 dark:group-hover:bg-vulcan-100`}>
<MarkdownIcon className={`h-32 text-vulcan-100 dark:text-whisper-100`} />
</div>
)
}
</div>
<div className="p-4">
<div className="p-4 w-full">
<div className={`flex justify-between items-center`}>
<Status draft={!!draft} />
@@ -36,7 +39,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
<h2 className="mt-2 mb-2 font-bold">{title}</h2>
<p className="text-xs text-vulcan-200">{description}</p>
<p className="text-xs text-vulcan-200 dark:text-whisper-800">{description}</p>
</div>
</button>
</li>
+22
View File
@@ -0,0 +1,22 @@
import { Menu } from '@headlessui/react';
import * as React from 'react';
export interface IMenuItemProps {
title: string;
value: any;
isCurrent: boolean;
onClick: (value: any) => void;
}
export const MenuItem: React.FunctionComponent<IMenuItemProps> = ({title, value, isCurrent, onClick}: React.PropsWithChildren<IMenuItemProps>) => {
return (
<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`}
>
{title}
</button>
</Menu.Item>
);
};
+32
View File
@@ -0,0 +1,32 @@
import * as React from 'react';
import { Tab } from '../constants/Tab';
export interface INavigationProps {
currentTab: Tab;
totalPages: number;
switchTab: (tabId: Tab) => void;
}
export const tabs = [
{ name: 'All articles', id: Tab.All},
{ name: 'Published', id: Tab.Published },
{ name: 'In draft', id: Tab.Draft }
];
export const Navigation: React.FunctionComponent<INavigationProps> = ({currentTab, totalPages, switchTab}: React.PropsWithChildren<INavigationProps>) => {
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)}
>
{tab.name}{(tab.id === currentTab && totalPages) ? ` (${totalPages})` : ''}
</button>
))}
</nav>
);
};
+22 -1
View File
@@ -1,13 +1,34 @@
import * as React from 'react';
import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon';
import { Page } from '../models/Page';
import { Settings } from '../models/Settings';
import { Item } from './Item';
import { List } from './List';
export interface IOverviewProps {
pages: Page[];
settings: Settings;
}
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages}: React.PropsWithChildren<IOverviewProps>) => {
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings}: React.PropsWithChildren<IOverviewProps>) => {
if (!pages || !pages.length) {
return (
<div className={`flex items-center justify-center mt-16`}>
<div className={`max-w-xl text-center`}>
<MarkdownIcon className={`text-vulcan-300 dark:text-whisper-800 h-32 mx-auto`} />
{
settings?.folders?.length > 0 ? (
<p className={`text-xl font-medium`}>No Markdown to show</p>
) : (
<p className={`text-lg font-medium`}>Make sure you registered a content folder in your project to let Front Matter find the contents.</p>
)
}
</div>
</div>
);
}
return (
<List>
+42
View File
@@ -0,0 +1,42 @@
import { FilterIcon, SearchIcon } from '@heroicons/react/solid';
import * as React from 'react';
import { useDebounce } from '../../hooks/useDebounce';
export interface ISearchboxProps {
onSearch: (searchText: string) => void;
}
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}: React.PropsWithChildren<ISearchboxProps>) => {
const [ value, setValue ] = React.useState('');
const debounceSearch = useDebounce<string>(value, 500);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
React.useEffect(() => {
onSearch(debounceSearch);
}, [debounceSearch]);
return (
<div className="flex space-x-4">
<div className="flex-1 min-w-0">
<label htmlFor="search" className="sr-only">Search</label>
<div className="relative flex justify-center">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<SearchIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
<input
type="search"
name="search"
className={`block w-full py-2 pl-10 pr-3 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`}
placeholder="Search"
value={value}
autoFocus={true}
onChange={handleChange}
/>
</div>
</div>
</div>
);
};
+63
View File
@@ -0,0 +1,63 @@
import { Menu, Transition } from '@headlessui/react';
import * as React from 'react';
import { SortOption } from '../constants/SortOption';
import { ChevronDownIcon } from '@heroicons/react/solid';
import { Fragment } from 'react';
import { MenuItem } from './MenuItem';
export interface ISortingProps {
currentSorting: SortOption;
switchSorting: (sortId: SortOption) => void;
}
export const sortOptions = [
{ name: "Last modified", id: SortOption.LastModified },
{ name: "By filename (asc)", id: SortOption.FileNameAsc },
{ name: "By filename (desc)", id: SortOption.FileNameDesc },
];
export const Sorting: React.FunctionComponent<ISortingProps> = ({currentSorting, switchSorting}: React.PropsWithChildren<ISortingProps>) => {
const crntSort = sortOptions.find(x => x.id === 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>
<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>
</Menu>
</div>
);
};
+1 -1
View File
@@ -4,7 +4,7 @@ export interface ISpinnerProps {}
export const Spinner: React.FunctionComponent<ISpinnerProps> = (props: React.PropsWithChildren<ISpinnerProps>) => {
return (
<div className={`fixed top-0 left-0 right-0 bottom-0 w-full h-full flex flex-wrap items-center justify-center bg-white bg-opacity-10 z-40`}>
<div className={`fixed top-0 left-0 right-0 bottom-0 w-full h-full flex flex-wrap items-center justify-center bg-white bg-opacity-10 z-50`}>
<div className="loader ease-linear rounded-full border-8 border-t-8 border-gray-50 h-32 w-32" />
</div>
);
+1 -1
View File
@@ -6,6 +6,6 @@ export interface IStatusProps {
export const Status: React.FunctionComponent<IStatusProps> = ({draft}: React.PropsWithChildren<IStatusProps>) => {
return (
<span className={`inline-block px-2 py-1 leading-none rounded-full font-semibold uppercase tracking-wide text-xs ${draft ? "bg-red-500 text-whisper-200" : "bg-teal-500 text-whisper-500"}`}>{draft ? "Draft" : "Published"}</span>
<span className={`inline-block px-2 py-1 leading-none rounded-full font-semibold uppercase tracking-wide text-xs text-whisper-200 dark:text-vulcan-500 ${draft ? "bg-red-500" : "bg-teal-500"}`}>{draft ? "Draft" : "Published"}</span>
);
};
+11 -3
View File
@@ -1,23 +1,29 @@
import { useState, useEffect } from 'react';
import { MessageHelper } from '../../helpers/MessageHelper';
import { ContentFolder } from '../../models';
import { DashboardCommand } from '../DashboardCommand';
import { DashboardMessage } from '../DashboardMessage';
import { Page } from '../models/Page';
import { Settings } from '../models/Settings';
const vscode = MessageHelper.getVsCodeAPI();
export default function useMessages(options?: any) {
const [loading, setLoading] = useState<boolean>(false);
const [pages, setPages] = useState<Page[]>([]);
const [settings, setSettings] = useState<Settings>({} as any);
window.addEventListener('message', event => {
const message = event.data;
switch (message.command) {
case DashboardCommand.loading:
setLoading(message.data);
break;
case DashboardCommand.data:
case DashboardCommand.settings:
setSettings(message.data);
break;
case DashboardCommand.pages:
setPages(message.data);
setLoading(false);
break;
@@ -26,11 +32,13 @@ export default function useMessages(options?: any) {
useEffect(() => {
setLoading(true);
vscode.postMessage({ command: DashboardMessage.getTheme });
vscode.postMessage({ command: DashboardMessage.getData });
}, ['']);
return {
loading,
pages
pages,
settings
};
}
+6
View File
@@ -0,0 +1,6 @@
import { ContentFolder } from './../../models/ContentFolder';
export interface Settings {
folders: ContentFolder[];
initialized: boolean
}
+1 -1
View File
@@ -4,7 +4,7 @@
.loader {
border-top-color: var(--vscode-activityBar-activeBorder);;
border-top-color: #15c2cb;
animation: spinner 1.5s linear infinite;
}