mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 17:32:58 +02:00
#41 - Better visualization of the article length
This commit is contained in:
+22
-2
@@ -93,6 +93,11 @@
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.seo__status__details h4 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.not-valid {
|
||||
color: var(--vscode-errorForeground);
|
||||
}
|
||||
@@ -244,13 +249,28 @@
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.ext_link_block button,
|
||||
.ext_link_block a {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
align-items: center;
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
border: 0px;
|
||||
border-radius: 0px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
font-size: var(--vscode-font-size);
|
||||
font-weight: var(--vscode-font-weight);
|
||||
line-height: 26px;
|
||||
padding: 0px 14px;
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ext_link_block a:hover,
|
||||
.ext_link_block a:active,
|
||||
.ext_link_block a:focus,
|
||||
.ext_link_block a:visited {
|
||||
color: var(--vscode-textLink-activeForeground);
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
}
|
||||
@@ -142,6 +142,11 @@
|
||||
"default": 160,
|
||||
"description": "Specifies the optimal description length for SEO (set to `-1` to turn it off)."
|
||||
},
|
||||
"frontMatter.taxonomy.seoContentLengh": {
|
||||
"type": "number",
|
||||
"default": 1760,
|
||||
"description": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal blog length for SEO in 2021. (set to `-1` to turn it off)."
|
||||
},
|
||||
"frontMatter.taxonomy.seoDescriptionField": {
|
||||
"type": "string",
|
||||
"default": "description",
|
||||
|
||||
@@ -18,6 +18,7 @@ export const SETTING_FRONTMATTER_TYPE = "taxonomy.frontMatterType";
|
||||
|
||||
export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength";
|
||||
export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
|
||||
export const SETTING_SEO_CONTENT_MIN_LENGTH = "taxonomy.seoContentLengh";
|
||||
export const SETTING_SEO_DESCRIPTION_FIELD = "taxonomy.seoDescriptionField";
|
||||
|
||||
export const SETTING_TEMPLATES_FOLDER = "templates.folder";
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface PanelSettings {
|
||||
export interface SEO {
|
||||
title: number;
|
||||
description: number;
|
||||
content: number;
|
||||
descriptionField: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,23 +77,19 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
|
||||
<div className={`ext_settings`}>
|
||||
<div className="ext_link_block">
|
||||
<Icon name={`settings`} />
|
||||
<a href="javascript:;" onClick={openSettings}>Open settings</a>
|
||||
<button onClick={openSettings}><Icon name={`settings`} /> Open settings</button>
|
||||
</div>
|
||||
|
||||
<div className="ext_link_block">
|
||||
<Icon name={`file`} />
|
||||
<a href="javascript:;" onClick={openFile}>Reveal file in folder</a>
|
||||
<button onClick={openFile}><Icon name={`file`} /> Reveal file in folder</button>
|
||||
</div>
|
||||
|
||||
<div className="ext_link_block">
|
||||
<Icon name={`folder-opened`} />
|
||||
<a href="javascript:;" onClick={openProject}>Reveal project folder</a>
|
||||
<button onClick={openProject}><Icon name={`folder-opened`} /> Reveal project folder</button>
|
||||
</div>
|
||||
|
||||
<div className="ext_link_block">
|
||||
<Icon name={`bug`} />
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/issues" title="Open an issue on GitHub">Report an issue</a>
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/issues" title="Open an issue on GitHub"><Icon name={`bug`} /> Report an issue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react';
|
||||
import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents';
|
||||
|
||||
export interface IArticleDetailsProps {
|
||||
details: {
|
||||
headings: number;
|
||||
paragraphs: number;
|
||||
wordCount: number;
|
||||
}
|
||||
}
|
||||
|
||||
export const ArticleDetails: React.FunctionComponent<IArticleDetailsProps> = ({details}: React.PropsWithChildren<IArticleDetailsProps>) => {
|
||||
|
||||
console.log(details);
|
||||
|
||||
if (!details || (details.headings === undefined && details.paragraphs === undefined)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`seo__status__details valid`}>
|
||||
<h4>More details</h4>
|
||||
|
||||
<VsTable>
|
||||
<VsTableHeader slot="header">
|
||||
<VsTableHeaderCell>Type</VsTableHeaderCell>
|
||||
<VsTableHeaderCell>Total</VsTableHeaderCell>
|
||||
</VsTableHeader>
|
||||
<VsTableBody slot="body">
|
||||
{
|
||||
details?.headings !== undefined && (
|
||||
<VsTableRow>
|
||||
<VsTableCell>Headings</VsTableCell>
|
||||
<VsTableCell>{details.headings}</VsTableCell>
|
||||
</VsTableRow>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
details?.paragraphs !== undefined && (
|
||||
<VsTableRow>
|
||||
<VsTableCell>Paragraphs</VsTableCell>
|
||||
<VsTableCell>{details.paragraphs}</VsTableCell>
|
||||
</VsTableRow>
|
||||
)
|
||||
}
|
||||
</VsTableBody>
|
||||
</VsTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents';
|
||||
|
||||
export interface ISeoDetailsProps {
|
||||
allowedLength?: number;
|
||||
allowedLength: number;
|
||||
title: string;
|
||||
value: number;
|
||||
valueTitle: string;
|
||||
noValidation?: boolean;
|
||||
}
|
||||
|
||||
export const SeoDetails: React.FunctionComponent<ISeoDetailsProps> = (props: React.PropsWithChildren<ISeoDetailsProps>) => {
|
||||
const { allowedLength, title, value, valueTitle } = props;
|
||||
const { allowedLength, title, value, valueTitle, noValidation } = props;
|
||||
|
||||
const validate = () => {
|
||||
if (allowedLength === undefined) {
|
||||
if (noValidation) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -19,12 +21,21 @@ export const SeoDetails: React.FunctionComponent<ISeoDetailsProps> = (props: Rea
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`seo__status__details ${validate}`}>
|
||||
<h4><strong>{title}</strong></h4>
|
||||
<ul>
|
||||
<li><b>{valueTitle}</b>: {value}</li>
|
||||
{ allowedLength && <li><b>Recommended length</b>: {allowedLength}</li>}
|
||||
</ul>
|
||||
<div className={`seo__status__details ${validate()}`}>
|
||||
<h4>{title}</h4>
|
||||
|
||||
<VsTable>
|
||||
<VsTableHeader slot="header">
|
||||
<VsTableHeaderCell className={validate()}>{valueTitle}</VsTableHeaderCell>
|
||||
<VsTableHeaderCell>Recommended</VsTableHeaderCell>
|
||||
</VsTableHeader>
|
||||
<VsTableBody slot="body">
|
||||
<VsTableRow>
|
||||
<VsTableCell className={validate()}>{value}</VsTableCell>
|
||||
<VsTableCell>{allowedLength}</VsTableCell>
|
||||
</VsTableRow>
|
||||
</VsTableBody>
|
||||
</VsTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { SEO } from '../../models/PanelSettings';
|
||||
import { ArticleDetails } from './ArticleDetails';
|
||||
import { Icon } from './Icon';
|
||||
import { SeoDetails } from './SeoDetails';
|
||||
|
||||
@@ -28,16 +29,17 @@ export const SeoStatus: React.FunctionComponent<ISeoStatusProps> = (props: React
|
||||
|
||||
{ (data[descriptionField] && seo.description > 0) && <SeoDetails title="Description" valueTitle="Length" allowedLength={seo.description} value={data[descriptionField].length} /> }
|
||||
|
||||
{ data?.articleDetails !== null && (
|
||||
<div className={`seo__status__details valid`}>
|
||||
<h4><strong>Article details</strong></h4>
|
||||
<ul>
|
||||
{ data?.articleDetails?.headings && <li><b>Headings</b>: {data?.articleDetails?.headings}</li> }
|
||||
{ data?.articleDetails?.paragraphs && <li><b>Paragraphs</b>: {data?.articleDetails?.paragraphs}</li> }
|
||||
{ data?.articleDetails?.wordCount && <li><b>Words</b>: {data?.articleDetails?.wordCount}</li> }
|
||||
</ul>
|
||||
</div>
|
||||
) }
|
||||
{
|
||||
seo.content > 0 && data?.articleDetails?.wordCount && (
|
||||
<SeoDetails title="Article length"
|
||||
valueTitle="Words"
|
||||
allowedLength={seo.content}
|
||||
value={data?.articleDetails?.wordCount}
|
||||
noValidation />
|
||||
)
|
||||
}
|
||||
|
||||
<ArticleDetails details={data.articleDetails} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
import {wrapWc} from 'wc-react';
|
||||
|
||||
export const VscodeIcon = wrapWc(`vscode-icon`);
|
||||
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`);
|
||||
@@ -2,7 +2,12 @@ import * as React from "react";
|
||||
import { render } from "react-dom";
|
||||
import { ViewPanel } from "./ViewPanel";
|
||||
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-icon';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table-header';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table-header-cell';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table-body';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table-row';
|
||||
import '@bendera/vscode-webview-elements/dist/vscode-table-cell';
|
||||
|
||||
declare const acquireVsCodeApi: <T = unknown>() => {
|
||||
getState: () => T;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings';
|
||||
import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings';
|
||||
import * as os from 'os';
|
||||
import { PanelSettings, CustomScript } from './../models/PanelSettings';
|
||||
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
|
||||
@@ -224,6 +224,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
seo: {
|
||||
title: config.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
|
||||
description: config.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1,
|
||||
content: config.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number || -1,
|
||||
descriptionField: config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description"
|
||||
},
|
||||
slug: {
|
||||
|
||||
Reference in New Issue
Block a user