mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-06 17:02:54 +02:00
#785 - Added custom script actions to the bottom of the cards
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
"common.back": "Back",
|
||||
"common.open": "Open",
|
||||
"common.openWithValue": "Open: {0}",
|
||||
"common.openCustomActions": "Open custom actions",
|
||||
"common.view": "View",
|
||||
"common.translate": "Translate",
|
||||
"common.languages": "Languages",
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Spinner: React.FunctionComponent<ISpinnerProps> = (
|
||||
|
||||
{
|
||||
type === 'initPages' && (
|
||||
<div className='spinner-msg h-full text-2xl flex justify-center items-center text-[var(--vscode-foreground)]'>
|
||||
<div className='spinner-msg h-full text-2xl flex justify-center items-center text-[var(--frontmatter-text)]'>
|
||||
<span>{l10n.t(LocalizationKey.loadingInitPages)}</span>
|
||||
<span className='dots'></span>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, LanguageIcon, EllipsisHorizontalIcon } from '@heroicons/react/24/outline';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EyeIcon, GlobeEuropeAfricaIcon, TrashIcon, LanguageIcon, EllipsisHorizontalIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { CustomScript, I18nConfig, ScriptType } from '../../../models';
|
||||
import { CustomScript, I18nConfig } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
@@ -10,13 +10,16 @@ import { SelectedItemActionAtom, SettingsSelector } from '../../state';
|
||||
import { COMMAND_NAME, GeneralCommands } from '../../../constants';
|
||||
import { PinIcon } from '../Icons/PinIcon';
|
||||
import { PinnedItemsAtom } from '../../state/atom/PinnedItems';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { RenameIcon } from '../../../components/icons/RenameIcon';
|
||||
import { openFile, openOnWebsite } from '../../utils';
|
||||
import { openOnWebsite } from '../../utils';
|
||||
import { CustomActions } from './CustomActions';
|
||||
import { TranslationMenu } from './TranslationMenu';
|
||||
|
||||
export interface IContentActionsProps {
|
||||
path: string;
|
||||
relPath: string;
|
||||
contentType: string;
|
||||
scripts: CustomScript[] | undefined;
|
||||
listView?: boolean;
|
||||
locale?: I18nConfig;
|
||||
@@ -33,6 +36,7 @@ export interface IContentActionsProps {
|
||||
export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
path,
|
||||
relPath,
|
||||
contentType,
|
||||
scripts,
|
||||
onOpen,
|
||||
listView,
|
||||
@@ -78,14 +82,6 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
})
|
||||
}, [path]);
|
||||
|
||||
const runCustomScript = React.useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>, script: CustomScript) => {
|
||||
e.stopPropagation();
|
||||
Messenger.send(DashboardMessage.runCustomScript, { script, path });
|
||||
},
|
||||
[path]
|
||||
);
|
||||
|
||||
const runCommand = React.useCallback((commandId: string) => {
|
||||
messageHandler.send(GeneralCommands.toVSCode.runCommand, {
|
||||
command: commandId,
|
||||
@@ -97,65 +93,6 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
return pinnedItems.includes(relPath);
|
||||
}, [pinnedItems, relPath]);
|
||||
|
||||
const customScriptActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter(
|
||||
(script) =>
|
||||
(script.type === undefined || script.type === ScriptType.Content) &&
|
||||
!script.bulk &&
|
||||
!script.hidden
|
||||
)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem key={script.id || script.title} onClick={(e) => runCustomScript(e, script)}>
|
||||
<CommandLineIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [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);
|
||||
|
||||
if (otherLocales.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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={() => openFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => openFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
}, [translations, locale, isDefaultLocale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -208,9 +145,15 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
{translationsMenu}
|
||||
<TranslationMenu
|
||||
isDefaultLocale={isDefaultLocale}
|
||||
locale={locale}
|
||||
translations={translations} />
|
||||
|
||||
{customScriptActions}
|
||||
<CustomActions
|
||||
filePath={path}
|
||||
contentType={contentType}
|
||||
scripts={scripts} />
|
||||
|
||||
<DropdownMenuItem onClick={onDelete} className={`focus:bg-[var(--vscode-statusBarItem-errorBackground)] focus:text-[var(--vscode-statusBarItem-errorForeground)]`}>
|
||||
<TrashIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { CommandLineIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandLineIcon as CommandLineIconSolid } from '@heroicons/react/24/solid';
|
||||
import { runCustomScript } from '../../utils';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ICustomActionsProps {
|
||||
filePath: string;
|
||||
contentType: string;
|
||||
scripts: CustomScript[] | undefined;
|
||||
showTrigger?: boolean;
|
||||
}
|
||||
|
||||
export const CustomActions: React.FunctionComponent<ICustomActionsProps> = ({
|
||||
filePath,
|
||||
contentType,
|
||||
scripts,
|
||||
showTrigger = false,
|
||||
}: React.PropsWithChildren<ICustomActionsProps>) => {
|
||||
|
||||
const onRunCustomScript = React.useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>, script: CustomScript) => {
|
||||
e.stopPropagation();
|
||||
runCustomScript(script, filePath);
|
||||
},
|
||||
[filePath]
|
||||
);
|
||||
|
||||
const customScripts = React.useMemo(() => {
|
||||
return (scripts || []).filter((script: CustomScript) => {
|
||||
if (script.contentTypes && script.contentTypes.length > 0) {
|
||||
return script.contentTypes.includes(contentType);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [scripts, contentType]);
|
||||
|
||||
const customActions = React.useMemo(() => {
|
||||
if (!customScripts || customScripts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
(customScripts || [])
|
||||
.filter(
|
||||
(script) =>
|
||||
(script.type === undefined || script.type === ScriptType.Content) &&
|
||||
!script.bulk &&
|
||||
!script.hidden
|
||||
)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.id || script.title}
|
||||
title={script.title}
|
||||
onClick={(e) => onRunCustomScript(e, script)}>
|
||||
<CommandLineIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
);
|
||||
}, [customScripts, onRunCustomScript]);
|
||||
|
||||
if (!customActions || customActions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (showTrigger) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
title={l10n.t(LocalizationKey.commonOpenCustomActions)}
|
||||
className='px-2 text-[var(--frontmatter-secondary-text)] hover:text-[var(--frontmatter-button-hoverBackground)] focus-visible:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonOpenCustomActions)}</span>
|
||||
<CommandLineIconSolid className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent>
|
||||
{customActions}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{customActions}</>;
|
||||
};
|
||||
@@ -6,16 +6,21 @@ import { LocalizationKey } from '../../../localization';
|
||||
import { openFile, openOnWebsite } from '../../utils';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedItemActionAtom } from '../../state';
|
||||
// import { ItemSelection } from '../Common/ItemSelection';
|
||||
import { CustomScript } from '../../../models';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IFooterActionsProps {
|
||||
filePath: string;
|
||||
contentType: string;
|
||||
websiteUrl?: string;
|
||||
scripts?: CustomScript[];
|
||||
}
|
||||
|
||||
export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
filePath,
|
||||
websiteUrl
|
||||
contentType,
|
||||
websiteUrl,
|
||||
scripts
|
||||
}: React.PropsWithChildren<IFooterActionsProps>) => {
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
|
||||
@@ -43,6 +48,12 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
<CustomActions
|
||||
filePath={filePath}
|
||||
contentType={contentType}
|
||||
scripts={scripts}
|
||||
showTrigger />
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonDelete)}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
|
||||
@@ -152,6 +152,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<ContentActions
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
locale={pageData.fmLocale}
|
||||
isDefaultLocale={pageData.fmDefaultLocale}
|
||||
translations={pageData.fmTranslations}
|
||||
@@ -210,7 +211,9 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
|
||||
<FooterActions
|
||||
filePath={pageData.fmFilePath}
|
||||
websiteUrl={settings?.websiteUrl} />
|
||||
contentType={pageData.fmContentType}
|
||||
websiteUrl={settings?.websiteUrl}
|
||||
scripts={settings?.scripts} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
@@ -232,6 +235,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<ContentActions
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={onOpenFile}
|
||||
listView
|
||||
|
||||
@@ -54,6 +54,7 @@ export const PinnedItem: React.FunctionComponent<IPinnedItemProps> = ({
|
||||
<ContentActions
|
||||
path={pageData.fmFilePath}
|
||||
relPath={pageData.fmRelFileWsPath}
|
||||
contentType={pageData.fmContentType}
|
||||
scripts={settings?.scripts}
|
||||
onOpen={openFile}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { openFile } from '../../utils/MessageHandlers';
|
||||
import { I18nConfig } from '../../../models';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ITranslationMenuProps {
|
||||
isDefaultLocale?: boolean;
|
||||
locale?: I18nConfig;
|
||||
translations?: {
|
||||
[locale: string]: {
|
||||
locale: I18nConfig;
|
||||
path: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const TranslationMenu: React.FunctionComponent<ITranslationMenuProps> = ({
|
||||
isDefaultLocale,
|
||||
locale,
|
||||
translations,
|
||||
}: React.PropsWithChildren<ITranslationMenuProps>) => {
|
||||
|
||||
const otherLocales = React.useMemo(() => {
|
||||
if (!translations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(translations).filter(([key]) => key !== locale?.locale);
|
||||
}, [translations]);
|
||||
|
||||
const crntLocale = React.useMemo(() => {
|
||||
if (!locale?.locale || !translations || !translations[locale.locale]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return translations[locale.locale];
|
||||
}, [translations, locale]);
|
||||
|
||||
|
||||
if (!locale || !translations || Object.keys(translations).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (otherLocales.length === 0 || !crntLocale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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={() => openFile(crntLocale.path)}>
|
||||
<span>{crntLocale.locale.title || crntLocale.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{
|
||||
otherLocales.map(([key, value]) => (
|
||||
<DropdownMenuItem
|
||||
key={key}
|
||||
onClick={() => openFile(value.path)}
|
||||
>
|
||||
<span>{value.locale.title || value.locale.locale}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
};
|
||||
@@ -62,7 +62,7 @@ export const RefreshDashboardData: React.FunctionComponent<IRefreshDashboardData
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`mr-2 text-[var(--vscode-foreground)] hover:text-[var(--vscode-textLink-foreground)]`}
|
||||
className={`mr-2 text-[var(--frontmatter-text)] hover:text-[var(--vscode-textLink-foreground)]`}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderRefreshDashboardLabel)}
|
||||
onClick={refresh}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { runCustomScript } from '../../utils';
|
||||
import { CommandLineIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandLineIcon as CommandLineIconSolid } from '@heroicons/react/24/solid';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ICustomActionsProps {
|
||||
filePath: string;
|
||||
scripts?: CustomScript[];
|
||||
showTrigger?: boolean;
|
||||
}
|
||||
|
||||
export const CustomActions: React.FunctionComponent<ICustomActionsProps> = ({
|
||||
filePath,
|
||||
scripts,
|
||||
showTrigger = false,
|
||||
}: React.PropsWithChildren<ICustomActionsProps>) => {
|
||||
|
||||
const customActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter((script) => script.type === ScriptType.MediaFile && !script.hidden)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.title}
|
||||
onClick={() => runCustomScript(script, filePath)}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
|
||||
if (showTrigger) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
title={l10n.t(LocalizationKey.commonOpenCustomActions)}
|
||||
className='px-2 text-[var(--frontmatter-secondary-text)] hover:text-[var(--frontmatter-button-hoverBackground)] focus-visible:outline-none'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonOpenCustomActions)}</span>
|
||||
<CommandLineIconSolid className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent>
|
||||
{customActions}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{customActions}</>;
|
||||
};
|
||||
@@ -10,7 +10,7 @@ export const DetailsItem: React.FunctionComponent<IDetailsItemProps> = ({ title,
|
||||
<>
|
||||
<div className="py-3 flex justify-between text-sm font-medium">
|
||||
<dt className={`text-[var(--vscode-editor-foreground)]`}>{title}</dt>
|
||||
<dd className={`text-right text-[var(--vscode-foreground)]`}>
|
||||
<dd className={`text-right text-[var(--frontmatter-text)]`}>
|
||||
{details}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +147,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
)}
|
||||
<div className="mt-4 flex items-start justify-between">
|
||||
<div>
|
||||
<h2 className={`text-lg font-medium text-[var(--vscode-foreground)]`}>
|
||||
<h2 className={`text-lg font-medium text-[var(--frontmatter-text)]`}>
|
||||
{media.filename}
|
||||
</h2>
|
||||
<p className={`text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
@@ -169,7 +169,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
|
||||
{!showForm && (
|
||||
<>
|
||||
<h3 className={`text-base flex items-center text-[var(--vscode-foreground)]`}>
|
||||
<h3 className={`text-base flex items-center text-[var(--frontmatter-text)]`}>
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaMetadataPanelFormMetadataTitle)}</span>
|
||||
<button onClick={onEdit}>
|
||||
<PencilSquareIcon className="w-4 h-4 ml-2" aria-hidden="true" />
|
||||
@@ -185,7 +185,7 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
|
||||
{!showForm && (
|
||||
<div>
|
||||
<h3 className={`text-base text-[var(--vscode-foreground)]`}>
|
||||
<h3 className={`text-base text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaMetadataPanelFormInformationTitle)}
|
||||
</h3>
|
||||
<dl className={`mt-2 border-t border-b divide-y border-[var(--frontmatter-border)] divide-[var(--frontmatter-border)]`}>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
<div className="relative mr-4">
|
||||
<FolderIcon className={`h-12 w-12`} />
|
||||
{isContentFolder && (
|
||||
<span className={`font-extrabold absolute bottom-3 left-1/2 transform -translate-x-1/2 text-[var(--vscode-foreground)]`}>
|
||||
<span className={`font-extrabold absolute bottom-3 left-1/2 transform -translate-x-1/2 text-[var(--frontmatter-text)]`}>
|
||||
C
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -5,15 +5,17 @@ import { LocalizationKey } from '../../../localization';
|
||||
import { ClipboardIcon, CodeBracketIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedItemActionAtom } from '../../state';
|
||||
import { MediaInfo, Snippet, ViewData } from '../../../models';
|
||||
import { CustomScript, MediaInfo, Snippet, ViewData } from '../../../models';
|
||||
import { copyToClipboard } from '../../utils';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IFooterActionsProps {
|
||||
media: MediaInfo;
|
||||
snippets: Snippet[];
|
||||
relPath?: string;
|
||||
viewData?: ViewData | undefined
|
||||
scripts?: CustomScript[];
|
||||
insertIntoArticle: () => void;
|
||||
insertSnippet: () => void;
|
||||
onDelete: () => void;
|
||||
@@ -24,6 +26,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
media,
|
||||
snippets,
|
||||
viewData,
|
||||
scripts,
|
||||
insertIntoArticle,
|
||||
insertSnippet,
|
||||
onDelete,
|
||||
@@ -34,6 +37,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)]`}>
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemView)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'view'
|
||||
@@ -44,6 +48,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemMenuItemEditMetadata)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => setSelectedItemAction({
|
||||
path: media.fsPath,
|
||||
action: 'edit'
|
||||
@@ -60,6 +65,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
? l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertField, viewData.fieldName)
|
||||
: l10n.t(LocalizationKey.dashboardMediaItemQuickActionInsertMarkdown)
|
||||
}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertIntoArticle}
|
||||
>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
@@ -68,6 +74,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
{viewData?.position && snippets.length > 0 && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={insertSnippet}>
|
||||
<CodeBracketIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
@@ -79,6 +86,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
relPath && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={() => copyToClipboard(parseWinPath(relPath) || '')}>
|
||||
<ClipboardIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
@@ -87,9 +95,14 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
<CustomActions
|
||||
filePath={media.fsPath}
|
||||
scripts={scripts}
|
||||
showTrigger />
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardMediaItemQuickActionDelete)}
|
||||
className={`hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
@@ -333,39 +333,39 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
processSnippet={processSnippet}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
|
||||
<p className={`text-sm font-bold pointer-events-none flex items-center break-all text-[var(--vscode-foreground)]}`}>
|
||||
<p className={`text-sm font-bold pointer-events-none flex items-center break-all text-[var(--frontmatter-text)]`}>
|
||||
{basename(parseWinPath(media.fsPath) || '')}
|
||||
</p>
|
||||
{!isImage && media.metadata.title && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonTitle)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.title}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.title}</span>
|
||||
</p>
|
||||
)}
|
||||
{media.metadata.caption && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonCaption)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.caption}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.caption}</span>
|
||||
</p>
|
||||
)}
|
||||
{!media.metadata.caption && media.metadata.alt && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-2`}>
|
||||
<b className={`mr-2 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonAlt)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>{media.metadata.alt}</span>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>{media.metadata.alt}</span>
|
||||
</p>
|
||||
)}
|
||||
{(media?.size || media?.dimensions) && (
|
||||
<p className={`mt-2 text-xs font-medium pointer-events-none flex flex-col items-start`}>
|
||||
<b className={`mr-1`}>
|
||||
<b className={`mr-1 text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonSize)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>
|
||||
<span className={`block mt-1 text-xs text-[var(--frontmatter-secondary-text)]`}>
|
||||
{mediaDetails}
|
||||
</span>
|
||||
</p>
|
||||
@@ -377,6 +377,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
relPath={relPath}
|
||||
snippets={mediaSnippets}
|
||||
viewData={viewData?.data}
|
||||
scripts={settings?.scripts}
|
||||
insertIntoArticle={insertIntoArticle}
|
||||
insertSnippet={insertSnippet}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { ClipboardIcon, CodeBracketIcon, CommandLineIcon, EllipsisHorizontalIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { CustomScript, MediaInfo, ScriptType, Snippet, ViewData } from '../../../models';
|
||||
import { ClipboardIcon, CodeBracketIcon, EllipsisHorizontalIcon, EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { CustomScript, MediaInfo, Snippet, ViewData } from '../../../models';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { copyToClipboard } from '../../utils';
|
||||
import { CustomActions } from './CustomActions';
|
||||
|
||||
export interface IItemMenuProps {
|
||||
media: MediaInfo;
|
||||
@@ -41,13 +42,6 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
copyToClipboard(parseWinPath(relPath) || '');
|
||||
}, [relPath]);
|
||||
|
||||
const runCustomScript = React.useCallback((script: CustomScript) => {
|
||||
messageHandler.send(DashboardMessage.runCustomScript, {
|
||||
script,
|
||||
path: media.fsPath
|
||||
});
|
||||
}, [media]);
|
||||
|
||||
const revealMedia = React.useCallback(() => {
|
||||
messageHandler.send(DashboardMessage.revealMedia, {
|
||||
file: media.fsPath,
|
||||
@@ -55,20 +49,6 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
});
|
||||
}, [selectedFolder]);
|
||||
|
||||
const customScriptActions = React.useMemo(() => {
|
||||
return (scripts || [])
|
||||
.filter((script) => script.type === ScriptType.MediaFile && !script.hidden)
|
||||
.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.title}
|
||||
onClick={() => runCustomScript(script)}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
));
|
||||
}, [scripts]);
|
||||
|
||||
return (
|
||||
<div className={`group/actions absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-1 -mr-2 -mt-1 group-hover/actions:bg-[var(--vscode-sideBar-background)] group-hover/actions:border-[var(--frontmatter-border)]`}>
|
||||
@@ -111,21 +91,19 @@ export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
|
||||
{customScriptActions}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuItem onClick={onCopyToClipboard}>
|
||||
<ClipboardIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{customScriptActions}
|
||||
</>
|
||||
<DropdownMenuItem onClick={onCopyToClipboard}>
|
||||
<ClipboardIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemQuickActionCopyPath)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
<CustomActions
|
||||
filePath={media.fsPath}
|
||||
scripts={scripts} />
|
||||
|
||||
<DropdownMenuItem onClick={revealMedia}>
|
||||
<EyeIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardMediaItemMenuItemRevealMedia)}</span>
|
||||
|
||||
@@ -177,7 +177,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
)}
|
||||
|
||||
{isDragActive && (
|
||||
<div className={`absolute top-0 left-0 w-full h-full flex flex-col justify-center items-center z-50 text-[var(--vscode-foreground)] bg-[var(--vscode-editor-background)] opacity-75`}>
|
||||
<div className={`absolute top-0 left-0 w-full h-full flex flex-col justify-center items-center z-50 text-[var(--frontmatter-text)] bg-[var(--vscode-editor-background)] opacity-75`}>
|
||||
<ArrowUpTrayIcon className={`h-32`} />
|
||||
<p className={`text-xl max-w-md text-center`}>
|
||||
{selectedFolder
|
||||
|
||||
@@ -18,7 +18,7 @@ export const QuickAction: React.FunctionComponent<IQuickActionProps> = ({
|
||||
type="button"
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
className={cn(`px-2 group inline-flex justify-center text-sm font-medium text-[var(--vscode-foreground)] hover:text-[var(--frontmatter-button-hoverBackground)]`, className)}
|
||||
className={cn(`px-2 group inline-flex justify-center text-sm font-medium text-[var(--frontmatter-text)] hover:text-[var(--frontmatter-button-hoverBackground)]`, className)}
|
||||
>
|
||||
{children}
|
||||
<span className="sr-only">{title}</span>
|
||||
|
||||
@@ -164,7 +164,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<>
|
||||
<li className={`group relative overflow-hidden shadow-md hover:shadow-xl dark:shadow-none border p-4 space-y-2 rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] border-[var(--frontmatter-border)]`}>
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<CodeBracketIcon className={`w-64 h-64 opacity-5 text-[var(--vscode-foreground)]`} />
|
||||
<CodeBracketIcon className={`w-64 h-64 opacity-5 text-[var(--frontmatter-text)]`} />
|
||||
</div>
|
||||
|
||||
<h2
|
||||
@@ -244,7 +244,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
</div>
|
||||
</FeatureFlag>
|
||||
|
||||
<p className={`text-xs text-[var(--vscode-foreground)]`}>{snippet.description}</p>
|
||||
<p className={`text-xs text-[var(--frontmatter-text)]`}>{snippet.description}</p>
|
||||
</li>
|
||||
|
||||
{showInsertDialog && (
|
||||
|
||||
@@ -110,7 +110,7 @@ export const NewForm: React.FunctionComponent<INewFormProps> = ({
|
||||
>
|
||||
{l10n.t(LocalizationKey.dashboardSnippetsViewNewFormSnippetInputIsMediaSnippetCheckboxLabel)}
|
||||
</label>
|
||||
<p id="isMediaSnippet-description" className={`text-[var(--vscode-foreground)] text-left`}>
|
||||
<p id="isMediaSnippet-description" className={`text-[var(--frontmatter-text)] text-left`}>
|
||||
{l10n.t(LocalizationKey.dashboardSnippetsViewNewFormSnippetInputIsMediaSnippetCheckboxDescription)}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../DashboardMessage';
|
||||
import { GeneralCommands } from '../../constants';
|
||||
import { CustomScript } from '../../models';
|
||||
|
||||
export const openFile = (filePath?: string) => {
|
||||
if (!filePath) {
|
||||
@@ -36,3 +37,11 @@ export const copyToClipboard = (value: string) => {
|
||||
|
||||
messageHandler.send(DashboardMessage.copyToClipboard, value);
|
||||
};
|
||||
|
||||
export const runCustomScript = (script: CustomScript, path: string) => {
|
||||
if (!script) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.send(DashboardMessage.runCustomScript, { script, path });
|
||||
};
|
||||
|
||||
@@ -59,7 +59,6 @@ export const darkenColor = (color: string | undefined, percentage: number) => {
|
||||
// Extract the alpha value
|
||||
const alphaMatch = color.match(/[\d\.]+(?=\))/);
|
||||
const alpha = alphaMatch ? Number(alphaMatch[0]) : 1;
|
||||
console.log('alpha:', alpha);
|
||||
|
||||
return `rgba(${darkenedR}, ${darkenedG}, ${darkenedB}, ${alpha})`;
|
||||
}
|
||||
|
||||
@@ -151,6 +151,10 @@ export enum LocalizationKey {
|
||||
* Open: {0}
|
||||
*/
|
||||
commonOpenWithValue = 'common.openWithValue',
|
||||
/**
|
||||
* Open custom actions
|
||||
*/
|
||||
commonOpenCustomActions = 'common.openCustomActions',
|
||||
/**
|
||||
* View
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user