From a7f183b6cc758a2d2095d50a2b23d80b748c689a Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 25 Nov 2024 11:59:22 +0100 Subject: [PATCH] #705 - further improve keywords section --- .vscode/settings.json | 6 -- l10n/bundle.l10n.json | 2 + src/localization/localization.enum.ts | 8 ++ .../components/SeoKeywordInfo.tsx | 91 +++++++++++------ src/panelWebView/components/SeoKeywords.tsx | 98 +++---------------- .../components/VSCode/VSCodeTable.tsx | 6 +- src/panelWebView/components/ValidInfo.tsx | 2 +- src/panelWebView/styles.css | 41 ++++---- 8 files changed, 109 insertions(+), 145 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 24f5c98b..a4e660c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,12 +10,6 @@ "values": ["#build", "#deploy", "#skip"] } ], - "workbench.colorCustomizations": { - "titleBar.activeBackground": "#15c2cb", - "titleBar.inactiveBackground": "#44ffd299", - "titleBar.activeForeground": "#0E131F", - "titleBar.inactiveForeground": "#0E131F99" - }, "files.exclude": { "out": false // set this to true to hide the "out" folder with the compiled JS files }, diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index d27383e5..1914ac87 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -496,6 +496,8 @@ "panel.seoDetails.recommended": "Recommended", + "panel.seoKeywords.checks": "Checks", + "panel.seoKeywords.density.tableTitle": "Freq.", "panel.seoKeywords.density": "Keyword density", "panel.seoKeywordInfo.validInfo.label": "Used in heading(s)", "panel.seoKeywordInfo.validInfo.content": "Content", diff --git a/src/localization/localization.enum.ts b/src/localization/localization.enum.ts index dc001c3a..bde280c5 100644 --- a/src/localization/localization.enum.ts +++ b/src/localization/localization.enum.ts @@ -1600,6 +1600,14 @@ export enum LocalizationKey { * Recommended */ panelSeoDetailsRecommended = 'panel.seoDetails.recommended', + /** + * Checks + */ + panelSeoKeywordsChecks = 'panel.seoKeywords.checks', + /** + * Freq. + */ + panelSeoKeywordsDensityTableTitle = 'panel.seoKeywords.density.tableTitle', /** * Keyword density */ diff --git a/src/panelWebView/components/SeoKeywordInfo.tsx b/src/panelWebView/components/SeoKeywordInfo.tsx index 8714f071..aca3c80d 100644 --- a/src/panelWebView/components/SeoKeywordInfo.tsx +++ b/src/panelWebView/components/SeoKeywordInfo.tsx @@ -5,6 +5,7 @@ import { Tag } from './Tag'; import { LocalizationKey, localize } from '../../localization'; import { Messenger } from '@estruyf/vscode/dist/client'; import { CommandToCode } from '../CommandToCode'; +import { Tooltip } from 'react-tooltip' export interface ISeoKeywordInfoProps { keywords: string[]; @@ -27,6 +28,9 @@ const SeoKeywordInfo: React.FunctionComponent = ({ wordCount, headings }: React.PropsWithChildren) => { + + const tooltipClasses = `!py-[2px] !px-[8px] !rounded-[3px] !border-[var(--vscode-editorHoverWidget-border)] !border !border-solid !bg-[var(--vscode-editorHoverWidget-background)] !text-[var(--vscode-editorHoverWidget-foreground)] !font-normal !opacity-100 shadow-[0_2px_8px_var(--vscode-widget-shadow)] !z-[9999]`; + const density = () => { if (!wordCount) { return null; @@ -35,7 +39,7 @@ const SeoKeywordInfo: React.FunctionComponent = ({ const pattern = new RegExp(`(^${keyword.toLowerCase()}(?=\\s|$))|(\\s${keyword.toLowerCase()}(?=\\s|$))`, 'ig'); const count = (content.match(pattern) || []).length; const density = (count / wordCount) * 100; - const densityTitle = `${density.toFixed(2)}% *`; + const densityTitle = `${density.toFixed(2)}* %`; if (density < 0.75) { return ; @@ -67,7 +71,7 @@ const SeoKeywordInfo: React.FunctionComponent = ({ } const exists = headings.filter((heading) => validateKeywords(heading, keyword)); - return 0} />; + return exists.length > 0; }; const onRemove = React.useCallback((tag: string) => { @@ -78,6 +82,50 @@ const SeoKeywordInfo: React.FunctionComponent = ({ }); }, [keywords]); + const checks = React.useMemo(() => { + return { + title: !!title && title.toLowerCase().includes(keyword.toLowerCase()), + description: !!description && description.toLowerCase().includes(keyword.toLowerCase()), + slug: + !!slug && + (slug.toLowerCase().includes(keyword.toLowerCase()) || + slug.toLowerCase().includes(keyword.replace(/ /g, '-').toLowerCase())), + content: !!content && content.toLowerCase().includes(keyword.toLowerCase()), + heading: checkHeadings() + }; + }, [title, description, slug, content, headings, wordCount]); + + const tooltipContent = React.useMemo(() => { + return ( + <> + {localize(LocalizationKey.commonTitle)}:
+ {localize(LocalizationKey.commonDescription)}:
+ {localize(LocalizationKey.commonSlug)}:
+ {localize(LocalizationKey.panelSeoKeywordInfoValidInfoContent)}:
+ {localize(LocalizationKey.panelSeoKeywordInfoValidInfoLabel)}: + + ) + }, [checks]); + + const checksMarkup = React.useMemo(() => { + const validData = Object.values(checks).filter((check) => check).length; + const totalChecks = Object.values(checks).length; + + const isValid = validData === totalChecks; + + return ( +
+ + {validData} + / + {totalChecks} +
+ ); + }, [checks]); + if (!keyword || typeof keyword !== 'string') { return null; } @@ -88,7 +136,7 @@ const SeoKeywordInfo: React.FunctionComponent = ({
void 0} title={localize(LocalizationKey.panelTagsTagWarning, keyword)} @@ -96,32 +144,17 @@ const SeoKeywordInfo: React.FunctionComponent = ({ />
- - - - - - - - - - - - - - {checkHeadings()} + + {checksMarkup} + + tooltipContent} /> {density()} diff --git a/src/panelWebView/components/SeoKeywords.tsx b/src/panelWebView/components/SeoKeywords.tsx index 16ad8945..18fa6ab0 100644 --- a/src/panelWebView/components/SeoKeywords.tsx +++ b/src/panelWebView/components/SeoKeywords.tsx @@ -1,10 +1,9 @@ import * as React from 'react'; import { SeoKeywordInfo } from './SeoKeywordInfo'; import { ErrorBoundary } from '@sentry/react'; -import { Tooltip } from 'react-tooltip' import { LocalizationKey, localize } from '../../localization'; import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable'; -import { Icon } from 'vscrui'; +import { Tooltip } from 'react-tooltip' export interface ISeoKeywordsProps { keywords: string[] | null; @@ -23,7 +22,7 @@ const SeoKeywords: React.FunctionComponent = ({ }: React.PropsWithChildren) => { const [isReady, setIsReady] = React.useState(false); - const tooltipClasses = `!py-[2px] !px-[8px] !rounded-[3px] !border-[var(--vscode-editorHoverWidget-border)] !border !border-solid !bg-[var(--vscode-editorHoverWidget-background)] !text-[var(--vscode-editorHoverWidget-foreground)] !font-normal !opacity-100`; + const tooltipClasses = `!py-[2px] !px-[8px] !rounded-[3px] !border-[var(--vscode-editorHoverWidget-border)] !border !border-solid !bg-[var(--vscode-editorHoverWidget-background)] !text-[var(--vscode-editorHoverWidget-foreground)] !font-normal !opacity-100 shadow-[0_2px_8px_var(--vscode-widget-shadow)]`; const validateKeywords = () => { if (!keywords) { @@ -59,98 +58,25 @@ const SeoKeywords: React.FunctionComponent = ({ return (
- + {localize(LocalizationKey.panelSeoKeywordsHeaderKeyword)} - -
- - -
-
- -
- - -
-
- -
- - -
-
- -
- - -
+ + {localize(LocalizationKey.panelSeoKeywordsChecks)} +
- H1 - - -
-
- -
- + data-tooltip-content={localize(LocalizationKey.panelSeoKeywordsDensity)}> + {localize(LocalizationKey.panelSeoKeywordsDensityTableTitle)} + = ({ {validKeywords.map((keyword, index) => { return ( - }> - + }> + ); })} @@ -172,7 +98,7 @@ const SeoKeywords: React.FunctionComponent = ({ {data.wordCount && ( -
+
{localize(LocalizationKey.panelSeoKeywordsDensityDescription)}
)} diff --git a/src/panelWebView/components/VSCode/VSCodeTable.tsx b/src/panelWebView/components/VSCode/VSCodeTable.tsx index 4dee145b..7a287c5b 100644 --- a/src/panelWebView/components/VSCode/VSCodeTable.tsx +++ b/src/panelWebView/components/VSCode/VSCodeTable.tsx @@ -3,9 +3,9 @@ import { cn } from "../../../utils/cn" const VSCodeTable = React.forwardRef< HTMLTableElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
+ React.HTMLAttributes & { disableOverflow?: boolean } +>(({ className, disableOverflow, ...props }, ref) => ( +
= ({ return (
{isValid ? ( - + ) : ( )} diff --git a/src/panelWebView/styles.css b/src/panelWebView/styles.css index c459fb2b..355be5f5 100644 --- a/src/panelWebView/styles.css +++ b/src/panelWebView/styles.css @@ -866,28 +866,29 @@ vscode-divider { padding: 0.25rem 0.25rem; margin-bottom: 0.5rem; margin-right: 0.5rem; -} - -.tag button { - background: none; - border: none; - color: inherit; - outline: none !important; - outline-offset: inherit !important; - margin: 0; - display: inline-flex; - align-items: center; - padding: 0.25rem; -} + + button { + background: none; + border: none; + color: inherit; + outline: none !important; + outline-offset: inherit !important; + margin: 0; + display: inline-flex; + align-items: center; + padding: 0.25rem; + width: auto; + } -.tag .tag__create { - margin-right: 0.25rem; -} + .tag__create { + margin-right: 0.25rem; -.tag .tag__create:hover { - color: var(--vscode-inputValidation-infoForeground, #000); - background-color: var(--vscode-inputValidation-infoBackground); - border-radius: 2px; + &:hover { + color: var(--vscode-inputValidation-infoForeground, #000); + background-color: var(--vscode-inputValidation-infoBackground); + border-radius: 2px; + } + } } .vscode-dark .tag .tag__create:hover {