mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-05 16:33:04 +02:00
Enhance SEO components with keyword management and styling updates #705
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { ValidInfo } from './ValidInfo';
|
||||
import { VSCodeTableCell, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
import { Tag } from './Tag';
|
||||
import { LocalizationKey, localize } from '../../localization';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
|
||||
export interface ISeoKeywordInfoProps {
|
||||
keywords: string[];
|
||||
keyword: string;
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -14,6 +19,7 @@ export interface ISeoKeywordInfoProps {
|
||||
|
||||
const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
keyword,
|
||||
keywords,
|
||||
title,
|
||||
description,
|
||||
slug,
|
||||
@@ -64,13 +70,32 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
return <ValidInfo isValid={exists.length > 0} />;
|
||||
};
|
||||
|
||||
const onRemove = React.useCallback((tag: string) => {
|
||||
const newSelection = keywords.filter((s) => s !== tag);
|
||||
Messenger.send(CommandToCode.updateKeywords, {
|
||||
values: newSelection,
|
||||
parents: undefined
|
||||
});
|
||||
}, [keywords]);
|
||||
|
||||
if (!keyword || typeof keyword !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{keyword}</VSCodeTableCell>
|
||||
<VSCodeTableCell>
|
||||
<div className='flex h-full items-center'>
|
||||
<Tag
|
||||
value={keyword}
|
||||
className={`!mx-0 !my-1 !px-2 !py-1`}
|
||||
onRemove={onRemove}
|
||||
onCreate={() => void 0}
|
||||
title={localize(LocalizationKey.panelTagsTagWarning, keyword)}
|
||||
disableConfigurable={true}
|
||||
/>
|
||||
</div>
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
<ValidInfo
|
||||
isValid={!!title && title.toLowerCase().includes(keyword.toLowerCase())}
|
||||
|
||||
@@ -41,6 +41,10 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const validKeywords = React.useMemo(() => {
|
||||
return validateKeywords();
|
||||
}, [keywords]);
|
||||
|
||||
// Workaround for lit components not updating render
|
||||
React.useEffect(() => {
|
||||
setIsReady(false);
|
||||
@@ -54,7 +58,7 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={`seo__keywords mb-8`}>
|
||||
<section className={`seo__keywords__table`}>
|
||||
<h4 className='!text-left'>{localize(LocalizationKey.panelSeoKeywordsTitle)}</h4>
|
||||
|
||||
<VSCodeTable>
|
||||
@@ -159,10 +163,10 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
</VSCodeTableHeader>
|
||||
|
||||
<VSCodeTableBody>
|
||||
{validateKeywords().map((keyword, index) => {
|
||||
{validKeywords.map((keyword, index) => {
|
||||
return (
|
||||
<ErrorBoundary key={keyword} fallback={<div />}>
|
||||
<SeoKeywordInfo key={index} keyword={keyword} {...data} />
|
||||
<SeoKeywordInfo key={index} keywords={validKeywords} keyword={keyword} {...data} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -38,7 +38,7 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
const descriptionFieldName = contentType?.fields.find(f => f.name === descriptionField)?.title || descriptionField;
|
||||
|
||||
return (
|
||||
<div className='space-y-8'>
|
||||
<div className='seo space-y-8'>
|
||||
<section className={`seo__insights`}>
|
||||
<h4 className='!text-left'>{localize(LocalizationKey.panelSeoStatusTitle)}</h4>
|
||||
|
||||
@@ -85,28 +85,30 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
</VSCodeTable>
|
||||
</section>
|
||||
|
||||
<SeoKeywords
|
||||
keywords={metadata?.keywords}
|
||||
title={metadata[titleField]}
|
||||
description={metadata[descriptionField]}
|
||||
slug={metadata.slug}
|
||||
headings={metadata?.articleDetails?.headingsText}
|
||||
wordCount={metadata?.articleDetails?.wordCount}
|
||||
content={metadata?.articleDetails?.content}
|
||||
/>
|
||||
|
||||
<FieldBoundary fieldName={`Keywords`}>
|
||||
<TagPicker
|
||||
type={TagType.keywords}
|
||||
icon={<Icon name="symbol-keyword" className='mr-2' />}
|
||||
crntSelected={(metadata.keywords as string[]) || []}
|
||||
options={[]}
|
||||
freeform={true}
|
||||
focussed={focusElm === TagType.keywords}
|
||||
unsetFocus={unsetFocus}
|
||||
disableConfigurable
|
||||
<section className={`seo__keywords`}>
|
||||
<SeoKeywords
|
||||
keywords={metadata?.keywords}
|
||||
title={metadata[titleField]}
|
||||
description={metadata[descriptionField]}
|
||||
slug={metadata.slug}
|
||||
headings={metadata?.articleDetails?.headingsText}
|
||||
wordCount={metadata?.articleDetails?.wordCount}
|
||||
content={metadata?.articleDetails?.content}
|
||||
/>
|
||||
</FieldBoundary>
|
||||
|
||||
<FieldBoundary fieldName={`Keywords`}>
|
||||
<TagPicker
|
||||
type={TagType.keywords}
|
||||
icon={<Icon name="symbol-keyword" className='mr-2' />}
|
||||
crntSelected={(metadata.keywords as string[]) || []}
|
||||
options={[]}
|
||||
freeform={true}
|
||||
focussed={focusElm === TagType.keywords}
|
||||
unsetFocus={unsetFocus}
|
||||
disableConfigurable
|
||||
/>
|
||||
</FieldBoundary>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}, [contentType, metadata, seo, focusElm, unsetFocus]);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { LocalizationKey, localize } from '../../localization';
|
||||
|
||||
export interface ITagProps {
|
||||
className: string;
|
||||
@@ -14,16 +13,14 @@ export interface ITagProps {
|
||||
onRemove: (tags: string) => void;
|
||||
}
|
||||
|
||||
const Tag: React.FunctionComponent<ITagProps> = (props: React.PropsWithChildren<ITagProps>) => {
|
||||
const { value, title, onRemove, onCreate, disableConfigurable } = props;
|
||||
|
||||
const Tag: React.FunctionComponent<ITagProps> = ({ value, title, onRemove, onCreate, disableConfigurable, className }: React.PropsWithChildren<ITagProps>) => {
|
||||
return (
|
||||
<>
|
||||
<div className={`tag`}>
|
||||
<div className={`tag ${className || ""}`}>
|
||||
{!disableConfigurable && onCreate && (
|
||||
<button
|
||||
className={`tag__create`}
|
||||
title={l10n.t(LocalizationKey.panelTagAdd, value)}
|
||||
title={localize(LocalizationKey.panelTagAdd, value)}
|
||||
type={`button`}
|
||||
onClick={() => onCreate(value)}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Tag } from './Tag';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { LocalizationKey, localize } from '../../localization';
|
||||
|
||||
export interface ITagsProps {
|
||||
values: string[];
|
||||
@@ -34,7 +33,7 @@ const Tags: React.FunctionComponent<ITagsProps> = (props: React.PropsWithChildre
|
||||
value={t}
|
||||
className={`article__tags__items__pill_exists`}
|
||||
onRemove={onRemove}
|
||||
title={l10n.t(LocalizationKey.commonRemoveValue, t)}
|
||||
title={localize(LocalizationKey.commonRemoveValue, t)}
|
||||
/>
|
||||
))}
|
||||
{unknownTags.map((t, idx) => (
|
||||
@@ -44,7 +43,7 @@ const Tags: React.FunctionComponent<ITagsProps> = (props: React.PropsWithChildre
|
||||
className={`article__tags__items__pill_notexists`}
|
||||
onRemove={onRemove}
|
||||
onCreate={onCreate}
|
||||
title={l10n.t(LocalizationKey.panelTagsTagWarning, t)}
|
||||
title={localize(LocalizationKey.panelTagsTagWarning, t)}
|
||||
disableConfigurable={disableConfigurable}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1180,3 +1180,15 @@ vscode-divider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SEO */
|
||||
.seo {
|
||||
.article__tags label,
|
||||
.article__tags__items {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.article__tags {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user