#785 - Added custom script actions to the bottom of the cards

This commit is contained in:
Elio Struyf
2024-04-01 16:39:05 +02:00
parent c859874470
commit 0b7f58d0ab
23 changed files with 322 additions and 134 deletions
+1
View File
@@ -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>
+10 -9
View File
@@ -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})`;
}
+4
View File
@@ -151,6 +151,10 @@ export enum LocalizationKey {
* Open: {0}
*/
commonOpenWithValue = 'common.openWithValue',
/**
* Open custom actions
*/
commonOpenCustomActions = 'common.openCustomActions',
/**
* View
*/