From 9c6845ed8afc98fd9c6332ffa22a55f901cea72b Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 29 Feb 2024 08:38:31 +0100 Subject: [PATCH 01/44] #768 - Update data view link --- src/constants/Links.ts | 13 +++++++++++++ .../components/DataView/DataView.tsx | 4 ++-- .../components/SnippetsView/NewForm.tsx | 4 ++-- .../components/SnippetsView/Snippets.tsx | 4 ++-- src/dashboardWebView/index.tsx | 3 ++- src/helpers/Telemetry.ts | 11 +++++++---- src/services/SponsorAI.ts | 8 ++++++-- 7 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/constants/Links.ts b/src/constants/Links.ts index 1bea4ebe..7c839c31 100644 --- a/src/constants/Links.ts +++ b/src/constants/Links.ts @@ -10,3 +10,16 @@ export const SENTRY_LINK = 'https://1ac45704bbe74264a7b4674bdc2abf48@o1022172.ingest.sentry.io/5988293'; export const DOCS_SUBMODULES = 'https://frontmatter.codes/docs/git-integration#git-submodules'; + +export const WEBSITE_LINKS = { + root: 'https://frontmatter.codes', + api: { + metrics: 'https://frontmatter.codes/api/metrics', + ai: 'https://frontmatter.codes/api/ai' + }, + docs: { + dataDashboard: 'https://frontmatter.codes/docs/dashboard/datafiles-view', + snippets: `https://frontmatter.codes/docs/snippets`, + snippetsPlaceholders: `https://frontmatter.codes/docs/snippets#placeholders` + } +}; diff --git a/src/dashboardWebView/components/DataView/DataView.tsx b/src/dashboardWebView/components/DataView/DataView.tsx index 5e73bd5d..b12a75b5 100644 --- a/src/dashboardWebView/components/DataView/DataView.tsx +++ b/src/dashboardWebView/components/DataView/DataView.tsx @@ -17,7 +17,7 @@ import { Container } from './SortableContainer'; import { SortableItem } from './SortableItem'; import { ChevronRightIcon, CircleStackIcon } from '@heroicons/react/24/outline'; import { DataType } from '../../../models/DataType'; -import { TelemetryEvent } from '../../../constants'; +import { TelemetryEvent, WEBSITE_LINKS } from '../../../constants'; import { NavigationItem } from '../Layout'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; @@ -265,7 +265,7 @@ export const DataView: React.FunctionComponent = (

{l10n.t(LocalizationKey.dashboardDataViewDataViewGetStartedLink)} diff --git a/src/dashboardWebView/components/SnippetsView/NewForm.tsx b/src/dashboardWebView/components/SnippetsView/NewForm.tsx index 479ec2ff..88302b8d 100644 --- a/src/dashboardWebView/components/SnippetsView/NewForm.tsx +++ b/src/dashboardWebView/components/SnippetsView/NewForm.tsx @@ -1,6 +1,6 @@ import { Messenger } from '@estruyf/vscode/dist/client'; import * as React from 'react'; -import { GeneralCommands } from '../../../constants'; +import { GeneralCommands, WEBSITE_LINKS } from '../../../constants'; import { SnippetInput } from './SnippetInput'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; @@ -30,7 +30,7 @@ export const NewForm: React.FunctionComponent = ({ const openLink = () => { Messenger.send( GeneralCommands.toVSCode.openLink, - 'https://frontmatter.codes/docs/snippets#placeholders' + WEBSITE_LINKS.docs.snippetsPlaceholders ); }; diff --git a/src/dashboardWebView/components/SnippetsView/Snippets.tsx b/src/dashboardWebView/components/SnippetsView/Snippets.tsx index 5fdf2f9e..a545dd82 100644 --- a/src/dashboardWebView/components/SnippetsView/Snippets.tsx +++ b/src/dashboardWebView/components/SnippetsView/Snippets.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { FeatureFlag } from '../../../components/features/FeatureFlag'; -import { FEATURE_FLAG } from '../../../constants'; +import { FEATURE_FLAG, WEBSITE_LINKS } from '../../../constants'; import { TelemetryEvent } from '../../../constants/TelemetryEvent'; import { SnippetParser } from '../../../helpers/SnippetParser'; import { DashboardMessage } from '../../DashboardMessage'; @@ -146,7 +146,7 @@ export const Snippets: React.FunctionComponent = (

{l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsReadMore)} diff --git a/src/dashboardWebView/index.tsx b/src/dashboardWebView/index.tsx index 4628c94c..a8f25f6d 100644 --- a/src/dashboardWebView/index.tsx +++ b/src/dashboardWebView/index.tsx @@ -12,6 +12,7 @@ import { Chatbot } from './components/Chatbot/Chatbot'; import { updateCssVariables } from './utils'; import { I10nProvider } from './providers/I10nProvider'; import { SentryInit } from '../utils/sentryInit'; +import { WEBSITE_LINKS } from '../constants'; declare const acquireVsCodeApi: () => { getState: () => T; @@ -91,7 +92,7 @@ if (elm) { render( diff --git a/src/helpers/Telemetry.ts b/src/helpers/Telemetry.ts index 12dc9fda..f40dfd24 100644 --- a/src/helpers/Telemetry.ts +++ b/src/helpers/Telemetry.ts @@ -1,8 +1,11 @@ import { workspace } from 'vscode'; import { Extension, Settings } from '.'; -import { EXTENSION_BETA_ID, EXTENSION_ID, SETTING_TELEMETRY_DISABLE } from '../constants'; - -const METRICS_URL = 'https://frontmatter.codes/api/metrics'; +import { + EXTENSION_BETA_ID, + EXTENSION_ID, + SETTING_TELEMETRY_DISABLE, + WEBSITE_LINKS +} from '../constants'; export class Telemetry { private static instance: Telemetry; @@ -77,7 +80,7 @@ export class Telemetry { // Set a new timeout instance.timeout = setTimeout(async () => { - await fetch(METRICS_URL, { + await fetch(WEBSITE_LINKS.api.metrics, { method: 'POST', headers: { 'Content-Type': 'application/json' diff --git a/src/services/SponsorAI.ts b/src/services/SponsorAI.ts index 0820be49..1bf7a9b2 100644 --- a/src/services/SponsorAI.ts +++ b/src/services/SponsorAI.ts @@ -1,11 +1,15 @@ -import { SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from '../constants'; +import { + SETTING_SEO_DESCRIPTION_LENGTH, + SETTING_SEO_TITLE_LENGTH, + WEBSITE_LINKS +} from '../constants'; import { Logger, Notifications, Settings, TaxonomyHelper } from '../helpers'; import { TagType } from '../panelWebView/TagType'; import { TaxonomyType } from '../models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../localization'; -const AI_URL = 'https://frontmatter.codes/api/ai'; +const AI_URL = WEBSITE_LINKS.api.ai; // const AI_URL = 'http://localhost:3000/api/ai'; export class SponsorAi { From 78cac94dd6a07a7e9b081a62c7ece03d21d8ca30 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 29 Feb 2024 08:38:46 +0100 Subject: [PATCH 02/44] 10.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7a7b841..22cdabfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-front-matter-beta", - "version": "10.0.1", + "version": "10.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-front-matter-beta", - "version": "10.0.1", + "version": "10.1.0", "license": "MIT", "dependencies": { "@radix-ui/react-dropdown-menu": "^2.0.6" diff --git a/package.json b/package.json index 9083c192..538d2952 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Front Matter CMS", "description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...", "icon": "assets/frontmatter-teal-128x128.png", - "version": "10.0.1", + "version": "10.1.0", "preview": false, "publisher": "eliostruyf", "galleryBanner": { From 3b26944a4a424b08f3c60664b46db7bfe6dd9a3d Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 29 Feb 2024 08:40:01 +0100 Subject: [PATCH 03/44] Update changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8aa616b..a1a739d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## [10.1.0] - 2024-xx-xx + +### ✨ New features + +### 🎨 Enhancements + +### ⚡️ Optimizations + +### 🐞 Fixes + +- [#768](https://github.com/estruyf/vscode-front-matter/issues/768): Update broken link to the documentation + ## [10.0.1] - 2024-02-28 ### 🐞 Fixes From 2a8d7b0ebe65397ef0df32ba2db2333ae66c4d20 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Fri, 1 Mar 2024 08:40:05 +0100 Subject: [PATCH 04/44] #671 - Implement checkbox on media card --- src/dashboardWebView/components/Media/Item.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/dashboardWebView/components/Media/Item.tsx b/src/dashboardWebView/components/Media/Item.tsx index c0dcb921..38a10b7c 100644 --- a/src/dashboardWebView/components/Media/Item.tsx +++ b/src/dashboardWebView/components/Media/Item.tsx @@ -30,6 +30,7 @@ import { LocalizationKey } from '../../../localization'; import { ItemMenu } from './ItemMenu'; import { getRelPath } from '../../utils'; import { Snippet } from '../../../models'; +import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react'; export interface IItemProps { media: MediaInfo; @@ -349,6 +350,14 @@ export const Item: React.FunctionComponent = ({ > {renderMedia} + +

+ { + e.stopPropagation(); + }} /> +
+ {hasViewData && (
Date: Mon, 11 Mar 2024 16:52:14 +0100 Subject: [PATCH 05/44] Multi-select actions --- l10n/bundle.l10n.json | 3 + package-lock.json | 8 +- package.json | 2 +- .../components/Header/ActionsBar.tsx | 159 ++++++++++++++++++ .../components/Header/ActionsBarItem.tsx | 26 +++ .../components/Header/Breadcrumb.tsx | 19 ++- .../components/Header/Header.tsx | 10 +- .../components/Media/FolderCreation.tsx | 6 +- .../components/Media/FolderItem.tsx | 11 +- .../components/Media/Item.tsx | 49 ++++-- src/dashboardWebView/hooks/useMedia.tsx | 6 +- src/dashboardWebView/hooks/useMediaFolder.tsx | 17 ++ .../state/atom/MultiSelectedItemsAtom.ts | 6 + .../state/atom/SelectedItemActionAtom.ts | 12 ++ src/dashboardWebView/state/atom/index.ts | 2 + src/localization/localization.enum.ts | 8 + 16 files changed, 302 insertions(+), 42 deletions(-) create mode 100644 src/dashboardWebView/components/Header/ActionsBar.tsx create mode 100644 src/dashboardWebView/components/Header/ActionsBarItem.tsx create mode 100644 src/dashboardWebView/hooks/useMediaFolder.tsx create mode 100644 src/dashboardWebView/state/atom/MultiSelectedItemsAtom.ts create mode 100644 src/dashboardWebView/state/atom/SelectedItemActionAtom.ts diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 523f8d44..62638a3d 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -229,6 +229,9 @@ "dashboard.media.folderCreation.hexo.create": "Create post asset folder", "dashboard.media.folderCreation.folder.create": "Create new folder", + "dashboard.media.folderItem.contentDirectory": "Content directory", + "dashboard.media.folderItem.publicDirectory": "Public directory", + "dashboard.media.item.buttom.insert.image": "Insert image", "dashboard.media.item.buttom.insert.snippet": "Insert snippet", diff --git a/package-lock.json b/package-lock.json index 22cdabfe..f2b2f01d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,7 +85,7 @@ "react-quill": "^2.0.0", "react-router-dom": "^6.8.0", "react-sortable-hoc": "^2.0.0", - "recoil": "^0.4.1", + "recoil": "^0.7.7", "remark-gfm": "^3.0.1", "rimraf": "^3.0.2", "semver": "^7.3.8", @@ -10193,9 +10193,9 @@ } }, "node_modules/recoil": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.4.1.tgz", - "integrity": "sha512-vp6KPwlHOjJ4bJofmdDchmgI9ilMTCoUisK8/WYLl8dThH7e7KmtZttiLgvDb2Em99dUfTEsk8vT8L1nUMgqXQ==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", + "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", "dev": true, "dependencies": { "hamt_plus": "1.0.2" diff --git a/package.json b/package.json index 538d2952..cf314eba 100644 --- a/package.json +++ b/package.json @@ -2816,7 +2816,7 @@ "react-quill": "^2.0.0", "react-router-dom": "^6.8.0", "react-sortable-hoc": "^2.0.0", - "recoil": "^0.4.1", + "recoil": "^0.7.7", "remark-gfm": "^3.0.1", "rimraf": "^3.0.2", "semver": "^7.3.8", diff --git a/src/dashboardWebView/components/Header/ActionsBar.tsx b/src/dashboardWebView/components/Header/ActionsBar.tsx new file mode 100644 index 00000000..f309d967 --- /dev/null +++ b/src/dashboardWebView/components/Header/ActionsBar.tsx @@ -0,0 +1,159 @@ +import * as React from 'react'; +import { NavigationType } from '../../models'; +import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state'; +import { ActionsBarItem } from './ActionsBarItem'; +import * as l10n from '@vscode/l10n'; +import { LocalizationKey } from '../../../localization'; +import { Alert } from '../Modals/Alert'; +import { messageHandler } from '@estruyf/vscode/dist/client'; +import { DashboardMessage } from '../../DashboardMessage'; +import { CustomScript, ScriptType } from '../../../models'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown'; + +export interface IActionsBarProps { + view: NavigationType; +} + +export const ActionsBar: React.FunctionComponent = ({ + view +}: React.PropsWithChildren) => { + const [selectedFiles, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom); + const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); + const [showAlert, setShowAlert] = React.useState(false); + const selectedFolder = useRecoilValue(SelectedMediaFolderSelector); + const settings = useRecoilValue(SettingsSelector); + + const onDeleteConfirm = React.useCallback(() => { + for (const file of selectedFiles) { + if (file) { + if (view === NavigationType.Contents) { + messageHandler.send(DashboardMessage.deleteFile, file); + } else if (view === NavigationType.Media) { + messageHandler.send(DashboardMessage.deleteMedia, { + file: file, + folder: selectedFolder + }); + } + } + } + setSelectedFiles([]); + setShowAlert(false); + }, [selectedFiles]); + + const runCustomScript = React.useCallback((script: CustomScript) => { + for (const file of selectedFiles) { + messageHandler.send(DashboardMessage.runCustomScript, { + script, + path: file + }); + } + + setSelectedFiles([]); + }, [selectedFiles]); + + const customScriptActions = React.useMemo(() => { + if (!settings?.scripts) { + return null; + } + + const { scripts } = settings; + if (view === NavigationType.Media) { + const mediaScripts = (scripts || []) + .filter((script) => script.type === ScriptType.MediaFile && !script.hidden); + + if (mediaScripts.length > 0) { + return ( + + + + Scripts + + + + + { + mediaScripts.map((script) => ( + runCustomScript(script)} + > + + {script.title} + + )) + } + + + ) + } + } + + return null; + }, [view, settings?.scripts, selectedFiles]); + + return ( + <> +
+ { + view === NavigationType.Media && ( +
+ 1} + onClick={() => setSelectedItemAction({ + path: selectedFiles[0], + action: 'edit' + })} + > + + + {customScriptActions} + + setShowAlert(true)} + > + +
+ ) + } + + { + selectedFiles.length > 0 && ( + + ) + } +
+ + {showAlert && ( + setShowAlert(false)} + trigger={onDeleteConfirm} + /> + )} + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/ActionsBarItem.tsx b/src/dashboardWebView/components/Header/ActionsBarItem.tsx new file mode 100644 index 00000000..91a34e43 --- /dev/null +++ b/src/dashboardWebView/components/Header/ActionsBarItem.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { cn } from '../../../utils/cn'; + +export interface IActionsBarItemProps { + className?: string; + disabled?: boolean; + onClick?: () => void; +} + +export const ActionsBarItem: React.FunctionComponent = ({ + children, + className, + disabled, + onClick +}: React.PropsWithChildren) => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/Breadcrumb.tsx b/src/dashboardWebView/components/Header/Breadcrumb.tsx index e3eb9540..e3ff65c6 100644 --- a/src/dashboardWebView/components/Header/Breadcrumb.tsx +++ b/src/dashboardWebView/components/Header/Breadcrumb.tsx @@ -4,24 +4,25 @@ import * as React from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { HOME_PAGE_NAVIGATION_ID } from '../../../constants'; import { parseWinPath } from '../../../helpers/parseWinPath'; -import { SearchAtom, SelectedMediaFolderAtom, SettingsAtom } from '../../state'; +import { SearchAtom, SettingsAtom } from '../../state'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import useMediaFolder from '../../hooks/useMediaFolder'; export interface IBreadcrumbProps { } export const Breadcrumb: React.FunctionComponent = ( _: React.PropsWithChildren ) => { - const [selectedFolder, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom); + const { selectedFolder, updateFolder } = useMediaFolder(); const [, setSearchValue] = useRecoilState(SearchAtom); const [folders, setFolders] = React.useState([]); const settings = useRecoilValue(SettingsAtom); - const updateFolder = (folder: string) => { + const updateMediaFolder = React.useCallback((folder: string) => { setSearchValue(''); - setSelectedFolder(folder); - }; + updateFolder(folder); + }, [updateFolder, setSearchValue]); React.useEffect(() => { if (!settings) { @@ -79,11 +80,11 @@ export const Breadcrumb: React.FunctionComponent = ( }, [selectedFolder, settings]); return ( -
    +
    1. diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index bf1a45e2..984af354 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -6,7 +6,7 @@ import { DashboardMessage } from '../../DashboardMessage'; import { Grouping } from '.'; import { ViewSwitch } from './ViewSwitch'; import { useRecoilValue, useResetRecoilState } from 'recoil'; -import { GroupingSelector, SortingAtom } from '../../state'; +import { GroupingSelector, MultiSelectedItemsAtom, SortingAtom } from '../../state'; import { Messenger } from '@estruyf/vscode/dist/client'; import { ClearFilters } from './ClearFilters'; import { MediaHeaderTop } from '../Media/MediaHeaderTop'; @@ -18,8 +18,7 @@ import { ArrowTopRightOnSquareIcon, BoltIcon, PlusIcon } from '@heroicons/react/ import { HeartIcon } from '@heroicons/react/24/solid'; import { useLocation, useNavigate } from 'react-router-dom'; import { routePaths } from '../..'; -import { useEffect, useMemo } from 'react'; -import { SyncButton } from './SyncButton'; +import { useMemo } from 'react'; import { Pagination } from './Pagination'; import { GroupOption } from '../../constants/GroupOption'; import usePagination from '../../hooks/usePagination'; @@ -32,6 +31,7 @@ import { SettingsLink } from '../SettingsView/SettingsLink'; import { Link } from '../Common/Link'; import { SPONSOR_LINK } from '../../../constants'; import { Filters } from './Filters'; +import { ActionsBar } from './ActionsBar'; export interface IHeaderProps { header?: React.ReactNode; @@ -51,6 +51,7 @@ export const Header: React.FunctionComponent = ({ }: React.PropsWithChildren) => { const grouping = useRecoilValue(GroupingSelector); const resetSorting = useResetRecoilState(SortingAtom); + const resetSelectedItems = useResetRecoilState(MultiSelectedItemsAtom); const location = useLocation(); const navigate = useNavigate(); const { pageSetNr } = usePagination(settings?.dashboardState.contents.pagination); @@ -70,6 +71,7 @@ export const Header: React.FunctionComponent = ({ const updateView = (view: NavigationType) => { navigate(routePaths[view]); resetSorting(); + resetSelectedItems(); }; const runBulkScript = (script: CustomScript) => { @@ -216,6 +218,8 @@ export const Header: React.FunctionComponent = ({ + + )} diff --git a/src/dashboardWebView/components/Media/FolderCreation.tsx b/src/dashboardWebView/components/Media/FolderCreation.tsx index 410eae5e..007b3b68 100644 --- a/src/dashboardWebView/components/Media/FolderCreation.tsx +++ b/src/dashboardWebView/components/Media/FolderCreation.tsx @@ -5,7 +5,6 @@ import { DashboardMessage } from '../../DashboardMessage'; import { AllContentFoldersAtom, AllStaticFoldersAtom, - SelectedMediaFolderAtom, SettingsSelector, ViewDataSelector } from '../../state'; @@ -18,13 +17,14 @@ import { extname } from 'path'; import { parseWinPath } from '../../../helpers/parseWinPath'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import useMediaFolder from '../../hooks/useMediaFolder'; export interface IFolderCreationProps { } export const FolderCreation: React.FunctionComponent = ( - props: React.PropsWithChildren + _: React.PropsWithChildren ) => { - const selectedFolder = useRecoilValue(SelectedMediaFolderAtom); + const { selectedFolder } = useMediaFolder(); const settings = useRecoilValue(SettingsSelector); const allStaticFolders = useRecoilValue(AllStaticFoldersAtom); const allContentFolders = useRecoilValue(AllContentFoldersAtom); diff --git a/src/dashboardWebView/components/Media/FolderItem.tsx b/src/dashboardWebView/components/Media/FolderItem.tsx index 00e6b158..7216f9af 100644 --- a/src/dashboardWebView/components/Media/FolderItem.tsx +++ b/src/dashboardWebView/components/Media/FolderItem.tsx @@ -1,8 +1,9 @@ import { FolderIcon } from '@heroicons/react/24/solid'; import { basename, join } from 'path'; import * as React from 'react'; -import { useRecoilState } from 'recoil'; -import { SelectedMediaFolderAtom } from '../../state'; +import * as l10n from '@vscode/l10n'; +import { LocalizationKey } from '../../../localization'; +import useMediaFolder from '../../hooks/useMediaFolder'; export interface IFolderItemProps { folder: string; @@ -15,7 +16,7 @@ export const FolderItem: React.FunctionComponent = ({ wsFolder, staticFolder }: React.PropsWithChildren) => { - const [, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom); + const { updateFolder } = useMediaFolder(); const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder; @@ -29,9 +30,9 @@ export const FolderItem: React.FunctionComponent = ({ className={`group relative hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-editor-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)]`} > +
      + ) => { + e.stopPropagation(); + onMultiSelect(); + }} + checked={selectedFiles.includes(pageData.fmFilePath)} /> +
      +
      { (statusPlaceholder || datePlaceholder) && ( diff --git a/src/dashboardWebView/components/Header/ActionsBar.tsx b/src/dashboardWebView/components/Header/ActionsBar.tsx index f309d967..e0c5f38e 100644 --- a/src/dashboardWebView/components/Header/ActionsBar.tsx +++ b/src/dashboardWebView/components/Header/ActionsBar.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { NavigationType } from '../../models'; -import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline'; +import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon } from '@heroicons/react/24/outline'; import { useRecoilState, useRecoilValue } from 'recoil'; import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state'; import { ActionsBarItem } from './ActionsBarItem'; @@ -25,6 +25,12 @@ export const ActionsBar: React.FunctionComponent = ({ const selectedFolder = useRecoilValue(SelectedMediaFolderSelector); const settings = useRecoilValue(SettingsSelector); + const viewFile = React.useCallback(() => { + if (selectedFiles.length === 1 && view === NavigationType.Contents) { + messageHandler.send(DashboardMessage.openFile, selectedFiles[0]); + } + }, [selectedFiles]); + const onDeleteConfirm = React.useCallback(() => { for (const file of selectedFiles) { if (file) { @@ -102,33 +108,49 @@ export const ActionsBar: React.FunctionComponent = ({ className={`w-full flex items-center justify-between py-2 px-4 border-b bg-[var(--vscode-sideBar-background)] text-[var(--vscode-sideBar-foreground)] border-[var(--frontmatter-border)]`} aria-label="Item actions" > - { - view === NavigationType.Media && ( -
      - 1} - onClick={() => setSelectedItemAction({ - path: selectedFiles[0], - action: 'edit' - })} - > - - - {customScriptActions} - - setShowAlert(true)} - > - -
      - ) - } +
      + { + view === NavigationType.Contents && ( + <> + 1} + onClick={viewFile} + > + + + ) + } + + { + view === NavigationType.Media && ( + <> + 1} + onClick={() => setSelectedItemAction({ + path: selectedFiles[0], + action: 'edit' + })} + > + + + {customScriptActions} + + setShowAlert(true)} + > + + + ) + } +
      { selectedFiles.length > 0 && ( diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index 984af354..ec2ecdcc 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -162,12 +162,8 @@ export const Header: React.FunctionComponent = ({ {location.pathname === routePaths.contents && ( <> -
      - - -
      - {/* */} - +
      +
      = ({ disabled={!settings?.initialized} />
      + +
      @@ -188,7 +186,7 @@ export const Header: React.FunctionComponent = ({
      @@ -210,6 +208,8 @@ export const Header: React.FunctionComponent = ({
      )} + + )} diff --git a/src/dashboardWebView/components/Header/Searchbox.tsx b/src/dashboardWebView/components/Header/Searchbox.tsx index 0f189015..9749f0b4 100644 --- a/src/dashboardWebView/components/Header/Searchbox.tsx +++ b/src/dashboardWebView/components/Header/Searchbox.tsx @@ -40,7 +40,7 @@ export const Searchbox: React.FunctionComponent = ({ }, [debounceSearch]); return ( -
      +
    2. -
      - ) => { - e.stopPropagation(); - onMultiSelect(); - }} - checked={selectedFiles.includes(pageData.fmFilePath)} /> -
      +
      { @@ -251,6 +235,8 @@ export const Item: React.FunctionComponent = ({ className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b hover:bg-opacity-70 border-[var(--frontmatter-border)] hover:bg-[var(--vscode-sideBar-background)]`} >
      + + + +
      { diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index ec2ecdcc..8a2ac464 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -124,7 +124,7 @@ export const Header: React.FunctionComponent = ({ return (
      -
      +
      @@ -186,7 +186,7 @@ export const Header: React.FunctionComponent = ({
      diff --git a/src/dashboardWebView/components/Layout/PageLayout.tsx b/src/dashboardWebView/components/Layout/PageLayout.tsx index 93d24a72..661894ff 100644 --- a/src/dashboardWebView/components/Layout/PageLayout.tsx +++ b/src/dashboardWebView/components/Layout/PageLayout.tsx @@ -20,7 +20,7 @@ export const PageLayout: React.FunctionComponent = ({ const settings = useRecoilValue(SettingsSelector); return ( -
      +
      = ({ }: React.PropsWithChildren) => { const [, setLightbox] = useRecoilState(LightboxAtom); const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); - const [selectedFiles, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom); const [showAlert, setShowAlert] = useState(false); const [showSnippetSelection, setShowSnippetSelection] = useState(false); const [snippet, setSnippet] = useState(undefined); @@ -246,14 +244,6 @@ export const Item: React.FunctionComponent = ({ return null; }, [media]); - const onMultiSelect = useCallback(() => { - if (selectedFiles.includes(media.fsPath)) { - setSelectedFiles(selectedFiles.filter((file) => file !== media.fsPath)); - } else { - setSelectedFiles([...selectedFiles, media.fsPath]); - } - }, [selectedFiles]); - const clearFormData = () => { setShowSnippetFormDialog(false); setSnippet(undefined); @@ -291,14 +281,7 @@ export const Item: React.FunctionComponent = ({ {renderMedia}
      -
      - ) => { - e.stopPropagation(); - onMultiSelect(); - }} - checked={selectedFiles.includes(media.fsPath)} /> -
      + {hasViewData && (
      = ({ disabled }: React.PropsWithChildren) => { return ( -
      +
      {label}:
      diff --git a/src/dashboardWebView/hooks/useSelectedItems.tsx b/src/dashboardWebView/hooks/useSelectedItems.tsx new file mode 100644 index 00000000..9694efa8 --- /dev/null +++ b/src/dashboardWebView/hooks/useSelectedItems.tsx @@ -0,0 +1,20 @@ +import { useCallback } from 'react'; +import { useRecoilState } from 'recoil'; +import { MultiSelectedItemsAtom } from '../state'; + +export default function useSelectedItems() { + const [selectedFiles, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom); + + const onMultiSelect = useCallback((filePath: string) => { + if (selectedFiles.includes(filePath)) { + setSelectedFiles(selectedFiles.filter((file) => file !== filePath)); + } else { + setSelectedFiles([...selectedFiles, filePath]); + } + }, [selectedFiles]); + + return { + selectedFiles, + onMultiSelect + }; +} \ No newline at end of file diff --git a/src/dashboardWebView/styles.css b/src/dashboardWebView/styles.css index 45f3bde0..0c830d85 100644 --- a/src/dashboardWebView/styles.css +++ b/src/dashboardWebView/styles.css @@ -411,7 +411,7 @@ } .question { - @apply relative ml-auto mr-3 w-5/6 rounded-full rounded-br-none bg-teal-900 py-2 px-4 text-whisper-500; + @apply relative ml-auto mr-3 w-5/6 rounded-full rounded-br-none bg-teal-900 px-4 py-2 text-whisper-500; &:after { --size: 1rem; diff --git a/src/dashboardWebView/utils/updateCssVariables.ts b/src/dashboardWebView/utils/updateCssVariables.ts index 4175fa06..1308ac04 100644 --- a/src/dashboardWebView/utils/updateCssVariables.ts +++ b/src/dashboardWebView/utils/updateCssVariables.ts @@ -52,7 +52,12 @@ export const updateCssVariables = () => { ); // Borders + const borderColor = styles.getPropertyValue('--vscode-panel-border'); document.documentElement.style.setProperty('--frontmatter-border', 'var(--vscode-panel-border)'); + document.documentElement.style.setProperty( + '--frontmatter-border-preserve', + preserveColor(borderColor) || 'var(--vscode-panel-border)' + ); // Other colors which should be preserved (no opacity) const buttonBackground = styles.getPropertyValue('--vscode-button-background'); From ec9f55b98223d024fba972940f0571d624935682 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 13 Mar 2024 11:32:42 +0100 Subject: [PATCH 10/44] Localization actions --- l10n/bundle.l10n.json | 6 + .../components/Contents/Contents.tsx | 25 ++- .../components/Header/ActionsBar.tsx | 114 ++++++++-- .../components/Media/Item.tsx | 2 + .../components/Media/Media.tsx | 195 +++++++++--------- .../providers/FilesProvider.tsx | 36 ++++ src/localization/localization.enum.ts | 20 ++ 7 files changed, 270 insertions(+), 128 deletions(-) create mode 100644 src/dashboardWebView/providers/FilesProvider.tsx diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index be31804e..130062ae 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -38,6 +38,9 @@ "common.open": "Open", "common.openWithValue": "Open: {0}", "common.view": "View", + "common.translate": "Translate", + "common.languages": "Languages", + "common.scripts": "Scripts", "loading.initPages": "Loading content", @@ -147,6 +150,9 @@ "dashboard.filters.languageFilter.label": "Locale", "dashboard.filters.languageFilter.all": "All", + "dashboard.header.actionsBar.alertDelete.title": "Delete selected files", + "dashboard.header.actionsBar.alertDelete.description": "Are you sure you want to delete the selected files?", + "dashboard.header.breadcrumb.home": "Home", "dashboard.header.clearFilters.title": "Clear filters, grouping, and sorting", diff --git a/src/dashboardWebView/components/Contents/Contents.tsx b/src/dashboardWebView/components/Contents/Contents.tsx index 0db5a4df..625e1af1 100644 --- a/src/dashboardWebView/components/Contents/Contents.tsx +++ b/src/dashboardWebView/components/Contents/Contents.tsx @@ -11,6 +11,7 @@ import { Messenger } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../../DashboardMessage'; import { TelemetryEvent } from '../../../constants'; import { PageLayout } from '../Layout/PageLayout'; +import { FilesProvider } from '../../providers/FilesProvider'; export interface IContentsProps { pages: Page[]; @@ -32,18 +33,20 @@ export const Contents: React.FunctionComponent = ({ }, []); return ( - -
      - {loading ? : } -
      + + +
      + {loading ? : } +
      - + - Content metrics -
      + Content metrics +
      + ); }; diff --git a/src/dashboardWebView/components/Header/ActionsBar.tsx b/src/dashboardWebView/components/Header/ActionsBar.tsx index f5a28feb..0c8dfd2d 100644 --- a/src/dashboardWebView/components/Header/ActionsBar.tsx +++ b/src/dashboardWebView/components/Header/ActionsBar.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { NavigationType } from '../../models'; -import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon } from '@heroicons/react/24/outline'; +import { NavigationType, Page } from '../../models'; +import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon, LanguageIcon } from '@heroicons/react/24/outline'; import { useRecoilState, useRecoilValue } from 'recoil'; import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state'; import { ActionsBarItem } from './ActionsBarItem'; @@ -10,7 +10,9 @@ import { Alert } from '../Modals/Alert'; import { messageHandler } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../../DashboardMessage'; import { CustomScript, ScriptType } from '../../../models'; -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown'; +import { useFilesContext } from '../../providers/FilesProvider'; +import { COMMAND_NAME, GeneralCommands } from '../../../constants'; export interface IActionsBarProps { view: NavigationType; @@ -24,10 +26,15 @@ export const ActionsBar: React.FunctionComponent = ({ const [showAlert, setShowAlert] = React.useState(false); const selectedFolder = useRecoilValue(SelectedMediaFolderSelector); const settings = useRecoilValue(SettingsSelector); + const { files } = useFilesContext(); const viewFile = React.useCallback(() => { - if (selectedFiles.length === 1 && view === NavigationType.Contents) { - messageHandler.send(DashboardMessage.openFile, selectedFiles[0]); + if (selectedFiles.length === 1) { + if (view === NavigationType.Contents) { + messageHandler.send(DashboardMessage.openFile, selectedFiles[0]); + } else if (view === NavigationType.Media) { + setSelectedItemAction({ path: selectedFiles[0], action: 'view' }) + } } }, [selectedFiles]); @@ -57,6 +64,75 @@ export const ActionsBar: React.FunctionComponent = ({ } }, [selectedFiles]); + const languageActions = React.useMemo(() => { + const actions: React.ReactNode[] = []; + + if (view === NavigationType.Contents && files.length > 0 && selectedFiles.length === 1) { + const selectedItem = selectedFiles[0]; + const page = ((files || []) as Page[]).find((f: Page) => f.fmFilePath === selectedItem); + + if (page?.fmLocale) { + const locale = page.fmLocale; + const translations = page.fmTranslations; + + actions.push( + { + messageHandler.send(GeneralCommands.toVSCode.runCommand, { + command: COMMAND_NAME.i18n.create, + args: selectedItem + }) + }}> + + {l10n.t(LocalizationKey.commonTranslate)} + + ) + + if (translations && Object.keys(translations).length > 0) { + const crntLocale = translations[locale.locale]; + const otherLocales = Object.entries(translations).filter(([key]) => key !== locale.locale); + + if (otherLocales.length > 0) { + actions.push( + + + + {l10n.t(LocalizationKey.commonLanguages)} + + + + + + messageHandler.send(DashboardMessage.openFile, crntLocale.path)}> + {crntLocale.locale.title || crntLocale.locale.locale} + + + + + { + otherLocales.map(([key, value]) => ( + messageHandler.send(DashboardMessage.openFile, value.path)} + > + {value.locale.title || value.locale.locale} + + )) + } + + + ) + } + } + } + } + + return actions; + }, [files, selectedFiles]); + const customScriptActions = React.useMemo(() => { if (!settings?.scripts) { return null; @@ -80,7 +156,7 @@ export const ActionsBar: React.FunctionComponent = ({ disabled={selectedFiles.length === 0} > - Scripts + {l10n.t(LocalizationKey.commonScripts)} @@ -111,19 +187,13 @@ export const ActionsBar: React.FunctionComponent = ({ aria-label="Item actions" >
      - { - view === NavigationType.Contents && ( - <> - 1} - onClick={viewFile} - > - - - ) - } + 1} + onClick={viewFile} + > + { view === NavigationType.Media && ( @@ -142,6 +212,8 @@ export const ActionsBar: React.FunctionComponent = ({ ) } + {languageActions} + {customScriptActions} = ({ {showAlert && ( setShowAlert(false)} diff --git a/src/dashboardWebView/components/Media/Item.tsx b/src/dashboardWebView/components/Media/Item.tsx index 8789a647..cc84aae9 100644 --- a/src/dashboardWebView/components/Media/Item.tsx +++ b/src/dashboardWebView/components/Media/Item.tsx @@ -313,6 +313,8 @@ export const Item: React.FunctionComponent = ({
      )} + +
      )} diff --git a/src/dashboardWebView/components/Media/Media.tsx b/src/dashboardWebView/components/Media/Media.tsx index a7aff12f..07f841f0 100644 --- a/src/dashboardWebView/components/Media/Media.tsx +++ b/src/dashboardWebView/components/Media/Media.tsx @@ -28,6 +28,7 @@ import { MediaInfo } from '../../../models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; import { MediaItemPanel } from './MediaItemPanel'; +import { FilesProvider } from '../../providers/FilesProvider'; export interface IMediaProps { } @@ -163,113 +164,115 @@ export const Media: React.FunctionComponent = ( }); return ( - -
      - {viewData?.data?.filePath && ( -
      -

      {l10n.t(LocalizationKey.dashboardMediaMediaDescription)}

      -

      - {l10n.t(LocalizationKey.dashboardMediaMediaDragAndDrop)} -

      -
      - )} - - {isDragActive && ( -
      - -

      - {selectedFolder - ? l10n.t(LocalizationKey.dashboardMediaMediaFolderUpload, selectedFolder) - : l10n.t(LocalizationKey.dashboardMediaMediaFolderDefault, currentStaticFolder || 'public')} -

      -
      - )} - - {allMedia.length === 0 && folders.length === 0 && !loading && ( -
      -
      - - -

      - {l10n.t(LocalizationKey.dashboardMediaMediaPlaceholder)} + + +

      + {viewData?.data?.filePath && ( +
      +

      {l10n.t(LocalizationKey.dashboardMediaMediaDescription)}

      +

      + {l10n.t(LocalizationKey.dashboardMediaMediaDragAndDrop)}

      -
      - )} - - {contentFolders && - contentFolders.length > 0 && - contentFolders.map( - (group, idx) => - group.folders && - group.folders.length > 0 && ( -
      -

      - {l10n.t(LocalizationKey.dashboardMediaMediaContentFolder)}: {group.title} -

      - - - {group.folders.map((folder) => ( - - ))} - -
      - ) )} - {publicFolders && publicFolders.length > 0 && ( -
      - {contentFolders && contentFolders.length > 0 && ( -

      - {l10n.t(LocalizationKey.dashboardMediaMediaPublicFolder)} - {currentStaticFolder && ( - - : {currentStaticFolder} - - )} -

      - )} + {isDragActive && ( +
      + +

      + {selectedFolder + ? l10n.t(LocalizationKey.dashboardMediaMediaFolderUpload, selectedFolder) + : l10n.t(LocalizationKey.dashboardMediaMediaFolderDefault, currentStaticFolder || 'public')} +

      +
      + )} - - {publicFolders.map((folder) => ( - +
      + - ))} - -
      - )} - - {allMedia.map((file, idx) => ( - - ))} - -
      +

      + {l10n.t(LocalizationKey.dashboardMediaMediaPlaceholder)} +

      +
      +
      + )} + + {contentFolders && + contentFolders.length > 0 && + contentFolders.map( + (group, idx) => + group.folders && + group.folders.length > 0 && ( +
      +

      + {l10n.t(LocalizationKey.dashboardMediaMediaContentFolder)}: {group.title} +

      + + + {group.folders.map((folder) => ( + + ))} + +
      + ) + )} + + {publicFolders && publicFolders.length > 0 && ( +
      + {contentFolders && contentFolders.length > 0 && ( +

      + {l10n.t(LocalizationKey.dashboardMediaMediaPublicFolder)} + {currentStaticFolder && ( + + : {currentStaticFolder} + + )} +

      + )} + + + {publicFolders.map((folder) => ( + + ))} + +
      + )} + + + {allMedia.map((file, idx) => ( + + ))} + +
      - + - {loading && } + {loading && } - + - + - Media metrics -
      + Media metrics + + ); }; diff --git a/src/dashboardWebView/providers/FilesProvider.tsx b/src/dashboardWebView/providers/FilesProvider.tsx new file mode 100644 index 00000000..64639e85 --- /dev/null +++ b/src/dashboardWebView/providers/FilesProvider.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { Page } from '../models'; +import { MediaInfo } from '../../models'; + +interface IFilesProviderProps { + files: Page[] | MediaInfo[]; +} + +const FilesContext = React.createContext(undefined); + +const FilesProvider: React.FunctionComponent = ({ files, children }: React.PropsWithChildren) => { + return ( + + {children} + + ) +}; + +const useFilesContext = (): IFilesProviderProps => { + const loadFunc = React.useContext(FilesContext); + + if (loadFunc === undefined) { + throw new Error('useFilesContext must be used within the FilesProvider'); + } + + return loadFunc; +}; + +FilesContext.displayName = 'FilesContext'; +FilesProvider.displayName = 'FilesProvider'; + +export { FilesProvider, useFilesContext }; \ No newline at end of file diff --git a/src/localization/localization.enum.ts b/src/localization/localization.enum.ts index 64b0ff31..ec291ce3 100644 --- a/src/localization/localization.enum.ts +++ b/src/localization/localization.enum.ts @@ -155,6 +155,18 @@ export enum LocalizationKey { * View */ commonView = 'common.view', + /** + * Translate + */ + commonTranslate = 'common.translate', + /** + * Languages + */ + commonLanguages = 'common.languages', + /** + * Scripts + */ + commonScripts = 'common.scripts', /** * Loading content */ @@ -487,6 +499,14 @@ export enum LocalizationKey { * All */ dashboardFiltersLanguageFilterAll = 'dashboard.filters.languageFilter.all', + /** + * Delete selected files + */ + dashboardHeaderActionsBarAlertDeleteTitle = 'dashboard.header.actionsBar.alertDelete.title', + /** + * Are you sure you want to delete the selected files? + */ + dashboardHeaderActionsBarAlertDeleteDescription = 'dashboard.header.actionsBar.alertDelete.description', /** * Home */ From 5e77419f5a3ee1c951beec5a73ff56f6a65934c8 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 13 Mar 2024 14:52:06 +0100 Subject: [PATCH 11/44] New table implementation --- assets/media/styles.css | 15 +-- package-lock.json | 73 ++++++++++--- package.json | 2 +- .../components/ArticleDetails.tsx | 76 ++++++------- src/panelWebView/components/Collapsible.tsx | 2 +- .../ContentType/ContentTypeValidator.tsx | 6 +- .../components/DataBlock/DataBlockRecords.tsx | 8 +- .../ErrorBoundary/FieldBoundary.tsx | 6 +- .../components/Fields/FieldTitle.tsx | 6 +- src/panelWebView/components/FileList.tsx | 6 +- .../components/FolderAndFiles.tsx | 6 +- .../components/GlobalSettings.tsx | 18 +-- .../components/Icons/CheckIcon.tsx | 23 ---- .../components/Icons/WarningIcon.tsx | 23 ---- .../components/JsonField/JsonField.tsx | 6 +- .../components/JsonField/JsonFieldRecords.tsx | 6 +- src/panelWebView/components/SeoDetails.tsx | 42 ++++--- src/panelWebView/components/SeoFieldInfo.tsx | 16 +-- .../components/SeoKeywordInfo.tsx | 26 +++-- src/panelWebView/components/SeoKeywords.tsx | 31 +++--- src/panelWebView/components/SeoStatus.tsx | 80 +++++--------- .../components/VSCode/VSCodeLabel.tsx | 27 +++++ .../components/VSCode/VSCodeTable.tsx | 103 ++++++++++++++++++ src/panelWebView/components/VSCode/index.ts | 1 + src/panelWebView/components/ValidInfo.tsx | 11 +- .../components/VscodeComponents.ts | 8 +- src/panelWebView/index.tsx | 10 +- 27 files changed, 346 insertions(+), 291 deletions(-) delete mode 100644 src/panelWebView/components/Icons/CheckIcon.tsx delete mode 100644 src/panelWebView/components/Icons/WarningIcon.tsx create mode 100644 src/panelWebView/components/VSCode/VSCodeLabel.tsx create mode 100644 src/panelWebView/components/VSCode/VSCodeTable.tsx create mode 100644 src/panelWebView/components/VSCode/index.ts diff --git a/assets/media/styles.css b/assets/media/styles.css index fd384c80..39803fb9 100644 --- a/assets/media/styles.css +++ b/assets/media/styles.css @@ -247,14 +247,6 @@ background-color: var(--vscode-button-secondaryHoverBackground); } -.table__cell { - overflow: hidden; -} - -.table__title { - text-transform: capitalize; -} - .table__cell__seo_details { padding: 10px; } @@ -281,11 +273,6 @@ margin-left: 0.5rem; } -.seo__status__note { - font-size: 10px; - padding: 3px 0; -} - /* Fields */ .field__toggle { position: relative; @@ -364,7 +351,7 @@ input:checked + .field__toggle__slider:before { } /* File list */ -.file_list vscode-label { +.file_list label { border-bottom: 1px solid var(--vscode-foreground); } diff --git a/package-lock.json b/package-lock.json index f2b2f01d..de7ee19e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ }, "devDependencies": { "@actions/core": "^1.10.0", - "@bendera/vscode-webview-elements": "0.6.2", "@estruyf/vscode": "^1.1.0", "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.1", @@ -38,6 +37,7 @@ "@types/vscode": "^1.73.0", "@typescript-eslint/eslint-plugin": "^5.50.0", "@typescript-eslint/parser": "^5.50.0", + "@vscode-elements/elements": "^1.2.0", "@vscode/l10n": "^0.0.14", "@vscode/webview-ui-toolkit": "^1.2.2", "@webpack-cli/serve": "^1.7.0", @@ -400,15 +400,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bendera/vscode-webview-elements": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@bendera/vscode-webview-elements/-/vscode-webview-elements-0.6.2.tgz", - "integrity": "sha512-smtr+KvCKV2MwjVrmyvrhonpaXVpxCjTMXUQOwDwWSAQ42x5pnlpjCGElz2dljc5VHS1Mh1ovPSQ/P3jAm7vMQ==", - "dev": true, - "dependencies": { - "lit-element": "^2.5.1" - } - }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -764,6 +755,21 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==", + "dev": true + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "dev": true, + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, "node_modules/@microsoft/fast-element": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.12.0.tgz", @@ -2073,6 +2079,12 @@ "integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true + }, "node_modules/@types/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.4.tgz", @@ -2337,6 +2349,15 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vscode-elements/elements": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode-elements/elements/-/elements-1.2.0.tgz", + "integrity": "sha512-aCsf9iEnx+PE2rRfAySjvFTSgqP4NUvHG0nOc5AxFB1FXHyG/ayYA2TN9XpT7zuO024tRAu+XoKREbRC7uAmLA==", + "dev": true, + "dependencies": { + "lit": "^3.1.2" + } + }, "node_modules/@vscode/l10n": { "version": "0.0.14", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.14.tgz", @@ -6574,20 +6595,36 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/lit": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.2.tgz", + "integrity": "sha512-VZx5iAyMtX7CV4K8iTLdCkMaYZ7ipjJZ0JcSdJ0zIdGxxyurjIn7yuuSxNBD7QmjvcNJwr0JS4cAdAtsy7gZ6w==", + "dev": true, + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.0.4", + "lit-html": "^3.1.2" + } + }, "node_modules/lit-element": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.5.1.tgz", - "integrity": "sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.4.tgz", + "integrity": "sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==", "dev": true, "dependencies": { - "lit-html": "^1.1.1" + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" } }, "node_modules/lit-html": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.4.1.tgz", - "integrity": "sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==", - "dev": true + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.2.tgz", + "integrity": "sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==", + "dev": true, + "dependencies": { + "@types/trusted-types": "^2.0.2" + } }, "node_modules/load-json-file": { "version": "4.0.0", diff --git a/package.json b/package.json index cf314eba..57cc2362 100644 --- a/package.json +++ b/package.json @@ -2744,7 +2744,6 @@ }, "devDependencies": { "@actions/core": "^1.10.0", - "@bendera/vscode-webview-elements": "0.6.2", "@estruyf/vscode": "^1.1.0", "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.1", @@ -2769,6 +2768,7 @@ "@types/vscode": "^1.73.0", "@typescript-eslint/eslint-plugin": "^5.50.0", "@typescript-eslint/parser": "^5.50.0", + "@vscode-elements/elements": "^1.2.0", "@vscode/l10n": "^0.0.14", "@vscode/webview-ui-toolkit": "^1.2.2", "@webpack-cli/serve": "^1.7.0", diff --git a/src/panelWebView/components/ArticleDetails.tsx b/src/panelWebView/components/ArticleDetails.tsx index 1a14e9fa..0fc8cbdb 100644 --- a/src/panelWebView/components/ArticleDetails.tsx +++ b/src/panelWebView/components/ArticleDetails.tsx @@ -1,14 +1,7 @@ import * as React from 'react'; -import { - VsTable, - VsTableBody, - VsTableHeader, - VsTableHeaderCell, - VsTableRow, - VsTableCell -} from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeTable, VSCodeTableBody, VSCodeTableCell, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface IArticleDetailsProps { details: { @@ -32,52 +25,55 @@ const ArticleDetails: React.FunctionComponent = ({

      {l10n.t(LocalizationKey.panelArticleDetailsTitle)}

      - - - - {l10n.t(LocalizationKey.panelArticleDetailsType)} - - - {l10n.t(LocalizationKey.panelArticleDetailsTotal)} - - - + + + + + {l10n.t(LocalizationKey.panelArticleDetailsType)} + + + {l10n.t(LocalizationKey.panelArticleDetailsTotal)} + + + + + {details?.headings !== undefined && ( - - {l10n.t(LocalizationKey.panelArticleDetailsHeadings)} - {details.headings} - + + {l10n.t(LocalizationKey.panelArticleDetailsHeadings)} + {details.headings} + )} {details?.paragraphs !== undefined && ( - - {l10n.t(LocalizationKey.panelArticleDetailsParagraphs)} - {details.paragraphs} - + + {l10n.t(LocalizationKey.panelArticleDetailsParagraphs)} + {details.paragraphs} + )} {details?.internalLinks !== undefined && ( - - {l10n.t(LocalizationKey.panelArticleDetailsInternalLinks)} - {details.internalLinks} - + + {l10n.t(LocalizationKey.panelArticleDetailsInternalLinks)} + {details.internalLinks} + )} {details?.externalLinks !== undefined && ( - - {l10n.t(LocalizationKey.panelArticleDetailsExternalLinks)} - {details.externalLinks} - + + {l10n.t(LocalizationKey.panelArticleDetailsExternalLinks)} + {details.externalLinks} + )} {details?.images !== undefined && ( - - {l10n.t(LocalizationKey.panelArticleDetailsImages)} - {details.images} - + + {l10n.t(LocalizationKey.panelArticleDetailsImages)} + {details.images} + )} - - + +
      ); }; diff --git a/src/panelWebView/components/Collapsible.tsx b/src/panelWebView/components/Collapsible.tsx index bad7f993..5f68ba6a 100644 --- a/src/panelWebView/components/Collapsible.tsx +++ b/src/panelWebView/components/Collapsible.tsx @@ -66,7 +66,7 @@ const Collapsible: React.FunctionComponent = ({ return ( -
      +
      {children}
      diff --git a/src/panelWebView/components/ContentType/ContentTypeValidator.tsx b/src/panelWebView/components/ContentType/ContentTypeValidator.tsx index cf6bf63b..96649bf8 100644 --- a/src/panelWebView/components/ContentType/ContentTypeValidator.tsx +++ b/src/panelWebView/components/ContentType/ContentTypeValidator.tsx @@ -5,9 +5,9 @@ import { useMemo } from 'react'; import { Field } from '../../../models'; import { CommandToCode } from '../../CommandToCode'; import { IMetadata } from '../Metadata'; -import { VsLabel } from '../VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { VSCodeLabel } from '../VSCode'; export interface IContentTypeValidatorProps { fields: Field[]; @@ -50,7 +50,7 @@ export const ContentTypeValidator: React.FunctionComponent - +
      -
      + {l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorHint).split(`\n`).map(s => (

      {s}

      ))} diff --git a/src/panelWebView/components/DataBlock/DataBlockRecords.tsx b/src/panelWebView/components/DataBlock/DataBlockRecords.tsx index 69073a41..a04093aa 100644 --- a/src/panelWebView/components/DataBlock/DataBlockRecords.tsx +++ b/src/panelWebView/components/DataBlock/DataBlockRecords.tsx @@ -1,12 +1,12 @@ -import { RectangleStackIcon, PlusIcon } from '@heroicons/react/24/outline'; +import { RectangleStackIcon } from '@heroicons/react/24/outline'; import * as React from 'react'; -import { VsLabel } from '../VscodeComponents'; import { DataBlockRecord } from '.'; import { SortableContainer, SortEnd } from 'react-sortable-hoc'; import { useCallback } from 'react'; import { FieldGroup } from '../../../models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { VSCodeLabel } from '../VSCode'; export interface IDataBlockRecordsProps { fieldGroups?: FieldGroup[]; @@ -52,7 +52,7 @@ export const DataBlockRecords = ({ return (
      - +
      @@ -61,7 +61,7 @@ export const DataBlockRecords = ({
      -
      + {records.map((v: any, idx: number) => ( diff --git a/src/panelWebView/components/ErrorBoundary/FieldBoundary.tsx b/src/panelWebView/components/ErrorBoundary/FieldBoundary.tsx index 8b6715af..6f921c65 100644 --- a/src/panelWebView/components/ErrorBoundary/FieldBoundary.tsx +++ b/src/panelWebView/components/ErrorBoundary/FieldBoundary.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import * as Sentry from '@sentry/react'; -import { VsLabel } from '../VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { VSCodeLabel } from '../VSCode'; export interface IFieldBoundaryProps { fieldName: string; @@ -34,11 +34,11 @@ export default class FieldBoundary extends React.Component< if (this.state.hasError) { return (
      - +
      {this.props.fieldName}
      -
      +
      {l10n.t(LocalizationKey.panelErrorBoundaryFieldBoundaryLabel)} diff --git a/src/panelWebView/components/Fields/FieldTitle.tsx b/src/panelWebView/components/Fields/FieldTitle.tsx index b2b0aa6e..f86a6810 100644 --- a/src/panelWebView/components/Fields/FieldTitle.tsx +++ b/src/panelWebView/components/Fields/FieldTitle.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useMemo } from 'react'; -import { VsLabel } from '../VscodeComponents'; import { RequiredAsterix } from './RequiredAsterix'; +import { VSCodeLabel } from '../VSCode'; export interface IFieldTitleProps { label: string | JSX.Element; @@ -23,7 +23,7 @@ export const FieldTitle: React.FunctionComponent = ({ }, [icon]); return ( - +
      {Icon} @@ -33,6 +33,6 @@ export const FieldTitle: React.FunctionComponent = ({ {actionElement}
      - + ); }; diff --git a/src/panelWebView/components/FileList.tsx b/src/panelWebView/components/FileList.tsx index 203d3776..d60165b8 100644 --- a/src/panelWebView/components/FileList.tsx +++ b/src/panelWebView/components/FileList.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { FileInfo } from '../../models'; import { FileItem } from './FileItem'; -import { VsLabel } from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeLabel } from './VSCode'; export interface IFileListProps { folderName: string; @@ -22,9 +22,9 @@ const FileList: React.FunctionComponent = ({ return (
      - + {folderName} - {files.length === 1 ? l10n.t(LocalizationKey.panelFileListLabelSingular) : l10n.t(LocalizationKey.panelFileListLabelPlural)}: {totalFiles} - +
        {files && diff --git a/src/panelWebView/components/FolderAndFiles.tsx b/src/panelWebView/components/FolderAndFiles.tsx index ee484911..eaae1155 100644 --- a/src/panelWebView/components/FolderAndFiles.tsx +++ b/src/panelWebView/components/FolderAndFiles.tsx @@ -2,9 +2,9 @@ import * as React from 'react'; import { FolderInfo } from '../../models'; import { Collapsible } from './Collapsible'; import { FileList } from './FileList'; -import { VsLabel } from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeLabel } from './VSCode'; export interface IFolderAndFilesProps { data: FolderInfo[] | undefined; @@ -35,9 +35,9 @@ const FolderAndFiles: React.FunctionComponent = ({ />
      ) : isBase ? ( - + {folder.title}: {folder.files} {folder.files > 1 ? l10n.t(LocalizationKey.panelFileListLabelPlural) : l10n.t(LocalizationKey.panelFileListLabelSingular)} - + ) : null}
      ))} diff --git a/src/panelWebView/components/GlobalSettings.tsx b/src/panelWebView/components/GlobalSettings.tsx index 7873cfb8..9819c312 100644 --- a/src/panelWebView/components/GlobalSettings.tsx +++ b/src/panelWebView/components/GlobalSettings.tsx @@ -3,12 +3,12 @@ import { PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; import { useDebounce } from '../../hooks/useDebounce'; import { Collapsible } from './Collapsible'; -import { VsLabel } from './VscodeComponents'; import useStartCommand from '../hooks/useStartCommand'; import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react'; import { Messenger } from '@estruyf/vscode/dist/client'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeLabel } from './VSCode'; export interface IGlobalSettingsProps { settings: PanelSettings | undefined; @@ -78,25 +78,25 @@ const GlobalSettings: React.FunctionComponent = ({ title={l10n.t(LocalizationKey.panelGlobalSettingsTitle)} >
      - + {l10n.t(LocalizationKey.panelGlobalSettingsActionModifiedDateLabel)} - + {l10n.t(LocalizationKey.panelGlobalSettingsActionModifiedDateDescription)}
      - + {l10n.t(LocalizationKey.panelGlobalSettingsActionFrontMatterLabel)} - + {l10n.t(LocalizationKey.panelGlobalSettingsActionFrontMatterDescription)}
      - + {l10n.t(LocalizationKey.panelGlobalSettingsActionPreviewLabel)} - + = ({ />
      - + {l10n.t(LocalizationKey.panelGlobalSettingsActionServerLabel)} - + = ( - props: React.PropsWithChildren -) => { - return ( - - - - ); -}; diff --git a/src/panelWebView/components/Icons/WarningIcon.tsx b/src/panelWebView/components/Icons/WarningIcon.tsx deleted file mode 100644 index f76ede02..00000000 --- a/src/panelWebView/components/Icons/WarningIcon.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from 'react'; - -export interface IWarningIconProps {} - -export const WarningIcon: React.FunctionComponent = ( - props: React.PropsWithChildren -) => { - return ( - - - - ); -}; diff --git a/src/panelWebView/components/JsonField/JsonField.tsx b/src/panelWebView/components/JsonField/JsonField.tsx index fe92e843..86954575 100644 --- a/src/panelWebView/components/JsonField/JsonField.tsx +++ b/src/panelWebView/components/JsonField/JsonField.tsx @@ -2,10 +2,10 @@ import * as React from 'react'; import { useState, useMemo, useCallback, useEffect } from 'react'; import { Field, PanelSettings } from '../../../models'; import { PencilIcon } from '@heroicons/react/24/outline'; -import { VsLabel } from '../VscodeComponents'; import { JsonFieldRecords, JsonFieldForm, JsonFieldSelector } from '.'; import { SortEnd } from 'react-sortable-hoc'; import { arrayMoveImmutable } from 'array-move'; +import { VSCodeLabel } from '../VSCode'; export interface IJsonFieldProps { label: string; @@ -101,12 +101,12 @@ export const JsonField: React.FunctionComponent = ({ return (
      - +
      {' '} {label}
      -
      + - +
      @@ -44,7 +44,7 @@ export const JsonFieldRecords = ({
      - + {records.map((v: any, idx: number) => ( diff --git a/src/panelWebView/components/SeoDetails.tsx b/src/panelWebView/components/SeoDetails.tsx index ab13f486..0ec6ffea 100644 --- a/src/panelWebView/components/SeoDetails.tsx +++ b/src/panelWebView/components/SeoDetails.tsx @@ -1,14 +1,7 @@ import * as React from 'react'; -import { - VsTable, - VsTableBody, - VsTableHeader, - VsTableHeaderCell, - VsTableRow, - VsTableCell -} from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeTable, VSCodeTableBody, VSCodeTableCell, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface ISeoDetailsProps { allowedLength: number; @@ -23,30 +16,33 @@ const SeoDetails: React.FunctionComponent = ( ) => { const { allowedLength, title, value, valueTitle, noValidation } = props; - const validate = () => { + const validate = React.useMemo(() => { if (noValidation) { return ''; } return value <= allowedLength ? 'valid' : 'not-valid'; - }; + }, [value, allowedLength, noValidation]); return ( -
      +

      {title}

      - - - {valueTitle} - {l10n.t(LocalizationKey.panelSeoDetailsRecommended)} - - - - {value} - {allowedLength} - - - + + + + {valueTitle} + {l10n.t(LocalizationKey.panelSeoDetailsRecommended)} + + + + + + {value} + {allowedLength} + + +
      ); }; diff --git a/src/panelWebView/components/SeoFieldInfo.tsx b/src/panelWebView/components/SeoFieldInfo.tsx index f8814bba..8d15a1e4 100644 --- a/src/panelWebView/components/SeoFieldInfo.tsx +++ b/src/panelWebView/components/SeoFieldInfo.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ValidInfo } from './ValidInfo'; -import { VsTableCell, VsTableRow } from './VscodeComponents'; +import { VSCodeTableCell, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface ISeoFieldInfoProps { title: string; @@ -16,15 +16,11 @@ const SeoFieldInfo: React.FunctionComponent = ({ isValid }: React.PropsWithChildren) => { return ( - - {title} - - {value}/{recommendation} - - - {isValid !== undefined ? : -} - - + + {title} + {value}/{recommendation} + {isValid !== undefined ? : -} + ); }; diff --git a/src/panelWebView/components/SeoKeywordInfo.tsx b/src/panelWebView/components/SeoKeywordInfo.tsx index bcb567ea..38418233 100644 --- a/src/panelWebView/components/SeoKeywordInfo.tsx +++ b/src/panelWebView/components/SeoKeywordInfo.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { ValidInfo } from './ValidInfo'; -import { VsTableCell, VsTableRow } from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeTableCell, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface ISeoKeywordInfoProps { keyword: string; @@ -71,22 +71,22 @@ const SeoKeywordInfo: React.FunctionComponent = ({ } return ( - - {keyword} - -
      + + {keyword} + +
      -
      +
      -
      +
      = ({ } />
      -
      +
      - {headings && headings.length > 0 &&
      {checkHeadings()}
      } - {wordCount &&
      {density()}
      } - - + {headings && headings.length > 0 && +
      {checkHeadings()}
      } + {wordCount && +
      {density()}
      } + + ); }; diff --git a/src/panelWebView/components/SeoKeywords.tsx b/src/panelWebView/components/SeoKeywords.tsx index 0a2ca00f..094a73ed 100644 --- a/src/panelWebView/components/SeoKeywords.tsx +++ b/src/panelWebView/components/SeoKeywords.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { SeoKeywordInfo } from './SeoKeywordInfo'; -import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell } from './VscodeComponents'; import { ErrorBoundary } from '@sentry/react'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface ISeoKeywordsProps { keywords: string[] | null; @@ -54,16 +54,19 @@ const SeoKeywords: React.FunctionComponent = ({

      {l10n.t(LocalizationKey.panelSeoKeywordsTitle)}

      - - - - {l10n.t(LocalizationKey.panelSeoKeywordsHeaderKeyword)} - - - {l10n.t(LocalizationKey.panelSeoKeywordsHeaderDetails)} - - - + + + + + {l10n.t(LocalizationKey.panelSeoKeywordsHeaderKeyword)} + + + {l10n.t(LocalizationKey.panelSeoKeywordsHeaderDetails)} + + + + + {validateKeywords().map((keyword, index) => { return ( }> @@ -71,11 +74,11 @@ const SeoKeywords: React.FunctionComponent = ({ ); })} - - + + {data.wordCount && ( -
      +
      {l10n.t(LocalizationKey.panelSeoKeywordsDensity)}
      )} diff --git a/src/panelWebView/components/SeoStatus.tsx b/src/panelWebView/components/SeoStatus.tsx index 28677c0b..4da28db3 100644 --- a/src/panelWebView/components/SeoStatus.tsx +++ b/src/panelWebView/components/SeoStatus.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { useEffect } from 'react'; import { SEO } from '../../models/PanelSettings'; import { TagType } from '../TagType'; import { ArticleDetails } from './ArticleDetails'; @@ -9,9 +8,9 @@ import { SymbolKeywordIcon } from './Icons/SymbolKeywordIcon'; import { SeoFieldInfo } from './SeoFieldInfo'; import { SeoKeywords } from './SeoKeywords'; import { TagPicker } from './TagPicker'; -import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell } from './VscodeComponents'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable'; export interface ISeoStatusProps { seo: SEO; @@ -27,90 +26,61 @@ const SeoStatus: React.FunctionComponent = ({ unsetFocus }: React.PropsWithChildren) => { const { title, slug } = data; - const [isOpen, setIsOpen] = React.useState(true); - const tableRef = React.useRef(); - const pushUpdate = React.useRef((value: boolean) => { - setTimeout(() => { - setIsOpen(value); - }, 10); - }).current; const { descriptionField, titleField } = seo; - // Workaround for lit components not updating render - useEffect(() => { - setTimeout(() => { - let height = 0; - - tableRef.current?.childNodes.forEach((elm: any) => { - height += elm.clientHeight; - }); - - if (height > 0 && tableRef.current) { - tableRef.current.style.height = `${height}px`; - } - }, 10); - }, [title, data[titleField], data[descriptionField], data?.articleDetails?.wordCount]); - - const renderContent = () => { - if (!isOpen) { - return null; - } - + const tableContent = React.useMemo(() => { return (

      {l10n.t(LocalizationKey.panelSeoStatusTitle)}

      - - - - {l10n.t(LocalizationKey.panelSeoStatusHeaderProperty)} - - - {l10n.t(LocalizationKey.panelSeoStatusHeaderLength)} - - - {l10n.t(LocalizationKey.panelSeoStatusHeaderValid)} - - - - {data[titleField] && seo.title > 0 && ( + + + + {l10n.t(LocalizationKey.panelSeoStatusHeaderProperty)} + {l10n.t(LocalizationKey.panelSeoStatusHeaderLength)} + {l10n.t(LocalizationKey.panelSeoStatusHeaderValid)} + + + + + {data[titleField] && seo.title > 0 ? ( - )} + ) : null} - {slug && seo.slug > 0 && ( + {slug && seo.slug > 0 ? ( - )} + ) : null} - {data[descriptionField] && seo.description > 0 && ( + {data[descriptionField] && seo.description > 0 ? ( - )} + ) : null} - {seo.content > 0 && data?.articleDetails?.wordCount > 0 && ( + {seo.content > 0 && data?.articleDetails?.wordCount > 0 ? ( - )} - - + ) : null} + +
      = ({
      ); - }; + }, [data, seo, focusElm, unsetFocus]); return ( - + {!title && !data[descriptionField] ? (

      @@ -150,7 +120,7 @@ const SeoStatus: React.FunctionComponent = ({

      ) : ( - renderContent() + tableContent )}
      ); diff --git a/src/panelWebView/components/VSCode/VSCodeLabel.tsx b/src/panelWebView/components/VSCode/VSCodeLabel.tsx new file mode 100644 index 00000000..6dcee08a --- /dev/null +++ b/src/panelWebView/components/VSCode/VSCodeLabel.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +export interface IVSCodeLabelProps { } + +export const VSCodeLabel: React.FunctionComponent = ({ + children +}: React.PropsWithChildren) => { + const DEFAULT_LINE_HEIGHT = 16; + const DEFAULT_FONT_SIZE = 13; + + const INPUT_LINE_HEIGHT_RATIO = DEFAULT_LINE_HEIGHT / DEFAULT_FONT_SIZE; + + return ( + + ); +}; \ No newline at end of file diff --git a/src/panelWebView/components/VSCode/VSCodeTable.tsx b/src/panelWebView/components/VSCode/VSCodeTable.tsx new file mode 100644 index 00000000..4dee145b --- /dev/null +++ b/src/panelWebView/components/VSCode/VSCodeTable.tsx @@ -0,0 +1,103 @@ +import * as React from "react" +import { cn } from "../../../utils/cn" + +const VSCodeTable = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
      + + +)) +VSCodeTable.displayName = "VSCodeTable" + +const VSCodeTableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +VSCodeTableHeader.displayName = "VSCodeTableHeader" + +const VSCodeTableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +VSCodeTableBody.displayName = "VSCodeTableBody" + +const VSCodeTableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0", + className + )} + {...props} + /> +)) +VSCodeTableFooter.displayName = "VSCodeTableFooter" + +const VSCodeTableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +VSCodeTableRow.displayName = "VSCodeTableRow" + +const VSCodeTableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
      +)) +VSCodeTableHead.displayName = "VSCodeTableHead" + +const VSCodeTableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +VSCodeTableCell.displayName = "VSCodeTableCell" + +export { + VSCodeTable, + VSCodeTableHeader, + VSCodeTableBody, + VSCodeTableFooter, + VSCodeTableHead, + VSCodeTableRow, + VSCodeTableCell, +} \ No newline at end of file diff --git a/src/panelWebView/components/VSCode/index.ts b/src/panelWebView/components/VSCode/index.ts new file mode 100644 index 00000000..a6f343b3 --- /dev/null +++ b/src/panelWebView/components/VSCode/index.ts @@ -0,0 +1 @@ +export * from './VSCodeLabel'; diff --git a/src/panelWebView/components/ValidInfo.tsx b/src/panelWebView/components/ValidInfo.tsx index 8f23e1a8..2e817775 100644 --- a/src/panelWebView/components/ValidInfo.tsx +++ b/src/panelWebView/components/ValidInfo.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; -import { CheckIcon } from './Icons/CheckIcon'; -import { WarningIcon } from './Icons/WarningIcon'; +import { CheckIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'; export interface IValidInfoProps { label?: string; @@ -14,13 +13,9 @@ const ValidInfo: React.FunctionComponent = ({ return ( <> {isValid ? ( - - - + ) : ( - - - + )} {label && {label}} diff --git a/src/panelWebView/components/VscodeComponents.ts b/src/panelWebView/components/VscodeComponents.ts index a3128e4d..5fbfc822 100644 --- a/src/panelWebView/components/VscodeComponents.ts +++ b/src/panelWebView/components/VscodeComponents.ts @@ -1,11 +1,5 @@ import { wrapWc } from 'wc-react'; // @bendera/vscode-webview-elements -export const VsTable = wrapWc(`vscode-table`); -export const VsTableHeader = wrapWc(`vscode-table-header`); -export const VsTableHeaderCell = wrapWc(`vscode-table-header-cell`); -export const VsTableBody = wrapWc(`vscode-table-body`); -export const VsTableRow = wrapWc(`vscode-table-row`); -export const VsTableCell = wrapWc(`vscode-table-cell`); export const VsCollapsible = wrapWc(`vscode-collapsible`); -export const VsLabel = wrapWc(`vscode-label`); +// export const VsLabel = wrapWc(`vscode-label`); diff --git a/src/panelWebView/index.tsx b/src/panelWebView/index.tsx index 613ddbbd..5b5a7c70 100644 --- a/src/panelWebView/index.tsx +++ b/src/panelWebView/index.tsx @@ -9,14 +9,8 @@ import { SentryInit } from '../utils/sentryInit'; import './styles.css'; // require('@vscode/codicons/dist/codicon.css'); -import '@bendera/vscode-webview-elements/dist/vscode-table.js'; -import '@bendera/vscode-webview-elements/dist/vscode-table-header.js'; -import '@bendera/vscode-webview-elements/dist/vscode-table-header-cell.js'; -import '@bendera/vscode-webview-elements/dist/vscode-table-body.js'; -import '@bendera/vscode-webview-elements/dist/vscode-table-row.js'; -import '@bendera/vscode-webview-elements/dist/vscode-table-cell.js'; -import '@bendera/vscode-webview-elements/dist/vscode-collapsible.js'; -import '@bendera/vscode-webview-elements/dist/vscode-label.js'; +import '@vscode-elements/elements/dist/vscode-collapsible/index.js'; +// import '@bendera/vscode-webview-elements/dist/vscode-label/index.js'; // import '@bendera/vscode-webview-elements/dist/vscode-checkbox.js'; // import '@vscode/webview-ui-toolkit/dist/esm/checkbox'; From 15870bcc99fb6b8bfd549fad63569a7ee667e4cb Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 13 Mar 2024 14:54:35 +0100 Subject: [PATCH 12/44] #771 - Fix lowercased data label --- CHANGELOG.md | 1 + l10n/bundle.l10n.fr.json | 2 +- l10n/bundle.l10n.it.json | 2 +- l10n/bundle.l10n.json | 2 +- src/dashboardWebView/components/Header/Header.tsx | 3 +-- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0741c512..9fd3265c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### 🐞 Fixes - [#768](https://github.com/estruyf/vscode-front-matter/issues/768): Update broken link to the documentation +- [#771](https://github.com/estruyf/vscode-front-matter/issues/771): Fix lowercase `data` tab label ## [10.0.2] - 2024-03-01 diff --git a/l10n/bundle.l10n.fr.json b/l10n/bundle.l10n.fr.json index 68d68d09..e7da0220 100644 --- a/l10n/bundle.l10n.fr.json +++ b/l10n/bundle.l10n.fr.json @@ -105,7 +105,7 @@ "dashboard.header.tabs.contents": "Contenus", "dashboard.header.tabs.media": "Médias", "dashboard.header.tabs.snippets": "Snippets", - "dashboard.header.tabs.data": "données", + "dashboard.header.tabs.data": "Données", "dashboard.header.tabs.taxonomies": "Taxonomies", "dashboard.header.viewSwitch.toGrid": "Afficher en grille", "dashboard.header.viewSwitch.toList": "Afficher en liste", diff --git a/l10n/bundle.l10n.it.json b/l10n/bundle.l10n.it.json index aa38a8b4..3a6946db 100644 --- a/l10n/bundle.l10n.it.json +++ b/l10n/bundle.l10n.it.json @@ -105,7 +105,7 @@ "dashboard.header.tabs.contents": "Contenuto", "dashboard.header.tabs.media": "Media", "dashboard.header.tabs.snippets": "Snippets", - "dashboard.header.tabs.data": "dati", + "dashboard.header.tabs.data": "Dati", "dashboard.header.tabs.taxonomies": "Tassonomie", "dashboard.header.viewSwitch.toGrid": "Passa alla griglia", "dashboard.header.viewSwitch.toList": "Passa all'elenco", diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 523f8d44..6e1f2b98 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -199,7 +199,7 @@ "dashboard.header.tabs.contents": "Contents", "dashboard.header.tabs.media": "Media", "dashboard.header.tabs.snippets": "Snippets", - "dashboard.header.tabs.data": "data", + "dashboard.header.tabs.data": "Data", "dashboard.header.tabs.taxonomies": "Taxonomies", "dashboard.header.viewSwitch.toGrid": "Change to grid", diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index bf1a45e2..10705f84 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -18,8 +18,7 @@ import { ArrowTopRightOnSquareIcon, BoltIcon, PlusIcon } from '@heroicons/react/ import { HeartIcon } from '@heroicons/react/24/solid'; import { useLocation, useNavigate } from 'react-router-dom'; import { routePaths } from '../..'; -import { useEffect, useMemo } from 'react'; -import { SyncButton } from './SyncButton'; +import { useMemo } from 'react'; import { Pagination } from './Pagination'; import { GroupOption } from '../../constants/GroupOption'; import usePagination from '../../hooks/usePagination'; From 0ae7cb27ce3c0bb5675be5924373d3f35103ca41 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 13 Mar 2024 14:58:58 +0100 Subject: [PATCH 13/44] Added localization --- l10n/bundle.l10n.json | 1 + src/dashboardWebView/components/Header/ActionsBar.tsx | 2 +- src/localization/localization.enum.ts | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 130062ae..92e0949f 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -150,6 +150,7 @@ "dashboard.filters.languageFilter.label": "Locale", "dashboard.filters.languageFilter.all": "All", + "dashboard.header.actionsBar.itemsSelected": "{0} selected", "dashboard.header.actionsBar.alertDelete.title": "Delete selected files", "dashboard.header.actionsBar.alertDelete.description": "Are you sure you want to delete the selected files?", diff --git a/src/dashboardWebView/components/Header/ActionsBar.tsx b/src/dashboardWebView/components/Header/ActionsBar.tsx index 0c8dfd2d..b42f906c 100644 --- a/src/dashboardWebView/components/Header/ActionsBar.tsx +++ b/src/dashboardWebView/components/Header/ActionsBar.tsx @@ -234,7 +234,7 @@ export const ActionsBar: React.FunctionComponent = ({ onClick={() => setSelectedFiles([])} >