mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 09:23:00 +02:00
#756 - Language dropdown
This commit is contained in:
+20
-6
@@ -23,6 +23,8 @@ import { ParsedFrontMatter } from '../parsers';
|
||||
// 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
|
||||
|
||||
export class i18n {
|
||||
/**
|
||||
@@ -146,21 +148,31 @@ export class i18n {
|
||||
*/
|
||||
public static async getTranslations(
|
||||
filePath: string
|
||||
): Promise<{ [locale: string]: string } | undefined> {
|
||||
): Promise<{ [locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
} } | undefined> {
|
||||
const i18nSettings = await i18n.getSettings(filePath);
|
||||
if (!i18nSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
const translations: { [locale: string]: 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, filePath);
|
||||
const translation = join(pageFolder.path, i18n.path, fileName);
|
||||
if (await existsAsync(translation)) {
|
||||
translations[i18n.locale] = translation;
|
||||
translations[i18n.locale] = {
|
||||
locale: i18n,
|
||||
path: translation
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +184,6 @@ export class i18n {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = parse(filePath).base;
|
||||
const defaultLanguageFolders = folders.filter((folder) => folder.defaultLocale);
|
||||
let defaultLanguageFolder: ContentFolder | undefined;
|
||||
for (const folder of defaultLanguageFolders) {
|
||||
@@ -190,7 +201,10 @@ export class i18n {
|
||||
for (const i18n of i18nSettings) {
|
||||
const translation = join(defaultLanguageFolder.path, i18n.path || '', fileName);
|
||||
if (await existsAsync(translation)) {
|
||||
translations[i18n.locale] = translation;
|
||||
translations[i18n.locale] = {
|
||||
locale: i18n,
|
||||
path: translation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
)}
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]'>
|
||||
<DropdownMenuTrigger className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] focus:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.dashboardContentsContentActionsActionMenuButtonTitle)}</span>
|
||||
<EllipsisVerticalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { Page } from '../../models';
|
||||
import { LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { ChevronDownIcon, LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { MenuItem } from '../Menu';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
|
||||
export interface II18nLabelProps {
|
||||
page: Page;
|
||||
@@ -9,14 +13,59 @@ 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>
|
||||
{/* <LanguageIcon className="mr-1 h-4 w-4 inline-block" />
|
||||
<span className="text-xs">{page.fmLocale.title || page.fmLocale.locale}</span> */}
|
||||
|
||||
{dropdown}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -70,6 +70,34 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
return [];
|
||||
}, [settings, pageData]);
|
||||
|
||||
const statusPlaceholder = useMemo(() => {
|
||||
if (!statusHtml && !cardFields?.state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
statusHtml ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: statusHtml }} />
|
||||
) : (
|
||||
cardFields?.state && draftField && draftField.name && pageData[draftField.name] ? <Status draft={pageData[draftField.name]} published={pageData.fmPublished} /> : null
|
||||
)
|
||||
)
|
||||
}, [statusHtml, cardFields?.state, draftField, pageData]);
|
||||
|
||||
const datePlaceholder = useMemo(() => {
|
||||
if (!dateHtml && !cardFields?.date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
dateHtml ? (
|
||||
<div className='mr-4' dangerouslySetInnerHTML={{ __html: dateHtml }} />
|
||||
) : (
|
||||
cardFields?.date && pageData.date ? <DateField className={`mr-4`} value={pageData.date} format={pageData.fmDateFormat} /> : null
|
||||
)
|
||||
)
|
||||
}, [dateHtml, cardFields?.date, pageData]);
|
||||
|
||||
const hasDraftOrDate = useMemo(() => {
|
||||
return cardFields && (cardFields.state || cardFields.date);
|
||||
}, [cardFields]);
|
||||
@@ -107,23 +135,14 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
</button>
|
||||
|
||||
<div className="relative p-4 w-full grow">
|
||||
<div className={`flex justify-between items-center ${hasDraftOrDate ? `mb-2` : ``}`}>
|
||||
{
|
||||
statusHtml ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: statusHtml }} />
|
||||
) : (
|
||||
cardFields?.state && draftField && draftField.name && <Status draft={pageData[draftField.name]} published={pageData.fmPublished} />
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
dateHtml ? (
|
||||
<div className='mr-4' dangerouslySetInnerHTML={{ __html: dateHtml }} />
|
||||
) : (
|
||||
cardFields?.date && <DateField className={`mr-4`} value={pageData.date} format={pageData.fmDateFormat} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
(statusPlaceholder || datePlaceholder) && (
|
||||
<div className={`flex justify-between items-center ${hasDraftOrDate ? `mb-2` : ``}`}>
|
||||
{statusPlaceholder}
|
||||
{datePlaceholder}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<ContentActions
|
||||
title={pageData.title}
|
||||
|
||||
@@ -114,6 +114,9 @@ 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))
|
||||
|
||||
// Process the tab data
|
||||
const draftTypes = Object.assign({}, tabInfo);
|
||||
draftTypes[Tab.All] = crntPages.length;
|
||||
|
||||
@@ -24,7 +24,12 @@ export interface Page {
|
||||
// i18n fields
|
||||
fmDefaultLocale?: boolean;
|
||||
fmLocale?: I18nConfig;
|
||||
fmTranslations?: { [locale: string]: string };
|
||||
fmTranslations?: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
}
|
||||
};
|
||||
|
||||
title: string;
|
||||
slug: string;
|
||||
|
||||
Reference in New Issue
Block a user