Insert taxonomy mappings

This commit is contained in:
Elio Struyf
2024-01-15 22:23:10 +01:00
parent 624afe9029
commit 033baea418
11 changed files with 222 additions and 86 deletions
@@ -1,5 +1,4 @@
import * as React from 'react';
import useThemeColors from '../../hooks/useThemeColors';
export interface IButtonProps {
secondary?: boolean;
@@ -15,19 +14,14 @@ export const Button: React.FunctionComponent<IButtonProps> = ({
secondary,
children
}: React.PropsWithChildren<IButtonProps>) => {
const { getColors } = useThemeColors();
return (
<button
type="button"
className={`${className || ''
} inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium focus:outline-none rounded ${getColors(
'text-white dark:text-vulcan-500 disabled:bg-gray-500',
'disabled:opacity-50'
)
} ${secondary ?
getColors(`bg-red-300 hover:bg-red-400`, `bg-[var(--vscode-button-secondaryBackground)] text-[--vscode-button-secondaryForeground] hover:bg-[var(--vscode-button-secondaryHoverBackground)]`) :
getColors(`bg-teal-600 hover:bg-teal-700`, `bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)] hover:bg-[var(--frontmatter-button-hoverBackground)]`)
} inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium focus:outline-none rounded disabled:opacity-50 ${secondary ?
`bg-[var(--vscode-button-secondaryBackground)] text-[--vscode-button-secondaryForeground] hover:bg-[var(--vscode-button-secondaryHoverBackground)]` :
`bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)] hover:bg-[var(--frontmatter-button-hoverBackground)]`
}
`}
onClick={onClick}
@@ -1,10 +1,9 @@
import * as React from 'react';
import { useForm } from 'uniforms';
import { SubmitField } from 'uniforms-unstyled';
import useThemeColors from '../../hooks/useThemeColors';
import { Button } from '../Common/Button';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { SubmitField } from '../../../components/uniforms-frontmatter';
export interface IDataFormControlsProps {
model: any | null;
@@ -16,10 +15,9 @@ export const DataFormControls: React.FunctionComponent<IDataFormControlsProps> =
onClear
}: React.PropsWithChildren<IDataFormControlsProps>) => {
const { formRef } = useForm();
const { getColors } = useThemeColors();
return (
<div className={`text-right ${getColors(`border-gray-200 dark:border-vulcan-300`, `border-[var(--frontmatter-border)]`)}`}>
<div className={`text-right border-[var(--frontmatter-border)]`}>
<SubmitField value={model ? `Update` : `Add`} />
<Button
@@ -10,7 +10,6 @@ import { SettingsSelector } from '../../state';
import { getTaxonomyField } from '../../../helpers/getTaxonomyField';
import { TaxonomyActions } from './TaxonomyActions';
import { TaxonomyLookup } from './TaxonomyLookup';
import useThemeColors from '../../hooks/useThemeColors';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { FilterInput } from './FilterInput';
@@ -31,7 +30,6 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
onContentTagging
}: React.PropsWithChildren<ITaxonomyManagerProps>) => {
const settings = useRecoilValue(SettingsSelector);
const { getColors } = useThemeColors();
const [filterValue, setFilterValue] = React.useState('');
const debounceFilterValue = useDebounce<string>(filterValue, 500);
const prevTaxonomy = usePrevious<string | null>(taxonomy);
@@ -143,35 +141,23 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
<div className={`py-6 px-4 flex flex-col h-full overflow-hidden`}>
<div className={`flex w-full justify-between flex-shrink-0`}>
<div>
<h2 className={`text-lg first-letter:uppercase ${getColors(
'text-gray-500 dark:text-whisper-900',
'text-[var(--frontmatter-text)]'
)
}`}>
<h2 className={`text-lg first-letter:uppercase text-[var(--frontmatter-text)]`}>
{taxonomy}
</h2>
<p className={`mt-2 text-sm first-letter:uppercase ${getColors(
'text-gray-500 dark:text-whisper-900',
'text-[var(--frontmatter-secondary-text)]'
)
}`}>
<p className={`mt-2 text-sm first-letter:uppercase text-[var(--frontmatter-secondary-text)]`}>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerDescription, taxonomy)}
</p>
</div>
<div className='flex gap-4 justify-center items-center'>
<FilterInput
placeholder='Filter'
placeholder={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerFilterInputPlaceholder, taxonomy)}
value={filterValue}
onChange={(value) => setFilterValue(value)}
onReset={() => setFilterValue('')} />
<div>
<button
className={`inline-flex items-center px-3 py-1 border border-transparent text-xs leading-4 font-medium focus:outline-none rounded ${getColors(
`text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 disabled:bg-gray-500`,
`text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`
)
}`}
className={`inline-flex items-center px-3 py-1 border border-transparent text-xs leading-4 font-medium focus:outline-none rounded text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`}
title={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerButtonCreate, taxonomy)}
onClick={onCreate}
>
@@ -183,38 +169,38 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
</div>
<div className="mt-6 pb-6 -mr-4 pr-4 flex flex-col flex-grow overflow-auto">
<table className="min-w-full divide-y divide-gray-200 dark:divide-vulcan-300">
<table className="min-w-full divide-y divide-[var(--frontmatter-border)]">
<thead>
<tr>
<th
scope="col"
className={`px-6 py-3 text-left text-xs font-medium uppercase ${getColors('text-gray-500 dark:text-whisper-900', 'text-[var(--frontmatter-secondary-text)]')}`}
className={`px-6 py-3 text-left text-xs font-medium uppercase text-[var(--frontmatter-secondary-text)]'`}
>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerTableHeadingName)}
</th>
<th
scope="col"
className={`px-6 py-3 text-left text-xs font-medium uppercase ${getColors('text-gray-500 dark:text-whisper-900', 'text-[var(--frontmatter-secondary-text)]')}`}
className={`px-6 py-3 text-left text-xs font-medium uppercase text-[var(--frontmatter-secondary-text)]`}
>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerTableHeadingCount)}
</th>
<th
scope="col"
className={`px-6 py-3 text-right text-xs font-medium uppercase ${getColors('text-gray-500 dark:text-whisper-900', 'text-[var(--frontmatter-secondary-text)]')}`}
className={`px-6 py-3 text-right text-xs font-medium uppercase text-[var(--frontmatter-secondary-text)]`}
>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerTableHeadingAction)}
</th>
</tr>
</thead>
<tbody className={`divide-y ${getColors(`divide-gray-200 dark:divide-vulcan-300`, `divide-[var(--frontmatter-border)]`)}`}>
<tbody className={`divide-y divide-[var(--frontmatter-border)]`}>
{items && items.length > 0
? items.map((item, index) => (
<tr key={index}>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium ${getColors(`text-gray-800 dark:text-gray-200`, `text-[var(--frontmatter-text)]`)}`}>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}>
<TagIcon className="inline-block h-4 w-4 mr-2" />
<span>{item}</span>
</td>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium ${getColors(`text-gray-800 dark:text-gray-200`, `text-[var(--frontmatter-text)]`)}`}>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}>
<TaxonomyLookup taxonomy={taxonomy} value={item} pages={pages} />
</td>
<td className={`px-6 py-4 whitespace-nowrap text-right text-sm font-medium`}>
@@ -226,7 +212,7 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
(unmappedItems.length === 0 && (
<tr>
<td
className={`px-6 py-4 whitespace-nowrap text-sm font-medium ${getColors(`text-gray-800 dark:text-gray-200`, `text-[var(--frontmatter-text)]`)}`}
className={`px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}
colSpan={4}
>
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerTableRowEmpty, taxonomy)}
@@ -239,13 +225,13 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
unmappedItems.map((item, index) => (
<tr key={index}>
<td
className={`px-6 py-4 whitespace-nowrap text-sm font-medium ${getColors(`text-gray-800 dark:text-gray-200`, `text-[var(--frontmatter-text)]`)}`}
className={`px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}
title={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyManagerTableUnmappedTitle)}
>
<ExclamationTriangleIcon className="inline-block h-4 w-4 mr-2" />
<span>{item}</span>
</td>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium ${getColors(`text-gray-800 dark:text-gray-200`, `text-[var(--frontmatter-text)]`)}`}>
<td className={`px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}>
<TaxonomyLookup taxonomy={taxonomy} value={item} pages={pages} />
</td>
<td className={`px-6 py-4 whitespace-nowrap text-right text-sm font-medium`}>
@@ -5,11 +5,16 @@ import { SettingsSelector } from '../../state';
import { getTaxonomyField } from '../../../helpers/getTaxonomyField';
import { Sorting } from '../../../helpers/Sorting';
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
import { Button } from '../Common/Button';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import { FilterInput } from './FilterInput';
import { useDebounce } from '../../../hooks/useDebounce';
export interface ITaxonomyTaggingProps {
taxonomy: string | null;
value: string;
pages: Page[];
onContentMapping: (value: string, pages: Page[]) => void;
onDismiss: () => void;
}
@@ -17,9 +22,28 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
taxonomy,
value,
pages,
onDismiss
onContentMapping,
onDismiss,
}: React.PropsWithChildren<ITaxonomyTaggingProps>) => {
const settings = useRecoilValue(SettingsSelector);
const [selectedPages, setSelectedPages] = React.useState<Page[]>([]);
const [filterValue, setFilterValue] = React.useState('');
const debounceFilterValue = useDebounce<string>(filterValue, 500);
const onCheckboxClick = React.useCallback((page: Page) => {
const pageIdx = selectedPages.findIndex((p: Page) => p.fmFilePath === page.fmFilePath);
if (pageIdx === -1) {
setSelectedPages([...selectedPages, page]);
} else {
const newSelectedPages = [...selectedPages];
newSelectedPages.splice(pageIdx, 1);
setSelectedPages(newSelectedPages);
}
}, [selectedPages]);
const checkIfChecked = React.useCallback((page: Page) => {
return selectedPages.find((p: Page) => p.fmFilePath === page.fmFilePath);
}, [selectedPages]);
const untaggedPages = React.useMemo(() => {
let untagged: Page[] = [];
@@ -56,32 +80,80 @@ export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = (
untagged = untagged.sort(Sorting.number('fmPublished')).reverse();
if (debounceFilterValue) {
return untagged.filter((p) => p.title.toLowerCase().includes(debounceFilterValue.toLowerCase()));
}
return untagged;
}, [pages, taxonomy, value]);
}, [pages, taxonomy, value, debounceFilterValue]);
console.log(`Selected pages`, selectedPages)
return (
<div className={`py-6 px-4 flex flex-col h-full overflow-hidden`}>
<div className={`flex w-full justify-between flex-shrink-0`}>
<h2 className='text-lg first-letter:uppercase flex items-center'>
<button onClick={onDismiss} title='Back' className='mr-2'>
<div className={`flex gap-2 items-center`}>
<button onClick={onDismiss} title='Back'>
<span className='sr-only'>Back</span>
<ArrowLeftIcon className='w-5 h-5 text-[var(--frontmatter-text)]' />
</button>
{taxonomy}: {value}
</h2>
<h2 className={`text-lg first-letter:uppercase text-[var(--frontmatter-text)]`}>
Map your content with: {value} <span className='ml-2'></span>
</h2>
</div>
<div className='flex gap-4 justify-center items-center'>
<FilterInput
placeholder='Filter'
value={filterValue}
onChange={(value) => setFilterValue(value)}
onReset={() => setFilterValue('')} />
</div>
</div>
<div className='mt-6 -mr-4 pr-4 flex flex-col flex-grow overflow-auto'>
<ul>
{untaggedPages.map((page) => (
<li key={page.fmFilePath}>
<input type='checkbox' />
<span>{page.title}</span>
</li>
))}
</ul>
<div className='mt-6 mb-2 -mr-4 pr-4 flex flex-col flex-grow overflow-auto'>
<table className="min-w-full divide-y divide-[var(--frontmatter-border)]">
<thead>
<tr>
<th
scope="col"
className={``}
>
</th>
<th
scope="col"
className={`pr-6 py-3 text-left text-xs font-medium uppercase text-[var(--frontmatter-secondary-text)]`}
>
Name
</th>
</tr>
</thead>
<tbody className={`divide-y divide-[var(--frontmatter-border)]`}>
{untaggedPages.map((page) => (
<tr key={page.fmFilePath} className='py-2'>
<td className={`pl-6 w-[25px]`}>
<VSCodeCheckbox
onClick={() => onCheckboxClick(page)}
checked={checkIfChecked(page)}>
<span className='sr-only'>Tag page</span>
</VSCodeCheckbox>
</td>
<td className={`pr-6 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}>
<button
title={page.title}
onClick={() => onCheckboxClick(page)}>
{page.title}
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className='flex justify-end space-x-2'>
<Button onClick={onDismiss} secondary>Cancel</Button>
<Button onClick={() => onContentMapping(value, selectedPages)}>Apply</Button>
</div>
</div>
);
@@ -1,4 +1,4 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
import { ChevronRightIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline';
import * as React from 'react';
import { useEffect, useState } from 'react';
@@ -32,6 +32,16 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
Messenger.send(DashboardMessage.importTaxonomy);
};
const onContentMapping = React.useCallback((value: string, pages: Page[]) => {
messageHandler.request(DashboardMessage.mapTaxonomy, {
taxonomy: selectedTaxonomy,
value,
pages
}).then(() => {
setContentTagging(null);
});
}, [selectedTaxonomy]);
useEffect(() => {
setTaxonomySettings({
tags: settings?.tags || [],
@@ -107,6 +117,7 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
value={contentTagging}
taxonomy={selectedTaxonomy}
pages={pages}
onContentMapping={(value: string, pages: Page[]) => onContentMapping(value, pages)}
onDismiss={() => setContentTagging(null)} />
) : (
<TaxonomyManager