mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-05-01 11:02:26 +02:00
Preparing the 1.10.0 release
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
## [1.10.0] - 2020-12-03
|
||||
|
||||
- FrontMatter panel implemented. This panel allows you to control all extension actions, but not only that. It makes adding tags and categories in a easier way to your page.
|
||||
|
||||
## [1.9.0] - 2020-11-25
|
||||
|
||||
- [#23](https://github.com/estruyf/vscode-front-matter/issues/23): Implemented the option to create and use templates for article creation (front matter will be updates as well).
|
||||
|
||||
12
README.md
12
README.md
@@ -10,6 +10,18 @@ The extension will automatically verify if your title and description are SEO co
|
||||
|
||||
> If you see something missing in your article creation flow, please feel free to reach out.
|
||||
|
||||
## FrontMatter Panel (introduced in 1.10.0)
|
||||
|
||||
In version `1.10.0` of this extension, the FrontMatter panel got introduced. This panel allows you to perform most of the extension actions by just a click on the button.
|
||||
|
||||

|
||||
|
||||
Originally this panel was created to make it easier to add tags and categories to your articles. As the current vscode multi-select is not optimal to use.
|
||||
|
||||
To leverage most of the capabilities of the extension. SEO information and common actions like slug optimization, updating the date and publish/drafting the article.
|
||||
|
||||
> **Info**: By default the tags/categories picker allows you to insert none existing tags/categories. When you enter a none existing tag/category, the panel shows an add `+` icon in front of that button. This allows you to store this tag/category to your settings. If you want to disable this feature, you can do that by setting the `frontMatter.panel.freeform` setting to `false`.
|
||||
|
||||
## Creating articles from templates
|
||||
|
||||
By default, the extension looks for files stored in a `.templates` folder which should be located in the root of your website project.
|
||||
|
||||
BIN
assets/frontmatter-panel.png
Normal file
BIN
assets/frontmatter-panel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -1,23 +0,0 @@
|
||||
|
||||
(function () {
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
|
||||
switch (message.type) {
|
||||
case 'addColor':
|
||||
addColor();
|
||||
break;
|
||||
case 'clearColors':
|
||||
colors = [];
|
||||
updateColorList(colors);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
vscode.postMessage({ command: 'get-data' });
|
||||
console.log('Ready to accept data.');
|
||||
};
|
||||
}());
|
||||
@@ -101,6 +101,10 @@
|
||||
border: 1px solid rgba(0, 0, 0, .9);
|
||||
}
|
||||
|
||||
.article__tags input {
|
||||
border: 1px solid var(--vscode-inputValidation-infoBorder);
|
||||
}
|
||||
|
||||
.article__tags ul {
|
||||
color: var(--vscode-dropdown-foreground);
|
||||
background-color: var(--vscode-dropdown-background);
|
||||
@@ -117,6 +121,7 @@
|
||||
}
|
||||
|
||||
.article__tags li[data-focus="true"] {
|
||||
color: var(--vscode-button-foreground);
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
@@ -128,18 +133,50 @@
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.article__tags__items__btn {
|
||||
.article__tags__items__item {
|
||||
display: inline-block;
|
||||
margin-bottom: .5rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.article__tags__items__item_add,
|
||||
.article__tags__items__item_delete {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.article__tags__items__btn span {
|
||||
.article__tags__items__item svg {
|
||||
display: inline;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.article__tags__items__item_delete span {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.article__tags__items__pill_notexists {
|
||||
color: var(--vscode-inputValidation-errorForeground);
|
||||
background-color: var(--vscode-inputValidation-errorBackground);
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
.article__tags__items__pill_notexists:hover {
|
||||
color: var(--vscode-inputValidation-errorForeground);
|
||||
background-color: var(--vscode-inputValidation-errorBackground);
|
||||
|
||||
filter: contrast(60%);
|
||||
}
|
||||
|
||||
.article__tags__items__item_add {
|
||||
color: var(--vscode-inputValidation-infoForeground);
|
||||
background-color: var(--vscode-inputValidation-infoBackground);
|
||||
border-right: 1px solid var(--vscode-inputValidation-infoBorder);
|
||||
}
|
||||
|
||||
.article__tags__items__item_add:hover {
|
||||
color: var(--vscode-inputValidation-infoForeground);
|
||||
background-color: var(--vscode-inputValidation-infoBackground);
|
||||
border-right: 1px solid var(--vscode-inputValidation-infoBorder);
|
||||
|
||||
filter: contrast(60%);
|
||||
}
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -71,6 +71,15 @@
|
||||
"react-transition-group": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"@material-ui/icons": {
|
||||
"version": "4.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz",
|
||||
"integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4"
|
||||
}
|
||||
},
|
||||
"@material-ui/lab": {
|
||||
"version": "4.0.0-alpha.56",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz",
|
||||
|
||||
18
package.json
18
package.json
@@ -146,6 +146,11 @@
|
||||
"type": "string",
|
||||
"default": "yyyy-MM-dd",
|
||||
"description": "Specify the prefix you want to add for your new article filenames."
|
||||
},
|
||||
"frontMatter.panel.freeform": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -217,6 +222,10 @@
|
||||
"test-compile": "tsc -p ./"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iarna/toml": "2.2.3",
|
||||
"@material-ui/core": "4.11.1",
|
||||
"@material-ui/icons": "4.11.2",
|
||||
"@material-ui/lab": "4.0.0-alpha.56",
|
||||
"@types/glob": "7.1.3",
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/mocha": "^5.2.6",
|
||||
@@ -229,16 +238,13 @@
|
||||
"gray-matter": "4.0.2",
|
||||
"html-loader": "1.3.2",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
"ts-loader": "8.0.3",
|
||||
"tslint": "6.1.3",
|
||||
"typescript": "4.0.2",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-cli": "3.3.12",
|
||||
"@iarna/toml": "2.2.3",
|
||||
"@material-ui/core": "4.11.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.56",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1"
|
||||
"webpack-cli": "3.3.12"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,6 @@ export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength";
|
||||
export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
|
||||
|
||||
export const SETTING_TEMPLATES_FOLDER = "templates.folder";
|
||||
export const SETTING_TEMPLATES_PREFIX = "templates.prefix";
|
||||
export const SETTING_TEMPLATES_PREFIX = "templates.prefix";
|
||||
|
||||
export const SETTING_PANEL_FREEFORM = "panel.freeform";
|
||||
@@ -4,6 +4,7 @@ export interface PanelSettings {
|
||||
slug: Slug;
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
freeform: boolean;
|
||||
}
|
||||
|
||||
export interface SEO {
|
||||
|
||||
@@ -4,5 +4,7 @@ export enum CommandToCode {
|
||||
updateDate = 'update-date',
|
||||
publish = 'publish',
|
||||
updateTags = "update-tags",
|
||||
updateCategories = "update-categories"
|
||||
updateCategories = "update-categories",
|
||||
addTagToSettings = "add-tag",
|
||||
addCategoryToSettings = "add-category"
|
||||
}
|
||||
@@ -35,10 +35,10 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
settings && metadata && <Actions metadata={metadata} settings={settings} />
|
||||
}
|
||||
{
|
||||
(settings && settings.tags && settings.tags.length > 0) && <TagPicker type={TagType.tags} crntSelected={metadata.tags || []} options={settings.tags} />
|
||||
(settings && settings.tags && settings.tags.length > 0) && <TagPicker type={TagType.tags} crntSelected={metadata.tags || []} options={settings.tags} freeform={settings.freeform} />
|
||||
}
|
||||
{
|
||||
(settings && settings.categories && settings.categories.length > 0) && <TagPicker type={TagType.categories} crntSelected={metadata.categories || []} options={settings.categories} />
|
||||
(settings && settings.categories && settings.categories.length > 0) && <TagPicker type={TagType.categories} crntSelected={metadata.categories || []} options={settings.categories} freeform={settings.freeform} />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import AddIcon from '@material-ui/icons/Add';
|
||||
import DeleteIcon from '@material-ui/icons/Delete';
|
||||
|
||||
export interface ITagProps {
|
||||
className: string;
|
||||
value: string;
|
||||
title: string;
|
||||
|
||||
onCreate?: (tags: string) => void;
|
||||
onRemove: (tags: string) => void;
|
||||
}
|
||||
|
||||
export const Tag: React.FunctionComponent<ITagProps> = (props: React.PropsWithChildren<ITagProps>) => {
|
||||
const { value, className, title, onRemove } = props;
|
||||
const { value, className, title, onRemove, onCreate } = props;
|
||||
|
||||
return (
|
||||
<button title={title} className={`article__tags__items__btn ${className}`} onClick={() => onRemove(value)}>{value} <span>x</span></button>
|
||||
<div className={`article__tags__items__item`}>
|
||||
{
|
||||
onCreate &&
|
||||
<button title={`Add ${value} to your settings`} className={`article__tags__items__item_add`} onClick={() => onCreate(value)}><AddIcon /></button>
|
||||
}
|
||||
<button title={title} className={`article__tags__items__item_delete ${className}`} onClick={() => onRemove(value)}>{value} <span><DeleteIcon /></span></button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -10,10 +10,11 @@ export interface ITagPickerProps {
|
||||
type: string;
|
||||
crntSelected: string[];
|
||||
options: string[];
|
||||
freeform: boolean;
|
||||
}
|
||||
|
||||
export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React.PropsWithChildren<ITagPickerProps>) => {
|
||||
const { type, crntSelected, options } = props;
|
||||
const { type, crntSelected, options, freeform } = props;
|
||||
const [ selected, setSelected ] = React.useState<string[]>([]);
|
||||
const prevSelected = usePrevious(crntSelected);
|
||||
|
||||
@@ -21,7 +22,7 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
|
||||
id: 'use-autocomplete',
|
||||
options: options,
|
||||
multiple: true,
|
||||
autoComplete: true,
|
||||
freeSolo: freeform,
|
||||
value: crntSelected,
|
||||
getOptionDisabled: (option) => selected.includes(option),
|
||||
onChange: (e, values: string[]) => {
|
||||
@@ -37,6 +38,11 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
|
||||
sendUpdate(newSelection);
|
||||
};
|
||||
|
||||
const onCreate = (tag: string) => {
|
||||
const cmdType = type === TagType.tags ? CommandToCode.addTagToSettings : CommandToCode.addCategoryToSettings;
|
||||
MessageHelper.sendMessage(cmdType, tag);
|
||||
};
|
||||
|
||||
const sendUpdate = (values: string[]) => {
|
||||
const cmdType = type === TagType.tags ? CommandToCode.updateTags : CommandToCode.updateCategories;
|
||||
MessageHelper.sendMessage(cmdType, values);
|
||||
@@ -66,7 +72,7 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
|
||||
) : null
|
||||
}
|
||||
|
||||
<Tags values={selected} onRemove={onRemove} options={options} />
|
||||
<Tags values={selected} onRemove={onRemove} onCreate={onCreate} options={options} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -5,17 +5,26 @@ export interface ITagsProps {
|
||||
values: string[];
|
||||
options: string[];
|
||||
|
||||
onCreate: (tags: string) => void;
|
||||
onRemove: (tags: string) => void;
|
||||
}
|
||||
|
||||
export const Tags: React.FunctionComponent<ITagsProps> = (props: React.PropsWithChildren<ITagsProps>) => {
|
||||
const { values, options, onRemove } = props;
|
||||
const { values, options, onCreate, onRemove } = props;
|
||||
|
||||
const knownTags = values.filter(v => options.includes(v));
|
||||
const unknownTags = values.filter(v => !options.includes(v));
|
||||
|
||||
return (
|
||||
<div className={`article__tags__items`}>
|
||||
{
|
||||
values.map(t => (
|
||||
<Tag key={t.replace(/ /g, "_")} value={t} className={`${options.includes(t) ? 'article__tags__items__pill_exists' : 'article__tags__items__pill_notexists'}`} onRemove={onRemove} title={`${options.includes(t) ? `Remove ${t}` : `Be aware, this tag "${t}" is not saved in your settings.`}`} />
|
||||
knownTags.map(t => (
|
||||
<Tag key={t.replace(/ /g, "_")} value={t} className={`article__tags__items__pill_exists`} onRemove={onRemove} title={`Remove ${t}`} />
|
||||
))
|
||||
}
|
||||
{
|
||||
unknownTags.map(t => (
|
||||
<Tag key={t.replace(/ /g, "_")} value={t} className={`article__tags__items__pill_notexists`} onRemove={onRemove} onCreate={onCreate} title={`Be aware, this tag "${t}" is not saved in your settings. Once removed, it will be gone forever.`} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { PanelSettings } from './../models/PanelSettings';
|
||||
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace } from "vscode";
|
||||
import { CONFIG_KEY, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants";
|
||||
import { ArticleHelper } from "../helpers";
|
||||
import { CONFIG_KEY, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../constants";
|
||||
import { ArticleHelper, SettingsHelper } from "../helpers";
|
||||
import { Command } from "../viewpanel/Command";
|
||||
import { CommandToCode } from '../viewpanel/CommandToCode';
|
||||
import { Article } from '../commands';
|
||||
import { TagType } from '../viewpanel/TagType';
|
||||
import { TaxonomyType } from '../models';
|
||||
|
||||
|
||||
export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
@@ -88,6 +89,12 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
case CommandToCode.updateCategories:
|
||||
this.updateTags(TagType.categories, msg.data || []);
|
||||
break;
|
||||
case CommandToCode.addTagToSettings:
|
||||
this.addTags(TagType.tags, msg.data);
|
||||
break;
|
||||
case CommandToCode.addCategoryToSettings:
|
||||
this.addTags(TagType.categories, msg.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -135,7 +142,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
suffix: config.get(SETTING_SLUG_SUFFIX) || ""
|
||||
},
|
||||
tags: config.get(SETTING_TAXONOMY_TAGS) || [],
|
||||
categories: config.get(SETTING_TAXONOMY_CATEGORIES) || []
|
||||
categories: config.get(SETTING_TAXONOMY_CATEGORIES) || [],
|
||||
freeform: config.get(SETTING_PANEL_FREEFORM)
|
||||
} as PanelSettings
|
||||
});
|
||||
}
|
||||
@@ -172,6 +180,26 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to the settings
|
||||
* @param tagType
|
||||
* @param value
|
||||
*/
|
||||
private async addTags(tagType: TagType, value: string) {
|
||||
if (value) {
|
||||
const config = workspace.getConfiguration(CONFIG_KEY);
|
||||
let options = tagType === TagType.tags ? config.get<string[]>(SETTING_TAXONOMY_TAGS) : config.get<string[]>(SETTING_TAXONOMY_CATEGORIES);
|
||||
|
||||
if (!options) {
|
||||
options = [];
|
||||
}
|
||||
|
||||
options.push(value);
|
||||
const taxType = tagType === TagType.tags ? TaxonomyType.Tag : TaxonomyType.Category;
|
||||
await SettingsHelper.update(taxType, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the panel
|
||||
* @param msg
|
||||
|
||||
Reference in New Issue
Block a user