diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b4bbb0a..7d00319d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,23 @@ # Change Log +## [5.2.0] - 2021-10-19 + +### 🎨 Enhancements + +- [#151](https://github.com/estruyf/vscode-front-matter/issues/151): Detect which site-generator or framework is used +- [#152](https://github.com/estruyf/vscode-front-matter/issues/152): Automatically set setting based on the used site-generator or framework +- [#154](https://github.com/estruyf/vscode-front-matter/issues/154): Bulk script support added +- [#155](https://github.com/estruyf/vscode-front-matter/issues/155): Fallback image added for the images shown in the editor panel + +### 🐞 Fixes + +- [#153](https://github.com/estruyf/vscode-front-matter/issues/153): Support old date formatting for date-fns +- [#156](https://github.com/estruyf/vscode-front-matter/issues/156): Fix for uploading media files into a new folder + ## [5.1.1] - 2021-10-14 +### 🐞 Fixes + - [#149](https://github.com/estruyf/vscode-front-matter/issues/149): Fix panel rendering when incorrect type for keywords is provided ## [5.1.0] - 2021-10-13 diff --git a/package-lock.json b/package-lock.json index 6be4108d..507bbc3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-front-matter-beta", - "version": "5.1.1", + "version": "5.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0bceb0bb..45959f04 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Front Matter", "description": "An essential Visual Studio Code extension when you want to manage the markdown pages of your static site like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...", "icon": "assets/frontmatter-teal-128x128.png", - "version": "5.1.1", + "version": "5.2.0", "preview": false, "publisher": "eliostruyf", "galleryBanner": { @@ -151,6 +151,22 @@ "nodeBin": { "type": "string", "description": "Path to the node executable. This is required when using NVM, so that there is no confusion of which node version to use." + }, + "bulk": { + "type": "boolean", + "description": "Run the script for all content files" + }, + "output": { + "type": "string", + "enum": [ + "editor", + "notification" + ], + "description": "Define where you want to output your script output. Default is a notification, but you can specify to show it in an editor panel." + }, + "outputType": { + "type": "string", + "description": "The type of output for the editor panel. Can be used to change it to 'markdown' for example" } }, "additionalProperties": false, @@ -180,6 +196,11 @@ "markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.openonstart)", "scope": "Dashboard" }, + "frontMatter.framework.id": { + "type": "string", + "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.panel.freeform": { "type": "boolean", "default": true, diff --git a/src/commands/Article.ts b/src/commands/Article.ts index 6a7f014f..93fcb779 100644 --- a/src/commands/Article.ts +++ b/src/commands/Article.ts @@ -9,6 +9,7 @@ import { extname, basename } from 'path'; import { COMMAND_NAME, DefaultFields } from '../constants'; import { DashboardData } from '../models/DashboardData'; import { ExplorerView } from '../explorerView/ExplorerView'; +import { DateHelper } from '../helpers/DateHelper'; export class Article { @@ -171,7 +172,7 @@ export class Article { let newFileName = `${slugName}${ext}`; if (filePrefix && typeof filePrefix === "string") { - newFileName = `${format(new Date(), filePrefix)}-${newFileName}`; + newFileName = `${format(new Date(), DateHelper.formatUpdate(filePrefix))}-${newFileName}`; } const newPath = editor.document.uri.fsPath.replace(fileName, newFileName); @@ -243,7 +244,7 @@ export class Article { const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; if (dateFormat && typeof dateFormat === "string") { - return format(dateValue, dateFormat); + return format(dateValue, DateHelper.formatUpdate(dateFormat)); } else { return typeof dateValue.toISOString === 'function' ? dateValue.toISOString() : dateValue?.toString(); } diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index c0487931..c6ac8102 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -1,10 +1,10 @@ -import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES, DefaultFields, HOME_PAGE_NAVIGATION_ID, ExtensionState, COMMAND_NAME } from '../constants'; +import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES, DefaultFields, HOME_PAGE_NAVIGATION_ID, ExtensionState, COMMAND_NAME, SETTINGS_FRAMEWORK_ID } from '../constants'; import { ArticleHelper } from './../helpers/ArticleHelper'; import { basename, dirname, extname, join, parse } from "path"; import { existsSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs"; import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env, Position } from "vscode"; import { Settings as SettingsHelper } from '../helpers'; -import { TaxonomyType } from '../models'; +import { Framework, TaxonomyType } from '../models'; import { Folders } from './Folders'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { DashboardMessage } from '../dashboardWebView/DashboardMessage'; @@ -24,6 +24,7 @@ import { MediaLibrary } from '../helpers/MediaLibrary'; import imageSize from 'image-size'; import { parseWinPath } from '../helpers/parseWinPath'; import { DateHelper } from '../helpers/DateHelper'; +import { FrameworkDetector } from '../helpers/FrameworkDetector'; export class Dashboard { private static webview: WebviewPanel | null = null; @@ -187,6 +188,9 @@ export class Dashboard { case DashboardMessage.createMediaFolder: await commands.executeCommand(COMMAND_NAME.createFolder, msg?.data); break; + case DashboardMessage.setFramework: + Dashboard.setFramework(msg?.data); + break; } }); } @@ -257,6 +261,7 @@ export class Dashboard { private static async getSettings() { const ext = Extension.getInstance(); const wsFolder = Folders.getWorkspaceFolder(); + const isInitialized = await Template.isInitialized(); Dashboard.postWebviewMessage({ command: DashboardCommand.settings, @@ -265,7 +270,7 @@ export class Dashboard { wsFolder: wsFolder ? wsFolder.fsPath : '', staticFolder: SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER), folders: Folders.get(), - initialized: await Template.isInitialized(), + initialized: isInitialized, tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag), categories: SettingsHelper.getTaxonomy(TaxonomyType.Category), openOnStart: SettingsHelper.get(SETTINGS_DASHBOARD_OPENONSTART), @@ -274,10 +279,30 @@ export class Dashboard { mediaSnippet: SettingsHelper.get(SETTINGS_DASHBOARD_MEDIA_SNIPPET) || [], contentTypes: SettingsHelper.get(SETTING_TAXONOMY_CONTENT_TYPES) || [], contentFolders: Folders.get().map(f => f.path), + crntFramework: SettingsHelper.get(SETTINGS_FRAMEWORK_ID), + framework: (!isInitialized && wsFolder) ? FrameworkDetector.get(wsFolder.fsPath) : null, } as Settings }); } + /** + * Set the current site-generator or framework + related settings + * @param frameworkId + */ + private static setFramework(frameworkId: string | null) { + SettingsHelper.update(SETTINGS_FRAMEWORK_ID, frameworkId, true); + + if (frameworkId) { + const allFrameworks = FrameworkDetector.getAll(); + const framework = allFrameworks.find((f: Framework) => f.name === frameworkId); + if (framework) { + SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, framework.static, true); + } else { + SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, "", true); + } + } + } + /** * Update a setting from the dashboard */ @@ -316,7 +341,15 @@ export class Dashboard { selectedFolder = ''; } - const relSelectedFolderPath = selectedFolder ? selectedFolder.substring((parseWinPath(wsFolder?.fsPath || "")).length + 1) : ''; + let relSelectedFolderPath = selectedFolder; + const parsedPath = parseWinPath(wsFolder?.fsPath || ""); + if (selectedFolder && selectedFolder.startsWith(parsedPath)) { + relSelectedFolderPath = selectedFolder.replace(parsedPath, ''); + } + + if (relSelectedFolderPath.startsWith('/')) { + relSelectedFolderPath = relSelectedFolderPath.substring(1); + } let allMedia: MediaInfo[] = []; @@ -549,9 +582,9 @@ export class Dashboard { if (imgData) { writeFileSync(staticPath, imgData.data); - Notifications.info(`File ${fileName} uploaded to: ${staticFolder}/${folder}`); + Notifications.info(`File ${fileName} uploaded to: ${folder}`); - const folderPath = `${staticFolder}/${folder}`; + const folderPath = `${folder}`; if (Dashboard.timers[folderPath]) { clearTimeout(Dashboard.timers[folderPath]); delete Dashboard.timers[folderPath]; diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index efbaeb5b..90dd53bc 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -5,6 +5,7 @@ import { commands, env, Uri, ViewColumn, window } from "vscode"; import { Settings } from '../helpers'; import { PreviewSettings } from '../models'; import { format } from 'date-fns'; +import { DateHelper } from '../helpers/DateHelper'; export class Preview { @@ -34,7 +35,7 @@ export class Preview { if (settings.pathname) { const articleDate = ArticleHelper.getDate(article); try { - slug = join(format(articleDate || new Date(), settings.pathname), slug); + slug = join(format(articleDate || new Date(), DateHelper.formatUpdate(settings.pathname)), slug); } catch (error) { slug = join(settings.pathname, slug); } diff --git a/src/constants/FrameworkDetectors.ts b/src/constants/FrameworkDetectors.ts new file mode 100644 index 00000000..5433233d --- /dev/null +++ b/src/constants/FrameworkDetectors.ts @@ -0,0 +1,21 @@ +export const FrameworkDetectors = [ + { + "framework": {"name": "gatsby", "dist": "public", "static": "static", "build": "gatsby build"}, + "requiredFiles": ["gatsby-config.js"], + "requiredDependencies": ["gatsby"] + }, + { + "framework": {"name": "hugo", "dist": "public", "static": "static", "build": "hugo"}, + "requiredFiles": ["config.toml", "config.yaml", "config.yml"] + }, + { + "framework": {"name": "next", "dist": ".next", "static": "public", "build": "next build"}, + "requiredFiles": ["next.config.js"], + "requiredDependencies": ["next"] + }, + { + "framework": {"name": "nuxt", "dist": "dist", "static": "static", "build": "nuxt"}, + "requiredFiles": ["nuxt.config.js"], + "requiredDependencies": ["nuxt"] + } +]; \ No newline at end of file diff --git a/src/constants/settings.ts b/src/constants/settings.ts index fa0f92b2..4643dbc0 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -42,6 +42,8 @@ export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight"; export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart"; export const SETTINGS_DASHBOARD_MEDIA_SNIPPET = "dashboard.mediaSnippet"; +export const SETTINGS_FRAMEWORK_ID = "framework.id"; + /** * @deprecated */ diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index cb86a9c6..242bec0c 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -17,5 +17,6 @@ export enum DashboardMessage { deleteMedia = 'deleteMedia', insertPreviewImage = 'insertPreviewImage', updateMediaMetadata = 'updateMediaMetadata', - createMediaFolder = 'createMediaFolder' + createMediaFolder = 'createMediaFolder', + setFramework = 'setFramework', } \ No newline at end of file diff --git a/src/dashboardWebView/components/Steps/Step.tsx b/src/dashboardWebView/components/Steps/Step.tsx index 7e961ef4..553f40f3 100644 --- a/src/dashboardWebView/components/Steps/Step.tsx +++ b/src/dashboardWebView/components/Steps/Step.tsx @@ -4,22 +4,17 @@ import { Status } from '../../models/Status'; export interface IStepProps { name: string; - description: string; + description: JSX.Element; status: Status; showLine: boolean; - onClick?: () => void; + onClick?: () => void | undefined; } export const Step: React.FunctionComponent = ({name, description, status, showLine, onClick}: React.PropsWithChildren) => { - return ( - <> - { - showLine ? ( - ); -}; \ No newline at end of file +}; + +BaseView.displayName = 'BaseView'; +export { BaseView }; \ No newline at end of file diff --git a/src/panelWebView/components/Collapsible.tsx b/src/panelWebView/components/Collapsible.tsx index 75522084..93f6598d 100644 --- a/src/panelWebView/components/Collapsible.tsx +++ b/src/panelWebView/components/Collapsible.tsx @@ -10,10 +10,26 @@ export interface ICollapsibleProps { sendUpdate?: (open: boolean) => void; } -export const Collapsible: React.FunctionComponent = ({id, children, title, sendUpdate, className}: React.PropsWithChildren) => { +const Collapsible: React.FunctionComponent = ({id, children, title, sendUpdate, className}: React.PropsWithChildren) => { const [ isOpen, setIsOpen ] = React.useState(false); const collapseKey = `collapse-${id}`; + useEffect(() => { + const collapsed = window.localStorage.getItem(collapseKey); + if (collapsed === null || collapsed === 'true') { + setIsOpen(true); + updateStorage(true); + } + + window.addEventListener('message', event => { + const message = event.data; + if (message.command === Command.closeSections) { + setIsOpen(false); + updateStorage(false); + } + }); + }, ['']); + const updateStorage = (value: boolean) => { window.localStorage.setItem(collapseKey, value.toString()); } @@ -32,22 +48,6 @@ export const Collapsible: React.FunctionComponent = ({id, chi } } - useEffect(() => { - const collapsed = window.localStorage.getItem(collapseKey); - if (collapsed === null || collapsed === 'true') { - setIsOpen(true); - updateStorage(true); - } - - window.addEventListener('message', event => { - const message = event.data; - if (message.command === Command.closeSections) { - setIsOpen(false); - updateStorage(false); - } - }); - }, ['']); - return (
@@ -55,4 +55,7 @@ export const Collapsible: React.FunctionComponent = ({id, chi
); -}; \ No newline at end of file +}; + +Collapsible.displayName = 'Collapsible'; +export { Collapsible }; \ No newline at end of file diff --git a/src/panelWebView/components/CustomScript.tsx b/src/panelWebView/components/CustomScript.tsx index 76991086..d39eb2ea 100644 --- a/src/panelWebView/components/CustomScript.tsx +++ b/src/panelWebView/components/CustomScript.tsx @@ -8,7 +8,7 @@ export interface ICustomScriptProps { script: string; } -export const CustomScript: React.FunctionComponent = ({title, script}: React.PropsWithChildren) => { +const CustomScript: React.FunctionComponent = ({title, script}: React.PropsWithChildren) => { const runCustomScript = () => { MessageHelper.sendMessage(CommandToCode.runCustomScript, { title, script }); @@ -17,4 +17,7 @@ export const CustomScript: React.FunctionComponent = ({title return ( ); -}; \ No newline at end of file +}; + +CustomScript.displayName = 'CustomScript'; +export { CustomScript }; \ No newline at end of file diff --git a/src/panelWebView/components/Fields/DateTimeField.tsx b/src/panelWebView/components/Fields/DateTimeField.tsx index 8b125ceb..7d63cf2a 100644 --- a/src/panelWebView/components/Fields/DateTimeField.tsx +++ b/src/panelWebView/components/Fields/DateTimeField.tsx @@ -24,11 +24,6 @@ const CustomInput = forwardRef(({ value, onClick } export const DateTimeField: React.FunctionComponent = ({label, date, format, onChange}: React.PropsWithChildren) => { const [ dateValue, setDateValue ] = React.useState(null); - - const onDateChange = (date: Date) => { - setDateValue(date); - onChange(date); - }; React.useEffect(() => { const crntValue = DateHelper.tryParse(date, format); @@ -38,6 +33,11 @@ export const DateTimeField: React.FunctionComponent = ({lab setDateValue(date); } }, [ date ]); + + const onDateChange = (date: Date) => { + setDateValue(date); + onChange(date); + }; return (
diff --git a/src/panelWebView/components/Fields/ImageFallback.tsx b/src/panelWebView/components/Fields/ImageFallback.tsx new file mode 100644 index 00000000..83f45bff --- /dev/null +++ b/src/panelWebView/components/Fields/ImageFallback.tsx @@ -0,0 +1,38 @@ +import { XCircleIcon } from '@heroicons/react/solid'; +import * as React from 'react'; + +export interface IImageFallbackProps { + src: string; +} + +export const ImageFallback: React.FunctionComponent = ({ src }: React.PropsWithChildren) => { + + if (!src) { + return ( +
+ + +

The image couldn't be loaded

+
+ ); + } + + return ( + + ); +}; \ No newline at end of file diff --git a/src/panelWebView/components/Fields/PreviewImage.tsx b/src/panelWebView/components/Fields/PreviewImage.tsx index d564fb4e..e9ba1c6c 100644 --- a/src/panelWebView/components/Fields/PreviewImage.tsx +++ b/src/panelWebView/components/Fields/PreviewImage.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { ImageFallback } from './ImageFallback'; import { PreviewImageValue } from './PreviewImageField'; export interface IPreviewImageProps { @@ -7,9 +8,10 @@ export interface IPreviewImageProps { } export const PreviewImage: React.FunctionComponent = ({ value, onRemove }: React.PropsWithChildren) => { + return (
- +
diff --git a/src/panelWebView/components/Fields/TextField.tsx b/src/panelWebView/components/Fields/TextField.tsx index 3e4f36d3..41623872 100644 --- a/src/panelWebView/components/Fields/TextField.tsx +++ b/src/panelWebView/components/Fields/TextField.tsx @@ -13,17 +13,17 @@ export interface ITextFieldProps { export const TextField: React.FunctionComponent = ({singleLine, limit, label, value, rows, onChange}: React.PropsWithChildren) => { const [ text, setText ] = React.useState(value); - - const onTextChange = (txtValue: string) => { - setText(txtValue); - onChange(txtValue); - }; React.useEffect(() => { if (text !== value) { setText(value); } }, [ value ]); + + const onTextChange = (txtValue: string) => { + setText(txtValue); + onChange(txtValue); + }; let isValid = true; if (limit && limit !== -1) { @@ -59,8 +59,6 @@ export const TextField: React.FunctionComponent = ({singleLine, ) } - - { limit && limit > 0 && (text || "").length > limit && (
diff --git a/src/panelWebView/components/FileItem.tsx b/src/panelWebView/components/FileItem.tsx index f75fbf94..1d2db400 100644 --- a/src/panelWebView/components/FileItem.tsx +++ b/src/panelWebView/components/FileItem.tsx @@ -9,7 +9,7 @@ export interface IFileItemProps { path: string; } -export const FileItem: React.FunctionComponent = ({ name, path }: React.PropsWithChildren) => { +const FileItem: React.FunctionComponent = ({ name, path }: React.PropsWithChildren) => { const openFile = () => { MessageHelper.sendMessage(CommandToCode.openInEditor, path); @@ -29,4 +29,7 @@ export const FileItem: React.FunctionComponent = ({ name, path } {name} ); -}; \ No newline at end of file +}; + +FileItem.displayName = 'FileItem'; +export { FileItem }; \ No newline at end of file diff --git a/src/panelWebView/components/FileList.tsx b/src/panelWebView/components/FileList.tsx index 857d6373..85b8d9e3 100644 --- a/src/panelWebView/components/FileList.tsx +++ b/src/panelWebView/components/FileList.tsx @@ -9,7 +9,7 @@ export interface IFileListProps { totalFiles: number; } -export const FileList: React.FunctionComponent = ({files, folderName, totalFiles}: React.PropsWithChildren) => { +const FileList: React.FunctionComponent = ({files, folderName, totalFiles}: React.PropsWithChildren) => { if (!files || files.length === 0) { return null; @@ -28,4 +28,7 @@ export const FileList: React.FunctionComponent = ({files, folder
); -}; \ No newline at end of file +}; + +FileList.displayName = 'FileList'; +export { FileList }; \ No newline at end of file diff --git a/src/panelWebView/components/FolderAndFiles.tsx b/src/panelWebView/components/FolderAndFiles.tsx index 9cfba778..93610cf3 100644 --- a/src/panelWebView/components/FolderAndFiles.tsx +++ b/src/panelWebView/components/FolderAndFiles.tsx @@ -9,7 +9,7 @@ export interface IFolderAndFilesProps { isBase?: boolean; } -export const FolderAndFiles: React.FunctionComponent = ({data, isBase}: React.PropsWithChildren) => { +const FolderAndFiles: React.FunctionComponent = ({data, isBase}: React.PropsWithChildren) => { if (!data) { return null; @@ -42,4 +42,7 @@ export const FolderAndFiles: React.FunctionComponent = ({d } ); -}; \ No newline at end of file +}; + +FolderAndFiles.displayName = 'FolderAndFiles'; +export { FolderAndFiles }; \ No newline at end of file diff --git a/src/panelWebView/components/GlobalSettings.tsx b/src/panelWebView/components/GlobalSettings.tsx index 85fd8446..fe503b15 100644 --- a/src/panelWebView/components/GlobalSettings.tsx +++ b/src/panelWebView/components/GlobalSettings.tsx @@ -11,7 +11,7 @@ export interface IGlobalSettingsProps { isBase?: boolean; } -export const GlobalSettings: React.FunctionComponent = ({settings, isBase}: React.PropsWithChildren) => { +const GlobalSettings: React.FunctionComponent = ({settings, isBase}: React.PropsWithChildren) => { const { modifiedDateUpdate, fmHighlighting } = settings || {}; const [ previewUrl, setPreviewUrl ] = React.useState(""); const [ isDirty, setIsDirty ] = React.useState(false); @@ -65,4 +65,7 @@ export const GlobalSettings: React.FunctionComponent = ({s ); -}; \ No newline at end of file +}; + +GlobalSettings.displayName = 'GlobalSettings'; +export { GlobalSettings }; \ No newline at end of file diff --git a/src/panelWebView/components/Icon.tsx b/src/panelWebView/components/Icon.tsx index 81848dd3..96cc048b 100644 --- a/src/panelWebView/components/Icon.tsx +++ b/src/panelWebView/components/Icon.tsx @@ -4,7 +4,10 @@ export interface IIconProps { name: string; } -export const Icon: React.FunctionComponent = ({ name }: React.PropsWithChildren) => { +const Icon: React.FunctionComponent = ({ name }: React.PropsWithChildren) => { return (); -}; \ No newline at end of file +}; + +Icon.displayName = 'Icon'; +export { Icon }; \ No newline at end of file diff --git a/src/panelWebView/components/Metadata.tsx b/src/panelWebView/components/Metadata.tsx index dc42d039..eff704c1 100644 --- a/src/panelWebView/components/Metadata.tsx +++ b/src/panelWebView/components/Metadata.tsx @@ -26,7 +26,7 @@ export interface IMetadataProps { unsetFocus: () => void; } -export const Metadata: React.FunctionComponent = ({settings, metadata, focusElm, unsetFocus}: React.PropsWithChildren) => { +const Metadata: React.FunctionComponent = ({settings, metadata, focusElm, unsetFocus}: React.PropsWithChildren) => { const contentType = useContentType(settings, metadata); const sendUpdate = (field: string | undefined, value: any) => { @@ -200,4 +200,7 @@ export const Metadata: React.FunctionComponent = ({settings, met } ); -}; \ No newline at end of file +}; + +Metadata.displayName = 'Metadata'; +export { Metadata }; \ No newline at end of file diff --git a/src/panelWebView/components/OtherActionButton.tsx b/src/panelWebView/components/OtherActionButton.tsx index a9641fea..308c9071 100644 --- a/src/panelWebView/components/OtherActionButton.tsx +++ b/src/panelWebView/components/OtherActionButton.tsx @@ -6,10 +6,13 @@ export interface IOtherActionButtonProps { onClick: (e: React.SyntheticEvent) => void; } -export const OtherActionButton: React.FunctionComponent = ({ className, disabled, onClick, children}: React.PropsWithChildren) => { +const OtherActionButton: React.FunctionComponent = ({ className, disabled, onClick, children}: React.PropsWithChildren) => { return (
); -}; \ No newline at end of file +}; + +OtherActionButton.displayName = 'OtherActionButton'; +export { OtherActionButton }; \ No newline at end of file diff --git a/src/panelWebView/components/OtherActions.tsx b/src/panelWebView/components/OtherActions.tsx index 6603fce3..a3362fe1 100644 --- a/src/panelWebView/components/OtherActions.tsx +++ b/src/panelWebView/components/OtherActions.tsx @@ -19,7 +19,7 @@ export interface IOtherActionsProps { isBase?: boolean; } -export const OtherActions: React.FunctionComponent = ({isFile, settings, isBase}: React.PropsWithChildren) => { +const OtherActions: React.FunctionComponent = ({isFile, settings, isBase}: React.PropsWithChildren) => { const openSettings = () => { MessageHelper.sendMessage(CommandToCode.openSettings); @@ -64,4 +64,7 @@ export const OtherActions: React.FunctionComponent = ({isFil ); -}; \ No newline at end of file +}; + +OtherActions.displayName = 'OtherActions'; +export { OtherActions }; \ No newline at end of file diff --git a/src/panelWebView/components/Preview.tsx b/src/panelWebView/components/Preview.tsx index 5f0d063c..738cc3be 100644 --- a/src/panelWebView/components/Preview.tsx +++ b/src/panelWebView/components/Preview.tsx @@ -7,7 +7,7 @@ export interface IPreviewProps { slug: string; } -export const Preview: React.FunctionComponent = ({slug}: React.PropsWithChildren) => { +const Preview: React.FunctionComponent = ({slug}: React.PropsWithChildren) => { const open = () => { MessageHelper.sendMessage(CommandToCode.openPreview); @@ -20,4 +20,7 @@ export const Preview: React.FunctionComponent = ({slug}: React.Pr return ( ); -}; \ No newline at end of file +}; + +Preview.displayName = 'Preview'; +export { Preview }; \ No newline at end of file diff --git a/src/panelWebView/components/PublishAction.tsx b/src/panelWebView/components/PublishAction.tsx index 55d98941..e9012c02 100644 --- a/src/panelWebView/components/PublishAction.tsx +++ b/src/panelWebView/components/PublishAction.tsx @@ -9,7 +9,7 @@ export interface IPublishActionProps { draft: boolean; } -export const PublishAction: React.FunctionComponent = (props: React.PropsWithChildren) => { +const PublishAction: React.FunctionComponent = (props: React.PropsWithChildren) => { const { draft } = props; const publish = () => { @@ -19,4 +19,7 @@ export const PublishAction: React.FunctionComponent = (prop return ( ); -}; \ No newline at end of file +}; + +PublishAction.displayName = 'PublishAction'; +export { PublishAction }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoDetails.tsx b/src/panelWebView/components/SeoDetails.tsx index 27c91680..52e44d7c 100644 --- a/src/panelWebView/components/SeoDetails.tsx +++ b/src/panelWebView/components/SeoDetails.tsx @@ -9,7 +9,7 @@ export interface ISeoDetailsProps { noValidation?: boolean; } -export const SeoDetails: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SeoDetails: React.FunctionComponent = (props: React.PropsWithChildren) => { const { allowedLength, title, value, valueTitle, noValidation } = props; const validate = () => { @@ -38,4 +38,7 @@ export const SeoDetails: React.FunctionComponent = (props: Rea
); -}; \ No newline at end of file +}; + +SeoDetails.displayName = 'SeoDetails'; +export { SeoDetails }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoFieldInfo.tsx b/src/panelWebView/components/SeoFieldInfo.tsx index 2b13dd05..d61cf4f5 100644 --- a/src/panelWebView/components/SeoFieldInfo.tsx +++ b/src/panelWebView/components/SeoFieldInfo.tsx @@ -9,7 +9,7 @@ export interface ISeoFieldInfoProps { isValid?: boolean; } -export const SeoFieldInfo: React.FunctionComponent = ({ title, value, recommendation, isValid }: React.PropsWithChildren) => { +const SeoFieldInfo: React.FunctionComponent = ({ title, value, recommendation, isValid }: React.PropsWithChildren) => { return ( {title} @@ -19,4 +19,7 @@ export const SeoFieldInfo: React.FunctionComponent = ({ titl ); -}; \ No newline at end of file +}; + +SeoFieldInfo.displayName = 'SeoFieldInfo'; +export { SeoFieldInfo }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoKeywordInfo.tsx b/src/panelWebView/components/SeoKeywordInfo.tsx index 66829fdd..bdf0b451 100644 --- a/src/panelWebView/components/SeoKeywordInfo.tsx +++ b/src/panelWebView/components/SeoKeywordInfo.tsx @@ -10,7 +10,11 @@ export interface ISeoKeywordInfoProps { content: string; } -export const SeoKeywordInfo: React.FunctionComponent = ({keyword, title, description, slug, content}: React.PropsWithChildren) => { +const SeoKeywordInfo: React.FunctionComponent = ({keyword, title, description, slug, content}: React.PropsWithChildren) => { + + if (!keyword) { + return null; + } return ( @@ -29,4 +33,7 @@ export const SeoKeywordInfo: React.FunctionComponent = ({k ); -}; \ No newline at end of file +}; + +SeoKeywordInfo.displayName = 'SeoKeywordInfo'; +export { SeoKeywordInfo }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoKeywords.tsx b/src/panelWebView/components/SeoKeywords.tsx index 46af35d9..177804d5 100644 --- a/src/panelWebView/components/SeoKeywords.tsx +++ b/src/panelWebView/components/SeoKeywords.tsx @@ -11,7 +11,7 @@ export interface ISeoKeywordsProps { content: string; } -export const SeoKeywords: React.FunctionComponent = ({keywords, ...data}: React.PropsWithChildren) => { +const SeoKeywords: React.FunctionComponent = ({keywords, ...data}: React.PropsWithChildren) => { const validateKeywords = () => { if (!keywords) { @@ -55,8 +55,9 @@ export const SeoKeywords: React.FunctionComponent = ({keyword } - - ); -}; \ No newline at end of file +}; + +SeoKeywords.displayName = 'SeoKeywords'; +export { SeoKeywords }; \ No newline at end of file diff --git a/src/panelWebView/components/SeoStatus.tsx b/src/panelWebView/components/SeoStatus.tsx index 407b562c..357a8568 100644 --- a/src/panelWebView/components/SeoStatus.tsx +++ b/src/panelWebView/components/SeoStatus.tsx @@ -11,14 +11,34 @@ export interface ISeoStatusProps { data: any; } -export const SeoStatus: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SeoStatus: React.FunctionComponent = (props: React.PropsWithChildren) => { const { data, seo } = props; const { title } = data; const [ isOpen, setIsOpen ] = React.useState(true); const tableRef = React.useRef(); + const pushUpdate = React.useRef((value: boolean) => { + setTimeout(() => { + setIsOpen(value); + }, 10); + }).current; const { descriptionField } = seo; + // Workaround for lit components not updating render + React.useEffect(() => { + setTimeout(() => { + let height = 0; + + tableRef.current?.childNodes.forEach((elm: any) => { + height += elm.clientHeight; + }); + + if (height > 0 && tableRef.current) { + tableRef.current.style.height = `${height}px`; + } + }, 10); + }, [title, data[descriptionField], data?.articleDetails?.wordCount]); + if (!title && !data[descriptionField]) { return null; } @@ -72,24 +92,14 @@ export const SeoStatus: React.FunctionComponent = (props: React ); }; - // Workaround for lit components not updating render - React.useEffect(() => { - setTimeout(() => { - let height = 0; - - tableRef.current?.childNodes.forEach((elm: any) => { - height += elm.clientHeight; - }); - - if (height > 0 && tableRef.current) { - tableRef.current.style.height = `${height}px`; - } - }, 10); - }, [title, data[descriptionField], data?.articleDetails?.wordCount]); + return ( - setIsOpen(value)}> + { renderContent() } ); -}; \ No newline at end of file +}; + +SeoStatus.displayName = 'SeoStatus'; +export { SeoStatus }; \ No newline at end of file diff --git a/src/panelWebView/components/SlugAction.tsx b/src/panelWebView/components/SlugAction.tsx index 85f57809..15967aad 100644 --- a/src/panelWebView/components/SlugAction.tsx +++ b/src/panelWebView/components/SlugAction.tsx @@ -11,7 +11,7 @@ export interface ISlugActionProps { slugOpts: Slug; } -export const SlugAction: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SlugAction: React.FunctionComponent = (props: React.PropsWithChildren) => { const { value, crntValue, slugOpts } = props; let slug = SlugHelper.createSlug(value); @@ -24,4 +24,7 @@ export const SlugAction: React.FunctionComponent = (props: Rea return ( ); -}; \ No newline at end of file +}; + +SlugAction.displayName = 'SlugAction'; +export { SlugAction }; \ No newline at end of file diff --git a/src/panelWebView/components/Spinner.tsx b/src/panelWebView/components/Spinner.tsx index 4b0d34ac..ebb4d187 100644 --- a/src/panelWebView/components/Spinner.tsx +++ b/src/panelWebView/components/Spinner.tsx @@ -2,8 +2,11 @@ import * as React from 'react'; export interface ISpinnerProps {} -export const Spinner: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Spinner: React.FunctionComponent = (props: React.PropsWithChildren) => { return (
Loading...
); -}; \ No newline at end of file +}; + +Spinner.displayName = 'Spinner'; +export { Spinner }; \ No newline at end of file diff --git a/src/panelWebView/components/SponsorMsg.tsx b/src/panelWebView/components/SponsorMsg.tsx index c0c92b48..e1c6c6bd 100644 --- a/src/panelWebView/components/SponsorMsg.tsx +++ b/src/panelWebView/components/SponsorMsg.tsx @@ -4,7 +4,7 @@ import { HeartIcon } from './Icons/HeartIcon'; export interface ISponsorMsgProps {} -export const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => { +const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => { return (

@@ -12,4 +12,7 @@ export const SponsorMsg: React.FunctionComponent = (props: Rea

); -}; \ No newline at end of file +}; + +SponsorMsg.displayName = 'SponsorMsg'; +export { SponsorMsg }; \ No newline at end of file diff --git a/src/panelWebView/components/Tag.tsx b/src/panelWebView/components/Tag.tsx index 03f900e5..23527b0f 100644 --- a/src/panelWebView/components/Tag.tsx +++ b/src/panelWebView/components/Tag.tsx @@ -13,7 +13,7 @@ export interface ITagProps { onRemove: (tags: string) => void; } -export const Tag: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Tag: React.FunctionComponent = (props: React.PropsWithChildren) => { const { value, className, title, onRemove, onCreate, disableConfigurable } = props; return ( @@ -25,4 +25,7 @@ export const Tag: React.FunctionComponent = (props: React.PropsWithCh ); -}; \ No newline at end of file +}; + +Tag.displayName = 'Tag'; +export { Tag }; \ No newline at end of file diff --git a/src/panelWebView/components/TagPicker.tsx b/src/panelWebView/components/TagPicker.tsx index 9be777ef..e25fc294 100644 --- a/src/panelWebView/components/TagPicker.tsx +++ b/src/panelWebView/components/TagPicker.tsx @@ -20,7 +20,7 @@ export interface ITagPickerProps { disableConfigurable?: boolean; } -export const TagPicker: React.FunctionComponent = (props: React.PropsWithChildren) => { +const TagPicker: React.FunctionComponent = (props: React.PropsWithChildren) => { const { label, icon, type, crntSelected, options, freeform, focussed, unsetFocus, disableConfigurable } = props; const [ selected, setSelected ] = React.useState([]); const [ inputValue, setInputValue ] = React.useState(""); @@ -194,4 +194,7 @@ export const TagPicker: React.FunctionComponent = (props: React disableConfigurable={!!disableConfigurable} /> ); -}; \ No newline at end of file +}; + +TagPicker.displayName = 'TagPicker'; +export { TagPicker }; \ No newline at end of file diff --git a/src/panelWebView/components/Tags.tsx b/src/panelWebView/components/Tags.tsx index 9a5091a3..a7f2cead 100644 --- a/src/panelWebView/components/Tags.tsx +++ b/src/panelWebView/components/Tags.tsx @@ -11,7 +11,7 @@ export interface ITagsProps { onRemove: (tags: string) => void; } -export const Tags: React.FunctionComponent = (props: React.PropsWithChildren) => { +const Tags: React.FunctionComponent = (props: React.PropsWithChildren) => { const { values, options, onCreate, onRemove, disableConfigurable } = props; const knownTags = values.filter(v => options.includes(v)); @@ -38,4 +38,7 @@ export const Tags: React.FunctionComponent = (props: React.PropsWith } ); -}; \ No newline at end of file +}; + +Tags.displayName = 'Tags'; +export { Tags }; \ No newline at end of file diff --git a/src/panelWebView/components/ValidInfo.tsx b/src/panelWebView/components/ValidInfo.tsx index 9936018a..431f923a 100644 --- a/src/panelWebView/components/ValidInfo.tsx +++ b/src/panelWebView/components/ValidInfo.tsx @@ -6,7 +6,7 @@ export interface IValidInfoProps { isValid: boolean; } -export const ValidInfo: React.FunctionComponent = ({isValid}: React.PropsWithChildren) => { +const ValidInfo: React.FunctionComponent = ({isValid}: React.PropsWithChildren) => { return ( <> { @@ -18,4 +18,7 @@ export const ValidInfo: React.FunctionComponent = ({isValid}: R } ); -}; \ No newline at end of file +}; + +ValidInfo.displayName = 'ValidInfo'; +export { ValidInfo }; \ No newline at end of file diff --git a/src/providers/ContentProvider.ts b/src/providers/ContentProvider.ts new file mode 100644 index 00000000..755e5406 --- /dev/null +++ b/src/providers/ContentProvider.ts @@ -0,0 +1,27 @@ +import { window, Position, TextDocumentContentProvider, Uri, workspace, WorkspaceEdit, Range, languages, ViewColumn } from "vscode"; + + +export default class ContentProvider implements TextDocumentContentProvider { + + public static get scheme() { return "frontmatter" }; + + provideTextDocumentContent(uri: Uri): string { + return uri.query; + } + + public static async show(data: string, title: string, outputType?: string) { + const apiData = JSON.stringify(data, null, 2); + + const uri = Uri.parse(`${ContentProvider.scheme}:${title} output`); + + const doc = await workspace.openTextDocument(uri); + + await window.showTextDocument(doc, { preview: true, viewColumn: ViewColumn.Beside, preserveFocus: true }); + + const workEdits = new WorkspaceEdit(); + workEdits.replace(doc.uri, new Range(new Position(0, 0), new Position(doc.lineCount, 0)), data); + await workspace.applyEdit(workEdits); + + await languages.setTextDocumentLanguage(doc, outputType || "text"); + } +} \ No newline at end of file