From 0b7f58d0abe83a30cef234ba91c79822a4a65788 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 1 Apr 2024 16:39:05 +0200 Subject: [PATCH] #785 - Added custom script actions to the bottom of the cards --- l10n/bundle.l10n.json | 1 + .../components/Common/Spinner.tsx | 2 +- .../components/Contents/ContentActions.tsx | 91 ++++--------------- .../components/Contents/CustomActions.tsx | 89 ++++++++++++++++++ .../components/Contents/FooterActions.tsx | 15 ++- .../components/Contents/Item.tsx | 6 +- .../components/Contents/PinnedItem.tsx | 1 + .../components/Contents/TranslationMenu.tsx | 80 ++++++++++++++++ .../Header/RefreshDashboardData.tsx | 2 +- .../components/Media/CustomActions.tsx | 55 +++++++++++ .../components/Media/DetailsItem.tsx | 2 +- .../components/Media/DetailsSlideOver.tsx | 6 +- .../components/Media/FolderItem.tsx | 2 +- .../components/Media/FooterActions.tsx | 17 +++- .../components/Media/Item.tsx | 19 ++-- .../components/Media/ItemMenu.tsx | 44 +++------ .../components/Media/Media.tsx | 2 +- .../components/Menu/QuickAction.tsx | 2 +- .../components/SnippetsView/Item.tsx | 4 +- .../components/SnippetsView/NewForm.tsx | 2 +- src/dashboardWebView/utils/MessageHandlers.ts | 9 ++ src/dashboardWebView/utils/darkenColor.ts | 1 - src/localization/localization.enum.ts | 4 + 23 files changed, 322 insertions(+), 134 deletions(-) create mode 100644 src/dashboardWebView/components/Contents/CustomActions.tsx create mode 100644 src/dashboardWebView/components/Contents/TranslationMenu.tsx create mode 100644 src/dashboardWebView/components/Media/CustomActions.tsx diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 279c3ad5..c7410152 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -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", diff --git a/src/dashboardWebView/components/Common/Spinner.tsx b/src/dashboardWebView/components/Common/Spinner.tsx index 6659732b..ab24795d 100644 --- a/src/dashboardWebView/components/Common/Spinner.tsx +++ b/src/dashboardWebView/components/Common/Spinner.tsx @@ -20,7 +20,7 @@ export const Spinner: React.FunctionComponent = ( { type === 'initPages' && ( -
+
{l10n.t(LocalizationKey.loadingInitPages)}
diff --git a/src/dashboardWebView/components/Contents/ContentActions.tsx b/src/dashboardWebView/components/Contents/ContentActions.tsx index 21c903cd..e22d8715 100644 --- a/src/dashboardWebView/components/Contents/ContentActions.tsx +++ b/src/dashboardWebView/components/Contents/ContentActions.tsx @@ -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 = ({ path, relPath, + contentType, scripts, onOpen, listView, @@ -78,14 +82,6 @@ export const ContentActions: React.FunctionComponent = ({ }) }, [path]); - const runCustomScript = React.useCallback( - (e: React.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 = ({ 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) => ( - runCustomScript(e, script)}> - - {script.title} - - )); - }, [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 ( - - - - {l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsMenu)} - - - - - openFile(crntLocale.path)}> - {crntLocale.locale.title || crntLocale.locale.locale} - - - - - { - otherLocales.map(([key, value]) => ( - openFile(value.path)} - > - {value.locale.title || value.locale.locale} - - )) - } - - - - ); - }, [translations, locale, isDefaultLocale]); - return ( <>
= ({ ) } - {translationsMenu} + - {customScriptActions} + diff --git a/src/dashboardWebView/components/Contents/CustomActions.tsx b/src/dashboardWebView/components/Contents/CustomActions.tsx new file mode 100644 index 00000000..3a1c9e22 --- /dev/null +++ b/src/dashboardWebView/components/Contents/CustomActions.tsx @@ -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 = ({ + filePath, + contentType, + scripts, + showTrigger = false, +}: React.PropsWithChildren) => { + + const onRunCustomScript = React.useCallback( + (e: React.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) => ( + onRunCustomScript(e, script)}> + + {script.title} + + )) + ); + }, [customScripts, onRunCustomScript]); + + if (!customActions || customActions.length === 0) { + return null; + } + + if (showTrigger) { + return ( + + + {l10n.t(LocalizationKey.commonOpenCustomActions)} + + + + {customActions} + + + ); + } + + return <>{customActions}; +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Contents/FooterActions.tsx b/src/dashboardWebView/components/Contents/FooterActions.tsx index 2f16c5de..cf75a6ab 100644 --- a/src/dashboardWebView/components/Contents/FooterActions.tsx +++ b/src/dashboardWebView/components/Contents/FooterActions.tsx @@ -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 = ({ filePath, - websiteUrl + contentType, + websiteUrl, + scripts }: React.PropsWithChildren) => { const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); @@ -43,6 +48,12 @@ export const FooterActions: React.FunctionComponent = ({ ) } + + = ({ = ({ + contentType={pageData.fmContentType} + websiteUrl={settings?.websiteUrl} + scripts={settings?.scripts} />
); @@ -232,6 +235,7 @@ export const Item: React.FunctionComponent = ({ = ({ diff --git a/src/dashboardWebView/components/Contents/TranslationMenu.tsx b/src/dashboardWebView/components/Contents/TranslationMenu.tsx new file mode 100644 index 00000000..476763ee --- /dev/null +++ b/src/dashboardWebView/components/Contents/TranslationMenu.tsx @@ -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 = ({ + isDefaultLocale, + locale, + translations, +}: React.PropsWithChildren) => { + + 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 ( + + + + {l10n.t(LocalizationKey.dashboardContentsContentActionsTranslationsMenu)} + + + + + openFile(crntLocale.path)}> + {crntLocale.locale.title || crntLocale.locale.locale} + + + + + { + otherLocales.map(([key, value]) => ( + openFile(value.path)} + > + {value.locale.title || value.locale.locale} + + )) + } + + + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/RefreshDashboardData.tsx b/src/dashboardWebView/components/Header/RefreshDashboardData.tsx index 5f03a4f4..75c6a628 100644 --- a/src/dashboardWebView/components/Header/RefreshDashboardData.tsx +++ b/src/dashboardWebView/components/Header/RefreshDashboardData.tsx @@ -62,7 +62,7 @@ export const RefreshDashboardData: React.FunctionComponent diff --git a/src/dashboardWebView/components/Media/CustomActions.tsx b/src/dashboardWebView/components/Media/CustomActions.tsx new file mode 100644 index 00000000..7faced6d --- /dev/null +++ b/src/dashboardWebView/components/Media/CustomActions.tsx @@ -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 = ({ + filePath, + scripts, + showTrigger = false, +}: React.PropsWithChildren) => { + + const customActions = React.useMemo(() => { + return (scripts || []) + .filter((script) => script.type === ScriptType.MediaFile && !script.hidden) + .map((script) => ( + runCustomScript(script, filePath)} + > + + {script.title} + + )); + }, [scripts]); + + + if (showTrigger) { + return ( + + + {l10n.t(LocalizationKey.commonOpenCustomActions)} + + + + {customActions} + + + ); + } + + return <>{customActions}; +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Media/DetailsItem.tsx b/src/dashboardWebView/components/Media/DetailsItem.tsx index e3162d43..4f22e01d 100644 --- a/src/dashboardWebView/components/Media/DetailsItem.tsx +++ b/src/dashboardWebView/components/Media/DetailsItem.tsx @@ -10,7 +10,7 @@ export const DetailsItem: React.FunctionComponent = ({ title, <>
{title}
-
+
{details}
diff --git a/src/dashboardWebView/components/Media/DetailsSlideOver.tsx b/src/dashboardWebView/components/Media/DetailsSlideOver.tsx index 10b25684..6da55038 100644 --- a/src/dashboardWebView/components/Media/DetailsSlideOver.tsx +++ b/src/dashboardWebView/components/Media/DetailsSlideOver.tsx @@ -147,7 +147,7 @@ export const DetailsSlideOver: React.FunctionComponent = )}
-

+

{media.filename}

@@ -169,7 +169,7 @@ export const DetailsSlideOver: React.FunctionComponent = {!showForm && ( <> -

+

{l10n.t(LocalizationKey.dashboardMediaMetadataPanelFormMetadataTitle)}