mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 09:23:00 +02:00
#756 - Language filter + card actions + submenu
This commit is contained in:
@@ -90,6 +90,8 @@
|
||||
"dashboard.contents.contentActions.menuItem.view": "View",
|
||||
"dashboard.contents.contentActions.alert.title": "Delete: {0}",
|
||||
"dashboard.contents.contentActions.alert.description": "Are you sure you want to delete the \"{0}\" content?",
|
||||
"dashboard.contents.contentActions.translations.create": "Create translation",
|
||||
"dashboard.contents.contentActions.translations.menu": "Translations",
|
||||
|
||||
"dashboard.contents.item.invalidTitle": "<invalid title>",
|
||||
"dashboard.contents.item.invalidDescription": "<invalid description>",
|
||||
@@ -128,6 +130,9 @@
|
||||
|
||||
"dashboard.errorView.description": "Please close the dashboard and try again.",
|
||||
|
||||
"dashboard.filters.languageFilter.label": "Locale",
|
||||
"dashboard.filters.languageFilter.all": "All",
|
||||
|
||||
"dashboard.header.breadcrumb.home": "Home",
|
||||
|
||||
"dashboard.header.clearFilters.title": "Clear filters, grouping, and sorting",
|
||||
@@ -523,6 +528,16 @@
|
||||
"commands.folders.get.notificationError.remove.action": "Remove folder",
|
||||
"commands.folders.get.notificationError.create.action": "Create folder",
|
||||
|
||||
"commands.i18n.create.warning.noFileSelected": "No file selected.",
|
||||
"commands.i18n.create.warning.noFile": "The file could not be retrieved.",
|
||||
"commands.i18n.create.warning.noContentType": "Content type could not be retrieved for the current file.",
|
||||
"commands.i18n.create.warning.noConfig": "No i18n configuration found.",
|
||||
"commands.i18n.create.warning.notDefaultLocale": "The current file cannot be used for i18n content creation.",
|
||||
"commands.i18n.create.error.fileExists": "The i18n translation already exists.",
|
||||
"commands.i18n.create.success.created": "Created \"{0}\" i18n content file.",
|
||||
"commands.i18n.create.quickPick.title": "Create content for locale",
|
||||
"commands.i18n.create.quickPick.placeHolder": "To which locale do you want to create a new content?",
|
||||
|
||||
"commands.preview.panel.title": "Preview: {0}",
|
||||
"commands.preview.askUserToPickFolder.title": "Select the folder of the article to preview",
|
||||
|
||||
|
||||
@@ -307,6 +307,13 @@
|
||||
"defaultLocale": {
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.defaultLocale.description%"
|
||||
},
|
||||
"locales": {
|
||||
"type": "array",
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.locales.description%",
|
||||
"items": {
|
||||
"$ref": "#i18n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -322,6 +329,7 @@
|
||||
"default": [],
|
||||
"markdownDescription": "%setting.frontMatter.content.i18n.markdownDescription%",
|
||||
"items": {
|
||||
"$id": "#i18n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
|
||||
@@ -77,6 +77,11 @@
|
||||
"setting.frontMatter.content.pageFolders.items.properties.contentTypes.description": "Defines which content types can be used for the current location. If not defined, all content types will be available.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.disableCreation.description": "Disable the creation of new content in the folder.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.defaultLocale.description": "Set the page folder as a default locale for the content. All content from this folder is translatable to the languages defined in the `frontMatter.content.i18n` setting.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.locales.description": "Define the locales for the page folder. This will be used for the translation of the content.",
|
||||
"setting.frontMatter.content.i18n.markdownDescription": "Specify the locales you want to use for your website. This setting can be overwritten on page folder level. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n)",
|
||||
"setting.frontMatter.content.i18n.items.properties.title.description": "Title of the locale",
|
||||
"setting.frontMatter.content.i18n.items.properties.locale.description": "Locale code",
|
||||
"setting.frontMatter.content.i18n.items.properties.path.description": "Relative path of the locale folder",
|
||||
"setting.frontMatter.content.placeholders.markdownDescription": "This array of placeholders defines the placeholders that you can use in your content types and templates for automatically populating your content its front matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.placeholders)",
|
||||
"setting.frontMatter.content.placeholders.items.properties.id.description": "ID of the placeholder, in your content type or template, use it as follows: {{placeholder}}",
|
||||
"setting.frontMatter.content.placeholders.items.properties.value.description": "The placeholder its value",
|
||||
|
||||
+54
-34
@@ -15,16 +15,22 @@ import { join, parse } from 'path';
|
||||
import { existsAsync } from '../utils';
|
||||
import { Folders } from '.';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { PagesListener } from '../listeners/dashboard';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
// TODO:
|
||||
// Allow sponsors to automatically translate the content
|
||||
// Support page bundles
|
||||
// Filter on locale
|
||||
// Filter on locale ✅
|
||||
// Locale settings on the page folder level and global level
|
||||
// Show the i18n content -> if default locale is in subfolder, the other content is not found
|
||||
// Update the page folder setting to include the locales property (use #ref)
|
||||
// Update the default card item when the translation is removed
|
||||
// Add action to create new translation
|
||||
// Show the i18n content -> if default locale is in subfolder, the other content is not found ✅
|
||||
// Update the page folder setting to include the locales property (use #ref) ✅
|
||||
// Update the default card item when the translation is removed ✅
|
||||
// Add action to create new translation ✅
|
||||
// Trigger page update when translation is created ✅
|
||||
// Add translations to the menu ✅
|
||||
// Localization of the React components ✅
|
||||
|
||||
export class i18n {
|
||||
/**
|
||||
@@ -146,34 +152,37 @@ export class i18n {
|
||||
* @param filePath - The path of the file for which translations are requested.
|
||||
* @returns A promise that resolves to an object containing translations for each locale, or undefined if i18n settings are not available.
|
||||
*/
|
||||
public static async getTranslations(
|
||||
filePath: string
|
||||
): Promise<{ [locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
} } | undefined> {
|
||||
public static async getTranslations(filePath: string): Promise<
|
||||
| {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
};
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
const i18nSettings = await i18n.getSettings(filePath);
|
||||
if (!i18nSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
const translations: { [locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
} } = {};
|
||||
const translations: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
};
|
||||
} = {};
|
||||
|
||||
const pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
const fileName = parse(filePath).base;
|
||||
if (pageFolder && pageFolder.defaultLocale) {
|
||||
for (const i18n of i18nSettings) {
|
||||
if (i18n.path) {
|
||||
const translation = join(pageFolder.path, i18n.path, fileName);
|
||||
if (await existsAsync(translation)) {
|
||||
translations[i18n.locale] = {
|
||||
locale: i18n,
|
||||
path: translation
|
||||
};
|
||||
}
|
||||
const translation = join(pageFolder.path, i18n.path || '', fileName);
|
||||
if (await existsAsync(translation)) {
|
||||
translations[i18n.locale] = {
|
||||
locale: i18n,
|
||||
path: translation
|
||||
};
|
||||
}
|
||||
}
|
||||
return translations;
|
||||
@@ -217,34 +226,38 @@ export class i18n {
|
||||
* If no file path is provided, the active file in the editor will be used.
|
||||
* @param filePath The path of the file where the new content file should be created.
|
||||
*/
|
||||
private static async create(fileUri?: Uri) {
|
||||
private static async create(fileUri?: Uri | string) {
|
||||
if (!fileUri) {
|
||||
const filePath = ArticleHelper.getActiveFile();
|
||||
fileUri = filePath ? Uri.file(filePath) : undefined;
|
||||
}
|
||||
|
||||
if (!fileUri) {
|
||||
Notifications.warning('No file selected');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoFileSelected));
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof fileUri === 'string') {
|
||||
fileUri = Uri.file(fileUri);
|
||||
}
|
||||
|
||||
const i18nSettings = await i18n.getSettings(fileUri.fsPath);
|
||||
if (!i18nSettings) {
|
||||
Notifications.warning('No i18n configuration found');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
const isDefaultLanguage = await i18n.isDefaultLanguage(fileUri.fsPath);
|
||||
if (!isDefaultLanguage) {
|
||||
Notifications.warning('The current file cannot be used for i18n content creation');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNotDefaultLocale));
|
||||
return;
|
||||
}
|
||||
|
||||
const locale = await window.showQuickPick(
|
||||
i18nSettings.filter((i18n) => i18n.path).map((i18n) => i18n.title || i18n.locale),
|
||||
{
|
||||
title: 'Create content for locale',
|
||||
placeHolder: 'To which locale do you want to create a new content?',
|
||||
title: l10n.t(LocalizationKey.commandsI18nCreateQuickPickTitle),
|
||||
placeHolder: l10n.t(LocalizationKey.commandsI18nCreateQuickPickPlaceHolder),
|
||||
ignoreFocusOut: true
|
||||
}
|
||||
);
|
||||
@@ -257,19 +270,19 @@ export class i18n {
|
||||
(i18n) => i18n.title === locale || i18n.locale === locale
|
||||
);
|
||||
if (!selectedI18n || !selectedI18n.path) {
|
||||
Notifications.warning('No i18n configuration found');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
let article = await ArticleHelper.getFrontMatterByPath(fileUri.fsPath);
|
||||
if (!article) {
|
||||
Notifications.warning('No content found');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoFile));
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
if (!contentType) {
|
||||
Notifications.warning('No content type found');
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoContentType));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -291,7 +304,7 @@ export class i18n {
|
||||
|
||||
const newFilePath = join(i18nDir, fileInfo.base);
|
||||
if (await existsAsync(newFilePath)) {
|
||||
Notifications.warning('File already exists');
|
||||
Notifications.error(l10n.t(LocalizationKey.commandsI18nCreateErrorFileExists));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,7 +316,14 @@ export class i18n {
|
||||
|
||||
await openFileInEditor(newFilePath);
|
||||
|
||||
Notifications.info(`Created "${selectedI18n.title || selectedI18n.locale}" i18n content file`);
|
||||
PagesListener.refresh();
|
||||
|
||||
Notifications.info(
|
||||
l10n.t(
|
||||
LocalizationKey.commandsI18nCreateSuccessCreated,
|
||||
selectedI18n.title || selectedI18n.locale
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,8 +16,6 @@ const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
@@ -27,7 +25,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-[var(--vscode-sideBar-background)] data-[state=open]:bg-[var(--vscode-sideBar-background)]",
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-[var(--vscode-list-hoverBackground)] data-[state=open]:bg-[var(--vscode-list-hoverBackground)]",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
@@ -47,7 +45,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-[var(--vscode-sideBar-background)] p-1 text-[var(--vscode-editor-foreground)] shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded border border-[var(--frontmatter-border)] bg-[var(--vscode-sideBar-background)] p-1 text-[var(--vscode-editor-foreground)] shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -65,7 +63,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] rounded-md border border-[var(--frontmatter-border)] bg-[var(--vscode-sideBar-background)] p-1 text-[var(--vscode-editor-foreground)] shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-96 overflow-auto",
|
||||
"z-50 min-w-[8rem] rounded border border-[var(--frontmatter-border)] bg-[var(--vscode-sideBar-background)] p-1 text-[var(--vscode-editor-foreground)] shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-96 overflow-auto",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -92,52 +90,6 @@ const DropdownMenuItem = React.forwardRef<
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-[var(--vscode-list-hoverBackground)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-[var(--vscode-list-hoverBackground)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckCircleIcon className="h-2 w-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
@@ -186,8 +138,6 @@ export {
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
@@ -196,5 +146,4 @@ export {
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
@@ -17,6 +17,7 @@ export const GeneralCommands = {
|
||||
getBranch: 'getBranch',
|
||||
selectBranch: 'gitSelectBranch'
|
||||
},
|
||||
runCommand: 'runCommand',
|
||||
getLocalization: 'getLocalization',
|
||||
openOnWebsite: 'openOnWebsite'
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, EllipsisVerticalIcon } from '@heroicons/react/24/outline';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, EllipsisVerticalIcon, LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { CustomScript, I18nConfig, ScriptType } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
@@ -9,10 +9,10 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { GeneralCommands } from '../../../constants';
|
||||
import { COMMAND_NAME, GeneralCommands } from '../../../constants';
|
||||
import { PinIcon } from '../Icons/PinIcon';
|
||||
import { PinnedItemsAtom } from '../../state/atom/PinnedItems';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
|
||||
export interface IContentActionsProps {
|
||||
title: string;
|
||||
@@ -20,6 +20,14 @@ export interface IContentActionsProps {
|
||||
relPath: string;
|
||||
scripts: CustomScript[] | undefined;
|
||||
listView?: boolean;
|
||||
locale?: I18nConfig;
|
||||
isDefaultLocale?: boolean;
|
||||
translations?: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
};
|
||||
};
|
||||
onOpen: () => void;
|
||||
}
|
||||
|
||||
@@ -29,7 +37,10 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
relPath,
|
||||
scripts,
|
||||
onOpen,
|
||||
listView
|
||||
listView,
|
||||
isDefaultLocale,
|
||||
translations,
|
||||
locale
|
||||
}: React.PropsWithChildren<IContentActionsProps>) => {
|
||||
const [pinnedItems, setPinnedItems] = useRecoilState(PinnedItemsAtom);
|
||||
const [showDeletionAlert, setShowDeletionAlert] = React.useState(false);
|
||||
@@ -52,6 +63,10 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
setShowDeletionAlert(false);
|
||||
};
|
||||
|
||||
const onOpenFile = (filePath: string) => {
|
||||
messageHandler.send(DashboardMessage.openFile, filePath);
|
||||
}
|
||||
|
||||
const openOnWebsite = React.useCallback((e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
if (settings?.websiteUrl && path) {
|
||||
@@ -84,6 +99,13 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
[path]
|
||||
);
|
||||
|
||||
const runCommand = React.useCallback((commandId: string) => {
|
||||
messageHandler.send(GeneralCommands.toVSCode.runCommand, {
|
||||
command: commandId,
|
||||
args: path
|
||||
})
|
||||
}, [path]);
|
||||
|
||||
const isPinned = React.useMemo(() => {
|
||||
return pinnedItems.includes(relPath);
|
||||
}, [pinnedItems, relPath]);
|
||||
@@ -104,6 +126,45 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
const translationsMenu = React.useMemo(() => {
|
||||
if (!locale || !translations || Object.keys(translations).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const crntLocale = translations[locale.locale];
|
||||
const otherLocales = Object.entries(translations).filter(([key]) => key !== locale.locale);
|
||||
|
||||
return (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsMenu)}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuItem onClick={() => onOpenFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => onOpenFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
}, [translations, locale, isDefaultLocale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -129,7 +190,10 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
<QuickAction title={l10n.t(LocalizationKey.commonDelete)} onClick={onDelete}>
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonDelete)}
|
||||
className={`hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
@@ -161,6 +225,17 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
locale && isDefaultLocale && (
|
||||
<DropdownMenuItem onClick={() => runCommand(COMMAND_NAME.i18n.create)}>
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsCreate)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
{translationsMenu}
|
||||
|
||||
{customScriptActions}
|
||||
|
||||
<DropdownMenuItem onClick={onDelete} className={`focus:bg-[var(--vscode-statusBarItem-errorBackground)] focus:text-[var(--vscode-statusBarItem-errorForeground)]`}>
|
||||
|
||||
@@ -13,59 +13,14 @@ export interface II18nLabelProps {
|
||||
export const I18nLabel: React.FunctionComponent<II18nLabelProps> = ({
|
||||
page
|
||||
}: React.PropsWithChildren<II18nLabelProps>) => {
|
||||
|
||||
const openFile = (filePath: string) => {
|
||||
messageHandler.send(DashboardMessage.openFile, filePath);
|
||||
}
|
||||
|
||||
const dropdown = React.useMemo(() => {
|
||||
console.log(page)
|
||||
if (!page.fmLocale || !page.fmTranslations || Object.keys(page.fmTranslations).length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="text-xs flex items-center focus:outline-none border rounded border-[var(--frontmatter-border)] p-1">
|
||||
<LanguageIcon className={`mr-2 h-4 w-4`} aria-hidden="true" />
|
||||
<span>{page.fmLocale.title || page.fmLocale.locale}</span>
|
||||
<ChevronDownIcon className="ml-1 h-4 w-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align='start'>
|
||||
<MenuItem
|
||||
title={page.fmLocale.title || page.fmLocale.locale}
|
||||
value={page.fmFilePath}
|
||||
onClick={(value) => openFile(value)} />
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
Object.entries(page.fmTranslations).map(([key, value]) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={key}
|
||||
title={value.locale.title || value.locale.locale}
|
||||
value={value.path}
|
||||
onClick={(value) => openFile(value)} />
|
||||
);
|
||||
})
|
||||
}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}, [page])
|
||||
|
||||
if (!page.fmLocale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-2 flex items-center">
|
||||
{/* <LanguageIcon className="mr-1 h-4 w-4 inline-block" />
|
||||
<span className="text-xs">{page.fmLocale.title || page.fmLocale.locale}</span> */}
|
||||
|
||||
{dropdown}
|
||||
<LanguageIcon className="mr-1 h-4 w-4 inline-block" />
|
||||
<span className="text-xs">{page.fmLocale.title || page.fmLocale.locale}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -79,7 +79,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
statusHtml ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: statusHtml }} />
|
||||
) : (
|
||||
cardFields?.state && draftField && draftField.name && pageData[draftField.name] ? <Status draft={pageData[draftField.name]} published={pageData.fmPublished} /> : null
|
||||
cardFields?.state && draftField && draftField.name && typeof pageData[draftField.name] !== "undefined" ? <Status draft={pageData[draftField.name]} published={pageData.fmPublished} /> : null
|
||||
)
|
||||
)
|
||||
}, [statusHtml, cardFields?.state, draftField, pageData]);
|
||||
@@ -111,8 +111,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={openFile}
|
||||
className={`relative h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]
|
||||
}`}
|
||||
className={`relative h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{
|
||||
imageHtml ?
|
||||
@@ -148,6 +147,9 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
title={pageData.title}
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
locale={pageData.fmLocale}
|
||||
isDefaultLocale={pageData.fmDefaultLocale}
|
||||
translations={pageData.fmTranslations}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={openFile}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuSeparator } from '../../../components/shadcn/Dropdown';
|
||||
import { LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { MenuButton, MenuItem } from '../Menu';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { DEFAULT_LOCALE_STATE, LocaleAtom, LocalesAtom } from '../../state';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ILanguageFilterProps { }
|
||||
|
||||
export const LanguageFilter: React.FunctionComponent<ILanguageFilterProps> = ({ }: React.PropsWithChildren<ILanguageFilterProps>) => {
|
||||
const locales = useRecoilValue(LocalesAtom);
|
||||
const [crntLocale, setCrntLocale] = useRecoilState(LocaleAtom);
|
||||
|
||||
const crntLocaleName = React.useMemo(() => {
|
||||
if (!crntLocale || !locales || locales.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const locale = locales.find(locale => locale.locale === crntLocale);
|
||||
|
||||
return locale?.title || locale?.locale;
|
||||
}, [crntLocale, locales]);
|
||||
|
||||
if (!locales || locales.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<MenuButton
|
||||
label={
|
||||
<>
|
||||
<LanguageIcon className={`inline-block w-4 h-4 mr-2`} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardFiltersLanguageFilterLabel)}</span>
|
||||
</>
|
||||
}
|
||||
title={crntLocaleName || l10n.t(LocalizationKey.dashboardFiltersLanguageFilterAll)}
|
||||
/>
|
||||
|
||||
<DropdownMenuContent align='start'>
|
||||
<MenuItem
|
||||
title={l10n.t(LocalizationKey.dashboardFiltersLanguageFilterAll)}
|
||||
value={null}
|
||||
isCurrent={crntLocale === DEFAULT_LOCALE_STATE}
|
||||
onClick={() => setCrntLocale(DEFAULT_LOCALE_STATE)}
|
||||
/>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
locales.map((locale) => (
|
||||
<MenuItem
|
||||
key={locale.locale}
|
||||
title={locale.title || locale.locale}
|
||||
value={locale.locale}
|
||||
isCurrent={locale.locale === crntLocale}
|
||||
onClick={(value) => setCrntLocale(value)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
CategoryAtom,
|
||||
DEFAULT_TAG_STATE,
|
||||
DEFAULT_CATEGORY_STATE,
|
||||
FiltersAtom
|
||||
FiltersAtom,
|
||||
LocaleAtom,
|
||||
DEFAULT_LOCALE_STATE
|
||||
} from '../../state';
|
||||
import { DefaultValue } from 'recoil';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
@@ -34,12 +36,14 @@ export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (
|
||||
const folder = useRecoilValue(FolderSelector);
|
||||
const tag = useRecoilValue(TagSelector);
|
||||
const category = useRecoilValue(CategorySelector);
|
||||
const locale = useRecoilValue(LocaleAtom);
|
||||
const filters = useRecoilValue(FiltersAtom);
|
||||
|
||||
const resetSorting = useResetRecoilState(SortingAtom);
|
||||
const resetFolder = useResetRecoilState(FolderAtom);
|
||||
const resetTag = useResetRecoilState(TagAtom);
|
||||
const resetCategory = useResetRecoilState(CategoryAtom);
|
||||
const resetLocale = useResetRecoilState(LocaleAtom);
|
||||
const resetFilters = useResetRecoilState(FiltersAtom);
|
||||
|
||||
const reset = () => {
|
||||
@@ -48,6 +52,7 @@ export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (
|
||||
resetFolder();
|
||||
resetTag();
|
||||
resetCategory();
|
||||
resetLocale();
|
||||
resetFilters();
|
||||
};
|
||||
|
||||
@@ -61,19 +66,20 @@ export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (
|
||||
folder !== DEFAULT_FOLDER_STATE ||
|
||||
tag !== DEFAULT_TAG_STATE ||
|
||||
category !== DEFAULT_CATEGORY_STATE ||
|
||||
locale !== DEFAULT_LOCALE_STATE ||
|
||||
hasCustomFilters
|
||||
) {
|
||||
setShow(true);
|
||||
} else {
|
||||
setShow(false);
|
||||
}
|
||||
}, [folder, tag, category, hasCustomFilters]);
|
||||
}, [folder, tag, category, locale, hasCustomFilters]);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`flex items-center hover:text-[var(--vscode-textLink-activeForeground)]`}
|
||||
className={`flex items-center hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={reset}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderClearFiltersTitle)}
|
||||
>
|
||||
|
||||
@@ -40,7 +40,7 @@ export const Filter: React.FunctionComponent<IFilterProps> = ({
|
||||
<MenuItem
|
||||
title={DEFAULT_VALUE}
|
||||
value={null}
|
||||
isCurrent={!!activeItem}
|
||||
isCurrent={!activeItem}
|
||||
onClick={() => onClick(null)}
|
||||
/>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { CategoryAtom, SettingsSelector, TagAtom, FiltersAtom, FilterValuesAtom
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { firstToUpper } from '../../../helpers/StringHelpers';
|
||||
import { LanguageFilter } from '../Filters/LanguageFilter';
|
||||
|
||||
export interface IFiltersProps { }
|
||||
|
||||
@@ -17,7 +18,6 @@ export const Filters: React.FunctionComponent<IFiltersProps> = (_: React.PropsWi
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const location = useLocation();
|
||||
|
||||
|
||||
const otherFilters = useMemo(() => settings?.filters?.filter((filter) => filter !== "pageFolders" && filter !== "tags" && filter !== "categories"), [settings?.filters]);
|
||||
|
||||
const otherFilterValues = useMemo(() => {
|
||||
@@ -74,6 +74,8 @@ export const Filters: React.FunctionComponent<IFiltersProps> = (_: React.PropsWi
|
||||
|
||||
return (
|
||||
<>
|
||||
<LanguageFilter />
|
||||
|
||||
{
|
||||
settings?.filters?.includes("pageFolders") && (
|
||||
<FoldersFilter />
|
||||
|
||||
@@ -119,7 +119,10 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
<QuickAction title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionDelete)} onClick={onDelete}>
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
className={`hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface IMenuItemProps {
|
||||
title: JSX.Element | string;
|
||||
value?: any;
|
||||
isCurrent?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick: (value: any, e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
}
|
||||
@@ -13,12 +14,13 @@ export const MenuItem: React.FunctionComponent<IMenuItemProps> = ({
|
||||
title,
|
||||
value,
|
||||
isCurrent,
|
||||
className,
|
||||
disabled,
|
||||
onClick
|
||||
}: React.PropsWithChildren<IMenuItemProps>) => {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
className={`${!isCurrent ? `font-normal` : `font-bold`}`}
|
||||
className={`${!isCurrent ? `font-normal` : `font-bold`} ${className || ''}`}
|
||||
disabled={disabled}
|
||||
onClick={(e) => onClick(value, e)}
|
||||
>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface IQuickActionProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
export const QuickAction: React.FunctionComponent<IQuickActionProps> = ({
|
||||
title,
|
||||
className,
|
||||
onClick,
|
||||
children
|
||||
}: React.PropsWithChildren<IQuickActionProps>) => {
|
||||
@@ -15,7 +18,7 @@ export const QuickAction: React.FunctionComponent<IQuickActionProps> = ({
|
||||
type="button"
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
className={`px-2 group inline-flex justify-center text-sm font-medium text-[var(--vscode-foreground)] hover:text-[var(--frontmatter-button-hoverBackground)]`}
|
||||
className={cn(`px-2 group inline-flex justify-center text-sm font-medium text-[var(--vscode-foreground)] hover:text-[var(--frontmatter-button-hoverBackground)]`, className)}
|
||||
>
|
||||
{children}
|
||||
<span className="sr-only">{title}</span>
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
FilterValuesAtom,
|
||||
FiltersAtom,
|
||||
FolderSelector,
|
||||
LocaleAtom,
|
||||
LocalesAtom,
|
||||
SearchSelector,
|
||||
SettingsSelector,
|
||||
SortingAtom,
|
||||
@@ -22,20 +24,25 @@ import { parseWinPath } from '../../helpers/parseWinPath';
|
||||
import { sortPages } from '../../utils/sortPages';
|
||||
import { ExtensionState } from '../../constants';
|
||||
import { SortingOption } from '../models';
|
||||
import { I18nConfig } from '../../models';
|
||||
import { usePrevious } from '../../panelWebView/hooks/usePrevious';
|
||||
|
||||
export default function usePages(pages: Page[]) {
|
||||
const [pageItems, setPageItems] = useRecoilState(AllPagesAtom);
|
||||
const [sortedPages, setSortedPages] = useState<Page[]>([]);
|
||||
const [pageItems, setPageItems] = useRecoilState(AllPagesAtom);
|
||||
const [sorting, setSorting] = useRecoilState(SortingAtom);
|
||||
const [tabInfo, setTabInfo] = useRecoilState(TabInfoAtom);
|
||||
const [locales, setLocales] = useRecoilState(LocalesAtom);
|
||||
const [, setFilterValues] = useRecoilState(FilterValuesAtom);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const tab = useRecoilValue(TabSelector);
|
||||
const folder = useRecoilValue(FolderSelector);
|
||||
const search = useRecoilValue(SearchSelector);
|
||||
const tag = useRecoilValue(TagSelector);
|
||||
const locale = useRecoilValue(LocaleAtom);
|
||||
const category = useRecoilValue(CategorySelector);
|
||||
const filters = useRecoilValue(FiltersAtom);
|
||||
const tabPrevious = usePrevious(tab);
|
||||
|
||||
/**
|
||||
* Process all the pages by applying the sorting, filtering and searching.
|
||||
@@ -90,6 +97,11 @@ export default function usePages(pages: Page[]) {
|
||||
);
|
||||
}
|
||||
|
||||
// If filtered by locale
|
||||
if (locale) {
|
||||
pagesSorted = pagesSorted.filter((page) => page.fmLocale && page.fmLocale.locale === locale);
|
||||
}
|
||||
|
||||
const filterNames = Object.keys(filters);
|
||||
if (filterNames.length > 0) {
|
||||
for (const filter of filterNames) {
|
||||
@@ -102,7 +114,7 @@ export default function usePages(pages: Page[]) {
|
||||
|
||||
setSortedPages(pagesSorted);
|
||||
},
|
||||
[settings, tab, folder, search, tag, category, sorting, tabInfo, filters]
|
||||
[settings, tab, folder, search, tag, category, locale, sorting, tabInfo, filters]
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -114,8 +126,23 @@ export default function usePages(pages: Page[]) {
|
||||
|
||||
let crntPages: Page[] = Object.assign([], pages);
|
||||
|
||||
// Filter out translations
|
||||
crntPages = crntPages.filter((page) => !page.fmLocale || (page.fmLocale && page.fmDefaultLocale))
|
||||
// Update the translations of pages
|
||||
crntPages = crntPages.map((page) => {
|
||||
if (page.fmTranslations) {
|
||||
const translations = Object.assign({}, page.fmTranslations);
|
||||
|
||||
for (const [key, value] of Object.entries(translations)) {
|
||||
const translatedPage = crntPages.find((p) => parseWinPath(p.fmFilePath).toLowerCase() === parseWinPath(value.path).toLowerCase());
|
||||
if (!translatedPage) {
|
||||
delete translations[key];
|
||||
}
|
||||
}
|
||||
|
||||
return { ...page, fmTranslations: translations };
|
||||
}
|
||||
|
||||
return page;
|
||||
});
|
||||
|
||||
// Process the tab data
|
||||
const draftTypes = Object.assign({}, tabInfo);
|
||||
@@ -193,10 +220,21 @@ export default function usePages(pages: Page[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (tabPrevious !== tab || !locales || locales.length === 0) {
|
||||
// Store the locale information
|
||||
const config: I18nConfig[] = [];
|
||||
crntPages.forEach((page) => {
|
||||
if (page.fmLocale && !config.some(locale => locale.locale === page.fmLocale?.locale)) {
|
||||
config.push(page.fmLocale);
|
||||
}
|
||||
});
|
||||
setLocales(config);
|
||||
}
|
||||
|
||||
// Set the pages
|
||||
setPageItems(crntPages);
|
||||
},
|
||||
[tab, tabInfo, settings, filters]
|
||||
[tab, tabInfo, settings, filters, locales, tabPrevious]
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -238,7 +276,7 @@ export default function usePages(pages: Page[]) {
|
||||
} else {
|
||||
startPageProcessing();
|
||||
}
|
||||
}, [settings?.draftField, pages, sorting, search, tag, category, filters, folder]);
|
||||
}, [settings?.draftField, pages, sorting, search, tag, category, locale, filters, folder]);
|
||||
|
||||
useEffect(() => {
|
||||
processByTab(sortedPages);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const DEFAULT_LOCALE_STATE = '';
|
||||
|
||||
export const LocaleAtom = atom<string | null>({
|
||||
key: 'LocaleAtom',
|
||||
default: DEFAULT_LOCALE_STATE
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import { atom } from 'recoil';
|
||||
import { I18nConfig } from '../../../models';
|
||||
|
||||
export const LocalesAtom = atom<I18nConfig[] | undefined>({
|
||||
key: 'LocalesAtom',
|
||||
default: undefined
|
||||
});
|
||||
@@ -9,6 +9,8 @@ export * from './FolderAtom';
|
||||
export * from './GroupingAtom';
|
||||
export * from './LightboxAtom';
|
||||
export * from './LoadingAtom';
|
||||
export * from './LocaleAtom';
|
||||
export * from './LocalesAtom';
|
||||
export * from './MediaFoldersAtom';
|
||||
export * from './MediaTotalAtom';
|
||||
export * from './ModeAtom';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Dashboard } from '../../commands/Dashboard';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { ArticleHelper, Extension } from '../../helpers';
|
||||
import { Logger } from '../../helpers/Logger';
|
||||
import { commands, Uri, window } from 'vscode';
|
||||
import { commands, Uri, window, workspace } from 'vscode';
|
||||
import { PostMessageData } from '../../models';
|
||||
import { Preview } from '../../commands';
|
||||
import { urlJoin } from 'url-join-ts';
|
||||
@@ -19,6 +19,12 @@ export abstract class BaseListener {
|
||||
case GeneralCommands.toVSCode.openOnWebsite:
|
||||
this.openOnWebsite(msg.payload);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.runCommand:
|
||||
if (msg.payload) {
|
||||
const { command, args } = msg.payload;
|
||||
commands.executeCommand(command, args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -315,6 +315,14 @@ export enum LocalizationKey {
|
||||
* Are you sure you want to delete the "{0}" content?
|
||||
*/
|
||||
dashboardContentsContentActionsAlertDescription = 'dashboard.contents.contentActions.alert.description',
|
||||
/**
|
||||
* Create translation
|
||||
*/
|
||||
dashboardContentsContentActionsTranslationsCreate = 'dashboard.contents.contentActions.translations.create',
|
||||
/**
|
||||
* Translations
|
||||
*/
|
||||
dashboardContentsContentActionsTranslationsMenu = 'dashboard.contents.contentActions.translations.menu',
|
||||
/**
|
||||
* <invalid title>
|
||||
*/
|
||||
@@ -427,6 +435,14 @@ export enum LocalizationKey {
|
||||
* Please close the dashboard and try again.
|
||||
*/
|
||||
dashboardErrorViewDescription = 'dashboard.errorView.description',
|
||||
/**
|
||||
* Locale
|
||||
*/
|
||||
dashboardFiltersLanguageFilterLabel = 'dashboard.filters.languageFilter.label',
|
||||
/**
|
||||
* All
|
||||
*/
|
||||
dashboardFiltersLanguageFilterAll = 'dashboard.filters.languageFilter.all',
|
||||
/**
|
||||
* Home
|
||||
*/
|
||||
@@ -1672,6 +1688,42 @@ export enum LocalizationKey {
|
||||
* Create folder
|
||||
*/
|
||||
commandsFoldersGetNotificationErrorCreateAction = 'commands.folders.get.notificationError.create.action',
|
||||
/**
|
||||
* No file selected.
|
||||
*/
|
||||
commandsI18nCreateWarningNoFileSelected = 'commands.i18n.create.warning.noFileSelected',
|
||||
/**
|
||||
* The file could not be retrieved.
|
||||
*/
|
||||
commandsI18nCreateWarningNoFile = 'commands.i18n.create.warning.noFile',
|
||||
/**
|
||||
* Content type could not be retrieved for the current file.
|
||||
*/
|
||||
commandsI18nCreateWarningNoContentType = 'commands.i18n.create.warning.noContentType',
|
||||
/**
|
||||
* No i18n configuration found.
|
||||
*/
|
||||
commandsI18nCreateWarningNoConfig = 'commands.i18n.create.warning.noConfig',
|
||||
/**
|
||||
* The current file cannot be used for i18n content creation.
|
||||
*/
|
||||
commandsI18nCreateWarningNotDefaultLocale = 'commands.i18n.create.warning.notDefaultLocale',
|
||||
/**
|
||||
* The i18n translation already exists.
|
||||
*/
|
||||
commandsI18nCreateErrorFileExists = 'commands.i18n.create.error.fileExists',
|
||||
/**
|
||||
* Created "{0}" i18n content file.
|
||||
*/
|
||||
commandsI18nCreateSuccessCreated = 'commands.i18n.create.success.created',
|
||||
/**
|
||||
* Create content for locale
|
||||
*/
|
||||
commandsI18nCreateQuickPickTitle = 'commands.i18n.create.quickPick.title',
|
||||
/**
|
||||
* To which locale do you want to create a new content?
|
||||
*/
|
||||
commandsI18nCreateQuickPickPlaceHolder = 'commands.i18n.create.quickPick.placeHolder',
|
||||
/**
|
||||
* Preview: {0}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user