mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
196 lines
7.6 KiB
TypeScript
196 lines
7.6 KiB
TypeScript
import { Messenger } from '@estruyf/vscode/dist/client';
|
|
import { CodeBracketIcon, PlusIcon } from '@heroicons/react/24/outline';
|
|
import * as React from 'react';
|
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import { useRecoilValue } from 'recoil';
|
|
import { FeatureFlag } from '../../../components/features/FeatureFlag';
|
|
import { FEATURE_FLAG, GeneralCommands, WEBSITE_LINKS } from '../../../constants';
|
|
import { SnippetParser } from '../../../helpers/SnippetParser';
|
|
import { DashboardMessage } from '../../DashboardMessage';
|
|
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
|
|
import { FilterInput } from '../Header/FilterInput';
|
|
import { PageLayout } from '../Layout/PageLayout';
|
|
import { SponsorMsg } from '../Layout/SponsorMsg';
|
|
import { Item } from './Item';
|
|
import { NewForm } from './NewForm';
|
|
import * as l10n from '@vscode/l10n';
|
|
import { LocalizationKey } from '../../../localization';
|
|
import { List } from '../Media/List';
|
|
import { SlideOver } from '../Modals/SlideOver';
|
|
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
|
|
|
|
export interface ISnippetsProps { }
|
|
|
|
export const Snippets: React.FunctionComponent<ISnippetsProps> = () => {
|
|
const settings = useRecoilValue(SettingsSelector);
|
|
const viewData = useRecoilValue(ViewDataSelector);
|
|
const mode = useRecoilValue(ModeAtom);
|
|
const [snippetTitle, setSnippetTitle] = useState<string>('');
|
|
const [snippetDescription, setSnippetDescription] = useState<string>('');
|
|
const [snippetBody, setSnippetBody] = useState<string>('');
|
|
const [showCreateDialog, setShowCreateDialog] = useState(false);
|
|
const [mediaSnippet, setMediaSnippet] = useState(false);
|
|
const [snippetFilter, setSnippetFilter] = useState<string>('');
|
|
|
|
const snippets = settings?.snippets || {};
|
|
const snippetKeys = useMemo(() => {
|
|
let allSnippetKeys = Object.keys(snippets).sort((a, b) => a.localeCompare(b));
|
|
|
|
if (viewData?.data?.filePath) {
|
|
allSnippetKeys = allSnippetKeys.filter((key) => {
|
|
return !snippets[key].isMediaSnippet;
|
|
});
|
|
}
|
|
|
|
return allSnippetKeys.filter((key) => {
|
|
const value = snippetFilter.toLowerCase();
|
|
const keyValue = key.toLowerCase();
|
|
const descriptionValue = snippets[key].description?.toLowerCase() || '';
|
|
|
|
// Contains in key or description, values included in key are ranked higher (sort and fuzzy search)
|
|
return keyValue.includes(value) || descriptionValue.includes(value);
|
|
});
|
|
}, [settings?.snippets, snippetFilter, viewData?.data?.filePath]);
|
|
|
|
const onSnippetAdd = useCallback(() => {
|
|
if (!snippetTitle || !snippetBody) {
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
const fields = SnippetParser.getFields(snippetBody, []);
|
|
|
|
Messenger.send(DashboardMessage.addSnippet, {
|
|
title: snippetTitle,
|
|
description: snippetDescription || '',
|
|
body: snippetBody,
|
|
fields,
|
|
isMediaSnippet: mediaSnippet
|
|
});
|
|
|
|
reset();
|
|
}, [snippetTitle, snippetDescription, snippetBody, mediaSnippet]);
|
|
|
|
const reset = () => {
|
|
setShowCreateDialog(false);
|
|
setSnippetTitle('');
|
|
setSnippetDescription('');
|
|
setSnippetBody('');
|
|
};
|
|
|
|
useEffect(() => {
|
|
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsSnippets));
|
|
|
|
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
|
message: `Snippets view loaded`,
|
|
location: 'DASHBOARD'
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<PageLayout
|
|
header={
|
|
<FeatureFlag
|
|
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
|
|
flag={FEATURE_FLAG.dashboard.snippets.manage}>
|
|
<div
|
|
className={`py-3 px-4 flex items-center justify-between border-b border-[var(--frontmatter-border)]`}
|
|
aria-label={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsAriaLabel)}
|
|
>
|
|
<FilterInput
|
|
placeholder={l10n.t(LocalizationKey.commonSearch)}
|
|
isReady={true}
|
|
autoFocus={(snippetKeys && snippetKeys.length > 0)}
|
|
value={snippetFilter}
|
|
onChange={(value: string) => setSnippetFilter(value)}
|
|
onReset={() => setSnippetFilter('')}
|
|
/>
|
|
|
|
<div className="flex flex-1 justify-end">
|
|
<button
|
|
className={`inline-flex items-center px-3 py-1 rounded text-xs leading-4 font-medium focus:outline-none text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`}
|
|
title={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsButtonCreate)}
|
|
onClick={() => setShowCreateDialog(true)}
|
|
>
|
|
<PlusIcon className={`mr-2 h-6 w-6`} />
|
|
<span className={`text-sm`}>
|
|
{l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsButtonCreate)}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</FeatureFlag>
|
|
}
|
|
>
|
|
<div className="flex flex-col h-full">
|
|
{viewData?.data?.filePath && (
|
|
<div className={`text-xl text-center mb-6`}>
|
|
<p>
|
|
{l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsSelectDescription)}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{(snippetKeys && snippetKeys.length > 0) ? (
|
|
<List
|
|
gap={4}
|
|
>
|
|
{snippetKeys.map((snippetKey: any, index: number) => (
|
|
<Item key={index} snippetKey={snippetKey} snippet={snippets[snippetKey]} />
|
|
))}
|
|
</List>
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center text-white">
|
|
<div className={`flex flex-col items-center text-[var(--frontmatter-text)]`}>
|
|
<CodeBracketIcon className="w-32 h-32" />
|
|
<p className="text-3xl mt-2">
|
|
{l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsEmptyMessage)}
|
|
</p>
|
|
<p className="text-xl mt-4">
|
|
<a
|
|
className={`text-[var(--frontmatter-link)] hover:text-[var(--frontmatter-link-hover)]`}
|
|
href={WEBSITE_LINKS.docs.snippets}
|
|
title={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsReadMore)}
|
|
>
|
|
{l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsReadMore)}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{showCreateDialog && (
|
|
<SlideOver
|
|
title={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsFormDialogTitle)}
|
|
description={``}
|
|
isSaveDisabled={!snippetTitle || !snippetBody}
|
|
trigger={onSnippetAdd}
|
|
dismiss={reset}
|
|
okBtnText={l10n.t(LocalizationKey.commonSave)}
|
|
cancelBtnText={l10n.t(LocalizationKey.commonCancel)}
|
|
>
|
|
<NewForm
|
|
title={snippetTitle}
|
|
description={snippetDescription}
|
|
body={snippetBody}
|
|
isMediaSnippet={mediaSnippet}
|
|
onMediaSnippetUpdate={(value: boolean) => setMediaSnippet(value)}
|
|
onTitleUpdate={(value: string) => setSnippetTitle(value)}
|
|
onDescriptionUpdate={(value: string) => setSnippetDescription(value)}
|
|
onBodyUpdate={(value: string) => setSnippetBody(value)}
|
|
/>
|
|
</SlideOver>
|
|
)}
|
|
</div>
|
|
|
|
<SponsorMsg
|
|
beta={settings?.beta}
|
|
version={settings?.versionInfo}
|
|
isBacker={settings?.isBacker}
|
|
/>
|
|
|
|
<img className='hidden' src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Ffrontmatter.codes%2Fmetrics%2Fdashboards&slug=snippets" alt="Snippets metrics" />
|
|
</PageLayout>
|
|
);
|
|
};
|