#756 - Language dropdown

This commit is contained in:
Elio Struyf
2024-02-18 21:20:07 +01:00
parent 5f0fd29cca
commit 36ac891c00
6 changed files with 118 additions and 28 deletions
+20 -6
View File
@@ -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}
+3
View File
@@ -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;
+6 -1
View File
@@ -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;