Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev

This commit is contained in:
Elio Struyf
2021-11-23 20:00:04 +01:00
14 changed files with 243 additions and 49 deletions
+5
View File
@@ -10,6 +10,11 @@
- [#178](https://github.com/estruyf/vscode-front-matter/issues/178): Sorting added to the media dashboard
- [#179](https://github.com/estruyf/vscode-front-matter/issues/179): Updated the `open dashboard` icon to make it easier to spot it
- [#180](https://github.com/estruyf/vscode-front-matter/issues/180): Added `{filename}` as placeholder for media snippets
- [#181](https://github.com/estruyf/vscode-front-matter/issues/181): Support for custom taxonomy fields added
### 🐞 Fixes
- [#183](https://github.com/estruyf/vscode-front-matter/issues/183): Fix type error on the `frontMatter.content.sorting` setting
## [5.5.0] - 2021-11-15
+96 -27
View File
@@ -97,6 +97,25 @@
"markdownDescription": "Specify if you want to automatically update the modified date of your article/page. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.autoupdatedate)",
"scope": "Content"
},
"frontMatter.content.defaultSorting": {
"type": "string",
"default": "",
"oneOf": [
{
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
]
},
{
"type": "string"
}
],
"markdownDescription": "Specify the default sorting option for the content dashboard. You can use one of the values from the enum or define your own ID. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.content.sorting.default)",
"scope": "Content"
},
"frontMatter.content.draftField": {
"type": "object",
"markdownDescription": "Define the draft field you want to use to manage your content. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.content.draftField)",
@@ -221,30 +240,6 @@
},
"scope": "Content"
},
"frontMatter.content.defaultSorting": {
"type": "string",
"default": "",
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
],
"markdownDescription": "Specify the default sorting option for the content dashboard. You can use one of the values from the ENUM or define your own ID. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.content.sorting.default)",
"scope": "Content"
},
"frontMatter.media.defaultSorting": {
"type": "string",
"default": "",
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
],
"markdownDescription": "Specify the default sorting option for the media dashboard. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.media.sorting.default)",
"scope": "Content"
},
"frontMatter.custom.scripts": {
"type": "array",
"default": [],
@@ -282,9 +277,9 @@
},
"type": {
"type": "string",
"default": "article",
"default": "content",
"enum": [
"article",
"content",
"mediaFolder",
"mediaFile"
],
@@ -323,6 +318,18 @@
"default": "",
"markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.framework.id)"
},
"frontMatter.media.defaultSorting": {
"type": "string",
"default": "",
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
"FileNameAsc",
"FileNameDesc"
],
"markdownDescription": "Specify the default sorting option for the media dashboard. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.media.sorting.default)",
"scope": "Content"
},
"frontMatter.panel.freeform": {
"type": "boolean",
"default": true,
@@ -400,6 +407,7 @@
"boolean",
"image",
"choice",
"taxonomy",
"tags",
"categories",
"draft"
@@ -456,12 +464,47 @@
"type": "boolean",
"default": false,
"description": "Do you want to hide the field from the metadata section?"
},
"taxonomyId": {
"type": "string",
"default": "",
"description": "The ID of your taxonomy field"
}
},
"additionalProperties": false,
"required": [
"type",
"name"
],
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "taxonomy"
}
}
},
"then": {
"required": [
"taxonomyId"
]
}
},
{
"if": {
"properties": {
"type": {
"const": "choice"
}
}
},
"then": {
"required": [
"choices"
]
}
}
]
}
},
@@ -530,6 +573,32 @@
],
"scope": "Taxonomy"
},
"frontMatter.taxonomy.customTaxonomy": {
"type": "array",
"markdownDescription": "Specify the custom taxonomy field data. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.tags)",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID for your taxonomy field"
},
"options": {
"type": "array",
"description": "Options from which you can pick",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"id",
"options"
]
},
"scope": "Taxonomy"
},
"frontMatter.taxonomy.dateField": {
"type": "string",
"default": "date",
@@ -899,4 +968,4 @@
"webpack-cli": "3.3.12"
},
"dependencies": {}
}
}
+1 -1
View File
@@ -318,7 +318,7 @@ export class Dashboard {
contentFolders: Folders.get(),
crntFramework: SettingsHelper.get<string>(SETTINGS_FRAMEWORK_ID),
framework: (!isInitialized && wsFolder) ? FrameworkDetector.get(wsFolder.fsPath) : null,
scripts: (SettingsHelper.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type && s.type !== ScriptType.Article),
scripts: (SettingsHelper.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type && s.type !== ScriptType.Content),
dashboardState: {
contents: {
sorting: await ext.getState<SortingOption | undefined>(ExtensionState.Dashboard.Contents.Sorting, "workspace"),
+1 -1
View File
@@ -23,7 +23,7 @@ export const DEFAULT_CONTENT_TYPE: ContentType = {
"type": "datetime"
},
{
"title": "Article preview",
"title": "Content preview",
"name": "preview",
"type": "image"
},
+2
View File
@@ -4,6 +4,8 @@ export const CONFIG_KEY = "frontMatter";
export const SETTING_TAXONOMY_TAGS = "taxonomy.tags";
export const SETTING_TAXONOMY_CATEGORIES = "taxonomy.categories";
export const SETTING_TAXONOMY_CUSTOM = "taxonomy.customTaxonomy";
export const SETTING_DATE_FORMAT = "taxonomy.dateFormat";
export const SETTING_COMMA_SEPARATED_FIELDS = "taxonomy.commaSeparatedFields";
export const SETTING_TAXONOMY_CONTENT_TYPES = "taxonomy.contentTypes";
+44 -3
View File
@@ -1,6 +1,6 @@
import { DashboardData } from '../models/DashboardData';
import { Template } from '../commands/Template';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS, SETTINGS_CONTENT_DRAFT_FIELD, SETTING_SEO_SLUG_LENGTH, SETTING_SITE_BASEURL } from '../constants';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS, SETTINGS_CONTENT_DRAFT_FIELD, SETTING_SEO_SLUG_LENGTH, SETTING_SITE_BASEURL, SETTING_TAXONOMY_CUSTOM } from '../constants';
import * as os from 'os';
import { PanelSettings, CustomScript as ICustomScript } from '../models/PanelSettings';
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
@@ -9,7 +9,7 @@ import { Command } from "../panelWebView/Command";
import { CommandToCode } from '../panelWebView/CommandToCode';
import { Article } from '../commands';
import { TagType } from '../panelWebView/TagType';
import { DraftField, TaxonomyType } from '../models';
import { CustomTaxonomyData, DraftField, ScriptType, TaxonomyType } from '../models';
import { exec } from 'child_process';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { Content } from 'mdast';
@@ -109,12 +109,18 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
case CommandToCode.updateKeywords:
this.updateTags(TagType.keywords, msg.data || []);
break;
case CommandToCode.updateCustomTaxonomy:
this.updateCustomTaxonomy(msg.data);
break;
case CommandToCode.addTagToSettings:
this.addTags(TagType.tags, msg.data);
break;
case CommandToCode.addCategoryToSettings:
this.addTags(TagType.categories, msg.data);
break;
case CommandToCode.addToCustomTaxonomy:
this.addCustomTaxonomy(msg.data);
break;
case CommandToCode.openSettings:
commands.executeCommand('workbench.action.openSettings', '@ext:eliostruyf.vscode-front-matter');
break;
@@ -405,8 +411,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
},
tags: Settings.get(SETTING_TAXONOMY_TAGS, true) || [],
categories: Settings.get(SETTING_TAXONOMY_CATEGORIES, true) || [],
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
freeform: Settings.get(SETTING_PANEL_FREEFORM),
scripts: (Settings.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === "article" || !s.type),
scripts: (Settings.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === ScriptType.Content || !s.type),
isInitialized: await Template.isInitialized(),
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
@@ -464,6 +471,40 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
}
}
/**
* Update the tags in the current document
* @param data
*/
private updateCustomTaxonomy(data: CustomTaxonomyData) {
if (!data?.id || !data?.name) {
return;
}
const editor = window.activeTextEditor;
if (!editor) {
return "";
}
const article = ArticleHelper.getFrontMatter(editor);
if (article && article.data) {
article.data[data.name] = data.options || [];
ArticleHelper.update(editor, article);
this.pushMetadata(article!.data);
}
}
/**
* Add tag to the settings
* @param data
*/
private async addCustomTaxonomy(data: CustomTaxonomyData) {
if (!data?.id || !data?.option) {
return;
}
await Settings.updateCustomTaxonomy(data.id, data.option);
}
/**
* Add tag to the settings
* @param tagType
+28 -2
View File
@@ -1,8 +1,8 @@
import { Notifications } from './Notifications';
import { commands, Uri, workspace, window } from 'vscode';
import * as vscode from 'vscode';
import { ContentType, TaxonomyType } from '../models';
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, SETTINGS_CONTENT_STATIC_FOLDER, ExtensionState } from '../constants';
import { ContentType, CustomTaxonomy, TaxonomyType } from '../models';
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, ExtensionState, SETTING_TAXONOMY_CUSTOM } from '../constants';
import { Folders } from '../commands/Folders';
import { join, basename } from 'path';
import { existsSync, readFileSync, watch, writeFileSync } from 'fs';
@@ -203,6 +203,32 @@ export class Settings {
await Settings.update(configSetting, options, true);
}
/**
* Update the custom taxonomy settings
*
* @param config
* @param type
* @param options
*/
public static async updateCustomTaxonomy(id: string, option: string) {
const customTaxonomies = Settings.get<CustomTaxonomy[]>(SETTING_TAXONOMY_CUSTOM, true) || [];
let taxIdx = customTaxonomies?.findIndex(o => o.id === id);
if (taxIdx === -1) {
customTaxonomies.push({
id,
options: []
} as CustomTaxonomy);
taxIdx = customTaxonomies?.findIndex(o => o.id === id);
}
customTaxonomies[taxIdx].options.push(option);
customTaxonomies[taxIdx].options = [...new Set(customTaxonomies[taxIdx].options)];
customTaxonomies[taxIdx].options = customTaxonomies[taxIdx].options.sort().filter(o => !!o);
await Settings.update(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
}
/**
* Promote settings from local to team level
*/
+7
View File
@@ -0,0 +1,7 @@
export interface CustomTaxonomyData {
id: string | undefined;
name: string | undefined;
options?: string[] | undefined;
option?: string | undefined;
}
+9 -2
View File
@@ -9,6 +9,7 @@ export interface PanelSettings {
tags: string[];
date: DateInfo;
categories: string[];
customTaxonomy: CustomTaxonomy[];
freeform: boolean;
scripts: CustomScript[];
isInitialized: boolean;
@@ -32,12 +33,13 @@ export interface ContentType {
export interface Field {
title?: string;
name: string;
type: "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft";
type: "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft" | "taxonomy";
choices?: string[] | Choice[];
single?: boolean;
multiple?: boolean;
isPreviewImage?: boolean;
hidden?: boolean;
taxonomyId?: string;
}
export interface DateInfo {
@@ -83,8 +85,13 @@ export interface PreviewSettings {
pathname: string | undefined;
}
export interface CustomTaxonomy {
id: string;
options: string[];
}
export enum ScriptType {
Article = "article",
Content = "content",
MediaFolder = "mediaFolder",
MediaFile = "mediaFile"
}
+1
View File
@@ -1,5 +1,6 @@
export * from './Choice';
export * from './ContentFolder';
export * from './CustomTaxonomyData';
export * from './DashboardData';
export * from './DraftField';
export * from './Framework';
+2
View File
@@ -26,4 +26,6 @@ export enum CommandToCode {
updateMetadata = "update-metadata",
openDashboard = "open-dashboard",
selectImage = "select-image",
updateCustomTaxonomy = "updateCustomTaxonomy",
addToCustomTaxonomy = "addToCustomTaxonomy",
}
+2 -1
View File
@@ -1,5 +1,6 @@
export enum TagType {
tags = "Tags",
categories = "Categories",
keywords = "Keywords"
keywords = "Keywords",
custom = "Custom"
}
+19
View File
@@ -159,6 +159,25 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, metadata,
unsetFocus={unsetFocus} />
</FieldBoundary>
);
} else if (field.type === 'taxonomy') {
const taxonomyData = settings.customTaxonomy.find(ct => ct.id === field.taxonomyId);
const selectedValues = metadata[field.name] || [];
return (
<FieldBoundary key={field.name} fieldName={field.title || field.name}>
<TagPicker
type={TagType.custom}
label={field.title || field.name}
icon={<ListUnorderedIcon />}
crntSelected={selectedValues as string[] || []}
options={taxonomyData?.options || []}
freeform={settings.freeform}
focussed={focusElm === TagType.custom}
unsetFocus={unsetFocus}
fieldName={field.name}
taxonomyId={field.taxonomyId} />
</FieldBoundary>
);
} else if (field.type === 'categories') {
return (
<FieldBoundary key={field.name} fieldName={field.title || field.name}>
+26 -12
View File
@@ -7,9 +7,10 @@ import Downshift from 'downshift';
import { AddIcon } from './Icons/AddIcon';
import { VsLabel } from './VscodeComponents';
import { MessageHelper } from '../../helpers/MessageHelper';
import { CustomTaxonomyData } from '../../models';
export interface ITagPickerProps {
type: string;
type: TagType;
icon: JSX.Element;
label?: string;
crntSelected: string[];
@@ -18,10 +19,12 @@ export interface ITagPickerProps {
focussed: boolean;
unsetFocus: () => void;
disableConfigurable?: boolean;
fieldName?: string;
taxonomyId?: string;
}
const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React.PropsWithChildren<ITagPickerProps>) => {
const { label, icon, type, crntSelected, options, freeform, focussed, unsetFocus, disableConfigurable } = props;
const { label, icon, type, crntSelected, options, freeform, focussed, unsetFocus, disableConfigurable, fieldName, taxonomyId } = props;
const [ selected, setSelected ] = React.useState<string[]>([]);
const [ inputValue, setInputValue ] = React.useState<string>("");
const prevSelected = usePrevious(crntSelected);
@@ -43,26 +46,37 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React.PropsW
* @param tag
*/
const onCreate = (tag: string) => {
const cmdType = type === TagType.tags ? CommandToCode.addTagToSettings : CommandToCode.addCategoryToSettings;
MessageHelper.sendMessage(cmdType, tag);
if (type === TagType.tags) {
MessageHelper.sendMessage(CommandToCode.addTagToSettings, tag);
} else if (type === TagType.categories) {
MessageHelper.sendMessage(CommandToCode.addCategoryToSettings, tag);
} else if (type === TagType.custom) {
MessageHelper.sendMessage(CommandToCode.addToCustomTaxonomy, {
id: taxonomyId,
name: fieldName,
option: tag
} as CustomTaxonomyData);
}
};
/**
* Send an update to VSCode
* @param values
*/
const sendUpdate = (values: string[]) => {
let cmdType = CommandToCode.updateCategories;
const sendUpdate = (values: string[]) => {
if (type === TagType.tags) {
cmdType = CommandToCode.updateTags;
MessageHelper.sendMessage(CommandToCode.updateTags, values);
} else if (type === TagType.categories) {
cmdType = CommandToCode.updateCategories;
MessageHelper.sendMessage(CommandToCode.updateCategories, values);
} else if (type === TagType.keywords) {
cmdType = CommandToCode.updateKeywords;
MessageHelper.sendMessage(CommandToCode.updateKeywords, values);
} else if (type === TagType.custom) {
MessageHelper.sendMessage(CommandToCode.updateCustomTaxonomy, {
id: taxonomyId,
name: fieldName,
options: values
} as CustomTaxonomyData);
}
MessageHelper.sendMessage(cmdType, values);
};
/**