mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-06 17:02:54 +02:00
Merge branch 'poc/content-tagging' into dev
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#731](https://github.com/estruyf/vscode-front-matter/issues/731): Added the ability to map/unmap taxonomy to multiple pages at once
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#727](https://github.com/estruyf/vscode-front-matter/pull/727): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"common.edit": "Edit",
|
||||
"common.delete": "Delete",
|
||||
"common.cancel": "Cancel",
|
||||
"common.apply": "Apply",
|
||||
"common.clear": "Clear",
|
||||
"common.clear.value": "Clear value",
|
||||
"common.search": "Search",
|
||||
@@ -20,6 +21,7 @@
|
||||
"common.slug": "Slug",
|
||||
"common.support": "Support",
|
||||
"common.remove.value": "Remove {0}",
|
||||
"common.filter": "Back",
|
||||
"common.filter.value": "Filter by {0}",
|
||||
"common.error.message": "Sorry, something went wrong.",
|
||||
"common.openOnWebsite": "Open on website",
|
||||
@@ -32,6 +34,7 @@
|
||||
"common.yes": "yes",
|
||||
"common.no": "no",
|
||||
"common.openSettings": "Open settings",
|
||||
"common.back": "Back",
|
||||
|
||||
"notifications.outputChannel.link": "output window",
|
||||
"notifications.outputChannel.description": "Check the {0} for more details.",
|
||||
@@ -294,6 +297,10 @@
|
||||
"dashboard.taxonomyView.taxonomyManager.table.heading.action": "Action",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.row.empty": "No {0} found",
|
||||
"dashboard.taxonomyView.taxonomyManager.table.unmapped.title": "Missing in your settings",
|
||||
"dashboard.taxonomyView.taxonomyManager.filterInput.placeholder": "Filter",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyTagging.pageTitle": "Map your content with: {0}",
|
||||
"dashboard.taxonomyView.taxonomyTagging.checkbox": "Tag page with {0}",
|
||||
|
||||
"dashboard.taxonomyView.taxonomyView.navigationBar.title": "Select the taxonomy",
|
||||
"dashboard.taxonomyView.taxonomyView.button.import": "Import taxonomy",
|
||||
@@ -661,9 +668,11 @@
|
||||
"helpers.taxonomyHelper.createNew.input.placeholder": "Enter the value you want to add",
|
||||
"helpers.taxonomyHelper.createNew.input.validate.noValue": "A value must be provided.",
|
||||
"helpers.taxonomyHelper.createNew.input.validate.exists": "The value already exists.",
|
||||
"helpers.taxonomyHelper.process.insert": "{0}: Inserting \"{1}\" to your selected pages.",
|
||||
"helpers.taxonomyHelper.process.edit": "{0}: Renaming \"{1}\" from {2} to {3}.",
|
||||
"helpers.taxonomyHelper.process.merge": "{0}: Merging \"{1}\" from {2} to {3}.",
|
||||
"helpers.taxonomyHelper.process.delete": "{0}: Deleting \"{1}\" from {2}.",
|
||||
"helpers.taxonomyHelper.process.insert.success": "Insert completed.",
|
||||
"helpers.taxonomyHelper.process.edit.success": "Edit completed.",
|
||||
"helpers.taxonomyHelper.process.merge.success": "Merge completed.",
|
||||
"helpers.taxonomyHelper.process.delete.success": "Deletion completed.",
|
||||
|
||||
@@ -2180,6 +2180,12 @@
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"webview/context": [
|
||||
{
|
||||
"command": "workbench.action.webview.openDeveloperTools",
|
||||
"when": "frontMatter:isDevelopment"
|
||||
}
|
||||
],
|
||||
"editor/title": [
|
||||
{
|
||||
"command": "frontMatter.markup.heading",
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '../constants';
|
||||
import { join } from 'path';
|
||||
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from 'vscode';
|
||||
import { Logger, Settings as SettingsHelper } from '../helpers';
|
||||
import { DashboardSettings, Logger, Settings as SettingsHelper } from '../helpers';
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
@@ -160,6 +160,7 @@ export class Dashboard {
|
||||
Dashboard.isDisposed = true;
|
||||
Dashboard._viewData = undefined;
|
||||
PanelMediaListener.getMediaSelection();
|
||||
DashboardSettings.updateAfterClose();
|
||||
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export const CONTEXT = {
|
||||
wysiwyg: 'frontMatter:markdown:wysiwyg',
|
||||
backer: 'frontMatter:backers:supporter',
|
||||
isValidFile: 'frontMatter:file:isValid',
|
||||
isDevelopment: 'frontMatter:isDevelopment',
|
||||
|
||||
hasViewModes: 'frontMatter:has:modes',
|
||||
|
||||
@@ -14,5 +15,5 @@ export const CONTEXT = {
|
||||
|
||||
isGitEnabled: 'frontMatter:git:enabled',
|
||||
|
||||
projectSwitchEnabled: 'frontMatter:project:switch:enabled',
|
||||
projectSwitchEnabled: 'frontMatter:project:switch:enabled'
|
||||
};
|
||||
|
||||
@@ -64,11 +64,13 @@ export enum DashboardMessage {
|
||||
createTaxonomy = 'createTaxonomy',
|
||||
importTaxonomy = 'importTaxonomy',
|
||||
moveTaxonomy = 'moveTaxonomy',
|
||||
mapTaxonomy = 'mapTaxonomy',
|
||||
|
||||
// Other
|
||||
getTheme = 'getTheme',
|
||||
updateSetting = 'updateSetting',
|
||||
setState = 'setState',
|
||||
getState = 'getState',
|
||||
runCustomScript = 'runCustomScript',
|
||||
sendTelemetry = 'sendTelemetry',
|
||||
logError = 'logError',
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -32,7 +32,6 @@ export const AstroContentTypes: React.FunctionComponent<IAstroContentTypesProps>
|
||||
if (collections.length > 0 && settings?.contentTypes?.length > 0) {
|
||||
// Find created content types from the collections
|
||||
const astroCollection = collections.find(c => settings.contentTypes.find((ct) => ct.name === c.name));
|
||||
console.log(`astroCollection`, astroCollection)
|
||||
if (astroCollection) {
|
||||
setStatus(Status.Completed);
|
||||
} else {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
@@ -26,6 +26,7 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({
|
||||
const [crntSorting, setCrntSorting] = useRecoilState(SortingAtom);
|
||||
const searchValue = useRecoilValue(SearchSelector);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const [crntSort, setCrntSort] = React.useState<SortingOption | null>(null);
|
||||
|
||||
const sortOptions: SortingOption[] = [
|
||||
{
|
||||
@@ -143,29 +144,37 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({
|
||||
];
|
||||
}
|
||||
|
||||
let crntSortingOption = crntSorting;
|
||||
if (!crntSortingOption) {
|
||||
if (view === NavigationType.Contents) {
|
||||
crntSortingOption = settings?.dashboardState?.contents?.sorting || null;
|
||||
} else if (view === NavigationType.Media) {
|
||||
crntSortingOption = settings?.dashboardState?.media?.sorting || null;
|
||||
}
|
||||
React.useEffect(() => {
|
||||
const getSorting = async () => {
|
||||
let crntSortingOption = crntSorting;
|
||||
if (!crntSortingOption) {
|
||||
const sortingState = await messageHandler.request<{ key: string; value: SortingOption; }>(DashboardMessage.getState, { key: view === NavigationType.Media ? ExtensionState.Dashboard.Media.Sorting : ExtensionState.Dashboard.Contents.Sorting });
|
||||
crntSortingOption = sortingState?.value || null;
|
||||
|
||||
if (crntSortingOption === null) {
|
||||
if (view === NavigationType.Contents && settings?.dashboardState.contents.defaultSorting) {
|
||||
crntSortingOption =
|
||||
allOptions.find((f) => f.id === settings?.dashboardState.contents.defaultSorting) || null;
|
||||
} else if (
|
||||
view === NavigationType.Media &&
|
||||
settings?.dashboardState.contents.defaultSorting
|
||||
) {
|
||||
crntSortingOption =
|
||||
allOptions.find((f) => f.id === settings?.dashboardState.contents.defaultSorting) || null;
|
||||
if (crntSortingOption === null) {
|
||||
if (view === NavigationType.Contents && settings?.dashboardState.contents.defaultSorting) {
|
||||
crntSortingOption =
|
||||
allOptions.find((f) => f.id === settings?.dashboardState.contents.defaultSorting) || null;
|
||||
} else if (
|
||||
view === NavigationType.Media &&
|
||||
settings?.dashboardState.media.defaultSorting
|
||||
) {
|
||||
crntSortingOption =
|
||||
allOptions.find((f) => f.id === settings?.dashboardState.media.defaultSorting) || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let crntSort = allOptions.find((x) => x.id === crntSortingOption?.id) || sortOptions[0];
|
||||
let sort = allOptions.find((x) => x.id === crntSortingOption?.id) || sortOptions[0];
|
||||
setCrntSort(sort);
|
||||
};
|
||||
|
||||
getSorting();
|
||||
}, [crntSorting]);
|
||||
|
||||
if (crntSort === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import useThemeColors from '../../hooks/useThemeColors';
|
||||
|
||||
export interface IInfoDialogProps {
|
||||
icon?: JSX.Element;
|
||||
@@ -17,7 +16,6 @@ export const InfoDialog: React.FunctionComponent<IInfoDialogProps> = ({
|
||||
description,
|
||||
children
|
||||
}: React.PropsWithChildren<IInfoDialogProps>) => {
|
||||
const { getColors } = useThemeColors();
|
||||
|
||||
return (
|
||||
<Transition.Root show={true} as={Fragment}>
|
||||
@@ -32,11 +30,7 @@ export const InfoDialog: React.FunctionComponent<IInfoDialogProps> = ({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className={`fixed inset-0 transition-opacity ${getColors(
|
||||
`bg-vulcan-500 bg-opacity-75`,
|
||||
`bg-[var(--vscode-editor-background)] opacity-75`
|
||||
)
|
||||
}`} />
|
||||
<Dialog.Overlay className={`fixed inset-0 transition-opacity bg-[var(--vscode-editor-background)] opacity-75`} />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
@@ -53,31 +47,22 @@ export const InfoDialog: React.FunctionComponent<IInfoDialogProps> = ({
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div className={`inline-block align-bottom rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6 border-2 ${getColors(
|
||||
`bg-white dark:bg-vulcan-500 border-whisper-900`,
|
||||
`bg-[var(--vscode-editor-background)] border-[var(--frontmatter-border)]`
|
||||
)
|
||||
}`}>
|
||||
<div className={`inline-block align-bottom rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6 border-2 bg-[var(--vscode-editor-background)] border-[var(--frontmatter-border)]`}>
|
||||
<div className="sm:flex sm:items-start">
|
||||
{icon && (
|
||||
<div className={`mt-3 sm:mr-4 mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full sm:mx-0 sm:h-10 sm:w-10 ${getColors(
|
||||
`bg-gray-50 dark:bg-vulcan-400`,
|
||||
`bg-[var(--vscode-sidebar-background)]'`
|
||||
)
|
||||
}`}>
|
||||
<div className={`mt-3 sm:mr-4 mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full sm:mx-0 sm:h-10 sm:w-10 bg-[var(--vscode-sidebar-background)]`}>
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-3 text-center sm:mt-0 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className={`text-lg leading-6 font-medium ${getColors(`text-vulcan-300 dark:text-whisper-900`, `text-[var(--vscode-editor-foreground)]`)
|
||||
}`}
|
||||
className={`text-lg leading-6 font-medium text-[var(--vscode-editor-foreground)]`}
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className={`text-sm ${getColors(`text-vulcan-500 dark:text-whisper-500`, `text-[var(--vscode-editor-foreground)]`)}`}>{description}</p>
|
||||
<p className={`text-sm text-[var(--vscode-editor-foreground)]`}>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ArrowUpCircleIcon,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
SquaresPlusIcon,
|
||||
TagIcon,
|
||||
TrashIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
@@ -18,12 +18,14 @@ export interface ITaxonomyActionsProps {
|
||||
field: string | null;
|
||||
value: string;
|
||||
unmapped?: boolean;
|
||||
onContentTagging: (value: string) => void;
|
||||
}
|
||||
|
||||
export const TaxonomyActions: React.FunctionComponent<ITaxonomyActionsProps> = ({
|
||||
field,
|
||||
value,
|
||||
unmapped
|
||||
unmapped,
|
||||
onContentTagging
|
||||
}: React.PropsWithChildren<ITaxonomyActionsProps>) => {
|
||||
const onEdit = useCallback(() => {
|
||||
Messenger.send(DashboardMessage.editTaxonomy, {
|
||||
@@ -60,59 +62,65 @@ export const TaxonomyActions: React.FunctionComponent<ITaxonomyActionsProps> = (
|
||||
});
|
||||
}, [field, value]);
|
||||
|
||||
const onTagging = useCallback(() => {
|
||||
onContentTagging(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className={`space-x-2`}>
|
||||
{unmapped && (
|
||||
<>
|
||||
<div className={`space-x-2`}>
|
||||
{unmapped && (
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonAddTitle, value)}
|
||||
onClick={onAdd}>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonAddTitle, value)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonAddTitle, value)}
|
||||
onClick={onAdd}>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
title={`Tag content`}
|
||||
onClick={onTagging}>
|
||||
<TagIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonEditTitle, value)}
|
||||
onClick={onEdit}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMergeTitle, value)}
|
||||
onClick={onMerge}>
|
||||
<MergeIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonAddTitle, value)}
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMergeTitle, value)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
<LinkButton
|
||||
title={`Tag content`}
|
||||
onClick={onEdit}>
|
||||
<SquaresPlusIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMoveTitle)}
|
||||
onClick={onMove}>
|
||||
<ArrowUpCircleIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMoveTitle)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonEditTitle, value)}
|
||||
onClick={onEdit}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMergeTitle, value)}
|
||||
onClick={onMerge}>
|
||||
<MergeIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMergeTitle, value)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMoveTitle)}
|
||||
onClick={onMove}>
|
||||
<ArrowUpCircleIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonMoveTitle)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonDeleteTitle, value)}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonDeleteTitle, value)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
</div>
|
||||
<LinkButton
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewButtonDeleteTitle, value)}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden={true} />
|
||||
<span className="sr-only">
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewButtonDeleteTitle, value)}
|
||||
</span>
|
||||
</LinkButton>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
@@ -21,15 +20,16 @@ export interface ITaxonomyManagerProps {
|
||||
data: TaxonomyData | undefined;
|
||||
taxonomy: string | null;
|
||||
pages: Page[];
|
||||
onContentTagging: (value: string) => void;
|
||||
}
|
||||
|
||||
export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = ({
|
||||
data,
|
||||
taxonomy,
|
||||
pages
|
||||
pages,
|
||||
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);
|
||||
@@ -141,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}
|
||||
>
|
||||
@@ -181,42 +169,42 @@ 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`}>
|
||||
<TaxonomyActions field={taxonomy} value={item} />
|
||||
<TaxonomyActions field={taxonomy} value={item} onContentTagging={onContentTagging} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
@@ -224,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)}
|
||||
@@ -237,17 +225,17 @@ 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`}>
|
||||
<TaxonomyActions field={taxonomy} value={item} unmapped />
|
||||
<TaxonomyActions field={taxonomy} value={item} onContentTagging={onContentTagging} unmapped />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
import * as React from 'react';
|
||||
import { Page, PageMappings, SortingOption } from '../../models';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { SettingsSelector, SortingAtom } 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';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { sortPages } from '../../../utils/sortPages';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { ExtensionState } from '../../../constants';
|
||||
|
||||
export interface ITaxonomyTaggingProps {
|
||||
taxonomy: string | null;
|
||||
value: string;
|
||||
pages: Page[];
|
||||
onContentMapping: (value: string, pageMappings: PageMappings) => void;
|
||||
onDismiss: () => void;
|
||||
}
|
||||
|
||||
export const TaxonomyTagging: React.FunctionComponent<ITaxonomyTaggingProps> = ({
|
||||
taxonomy,
|
||||
value,
|
||||
pages,
|
||||
onContentMapping,
|
||||
onDismiss,
|
||||
}: React.PropsWithChildren<ITaxonomyTaggingProps>) => {
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const [sortedPages, setSortedPages] = React.useState<Page[]>([]);
|
||||
const [pageMappings, setPageMappings] = React.useState<{
|
||||
tagged: Page[];
|
||||
untagged: Page[];
|
||||
}>({
|
||||
tagged: [],
|
||||
untagged: []
|
||||
});
|
||||
const [filterValue, setFilterValue] = React.useState('');
|
||||
const debounceFilterValue = useDebounce<string>(filterValue, 500);
|
||||
|
||||
const untaggedPages = React.useMemo(() => {
|
||||
let untagged: Page[] = [];
|
||||
|
||||
if (!sortedPages || !settings?.contentTypes || !taxonomy) {
|
||||
return untagged;
|
||||
}
|
||||
|
||||
for (const page of sortedPages) {
|
||||
if (taxonomy === 'tags') {
|
||||
if (!page.fmTags || page.fmTags.indexOf(value) === -1) {
|
||||
untagged.push(page);
|
||||
}
|
||||
continue;
|
||||
} else if (taxonomy === 'categories') {
|
||||
if (!page.fmCategories || page.fmCategories.indexOf(value) === -1) {
|
||||
untagged.push(page);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
const contentType = settings.contentTypes.find((ct) => ct.name === page.fmContentType);
|
||||
|
||||
if (!contentType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fieldName = getTaxonomyField(taxonomy, contentType);
|
||||
|
||||
if (fieldName && (!page[fieldName] || page[fieldName].indexOf(value) === -1)) {
|
||||
untagged.push(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
untagged = untagged.sort(Sorting.number('fmPublished')).reverse();
|
||||
|
||||
if (debounceFilterValue) {
|
||||
return untagged.filter((p) => p.title.toLowerCase().includes(debounceFilterValue.toLowerCase()));
|
||||
}
|
||||
|
||||
return untagged;
|
||||
}, [sortedPages, taxonomy, value, debounceFilterValue]);
|
||||
|
||||
const onCheckboxClick = React.useCallback((page: Page) => {
|
||||
const untaggedPage = untaggedPages.find((p: Page) => p.fmFilePath === page.fmFilePath);
|
||||
|
||||
const clonedPageMappings = Object.assign({}, pageMappings);
|
||||
const taggedIdx = pageMappings.tagged.findIndex((p: Page) => p.fmFilePath === page.fmFilePath);
|
||||
const untaggedIdx = pageMappings.untagged.findIndex((p: Page) => p.fmFilePath === page.fmFilePath);
|
||||
|
||||
if (untaggedPage) {
|
||||
// Page was not yet tagged
|
||||
if (taggedIdx === -1) {
|
||||
clonedPageMappings.tagged.push(page);
|
||||
|
||||
if (untaggedIdx !== -1) {
|
||||
clonedPageMappings.untagged.splice(untaggedIdx, 1);
|
||||
}
|
||||
} else {
|
||||
clonedPageMappings.tagged.splice(taggedIdx, 1);
|
||||
|
||||
if (untaggedIdx === -1) {
|
||||
clonedPageMappings.untagged.push(page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Page was already tagged, so only the untagged array needs to be updated
|
||||
if (untaggedIdx === -1) {
|
||||
clonedPageMappings.untagged.push(page);
|
||||
} else {
|
||||
clonedPageMappings.untagged.splice(taggedIdx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
setPageMappings(clonedPageMappings);
|
||||
}, [pageMappings, untaggedPages]);
|
||||
|
||||
const checkIfChecked = React.useCallback((page: Page) => {
|
||||
return (!untaggedPages.find((p: Page) => p.fmFilePath === page.fmFilePath) && !pageMappings.untagged.find((p: Page) => p.fmFilePath === page.fmFilePath)) || pageMappings.tagged.find((p: Page) => p.fmFilePath === page.fmFilePath);
|
||||
}, [untaggedPages, pageMappings.tagged]);
|
||||
|
||||
React.useEffect(() => {
|
||||
messageHandler.request<{ key: string; value: SortingOption; }>(DashboardMessage.getState, {
|
||||
key: ExtensionState.Dashboard.Contents.Sorting
|
||||
}).then(({ key, value }) => {
|
||||
if (key === ExtensionState.Dashboard.Contents.Sorting && value) {
|
||||
const sorted = sortPages(pages, value)
|
||||
setSortedPages(sorted);
|
||||
} else {
|
||||
setSortedPages(pages);
|
||||
}
|
||||
});
|
||||
}, [pages]);
|
||||
|
||||
return (
|
||||
<div className={`py-6 px-4 flex flex-col h-full overflow-hidden`}>
|
||||
<div className={`flex w-full justify-between flex-shrink-0`}>
|
||||
<div className={`flex gap-2 items-center`}>
|
||||
<button onClick={onDismiss} title={l10n.t(LocalizationKey.commonBack)}>
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.commonBack)}</span>
|
||||
<ArrowLeftIcon className='w-5 h-5 text-[var(--frontmatter-text)]' />
|
||||
</button>
|
||||
<h2 className={`text-lg first-letter:uppercase text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingPageTitle, value)}
|
||||
</h2>
|
||||
</div>
|
||||
<div className='flex gap-4 justify-center items-center'>
|
||||
<FilterInput
|
||||
placeholder={l10n.t(LocalizationKey.commonFilter)}
|
||||
value={filterValue}
|
||||
onChange={(value) => setFilterValue(value)}
|
||||
onReset={() => setFilterValue('')} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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)]`}
|
||||
>
|
||||
{l10n.t(LocalizationKey.commonTitle)}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className={`divide-y divide-[var(--frontmatter-border)]`}>
|
||||
{untaggedPages && sortedPages && sortedPages.map((page) => (
|
||||
<tr key={page.fmFilePath} className='py-2'>
|
||||
<td className={`pl-6 w-[25px]`}>
|
||||
<VSCodeCheckbox
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingCheckbox, value)}
|
||||
onClick={() => onCheckboxClick(page)}
|
||||
checked={checkIfChecked(page)}>
|
||||
<span className='sr-only'>
|
||||
{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingCheckbox, value)}
|
||||
</span>
|
||||
</VSCodeCheckbox>
|
||||
</td>
|
||||
<td className={`pr-6 whitespace-nowrap text-sm font-medium text-[var(--frontmatter-text)]`}>
|
||||
<button
|
||||
title={l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyTaggingCheckbox, value)}
|
||||
className='hover:text-[var(--vscode-textLink-activeForeground)]'
|
||||
onClick={() => onCheckboxClick(page)}>
|
||||
{page.title}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end space-x-2'>
|
||||
<Button onClick={onDismiss} secondary>{l10n.t(LocalizationKey.commonCancel)}</Button>
|
||||
<Button onClick={() => onContentMapping(value, pageMappings)}>{l10n.t(LocalizationKey.commonApply)}</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';
|
||||
@@ -6,7 +6,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { TelemetryEvent } from '../../../constants';
|
||||
import { TaxonomyData } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Page } from '../../models';
|
||||
import { Page, PageMappings } from '../../models';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { NavigationBar, NavigationItem } from '../Layout';
|
||||
import { PageLayout } from '../Layout/PageLayout';
|
||||
@@ -14,6 +14,7 @@ import { SponsorMsg } from '../Layout/SponsorMsg';
|
||||
import { TaxonomyManager } from './TaxonomyManager';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { TaxonomyTagging } from './TaxonomyTagging';
|
||||
|
||||
export interface ITaxonomyViewProps {
|
||||
pages: Page[];
|
||||
@@ -25,11 +26,24 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const [taxonomySettings, setTaxonomySettings] = useState<TaxonomyData>();
|
||||
const [selectedTaxonomy, setSelectedTaxonomy] = useState<string | null>(`tags`);
|
||||
const [contentTagging, setContentTagging] = useState<string | null>(null);
|
||||
|
||||
const onImport = () => {
|
||||
Messenger.send(DashboardMessage.importTaxonomy);
|
||||
};
|
||||
|
||||
const onContentMapping = React.useCallback((value: string, pageMappings: PageMappings) => {
|
||||
messageHandler.request(DashboardMessage.mapTaxonomy, {
|
||||
taxonomy: selectedTaxonomy,
|
||||
value,
|
||||
pageMappings
|
||||
}).then(() => {
|
||||
setContentTagging(null);
|
||||
}).catch(() => {
|
||||
setContentTagging(null);
|
||||
});
|
||||
}, [selectedTaxonomy]);
|
||||
|
||||
useEffect(() => {
|
||||
setTaxonomySettings({
|
||||
tags: settings?.tags || [],
|
||||
@@ -62,7 +76,10 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
>
|
||||
<NavigationItem
|
||||
isSelected={selectedTaxonomy === 'tags'}
|
||||
onClick={() => setSelectedTaxonomy(`tags`)}
|
||||
onClick={() => {
|
||||
setSelectedTaxonomy(`tags`);
|
||||
setContentTagging(null);
|
||||
}}
|
||||
>
|
||||
<ChevronRightIcon className="-ml-1 w-5 mr-2" />
|
||||
<span>{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyViewNavigationItemTags)}</span>
|
||||
@@ -70,7 +87,10 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
|
||||
<NavigationItem
|
||||
isSelected={selectedTaxonomy === 'categories'}
|
||||
onClick={() => setSelectedTaxonomy(`categories`)}
|
||||
onClick={() => {
|
||||
setSelectedTaxonomy(`categories`);
|
||||
setContentTagging(null);
|
||||
}}
|
||||
>
|
||||
<ChevronRightIcon className="-ml-1 w-5 mr-2" />
|
||||
<span>{l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyViewNavigationItemCategories)}</span>
|
||||
@@ -81,7 +101,10 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
<NavigationItem
|
||||
key={`${taxonomy.id}-${index}`}
|
||||
isSelected={selectedTaxonomy === taxonomy.id}
|
||||
onClick={() => setSelectedTaxonomy(taxonomy.id)}
|
||||
onClick={() => {
|
||||
setSelectedTaxonomy(taxonomy.id);
|
||||
setContentTagging(null);
|
||||
}}
|
||||
>
|
||||
<ChevronRightIcon className="-ml-1 w-5 mr-2" />
|
||||
<span className={`first-letter:uppercase`}>{taxonomy.id}</span>
|
||||
@@ -90,10 +113,22 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
</NavigationBar>
|
||||
|
||||
<div className={`w-10/12 h-full overflow-hidden`}>
|
||||
<TaxonomyManager
|
||||
data={taxonomySettings}
|
||||
taxonomy={selectedTaxonomy}
|
||||
pages={pages} />
|
||||
{
|
||||
contentTagging ? (
|
||||
<TaxonomyTagging
|
||||
value={contentTagging}
|
||||
taxonomy={selectedTaxonomy}
|
||||
pages={pages}
|
||||
onContentMapping={(value: string, pageMappings: PageMappings) => onContentMapping(value, pageMappings)}
|
||||
onDismiss={() => setContentTagging(null)} />
|
||||
) : (
|
||||
<TaxonomyManager
|
||||
data={taxonomySettings}
|
||||
taxonomy={selectedTaxonomy}
|
||||
pages={pages}
|
||||
onContentTagging={(value: string) => setContentTagging(value)} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ import {
|
||||
TabSelector,
|
||||
TagSelector
|
||||
} from '../state';
|
||||
import { SortOrder, SortType } from '../../models';
|
||||
import { Sorting } from '../../helpers/Sorting';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { parseWinPath } from '../../helpers/parseWinPath';
|
||||
import { sortPages } from '../../utils/sortPages';
|
||||
import { ExtensionState } from '../../constants';
|
||||
import { SortingOption } from '../models';
|
||||
|
||||
export default function usePages(pages: Page[]) {
|
||||
const [pageItems, setPageItems] = useRecoilState(AllPagesAtom);
|
||||
@@ -37,7 +38,7 @@ export default function usePages(pages: Page[]) {
|
||||
* Process all the pages by applying the sorting, filtering and searching.
|
||||
*/
|
||||
const processPages = useCallback(
|
||||
(searchedPages: Page[], fullProcess: boolean = true) => {
|
||||
(searchedPages: Page[]) => {
|
||||
const framework = settings?.crntFramework;
|
||||
|
||||
// Filter the pages
|
||||
@@ -67,35 +68,7 @@ export default function usePages(pages: Page[]) {
|
||||
// Sort the pages
|
||||
let pagesSorted: Page[] = Object.assign([], pagesToShow);
|
||||
if (!search) {
|
||||
if (sorting && sorting.id === SortOption.FileNameAsc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.alphabetically('fmFileName'));
|
||||
} else if (sorting && sorting.id === SortOption.FileNameDesc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.alphabetically('fmFileName')).reverse();
|
||||
} else if (sorting && sorting.id === SortOption.PublishedAsc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number('fmPublished'));
|
||||
} else if (sorting && sorting.id === SortOption.LastModifiedAsc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number('fmModified'));
|
||||
} else if (sorting && sorting.id === SortOption.PublishedDesc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number('fmPublished')).reverse();
|
||||
} else if (sorting && sorting.id === SortOption.LastModifiedDesc) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number('fmModified')).reverse();
|
||||
} else if (sorting && sorting.id && sorting.name) {
|
||||
const { order, name, type } = sorting;
|
||||
|
||||
if (type === SortType.string) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.alphabetically(name));
|
||||
} else if (type === SortType.date) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.date(name));
|
||||
} else if (type === SortType.number) {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number(name));
|
||||
}
|
||||
|
||||
if (order === SortOrder.desc) {
|
||||
pagesSorted = pagesSorted.reverse();
|
||||
}
|
||||
} else {
|
||||
pagesSorted = pagesSorted.sort(Sorting.number('fmModified')).reverse();
|
||||
}
|
||||
pagesSorted = sortPages(pagesSorted, sorting);
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
@@ -208,20 +181,27 @@ export default function usePages(pages: Page[]) {
|
||||
useEffect(() => {
|
||||
let usedSorting = sorting;
|
||||
|
||||
if (!usedSorting) {
|
||||
const lastSort = settings?.dashboardState.contents.sorting;
|
||||
if (lastSort) {
|
||||
setSorting(lastSort);
|
||||
return;
|
||||
const startPageProcessing = () => {
|
||||
// Check if search needs to be performed
|
||||
let searchedPages = pages;
|
||||
if (search) {
|
||||
Messenger.send(DashboardMessage.searchPages, { query: search });
|
||||
} else {
|
||||
processPages(searchedPages);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if search needs to be performed
|
||||
let searchedPages = pages;
|
||||
if (search) {
|
||||
Messenger.send(DashboardMessage.searchPages, { query: search });
|
||||
if (!usedSorting) {
|
||||
messageHandler.request<{ key: string; value: SortingOption; }>(DashboardMessage.getState, {
|
||||
key: ExtensionState.Dashboard.Contents.Sorting
|
||||
}).then(({ key, value }) => {
|
||||
if (key === ExtensionState.Dashboard.Contents.Sorting && value) {
|
||||
setSorting(value);
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
processPages(searchedPages);
|
||||
startPageProcessing();
|
||||
}
|
||||
}, [settings?.draftField, pages, sorting, search, tag, category, folder]);
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Page } from './Page';
|
||||
|
||||
export interface PageMappings {
|
||||
tagged: Page[];
|
||||
untagged: Page[];
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './DashboardViewType';
|
||||
export * from './NavigationType';
|
||||
export * from './Page';
|
||||
export * from './PageMappings';
|
||||
export * from './Settings';
|
||||
export * from './SortingOption';
|
||||
export * from './Status';
|
||||
|
||||
@@ -130,10 +130,10 @@
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
@apply mt-4 inline-flex w-auto cursor-pointer items-center border border-transparent bg-teal-600 px-3 py-2 text-sm font-medium leading-4 text-white;
|
||||
@apply mt-4 inline-flex w-auto items-center rounded border border-transparent bg-[var(--frontmatter-button-background)] px-3 py-2 text-sm font-medium leading-4 text-[var(--vscode-button-foreground)];
|
||||
|
||||
&:hover {
|
||||
@apply bg-teal-700;
|
||||
@apply bg-[var(--frontmatter-button-hoverBackground)];
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@@ -141,7 +141,7 @@
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply bg-gray-500 opacity-50;
|
||||
@apply opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
import { GitListener } from './listeners/general/GitListener';
|
||||
import * as vscode from 'vscode';
|
||||
import { COMMAND_NAME, EXTENSION_NAME, TelemetryEvent } from './constants';
|
||||
import { COMMAND_NAME, CONTEXT, EXTENSION_NAME, TelemetryEvent } from './constants';
|
||||
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
|
||||
import { TagType } from './panelWebView/TagType';
|
||||
import { PanelProvider } from './panelWebView/PanelProvider';
|
||||
@@ -46,6 +46,11 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
const { subscriptions, extensionUri, extensionPath } = context;
|
||||
|
||||
const extension = Extension.getInstance(context);
|
||||
// Set development context
|
||||
if (!Extension.getInstance().isProductionMode) {
|
||||
vscode.commands.executeCommand('setContext', CONTEXT.isDevelopment, true);
|
||||
}
|
||||
|
||||
Backers.init(context).then(() => {});
|
||||
|
||||
// Make sure the EN language file is loaded
|
||||
|
||||
@@ -59,6 +59,22 @@ export class DashboardSettings {
|
||||
return this.cachedSettings;
|
||||
}
|
||||
|
||||
public static async updateAfterClose() {
|
||||
if (this.cachedSettings) {
|
||||
this.cachedSettings = await this.getSettings();
|
||||
|
||||
const ext = Extension.getInstance();
|
||||
|
||||
// Update states
|
||||
this.cachedSettings.dashboardState.contents.sorting = await ext.getState<
|
||||
SortingOption | undefined
|
||||
>(ExtensionState.Dashboard.Contents.Sorting, 'workspace');
|
||||
this.cachedSettings.dashboardState.media.sorting = await ext.getState<
|
||||
SortingOption | undefined
|
||||
>(ExtensionState.Dashboard.Media.Sorting, 'workspace');
|
||||
}
|
||||
}
|
||||
|
||||
public static async getSettings() {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
@@ -24,6 +24,8 @@ import { SettingsListener as PanelSettingsListener } from '../listeners/panel';
|
||||
import { SettingsListener as DashboardSettingsListener } from '../listeners/dashboard';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { Page } from '../dashboardWebView/models';
|
||||
import { Logger } from './Logger';
|
||||
|
||||
export class TaxonomyHelper {
|
||||
private static db: JsonDB;
|
||||
@@ -254,21 +256,26 @@ export class TaxonomyHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the taxonomy changes
|
||||
* @param type
|
||||
* @param taxonomyType
|
||||
* @param oldValue
|
||||
* @param newValue
|
||||
* @returns
|
||||
* Processes the taxonomy changes based on the specified type.
|
||||
* @param type - The type of taxonomy change ('insert', 'edit', 'merge', 'delete').
|
||||
* @param taxonomyType - The type of taxonomy ('Tag', 'Category', or custom taxonomy name).
|
||||
* @param oldValue - The old value of the taxonomy.
|
||||
* @param newValue - The new value of the taxonomy (optional).
|
||||
* @param pages - The array of Page objects (optional).
|
||||
* @param needsSettingsUpdate - Indicates whether the settings need to be updated (default: true).
|
||||
*/
|
||||
public static async process(
|
||||
type: 'edit' | 'merge' | 'delete',
|
||||
type: 'insert' | 'edit' | 'merge' | 'delete',
|
||||
taxonomyType: TaxonomyType | string,
|
||||
oldValue: string,
|
||||
newValue?: string
|
||||
newValue?: string,
|
||||
pages?: Page[],
|
||||
needsSettingsUpdate: boolean = true
|
||||
) {
|
||||
// Retrieve all the markdown files
|
||||
const allFiles = await FilesHelper.getAllFiles();
|
||||
const allFiles = pages
|
||||
? pages?.map((p) => ({ fsPath: p.fmFilePath }))
|
||||
: await FilesHelper.getAllFiles();
|
||||
if (!allFiles) {
|
||||
return;
|
||||
}
|
||||
@@ -284,7 +291,13 @@ export class TaxonomyHelper {
|
||||
|
||||
let progressText = ``;
|
||||
|
||||
if (type === 'edit') {
|
||||
if (type === 'insert') {
|
||||
progressText = l10n.t(
|
||||
LocalizationKey.helpersTaxonomyHelperProcessInsert,
|
||||
EXTENSION_NAME,
|
||||
newValue || ''
|
||||
);
|
||||
} else if (type === 'edit') {
|
||||
progressText = l10n.t(
|
||||
LocalizationKey.helpersTaxonomyHelperProcessEdit,
|
||||
EXTENSION_NAME,
|
||||
@@ -309,7 +322,7 @@ export class TaxonomyHelper {
|
||||
);
|
||||
}
|
||||
|
||||
window.withProgress(
|
||||
await window.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: progressText,
|
||||
@@ -342,7 +355,18 @@ export class TaxonomyHelper {
|
||||
taxonomies = taxonomies.split(`,`);
|
||||
}
|
||||
|
||||
if (taxonomies && taxonomies.length > 0) {
|
||||
let needsFileUpdate = false;
|
||||
if (type === 'insert' && newValue) {
|
||||
if (taxonomies && taxonomies.length > 0) {
|
||||
taxonomies.push(newValue);
|
||||
} else {
|
||||
taxonomies = [newValue];
|
||||
}
|
||||
|
||||
const newTaxValue = [...new Set(taxonomies)].sort();
|
||||
ContentType.setFieldValue(data, fieldNames, newTaxValue);
|
||||
needsFileUpdate = true;
|
||||
} else if (type !== 'insert' && taxonomies && taxonomies.length > 0) {
|
||||
const idx = taxonomies.findIndex((o) => o === oldValue);
|
||||
|
||||
if (idx !== -1) {
|
||||
@@ -354,28 +378,36 @@ export class TaxonomyHelper {
|
||||
|
||||
const newTaxValue = [...new Set(taxonomies)].sort();
|
||||
ContentType.setFieldValue(data, fieldNames, newTaxValue);
|
||||
|
||||
const spaces = window.activeTextEditor?.options?.tabSize;
|
||||
// Update the file
|
||||
await writeFileAsync(
|
||||
parseWinPath(file.fsPath),
|
||||
FrontMatterParser.toFile(article.content, article.data, mdFile, {
|
||||
indent: spaces || 2
|
||||
} as DumpOptions as any),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
needsFileUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the file when needed
|
||||
if (needsFileUpdate) {
|
||||
const spaces = window.activeTextEditor?.options?.tabSize;
|
||||
|
||||
await writeFileAsync(
|
||||
parseWinPath(file.fsPath),
|
||||
FrontMatterParser.toFile(article.content, article.data, mdFile, {
|
||||
indent: spaces || 2
|
||||
} as DumpOptions as any),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Continue with the next file
|
||||
Logger.error(`Failed to ${type} taxonomy value in ${file.fsPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.addToSettings(taxonomyType, oldValue, newValue);
|
||||
if (needsSettingsUpdate) {
|
||||
await this.addToSettings(taxonomyType, oldValue, newValue);
|
||||
}
|
||||
|
||||
if (type === 'edit') {
|
||||
if (type === 'insert') {
|
||||
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessInsertSuccess));
|
||||
} else if (type === 'edit') {
|
||||
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessEditSuccess));
|
||||
} else if (type === 'merge') {
|
||||
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessMergeSuccess));
|
||||
@@ -502,6 +534,21 @@ export class TaxonomyHelper {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the taxonomy type based from the string
|
||||
* @param taxonomyType
|
||||
* @returns
|
||||
*/
|
||||
public static getTypeFromString(taxonomyType: string): TaxonomyType | string {
|
||||
if (taxonomyType === 'tags') {
|
||||
return TaxonomyType.Tag;
|
||||
} else if (taxonomyType === 'categories') {
|
||||
return TaxonomyType.Category;
|
||||
} else {
|
||||
return taxonomyType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the fields for the taxonomy field
|
||||
* @returns
|
||||
@@ -587,19 +634,4 @@ export class TaxonomyHelper {
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the taxonomy type based from the string
|
||||
* @param taxonomyType
|
||||
* @returns
|
||||
*/
|
||||
private static getTypeFromString(taxonomyType: string): TaxonomyType | string {
|
||||
if (taxonomyType === 'tags') {
|
||||
return TaxonomyType.Tag;
|
||||
} else if (taxonomyType === 'categories') {
|
||||
return TaxonomyType.Category;
|
||||
} else {
|
||||
return taxonomyType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ export class ExtensionListener extends BaseListener {
|
||||
case DashboardMessage.setState:
|
||||
this.setState(msg?.payload);
|
||||
break;
|
||||
case DashboardMessage.getState:
|
||||
this.getState(msg.command, msg?.payload, msg.requestId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +39,17 @@ export class ExtensionListener extends BaseListener {
|
||||
Extension.getInstance().setState(key, value, 'workspace');
|
||||
}
|
||||
}
|
||||
|
||||
private static async getState(command: string, data: any, requestId?: string) {
|
||||
if (!command || !requestId || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { key } = data;
|
||||
if (key) {
|
||||
const value = await Extension.getInstance().getState<any | undefined>(key, 'workspace');
|
||||
|
||||
this.sendRequest(command as any, requestId, { key, value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
|
||||
import { TaxonomyHelper } from '../../helpers';
|
||||
import { PostMessageData } from '../../models';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { Page, PageMappings } from '../../dashboardWebView/models';
|
||||
|
||||
export class TaxonomyListener extends BaseListener {
|
||||
/**
|
||||
@@ -39,6 +40,51 @@ export class TaxonomyListener extends BaseListener {
|
||||
case DashboardMessage.importTaxonomy:
|
||||
commands.executeCommand(COMMAND_NAME.exportTaxonomy);
|
||||
break;
|
||||
case DashboardMessage.mapTaxonomy:
|
||||
TaxonomyListener.mapTaxonomy(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async mapTaxonomy({
|
||||
command,
|
||||
requestId,
|
||||
payload: { taxonomy, value, pageMappings }
|
||||
}: {
|
||||
command: string;
|
||||
requestId?: string;
|
||||
payload: { taxonomy: string; value: string; pageMappings: PageMappings };
|
||||
}) {
|
||||
if (!command || !requestId || !taxonomy || !value || !pageMappings) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (pageMappings.tagged.length > 0) {
|
||||
await TaxonomyHelper.process(
|
||||
'insert',
|
||||
TaxonomyHelper.getTypeFromString(taxonomy),
|
||||
'',
|
||||
value,
|
||||
pageMappings.tagged,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (pageMappings.untagged.length > 0) {
|
||||
await TaxonomyHelper.process(
|
||||
'delete',
|
||||
TaxonomyHelper.getTypeFromString(taxonomy),
|
||||
value,
|
||||
'',
|
||||
pageMappings.untagged,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
this.sendRequest(command as any, requestId, {});
|
||||
} catch (e) {
|
||||
this.sendError(command as any, requestId, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ export enum LocalizationKey {
|
||||
* Cancel
|
||||
*/
|
||||
commonCancel = 'common.cancel',
|
||||
/**
|
||||
* Apply
|
||||
*/
|
||||
commonApply = 'common.apply',
|
||||
/**
|
||||
* Clear
|
||||
*/
|
||||
@@ -83,6 +87,10 @@ export enum LocalizationKey {
|
||||
* Remove {0}
|
||||
*/
|
||||
commonRemoveValue = 'common.remove.value',
|
||||
/**
|
||||
* Back
|
||||
*/
|
||||
commonFilter = 'common.filter',
|
||||
/**
|
||||
* Filter by {0}
|
||||
*/
|
||||
@@ -131,6 +139,10 @@ export enum LocalizationKey {
|
||||
* Open settings
|
||||
*/
|
||||
commonOpenSettings = 'common.openSettings',
|
||||
/**
|
||||
* Back
|
||||
*/
|
||||
commonBack = 'common.back',
|
||||
/**
|
||||
* output window
|
||||
*/
|
||||
@@ -975,6 +987,18 @@ export enum LocalizationKey {
|
||||
* Missing in your settings
|
||||
*/
|
||||
dashboardTaxonomyViewTaxonomyManagerTableUnmappedTitle = 'dashboard.taxonomyView.taxonomyManager.table.unmapped.title',
|
||||
/**
|
||||
* Filter
|
||||
*/
|
||||
dashboardTaxonomyViewTaxonomyManagerFilterInputPlaceholder = 'dashboard.taxonomyView.taxonomyManager.filterInput.placeholder',
|
||||
/**
|
||||
* Map your content with: {0}
|
||||
*/
|
||||
dashboardTaxonomyViewTaxonomyTaggingPageTitle = 'dashboard.taxonomyView.taxonomyTagging.pageTitle',
|
||||
/**
|
||||
* Tag page with {0}
|
||||
*/
|
||||
dashboardTaxonomyViewTaxonomyTaggingCheckbox = 'dashboard.taxonomyView.taxonomyTagging.checkbox',
|
||||
/**
|
||||
* Select the taxonomy
|
||||
*/
|
||||
@@ -2172,6 +2196,10 @@ export enum LocalizationKey {
|
||||
* The value already exists.
|
||||
*/
|
||||
helpersTaxonomyHelperCreateNewInputValidateExists = 'helpers.taxonomyHelper.createNew.input.validate.exists',
|
||||
/**
|
||||
* {0}: Inserting "{1}" to your selected pages.
|
||||
*/
|
||||
helpersTaxonomyHelperProcessInsert = 'helpers.taxonomyHelper.process.insert',
|
||||
/**
|
||||
* {0}: Renaming "{1}" from {2} to {3}.
|
||||
*/
|
||||
@@ -2184,6 +2212,10 @@ export enum LocalizationKey {
|
||||
* {0}: Deleting "{1}" from {2}.
|
||||
*/
|
||||
helpersTaxonomyHelperProcessDelete = 'helpers.taxonomyHelper.process.delete',
|
||||
/**
|
||||
* Insert completed.
|
||||
*/
|
||||
helpersTaxonomyHelperProcessInsertSuccess = 'helpers.taxonomyHelper.process.insert.success',
|
||||
/**
|
||||
* Edit completed.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { SortOption } from '../dashboardWebView/constants/SortOption';
|
||||
import { Page, SortingOption } from '../dashboardWebView/models';
|
||||
import { Sorting } from '../helpers/Sorting';
|
||||
import { SortOrder, SortType } from '../models';
|
||||
|
||||
export const sortPages = (pages: Page[], sorting: SortingOption | null) => {
|
||||
if (sorting && sorting.id === SortOption.FileNameAsc) {
|
||||
pages = pages.sort(Sorting.alphabetically('fmFileName'));
|
||||
} else if (sorting && sorting.id === SortOption.FileNameDesc) {
|
||||
pages = pages.sort(Sorting.alphabetically('fmFileName')).reverse();
|
||||
} else if (sorting && sorting.id === SortOption.PublishedAsc) {
|
||||
pages = pages.sort(Sorting.number('fmPublished'));
|
||||
} else if (sorting && sorting.id === SortOption.LastModifiedAsc) {
|
||||
pages = pages.sort(Sorting.number('fmModified'));
|
||||
} else if (sorting && sorting.id === SortOption.PublishedDesc) {
|
||||
pages = pages.sort(Sorting.number('fmPublished')).reverse();
|
||||
} else if (sorting && sorting.id === SortOption.LastModifiedDesc) {
|
||||
pages = pages.sort(Sorting.number('fmModified')).reverse();
|
||||
} else if (sorting && sorting.id && sorting.name) {
|
||||
const { order, name, type } = sorting;
|
||||
|
||||
if (type === SortType.string) {
|
||||
pages = pages.sort(Sorting.alphabetically(name));
|
||||
} else if (type === SortType.date) {
|
||||
pages = pages.sort(Sorting.date(name));
|
||||
} else if (type === SortType.number) {
|
||||
pages = pages.sort(Sorting.number(name));
|
||||
}
|
||||
|
||||
if (order === SortOrder.desc) {
|
||||
pages = pages.reverse();
|
||||
}
|
||||
} else {
|
||||
pages = pages.sort(Sorting.number('fmModified')).reverse();
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
Reference in New Issue
Block a user