mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-06-28 14:01:57 +02:00
Merge branch 'dev' of github.com:estruyf/vscode-front-matter into poc/git-branching
This commit is contained in:
@@ -5,10 +5,12 @@
|
||||
### ✨ New features
|
||||
|
||||
- [#731](https://github.com/estruyf/vscode-front-matter/issues/731): Added the ability to map/unmap taxonomy to multiple pages at once
|
||||
- [#746](https://github.com/estruyf/vscode-front-matter/issues/746): Placeholder support added to to the `slug` field
|
||||
- [#749](https://github.com/estruyf/vscode-front-matter/issues/749): Ability to set your own filters on the content dashboard with the `frontMatter.content.filters` setting
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#673](https://github.com/estruyf/vscode-front-matter/pull/673): Added git settings to the welcome view and settings view
|
||||
- [#727](https://github.com/estruyf/vscode-front-matter/pull/727): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387)
|
||||
- [#737](https://github.com/estruyf/vscode-front-matter/issues/737): Optimize the grid layout of the content and media dashboards
|
||||
- [#739](https://github.com/estruyf/vscode-front-matter/pull/739): New Git settings to disable and require a commit message
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
"settings.diagnostic": "Diagnostic",
|
||||
"settings.diagnostic.description": "You can run the diagnostics to check the whole Front Matter CMS configuration.",
|
||||
"settings.diagnostic.link": "Run full diagnostics",
|
||||
"settings.git": "Git synchronization",
|
||||
"settings.git.enabled": "Enable Git synchronization to easily sync your changes with your repository.",
|
||||
"settings.git.commitMessage": "Commit message",
|
||||
"settings.git.submoduleInfo": "When working with Git submodules, you can refer to the submodule settings in the documentation.",
|
||||
"settings.git.submoduleLink": "Read more about Git submodules",
|
||||
|
||||
"settings.commonSettings.website.title": "Website and SSG settings",
|
||||
"settings.commonSettings.previewUrl": "Preview URL",
|
||||
@@ -278,6 +283,8 @@
|
||||
"dashboard.steps.stepsToGetStarted.contentFolders.information.description": "You can also perform this action by right-clicking on the folder in the explorer view, and selecting register folder",
|
||||
"dashboard.steps.stepsToGetStarted.tags.name": "Import all tags and categories (optional)",
|
||||
"dashboard.steps.stepsToGetStarted.tags.description": "Now that Front Matter knows all the content folders. Would you like to import all tags and categories from the available content?",
|
||||
"dashboard.steps.stepsToGetStarted.git.name": "Do you want to enable Git synchronization?",
|
||||
"dashboard.steps.stepsToGetStarted.git.description": "Enable Git synchronization to eaily sync your changes with your repository.",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.name": "Show the dashboard",
|
||||
"dashboard.steps.stepsToGetStarted.showDashboard.description": "Once all actions are completed, the dashboard can be loaded.",
|
||||
"dashboard.steps.stepsToGetStarted.template.name": "Use a configuration template",
|
||||
|
||||
@@ -1604,6 +1604,14 @@
|
||||
"default": null,
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description%"
|
||||
},
|
||||
"slugTemplate": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null,
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.slugTemplate.description%"
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
@@ -1842,6 +1850,11 @@
|
||||
"markdownDescription": "%setting.frontMatter.taxonomy.slugSuffix.markdownDescription%",
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.slugTemplate": {
|
||||
"type": "string",
|
||||
"markdownDescription": "%setting.frontMatter.taxonomy.slugTemplate.markdownDescription%",
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.tags": {
|
||||
"type": "array",
|
||||
"markdownDescription": "%setting.frontMatter.taxonomy.tags.markdownDescription%",
|
||||
|
||||
@@ -211,6 +211,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "Specify if the comparison is case sensitive. Default: true",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "Specify if you want to create a folder when creating new content.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "Defines a custom preview path for the content type.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.slugTemplate.description": "Defines a custom slug template for the content type.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.template.description": "An optional template that can be used for creating new content.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.postScript.description": "An optional post script that can be used after new content creation.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.filePrefix.description": "Defines a prefix for the file name.",
|
||||
@@ -237,6 +238,7 @@
|
||||
"setting.frontMatter.taxonomy.seoTitleLength.markdownDescription": "Specifies the optimal title length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlelength)",
|
||||
"setting.frontMatter.taxonomy.slugPrefix.markdownDescription": "Specify a prefix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugprefix)",
|
||||
"setting.frontMatter.taxonomy.slugSuffix.markdownDescription": "Specify a suffix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugsuffix)",
|
||||
"setting.frontMatter.taxonomy.slugTemplate.markdownDescription": "Defines a custom slug template for the content you will create. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugtemplate)",
|
||||
"setting.frontMatter.taxonomy.tags.markdownDescription": "Specifies the tags which can be used in the Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags)",
|
||||
"setting.frontMatter.telemetry.disable.markdownDescription": "Specify if you want to disable the telemetry. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.telemetry.disable)",
|
||||
"setting.frontMatter.templates.enabled.markdownDescription": "Specify if you want to use templates. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.enabled)",
|
||||
|
||||
+27
-14
@@ -15,18 +15,23 @@ import {
|
||||
import * as vscode from 'vscode';
|
||||
import { CustomPlaceholder, Field } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { ArticleHelper, Settings, SlugHelper } from '../helpers';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Settings,
|
||||
SlugHelper,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders
|
||||
} from '../helpers';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { extname, basename, parse, dirname } from 'path';
|
||||
import { COMMAND_NAME, DefaultFields } from '../constants';
|
||||
import { DashboardData, SnippetRange } from '../models/DashboardData';
|
||||
import { DashboardData, SnippetInfo, SnippetRange } from '../models/DashboardData';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { MediaListener } from '../listeners/panel';
|
||||
import { NavigationType } from '../dashboardWebView/models';
|
||||
import { processKnownPlaceholders } from '../helpers/PlaceholderHelper';
|
||||
import { Position } from 'vscode';
|
||||
import { SNIPPET } from '../constants/Snippet';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
@@ -124,7 +129,7 @@ export class Article {
|
||||
/**
|
||||
* Generate the new slug
|
||||
*/
|
||||
public static generateSlug(title: string) {
|
||||
public static generateSlug(title: string, article?: ParsedFrontMatter, slugTemplate?: string) {
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
@@ -132,13 +137,15 @@ export class Article {
|
||||
const prefix = Settings.get(SETTING_SLUG_PREFIX) as string;
|
||||
const suffix = Settings.get(SETTING_SLUG_SUFFIX) as string;
|
||||
|
||||
const slug = SlugHelper.createSlug(title);
|
||||
if (article?.data) {
|
||||
const slug = SlugHelper.createSlug(title, article?.data, slugTemplate);
|
||||
|
||||
if (slug) {
|
||||
return {
|
||||
slug,
|
||||
slugWithPrefixAndSuffix: `${prefix}${slug}${suffix}`
|
||||
};
|
||||
if (slug) {
|
||||
return {
|
||||
slug,
|
||||
slugWithPrefixAndSuffix: `${prefix}${slug}${suffix}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -168,7 +175,7 @@ export class Article {
|
||||
|
||||
const titleField = 'title';
|
||||
const articleTitle: string = article.data[titleField];
|
||||
const slugInfo = Article.generateSlug(articleTitle);
|
||||
const slugInfo = Article.generateSlug(articleTitle, article, contentType.slugTemplate);
|
||||
|
||||
if (slugInfo && slugInfo.slug && slugInfo.slugWithPrefixAndSuffix) {
|
||||
article.data['slug'] = slugInfo.slugWithPrefixAndSuffix;
|
||||
@@ -192,9 +199,13 @@ export class Article {
|
||||
);
|
||||
for (const pField of customPlaceholderFields) {
|
||||
article.data[pField.name] = customPlaceholder.value;
|
||||
article.data[pField.name] = processKnownPlaceholders(
|
||||
article.data[pField.name] = processArticlePlaceholdersFromData(
|
||||
article.data[pField.name],
|
||||
article.data,
|
||||
contentType
|
||||
);
|
||||
article.data[pField.name] = processTimePlaceholders(
|
||||
article.data[pField.name],
|
||||
articleTitle,
|
||||
dateFormat
|
||||
);
|
||||
}
|
||||
@@ -388,7 +399,7 @@ export class Article {
|
||||
snippetStartBeforePos = linesBeforeSelection.length - snippetStartBeforePos - 1;
|
||||
}
|
||||
|
||||
let snippetInfo: { id: string; fields: any[] } | undefined = undefined;
|
||||
let snippetInfo: SnippetInfo | undefined = undefined;
|
||||
let range: SnippetRange | undefined = undefined;
|
||||
if (
|
||||
snippetEndAfterPos > -1 &&
|
||||
@@ -412,6 +423,7 @@ export class Article {
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
|
||||
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: NavigationType.Snippets,
|
||||
@@ -419,6 +431,7 @@ export class Article {
|
||||
fileTitle: article?.data.title || '',
|
||||
filePath: editor.document.uri.fsPath,
|
||||
fieldName: basename(editor.document.uri.fsPath),
|
||||
contentType,
|
||||
position,
|
||||
range,
|
||||
selection: selectionText,
|
||||
|
||||
@@ -31,6 +31,7 @@ import { GitListener, ModeListener } from '../listeners/general';
|
||||
import { Folders } from './Folders';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
@@ -204,7 +205,7 @@ export class Dashboard {
|
||||
* @param msg
|
||||
*/
|
||||
public static postWebviewMessage(msg: {
|
||||
command: DashboardCommand;
|
||||
command: DashboardCommand | DashboardMessage;
|
||||
requestId?: string;
|
||||
payload?: unknown;
|
||||
error?: unknown;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ContentFolder, FileInfo, FolderInfo, StaticFolder } from '../models';
|
||||
import uniqBy = require('lodash.uniqby');
|
||||
import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Logger, processKnownPlaceholders, Settings } from '../helpers';
|
||||
import { Logger, Settings, processTimePlaceholders } from '../helpers';
|
||||
import { existsSync } from 'fs';
|
||||
import { format } from 'date-fns';
|
||||
import { Dashboard } from './Dashboard';
|
||||
@@ -377,7 +377,7 @@ export class Folders {
|
||||
let folderPath: string | undefined = Folders.absWsFolder(folder, wsFolder);
|
||||
if (folderPath.includes(`{{`) && folderPath.includes(`}}`)) {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
folderPath = processKnownPlaceholders(folderPath, undefined, dateFormat);
|
||||
folderPath = processTimePlaceholders(folderPath, dateFormat);
|
||||
} else {
|
||||
if (folderPath && !existsSync(folderPath)) {
|
||||
Notifications.errorShowOnce(
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { join, parse } from 'path';
|
||||
import { commands, env, Uri, ViewColumn, window, WebviewPanel, extensions } from 'vscode';
|
||||
import { Extension, parseWinPath, processKnownPlaceholders, Settings } from '../helpers';
|
||||
import { Extension, parseWinPath, processTimePlaceholders, Settings } from '../helpers';
|
||||
import { ContentFolder, ContentType, PreviewSettings } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
@@ -294,7 +294,7 @@ export class Preview {
|
||||
if (pathname) {
|
||||
// Known placeholders
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
pathname = processKnownPlaceholders(pathname, article?.data?.title, dateFormat);
|
||||
pathname = processTimePlaceholders(pathname, dateFormat);
|
||||
|
||||
// Custom placeholders
|
||||
pathname = await ArticleHelper.processCustomPlaceholders(
|
||||
@@ -318,7 +318,7 @@ export class Preview {
|
||||
}
|
||||
|
||||
// Support front matter placeholders - {{fm.<field>}}
|
||||
pathname = processFmPlaceholders(pathname, article?.data);
|
||||
pathname = article?.data ? processFmPlaceholders(pathname, article?.data) : pathname;
|
||||
|
||||
try {
|
||||
const articleDate = ArticleHelper.getDate(article);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const GIT_CONFIG = {
|
||||
defaultCommitMessage: 'Synced by Front Matter'
|
||||
};
|
||||
@@ -8,3 +8,5 @@ export const DOCUMENTATION_SETTINGS_LINK = 'https://frontmatter.codes/docs/setti
|
||||
|
||||
export const SENTRY_LINK =
|
||||
'https://1ac45704bbe74264a7b4674bdc2abf48@o1022172.ingest.sentry.io/5988293';
|
||||
|
||||
export const DOCS_SUBMODULES = 'https://frontmatter.codes/docs/git-integration#git-submodules';
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from './ExtensionState';
|
||||
export * from './Features';
|
||||
export * from './FrameworkDetectors';
|
||||
export * from './GeneralCommands';
|
||||
export * from './Git';
|
||||
export * from './Links';
|
||||
export * from './LocalStore';
|
||||
export * from './Navigation';
|
||||
|
||||
@@ -25,6 +25,7 @@ export const SETTING_TAXONOMY_CONTENT_TYPES = 'taxonomy.contentTypes';
|
||||
|
||||
export const SETTING_SLUG_PREFIX = 'taxonomy.slugPrefix';
|
||||
export const SETTING_SLUG_SUFFIX = 'taxonomy.slugSuffix';
|
||||
export const SETTING_SLUG_TEMPLATE = 'taxonomy.slugTemplate';
|
||||
export const SETTING_SLUG_UPDATE_FILE_NAME = 'taxonomy.alignFilename';
|
||||
|
||||
export const SETTING_INDENT_ARRAY = 'taxonomy.indentArrays';
|
||||
|
||||
@@ -54,6 +54,7 @@ export enum DashboardMessage {
|
||||
insertSnippet = 'insertSnippet',
|
||||
addSnippet = 'addSnippet',
|
||||
updateSnippet = 'updateSnippet',
|
||||
updateSnippetPlaceholders = 'updateSnippetPlaceholders',
|
||||
|
||||
// Taxonomy dashboard
|
||||
getTaxonomyData = 'getTaxonomyData',
|
||||
|
||||
@@ -6,15 +6,17 @@ import { useRecoilValue } from 'recoil';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { SettingsInput } from './SettingsInput';
|
||||
import { VSCodeButton } from '@vscode/webview-ui-toolkit/react';
|
||||
import { FrameworkDetectors, SETTING_FRAMEWORK_START, SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL } from '../../../constants';
|
||||
import { DOCS_SUBMODULES, FrameworkDetectors, GIT_CONFIG, SETTING_FRAMEWORK_START, SETTING_GIT_COMMIT_MSG, SETTING_GIT_ENABLED, SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL } from '../../../constants';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { SettingsCheckbox } from './SettingsCheckbox';
|
||||
import { ChevronRightIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
export interface ICommonSettingsProps { }
|
||||
|
||||
interface Config {
|
||||
name: string;
|
||||
value: string;
|
||||
value: string | boolean;
|
||||
}
|
||||
|
||||
export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (props: React.PropsWithChildren<ICommonSettingsProps>) => {
|
||||
@@ -22,7 +24,7 @@ export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (pr
|
||||
const [config, setConfig] = React.useState<Config[]>([]);
|
||||
const [updated, setUpdated] = React.useState<boolean>(false);
|
||||
|
||||
const onSettingChange = React.useCallback((name: string, value: string) => {
|
||||
const onSettingChange = React.useCallback((name: string, value: string | boolean) => {
|
||||
setConfig((prev) => {
|
||||
const setting = prev.find((c) => c.name === name);
|
||||
if (setting) {
|
||||
@@ -39,7 +41,13 @@ export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (pr
|
||||
}, [config]);
|
||||
|
||||
const retrieveSettings = () => {
|
||||
messageHandler.request<Config[]>(DashboardMessage.getSettings, [SETTING_PREVIEW_HOST, SETTING_WEBSITE_URL, SETTING_FRAMEWORK_START]).then((config) => {
|
||||
messageHandler.request<Config[]>(DashboardMessage.getSettings, [
|
||||
SETTING_PREVIEW_HOST,
|
||||
SETTING_WEBSITE_URL,
|
||||
SETTING_FRAMEWORK_START,
|
||||
SETTING_GIT_ENABLED,
|
||||
SETTING_GIT_COMMIT_MSG,
|
||||
]).then((config) => {
|
||||
setConfig(config);
|
||||
setUpdated(false);
|
||||
});
|
||||
@@ -65,6 +73,42 @@ export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (pr
|
||||
<Startup settings={settings} />
|
||||
</div>
|
||||
|
||||
<div className='py-4'>
|
||||
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsGit)}</h2>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<SettingsCheckbox
|
||||
label={l10n.t(LocalizationKey.settingsGitEnabled)}
|
||||
name={SETTING_GIT_ENABLED}
|
||||
value={(config.find((c) => c.name === SETTING_GIT_ENABLED)?.value || false) as boolean}
|
||||
onChange={onSettingChange}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
label={l10n.t(LocalizationKey.settingsGitCommitMessage)}
|
||||
name={SETTING_GIT_COMMIT_MSG}
|
||||
value={(config.find((c) => c.name === SETTING_GIT_COMMIT_MSG)?.value || "") as string}
|
||||
placeholder={GIT_CONFIG.defaultCommitMessage}
|
||||
onChange={onSettingChange}
|
||||
/>
|
||||
|
||||
<p className={`text-[var(--frontmatter-secondary-text)] flex items-center`}>
|
||||
<ChevronRightIcon className='h-4 w-4 inline' />
|
||||
|
||||
<span>
|
||||
{l10n.t(LocalizationKey.settingsGitSubmoduleInfo)}
|
||||
</span>
|
||||
|
||||
<a
|
||||
href={DOCS_SUBMODULES}
|
||||
title={l10n.t(LocalizationKey.settingsGitSubmoduleLink)}
|
||||
className='text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]'>
|
||||
{l10n.t(LocalizationKey.settingsGitSubmoduleLink)}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='py-4'>
|
||||
<h2 className='text-xl mb-2'>{l10n.t(LocalizationKey.settingsCommonSettingsWebsiteTitle)}</h2>
|
||||
|
||||
@@ -72,21 +116,21 @@ export const CommonSettings: React.FunctionComponent<ICommonSettingsProps> = (pr
|
||||
<SettingsInput
|
||||
label={l10n.t(LocalizationKey.settingsCommonSettingsPreviewUrl)}
|
||||
name={SETTING_PREVIEW_HOST}
|
||||
value={config.find((c) => c.name === SETTING_PREVIEW_HOST)?.value || ""}
|
||||
value={(config.find((c) => c.name === SETTING_PREVIEW_HOST)?.value || "") as string}
|
||||
onChange={onSettingChange}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
label={l10n.t(LocalizationKey.settingsCommonSettingsWebsiteUrl)}
|
||||
name={SETTING_WEBSITE_URL}
|
||||
value={config.find((c) => c.name === SETTING_WEBSITE_URL)?.value || ""}
|
||||
value={(config.find((c) => c.name === SETTING_WEBSITE_URL)?.value || "") as string}
|
||||
onChange={onSettingChange}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
label={l10n.t(LocalizationKey.settingsCommonSettingsStartCommand)}
|
||||
name={SETTING_FRAMEWORK_START}
|
||||
value={config.find((c) => c.name === SETTING_FRAMEWORK_START)?.value || ""}
|
||||
value={(config.find((c) => c.name === SETTING_FRAMEWORK_START)?.value || "") as string}
|
||||
onChange={onSettingChange}
|
||||
fallback={FrameworkDetectors.find((f) => f.framework.name === settings?.crntFramework)?.commands.start || ""}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ISettingsCheckboxProps {
|
||||
label: string;
|
||||
name: string;
|
||||
value: boolean;
|
||||
onChange: (key: string, value: boolean) => void;
|
||||
}
|
||||
|
||||
export const SettingsCheckbox: React.FunctionComponent<ISettingsCheckboxProps> = ({
|
||||
label,
|
||||
name,
|
||||
value,
|
||||
onChange
|
||||
}: React.PropsWithChildren<ISettingsCheckboxProps>) => {
|
||||
const [isEnabled, setIsEnabled] = React.useState(false);
|
||||
|
||||
const updateValue = (value: boolean) => {
|
||||
setIsEnabled(value);
|
||||
onChange(name, value);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsEnabled(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<VSCodeCheckbox
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateValue(e.target.checked)}
|
||||
checked={isEnabled}>
|
||||
{label}
|
||||
</VSCodeCheckbox>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ export interface ISettingsInputProps {
|
||||
label: string;
|
||||
name: string;
|
||||
value: string;
|
||||
placeholder?: string;
|
||||
onChange: (key: string, value: string) => void;
|
||||
fallback?: string;
|
||||
}
|
||||
@@ -13,6 +14,7 @@ export const SettingsInput: React.FunctionComponent<ISettingsInputProps> = ({
|
||||
label,
|
||||
name,
|
||||
value,
|
||||
placeholder,
|
||||
onChange,
|
||||
fallback
|
||||
}: React.PropsWithChildren<ISettingsInputProps>) => {
|
||||
@@ -24,6 +26,7 @@ export const SettingsInput: React.FunctionComponent<ISettingsInputProps> = ({
|
||||
boxShadow: 'none'
|
||||
}}
|
||||
value={value || fallback || ""}
|
||||
placeholder={placeholder}
|
||||
onInput={(e: React.ChangeEvent<HTMLInputElement>) => onChange(name, e.target.value)}>
|
||||
{label}
|
||||
</VSCodeTextField>
|
||||
|
||||
@@ -260,6 +260,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
ref={formRef}
|
||||
snippetKey={snippetKey}
|
||||
snippet={snippet}
|
||||
filePath={viewData?.data?.filePath}
|
||||
fieldInfo={viewData?.data?.snippetInfo?.fields}
|
||||
selection={viewData?.data?.selection} />
|
||||
</FormDialog>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { processKnownPlaceholders } from '../../../helpers/PlaceholderHelper';
|
||||
import { SnippetParser } from '../../../helpers/SnippetParser';
|
||||
import { Snippet, SnippetField, SnippetInfoField, SnippetSpecialPlaceholders } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
@@ -14,6 +13,7 @@ export interface ISnippetFormProps {
|
||||
snippetKey?: string;
|
||||
snippet: Snippet;
|
||||
selection: string | undefined;
|
||||
filePath?: string;
|
||||
fieldInfo?: SnippetInfoField[];
|
||||
mediaData?: any;
|
||||
onInsert?: (mediaData: any) => void;
|
||||
@@ -24,7 +24,7 @@ export interface SnippetFormHandle {
|
||||
}
|
||||
|
||||
const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFormProps> = (
|
||||
{ snippetKey, snippet, selection, fieldInfo, mediaData, onInsert },
|
||||
{ snippetKey, snippet, selection, filePath, fieldInfo, mediaData, onInsert },
|
||||
ref
|
||||
) => {
|
||||
const viewData = useRecoilValue(ViewDataSelector);
|
||||
@@ -41,20 +41,19 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
|
||||
);
|
||||
|
||||
const insertPlaceholderValues = useCallback(
|
||||
(value: SnippetSpecialPlaceholders) => {
|
||||
async (value: SnippetSpecialPlaceholders) => {
|
||||
if (value === 'FM_SELECTED_TEXT') {
|
||||
return selection || '';
|
||||
}
|
||||
|
||||
value = processKnownPlaceholders(
|
||||
value = await messageHandler.request<string>(DashboardMessage.updateSnippetPlaceholders, {
|
||||
value,
|
||||
viewData?.data?.fileTitle || '',
|
||||
settings?.date.format || ''
|
||||
);
|
||||
filePath
|
||||
});
|
||||
|
||||
return value;
|
||||
},
|
||||
[selection]
|
||||
[selection, filePath]
|
||||
);
|
||||
|
||||
const insertValueFromMedia = useCallback(
|
||||
@@ -124,7 +123,7 @@ ${snippetBody}
|
||||
}
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
const processFields = useCallback(async () => {
|
||||
// Get all placeholder variables from the snippet
|
||||
const body = typeof snippet.body === 'string' ? snippet.body : snippet.body.join(`\n`);
|
||||
|
||||
@@ -143,7 +142,7 @@ ${snippetBody}
|
||||
if (idx > -1) {
|
||||
allFields.push({
|
||||
...field,
|
||||
value: insertPlaceholderValues(field.default || '')
|
||||
value: await insertPlaceholderValues(field.default || '')
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -163,6 +162,10 @@ ${snippetBody}
|
||||
}
|
||||
|
||||
setFields(allFields);
|
||||
}, [snippet, insertPlaceholderValues, insertValueFromMedia]);
|
||||
|
||||
useEffect(() => {
|
||||
processFields();
|
||||
}, [snippet]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,11 +13,12 @@ import { FrameworkDetectors } from '../../../constants/FrameworkDetectors';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { SelectItem } from './SelectItem';
|
||||
import { Templates } from '../../../constants';
|
||||
import { GeneralCommands, SETTING_GIT_ENABLED, Templates } from '../../../constants';
|
||||
import { TemplateItem } from './TemplateItem';
|
||||
import { Spinner } from '../Common/Spinner';
|
||||
import { AstroContentTypes } from '../Configuration/Astro/AstroContentTypes';
|
||||
import { ContentFolders } from '../Configuration/Common/ContentFolders';
|
||||
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||
|
||||
export interface IStepsToGetStartedProps {
|
||||
settings: Settings;
|
||||
@@ -29,6 +30,8 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [framework, setFramework] = useState<string | null>(null);
|
||||
const [taxImported, setTaxImported] = useState<boolean>(false);
|
||||
const [isGitEnabled, setIsGitEnabled] = useState<boolean>(false);
|
||||
const [isGitRepo, setIsGitRepo] = useState<boolean>(false);
|
||||
const [templates, setTemplates] = useState<Template[]>([]);
|
||||
const [astroCollectionsStatus, setAstroCollectionsStatus] = useState<Status>(Status.Optional);
|
||||
|
||||
@@ -73,6 +76,15 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
|
||||
setTaxImported(true);
|
||||
};
|
||||
|
||||
const updateSetting = (name: string, value: any) => {
|
||||
setIsGitEnabled(value);
|
||||
Messenger.send(DashboardMessage.updateSetting, {
|
||||
name,
|
||||
value,
|
||||
global: true
|
||||
});
|
||||
}
|
||||
|
||||
const crntTemplates = useMemo(() => {
|
||||
if (!templates || templates.length === 0 || !settings.crntFramework) {
|
||||
return [];
|
||||
@@ -230,6 +242,21 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
|
||||
show: settings.crntFramework === 'astro' || framework === 'astro',
|
||||
status: settings.initialized && settings.staticFolder && settings.staticFolder !== "/" ? Status.Completed : Status.NotStarted,
|
||||
},
|
||||
{
|
||||
id: `welcome-git`,
|
||||
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedGitName),
|
||||
description: (
|
||||
<div className='mt-1'>
|
||||
<VSCodeCheckbox
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateSetting(SETTING_GIT_ENABLED, e.target.checked)}
|
||||
checked={isGitEnabled}>
|
||||
{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedGitDescription)}
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
),
|
||||
show: isGitRepo,
|
||||
status: settings.git.actions ? Status.Completed : Status.NotStarted
|
||||
},
|
||||
{
|
||||
id: `welcome-import`,
|
||||
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedTagsName),
|
||||
@@ -257,7 +284,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
|
||||
: undefined
|
||||
}
|
||||
]
|
||||
), [settings, framework, taxImported, templates, astroCollectionsStatus]);
|
||||
), [settings, framework, taxImported, templates, astroCollectionsStatus, isGitRepo]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (settings.crntFramework || settings.framework?.name) {
|
||||
@@ -265,6 +292,14 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
|
||||
}
|
||||
}, [settings.crntFramework, settings.framework]);
|
||||
|
||||
React.useEffect(() => {
|
||||
messageHandler.request<boolean>(GeneralCommands.toVSCode.gitIsRepo).then((result) => {
|
||||
setIsGitRepo(result);
|
||||
});
|
||||
|
||||
setIsGitEnabled(settings.git.actions);
|
||||
}, [settings.git.actions]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchTemplates = async () => {
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,17 @@ import {
|
||||
} from '../constants';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { FrontMatterParser, ParsedFrontMatter } from '../parsers';
|
||||
import { ContentType, Extension, Logger, Settings, SlugHelper, isValidFile, parseWinPath } from '.';
|
||||
import {
|
||||
ContentType,
|
||||
Extension,
|
||||
Logger,
|
||||
Settings,
|
||||
SlugHelper,
|
||||
isValidFile,
|
||||
parseWinPath,
|
||||
processArticlePlaceholdersFromPath,
|
||||
processTimePlaceholders
|
||||
} from '.';
|
||||
import { format, parse } from 'date-fns';
|
||||
import { Notifications } from './Notifications';
|
||||
import { Article } from '../commands';
|
||||
@@ -37,7 +47,6 @@ import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { Link, Parent } from 'mdast-util-from-markdown/lib';
|
||||
import { Content } from 'mdast';
|
||||
import { processKnownPlaceholders } from './PlaceholderHelper';
|
||||
import { CustomScript } from './CustomScript';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { existsAsync, readFileAsync } from '../utils';
|
||||
@@ -57,6 +66,19 @@ export class ArticleHelper {
|
||||
return ArticleHelper.getFrontMatterFromDocument(editor.document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the front matter from the current active document.
|
||||
* @returns The front matter object if found, otherwise undefined.
|
||||
*/
|
||||
public static getFrontMatterFromCurrentDocument() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
return ArticleHelper.getFrontMatterFromDocument(editor.document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of the specified document
|
||||
*
|
||||
@@ -524,7 +546,12 @@ export class ArticleHelper {
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
public static async updatePlaceholders(data: any, title: string, filePath: string) {
|
||||
public static async updatePlaceholders(
|
||||
data: any,
|
||||
title: string,
|
||||
filePath: string,
|
||||
slugTemplate?: string
|
||||
) {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
const fmData = Object.assign({}, data);
|
||||
|
||||
@@ -536,10 +563,11 @@ export class ArticleHelper {
|
||||
}
|
||||
|
||||
if (fieldName === 'slug' && (fieldValue === null || fieldValue === '')) {
|
||||
fmData[fieldName] = SlugHelper.createSlug(title);
|
||||
fmData[fieldName] = SlugHelper.createSlug(title, fmData, slugTemplate);
|
||||
}
|
||||
|
||||
fmData[fieldName] = processKnownPlaceholders(fmData[fieldName], title, dateFormat);
|
||||
fmData[fieldName] = await processArticlePlaceholdersFromPath(fmData[fieldName], filePath);
|
||||
fmData[fieldName] = processTimePlaceholders(fmData[fieldName], dateFormat);
|
||||
fmData[fieldName] = await this.processCustomPlaceholders(fmData[fieldName], title, filePath);
|
||||
}
|
||||
|
||||
@@ -597,7 +625,11 @@ export class ArticleHelper {
|
||||
}
|
||||
|
||||
const regex = new RegExp(`{{${placeholder.id}}}`, 'g');
|
||||
const updatedValue = processKnownPlaceholders(placeHolderValue, title, dateFormat);
|
||||
let updatedValue = filePath
|
||||
? await processArticlePlaceholdersFromPath(placeHolderValue, filePath)
|
||||
: placeHolderValue;
|
||||
|
||||
updatedValue = processTimePlaceholders(updatedValue, dateFormat);
|
||||
|
||||
if (value === `{{${placeholder.id}}}`) {
|
||||
value = updatedValue;
|
||||
|
||||
+25
-12
@@ -1,6 +1,13 @@
|
||||
import { ModeListener } from './../listeners/general/ModeListener';
|
||||
import { PagesListener } from './../listeners/dashboard';
|
||||
import { ArticleHelper, CustomScript, Logger, Settings } from '.';
|
||||
import {
|
||||
ArticleHelper,
|
||||
CustomScript,
|
||||
Logger,
|
||||
Settings,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders
|
||||
} from '.';
|
||||
import {
|
||||
DefaultFieldValues,
|
||||
EXTENSION_NAME,
|
||||
@@ -26,7 +33,6 @@ import { Questions } from './Questions';
|
||||
import { Notifications } from './Notifications';
|
||||
import { DEFAULT_CONTENT_TYPE_NAME } from '../constants/ContentType';
|
||||
import { Telemetry } from './Telemetry';
|
||||
import { processKnownPlaceholders } from './PlaceholderHelper';
|
||||
import { basename } from 'path';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { encodeEmoji, existsAsync, fieldWhenClause, writeFileAsync } from '../utils';
|
||||
@@ -299,17 +305,17 @@ export class ContentType {
|
||||
|
||||
Telemetry.send(TelemetryEvent.addMissingFields);
|
||||
|
||||
const content = ArticleHelper.getCurrent();
|
||||
const article = ArticleHelper.getCurrent();
|
||||
|
||||
if (!content || !content.data) {
|
||||
if (!article || !article.data) {
|
||||
Notifications.warning(
|
||||
l10n.t(LocalizationKey.helpersContentTypeAddMissingFieldsNoFrontMatterWarning)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(content);
|
||||
const updatedFields = ContentType.generateFields(content.data, contentType.fields);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const updatedFields = ContentType.generateFields(article.data, contentType.fields);
|
||||
|
||||
const contentTypes = ContentType.getAll() || [];
|
||||
const index = contentTypes.findIndex((ct) => ct.name === contentType.name);
|
||||
@@ -927,7 +933,8 @@ export class ContentType {
|
||||
titleValue,
|
||||
templateData?.data || {},
|
||||
newFilePath,
|
||||
!!contentType.clearEmpty
|
||||
!!contentType.clearEmpty,
|
||||
contentType
|
||||
);
|
||||
|
||||
const article: ParsedFrontMatter = {
|
||||
@@ -982,6 +989,7 @@ export class ContentType {
|
||||
data: any,
|
||||
filePath: string,
|
||||
clearEmpty: boolean,
|
||||
contentType: IContentType,
|
||||
isRoot: boolean = true
|
||||
): Promise<any> {
|
||||
if (obj.fields) {
|
||||
@@ -995,9 +1003,9 @@ export class ContentType {
|
||||
|
||||
if (field.name === 'title') {
|
||||
if (field.default) {
|
||||
data[field.name] = processKnownPlaceholders(
|
||||
field.default,
|
||||
titleValue,
|
||||
data[field.name] = processArticlePlaceholdersFromData(field.default, data, contentType);
|
||||
data[field.name] = processTimePlaceholders(
|
||||
data[field.name],
|
||||
field.dateFormat || dateFormat
|
||||
);
|
||||
data[field.name] = await ArticleHelper.processCustomPlaceholders(
|
||||
@@ -1018,6 +1026,7 @@ export class ContentType {
|
||||
{},
|
||||
filePath,
|
||||
clearEmpty,
|
||||
contentType,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -1028,9 +1037,13 @@ export class ContentType {
|
||||
const defaultValue = field.default;
|
||||
|
||||
if (typeof defaultValue === 'string') {
|
||||
data[field.name] = processKnownPlaceholders(
|
||||
data[field.name] = processArticlePlaceholdersFromData(
|
||||
defaultValue,
|
||||
titleValue,
|
||||
data,
|
||||
contentType
|
||||
);
|
||||
data[field.name] = processTimePlaceholders(
|
||||
data[field.name],
|
||||
field.dateFormat || dateFormat
|
||||
);
|
||||
data[field.name] = await ArticleHelper.processCustomPlaceholders(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { stopWords, charMap } from '../constants';
|
||||
import { Settings } from '.';
|
||||
import { stopWords, charMap, SETTING_DATE_FORMAT, SETTING_SLUG_TEMPLATE } from '../constants';
|
||||
import { processTimePlaceholders, processFmPlaceholders } from '.';
|
||||
|
||||
export class SlugHelper {
|
||||
/**
|
||||
@@ -6,13 +8,41 @@ export class SlugHelper {
|
||||
*
|
||||
* @param articleTitle
|
||||
*/
|
||||
public static createSlug(articleTitle: string): string | null {
|
||||
public static createSlug(
|
||||
articleTitle: string,
|
||||
articleData: { [key: string]: any },
|
||||
slugTemplate?: string
|
||||
): string | null {
|
||||
if (!articleTitle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove punctuation from input string, and split it into words.
|
||||
let cleanTitle = this.removePunctuation(articleTitle).trim();
|
||||
if (!slugTemplate) {
|
||||
slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE) || undefined;
|
||||
}
|
||||
|
||||
if (slugTemplate) {
|
||||
if (slugTemplate.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
slugTemplate = slugTemplate.replace(regex, SlugHelper.slugify(articleTitle));
|
||||
}
|
||||
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
articleTitle = processTimePlaceholders(slugTemplate, dateFormat);
|
||||
articleTitle = processFmPlaceholders(articleTitle, articleData);
|
||||
return articleTitle;
|
||||
}
|
||||
|
||||
return SlugHelper.slugify(articleTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a title into a slug by removing punctuation, stop words, and replacing characters.
|
||||
* @param title - The title to be slugified.
|
||||
* @returns The slugified version of the title.
|
||||
*/
|
||||
public static slugify(title: string): string {
|
||||
let cleanTitle = this.removePunctuation(title).trim();
|
||||
if (cleanTitle) {
|
||||
cleanTitle = cleanTitle.toLowerCase();
|
||||
// Split into words
|
||||
@@ -23,8 +53,7 @@ export class SlugHelper {
|
||||
cleanTitle = this.replaceCharacters(cleanTitle);
|
||||
return cleanTitle;
|
||||
}
|
||||
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,6 @@ export * from './MediaHelpers';
|
||||
export * from './MediaLibrary';
|
||||
export * from './Notifications';
|
||||
export * from './PanelSettings';
|
||||
export * from './PlaceholderHelper';
|
||||
export * from './Questions';
|
||||
export * from './Sanitize';
|
||||
export * from './SeoHelper';
|
||||
@@ -32,5 +31,7 @@ export * from './getTaxonomyField';
|
||||
export * from './isValidFile';
|
||||
export * from './openFileInEditor';
|
||||
export * from './parseWinPath';
|
||||
export * from './processArticlePlaceholders';
|
||||
export * from './processFmPlaceholders';
|
||||
export * from './processPathPlaceholders';
|
||||
export * from './processTimePlaceholders';
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ContentType } from '../models';
|
||||
import { ArticleHelper } from './ArticleHelper';
|
||||
import { SlugHelper } from './SlugHelper';
|
||||
|
||||
export const processArticlePlaceholdersFromData = (
|
||||
value: string,
|
||||
data: { [key: string]: any },
|
||||
contentType: ContentType
|
||||
): string => {
|
||||
if (value.includes('{{title}}') && data.title) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, data.title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}')) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(
|
||||
regex,
|
||||
SlugHelper.createSlug(data.title || '', data, contentType.slugTemplate) || ''
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const processArticlePlaceholdersFromPath = async (
|
||||
value: string,
|
||||
filePath: string
|
||||
): Promise<string> => {
|
||||
const article = await ArticleHelper.getFrontMatterByPath(filePath);
|
||||
if (!article) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, article.data.title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}') && filePath) {
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
if (contentType) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(
|
||||
regex,
|
||||
SlugHelper.createSlug(article.data.title || '', article.data, contentType.slugTemplate) ||
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export const processFmPlaceholders = (value: string, fmData: any) => {
|
||||
export const processFmPlaceholders = (value: string, fmData: { [key: string]: any }) => {
|
||||
// Example: {{fm.date}} or {{fm.date | dateFormat 'DD.MM.YYYY'}}
|
||||
if (value && value.includes('{{fm.')) {
|
||||
const regex = /{{fm.[^}]*}}/g;
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from './DateHelper';
|
||||
import { SlugHelper } from './SlugHelper';
|
||||
|
||||
/**
|
||||
* Replace the known placeholders
|
||||
* Replace the time placeholders
|
||||
* @param value
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
export const processKnownPlaceholders = (
|
||||
value: string,
|
||||
title: string | undefined,
|
||||
dateFormat: string
|
||||
) => {
|
||||
export const processTimePlaceholders = (value: string, dateFormat?: string) => {
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.includes('{{title}}')) {
|
||||
const regex = new RegExp('{{title}}', 'g');
|
||||
value = value.replace(regex, title || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}')) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(regex, SlugHelper.createSlug(title || '') || '');
|
||||
}
|
||||
|
||||
if (value.includes('{{now}}')) {
|
||||
const regex = new RegExp('{{now}}', 'g');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Dashboard } from '../../commands/Dashboard';
|
||||
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
|
||||
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
|
||||
import { Logger } from '../../helpers/Logger';
|
||||
import { PostMessageData } from '../../models';
|
||||
|
||||
@@ -20,7 +21,11 @@ export abstract class BaseListener {
|
||||
});
|
||||
}
|
||||
|
||||
public static sendRequest(command: DashboardCommand, requestId: string, payload: any) {
|
||||
public static sendRequest(
|
||||
command: DashboardCommand | DashboardMessage,
|
||||
requestId: string,
|
||||
payload: any
|
||||
) {
|
||||
Dashboard.postWebviewMessage({
|
||||
command,
|
||||
requestId,
|
||||
|
||||
@@ -127,9 +127,9 @@ export class SettingsListener extends BaseListener {
|
||||
* Update a setting from the dashboard
|
||||
* @param data
|
||||
*/
|
||||
private static async update(data: { name: string; value: any }) {
|
||||
private static async update(data: { name: string; value: any; global?: boolean }) {
|
||||
if (data.name) {
|
||||
await Settings.update(data.name, data.value);
|
||||
await Settings.update(data.name, data.value, data.global);
|
||||
this.getSettings(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { EditorHelper } from '@estruyf/vscode';
|
||||
import { window, Range, Position } from 'vscode';
|
||||
import { Dashboard } from '../../commands/Dashboard';
|
||||
import { SETTING_CONTENT_SNIPPETS, TelemetryEvent } from '../../constants';
|
||||
import { SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, TelemetryEvent } from '../../constants';
|
||||
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
|
||||
import { Notifications, Settings, Telemetry } from '../../helpers';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Notifications,
|
||||
Settings,
|
||||
Telemetry,
|
||||
processArticlePlaceholdersFromPath,
|
||||
processTimePlaceholders
|
||||
} from '../../helpers';
|
||||
import { PostMessageData, Snippets } from '../../models';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { SettingsListener } from './SettingsListener';
|
||||
@@ -25,6 +32,9 @@ export class SnippetListener extends BaseListener {
|
||||
Telemetry.send(TelemetryEvent.insertContentSnippet);
|
||||
this.insertSnippet(msg.payload);
|
||||
break;
|
||||
case DashboardMessage.updateSnippetPlaceholders:
|
||||
this.updateSnippetPlaceholders(msg.command, msg.payload, msg.requestId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,4 +134,25 @@ export class SnippetListener extends BaseListener {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static async updateSnippetPlaceholders(
|
||||
command: DashboardMessage,
|
||||
data: { value: string; filePath: string },
|
||||
requestId?: string
|
||||
) {
|
||||
if (!data.value || !command || !requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let value = data.value;
|
||||
|
||||
if (data.filePath) {
|
||||
value = await processArticlePlaceholdersFromPath(data.value, data.filePath);
|
||||
}
|
||||
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
value = processTimePlaceholders(value, dateFormat);
|
||||
|
||||
this.sendRequest(command, requestId, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
CONTEXT,
|
||||
GIT_CONFIG,
|
||||
SETTING_DATE_FORMAT,
|
||||
SETTING_GIT_COMMIT_MSG,
|
||||
SETTING_GIT_DISABLED_BRANCHES,
|
||||
SETTING_GIT_ENABLED,
|
||||
SETTING_GIT_REQUIRES_COMMIT_MSG,
|
||||
SETTING_GIT_SUBMODULE_BRANCH,
|
||||
SETTING_GIT_SUBMODULE_FOLDER,
|
||||
SETTING_GIT_SUBMODULE_PULL,
|
||||
SETTING_GIT_SUBMODULE_PUSH
|
||||
} from './../../constants/settings';
|
||||
SETTING_GIT_SUBMODULE_PUSH,
|
||||
TelemetryEvent
|
||||
} from './../../constants';
|
||||
import { Settings } from './../../helpers/SettingsHelper';
|
||||
import { Dashboard } from '../../commands/Dashboard';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
@@ -15,19 +22,11 @@ import {
|
||||
Logger,
|
||||
Notifications,
|
||||
parseWinPath,
|
||||
processKnownPlaceholders,
|
||||
processTimePlaceholders,
|
||||
Telemetry
|
||||
} from '../../helpers';
|
||||
import { GeneralCommands } from './../../constants/GeneralCommands';
|
||||
import simpleGit, { SimpleGit } from 'simple-git';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
CONTEXT,
|
||||
SETTING_DATE_FORMAT,
|
||||
SETTING_GIT_COMMIT_MSG,
|
||||
SETTING_GIT_ENABLED,
|
||||
TelemetryEvent
|
||||
} from '../../constants';
|
||||
import { Folders } from '../../commands/Folders';
|
||||
import { Event, commands, extensions } from 'vscode';
|
||||
import { GitAPIState, GitRepository, PostMessageData } from '../../models';
|
||||
@@ -115,10 +114,21 @@ export class GitListener {
|
||||
break;
|
||||
case GeneralCommands.toVSCode.git.selectBranch:
|
||||
this.selectBranch();
|
||||
case GeneralCommands.toVSCode.git.isRepo:
|
||||
this.checkIsGitRepo(msg.command, msg.requestId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static async checkIsGitRepo(command: string, requestId?: string) {
|
||||
if (!command || !requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRepo = await GitListener.isGitRepository();
|
||||
Dashboard.postWebviewMessage({ command: command as any, payload: isRepo, requestId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the current branch in the Git repository.
|
||||
* @returns {Promise<void>} A promise that resolves when the branch command has been executed.
|
||||
@@ -222,7 +232,7 @@ export class GitListener {
|
||||
|
||||
if (commitMsg) {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
commitMsg = processKnownPlaceholders(commitMsg, undefined, dateFormat);
|
||||
commitMsg = processTimePlaceholders(commitMsg, dateFormat);
|
||||
commitMsg = await ArticleHelper.processCustomPlaceholders(commitMsg, undefined, undefined);
|
||||
}
|
||||
|
||||
@@ -243,7 +253,7 @@ export class GitListener {
|
||||
// Check if anything changed
|
||||
if (status.files.length > 0) {
|
||||
await subGit.raw(['add', '.', '-A']);
|
||||
await subGit.commit(commitMsg);
|
||||
await subGit.commit(commitMsg || GIT_CONFIG.defaultCommitMessage);
|
||||
}
|
||||
await subGit.push();
|
||||
} catch (e) {
|
||||
@@ -265,7 +275,13 @@ export class GitListener {
|
||||
// First line is the submodule folder name
|
||||
if (lines.length > 1) {
|
||||
await git.subModule(['foreach', 'git', 'add', '.', '-A']);
|
||||
await git.subModule(['foreach', 'git', 'commit', '-m', commitMsg]);
|
||||
await git.subModule([
|
||||
'foreach',
|
||||
'git',
|
||||
'commit',
|
||||
'-m',
|
||||
commitMsg || GIT_CONFIG.defaultCommitMessage
|
||||
]);
|
||||
await git.subModule(['foreach', 'git', 'push']);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -284,7 +300,7 @@ export class GitListener {
|
||||
|
||||
if (status.files.length > 0) {
|
||||
await git.raw(['add', '.', '-A']);
|
||||
await git.commit(commitMsg);
|
||||
await git.commit(commitMsg || GIT_CONFIG.defaultCommitMessage);
|
||||
}
|
||||
|
||||
await git.push();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Article } from '../../commands';
|
||||
import { ArticleHelper } from '../../helpers';
|
||||
import { PostMessageData } from '../../models';
|
||||
import { Command } from '../../panelWebView/Command';
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { BaseListener } from './BaseListener';
|
||||
|
||||
@@ -17,7 +17,7 @@ export class ArticleListener extends BaseListener {
|
||||
Article.updateSlug();
|
||||
break;
|
||||
case CommandToCode.generateSlug:
|
||||
this.generateSlug(msg.payload);
|
||||
this.generateSlug(msg.command, msg.payload, msg.requestId);
|
||||
break;
|
||||
case CommandToCode.updateLastMod:
|
||||
Article.setLastModifiedDate();
|
||||
@@ -32,10 +32,19 @@ export class ArticleListener extends BaseListener {
|
||||
* Generate a slug
|
||||
* @param title
|
||||
*/
|
||||
private static generateSlug(title: string) {
|
||||
const slug = Article.generateSlug(title);
|
||||
private static generateSlug(
|
||||
command: CommandToCode,
|
||||
{ title, slugTemplate }: { title: string; slugTemplate?: string },
|
||||
requestId?: string
|
||||
) {
|
||||
if (!command || !requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatterFromCurrentDocument();
|
||||
const slug = Article.generateSlug(title, article, slugTemplate);
|
||||
if (slug) {
|
||||
this.sendMsg(Command.updatedSlug, slug);
|
||||
this.sendRequest(command, requestId, slug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,16 @@ import { Command } from '../../panelWebView/Command';
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { authentication, commands, window } from 'vscode';
|
||||
import { ArticleHelper, ContentType, Extension, Logger, Settings } from '../../helpers';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Extension,
|
||||
Logger,
|
||||
Settings,
|
||||
ContentType,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders,
|
||||
processFmPlaceholders
|
||||
} from '../../helpers';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
DefaultFields,
|
||||
@@ -20,8 +29,7 @@ import {
|
||||
} from '../../constants';
|
||||
import { Article, Preview } from '../../commands';
|
||||
import { ParsedFrontMatter } from '../../parsers';
|
||||
import { processKnownPlaceholders } from '../../helpers/PlaceholderHelper';
|
||||
import { Field, Mode, PostMessageData } from '../../models';
|
||||
import { Field, Mode, PostMessageData, ContentType as IContentType } from '../../models';
|
||||
import { encodeEmoji, fieldWhenClause } from '../../utils';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { MessageHandlerData } from '@estruyf/vscode';
|
||||
@@ -66,7 +74,11 @@ export class DataListener extends BaseListener {
|
||||
this.isServerStarted(msg.command, msg?.requestId);
|
||||
break;
|
||||
case CommandToCode.updatePlaceholder:
|
||||
this.updatePlaceholder(msg?.payload?.field, msg?.payload?.value, msg?.payload?.title);
|
||||
this.updatePlaceholder(
|
||||
msg.command,
|
||||
msg.payload as { field: string; value: string; data: { [key: string]: any } },
|
||||
msg.requestId
|
||||
);
|
||||
break;
|
||||
case CommandToCode.generateContentType:
|
||||
commands.executeCommand(COMMAND_NAME.generateContentType);
|
||||
@@ -211,7 +223,11 @@ export class DataListener extends BaseListener {
|
||||
|
||||
if (keys.length > 0 && contentTypes && wsFolder) {
|
||||
// Get the current content type
|
||||
const contentType = ArticleHelper.getContentType(updatedMetadata);
|
||||
const contentType = ArticleHelper.getContentType({
|
||||
content: '',
|
||||
data: updatedMetadata,
|
||||
path: filePath
|
||||
});
|
||||
let slugField;
|
||||
if (contentType) {
|
||||
ImageHelper.processImageFields(updatedMetadata, contentType.fields);
|
||||
@@ -597,18 +613,37 @@ export class DataListener extends BaseListener {
|
||||
* @param value
|
||||
* @param title
|
||||
*/
|
||||
private static async updatePlaceholder(field: string, value: string, title: string) {
|
||||
if (field && value) {
|
||||
private static async updatePlaceholder(
|
||||
command: CommandToCode,
|
||||
articleData: {
|
||||
field: string;
|
||||
value: string;
|
||||
data: { [key: string]: any };
|
||||
contentType?: IContentType;
|
||||
},
|
||||
requestId?: string
|
||||
) {
|
||||
if (!command || !requestId || !articleData) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { field, value, data, contentType } = articleData;
|
||||
|
||||
value = value || '';
|
||||
if (field) {
|
||||
const crntFile = window.activeTextEditor?.document;
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
value = processKnownPlaceholders(value, title || '', dateFormat);
|
||||
value =
|
||||
data && contentType ? processArticlePlaceholdersFromData(value, data, contentType) : value;
|
||||
value = processTimePlaceholders(value, dateFormat);
|
||||
value = processFmPlaceholders(value, data);
|
||||
value = await ArticleHelper.processCustomPlaceholders(
|
||||
value,
|
||||
title || '',
|
||||
data.title || '',
|
||||
crntFile?.uri.fsPath || ''
|
||||
);
|
||||
}
|
||||
|
||||
this.sendMsg(Command.updatePlaceholder, { field, value });
|
||||
this.sendRequest(Command.updatePlaceholder, requestId, { field, value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,26 @@ export enum LocalizationKey {
|
||||
* Run full diagnostics
|
||||
*/
|
||||
settingsDiagnosticLink = 'settings.diagnostic.link',
|
||||
/**
|
||||
* Git synchronization
|
||||
*/
|
||||
settingsGit = 'settings.git',
|
||||
/**
|
||||
* Enable Git synchronization to easily sync your changes with your repository.
|
||||
*/
|
||||
settingsGitEnabled = 'settings.git.enabled',
|
||||
/**
|
||||
* Commit message
|
||||
*/
|
||||
settingsGitCommitMessage = 'settings.git.commitMessage',
|
||||
/**
|
||||
* When working with Git submodules, you can refer to the submodule settings in the documentation.
|
||||
*/
|
||||
settingsGitSubmoduleInfo = 'settings.git.submoduleInfo',
|
||||
/**
|
||||
* Read more about Git submodules
|
||||
*/
|
||||
settingsGitSubmoduleLink = 'settings.git.submoduleLink',
|
||||
/**
|
||||
* Website and SSG settings
|
||||
*/
|
||||
@@ -919,6 +939,14 @@ export enum LocalizationKey {
|
||||
* Now that Front Matter knows all the content folders. Would you like to import all tags and categories from the available content?
|
||||
*/
|
||||
dashboardStepsStepsToGetStartedTagsDescription = 'dashboard.steps.stepsToGetStarted.tags.description',
|
||||
/**
|
||||
* Do you want to enable Git synchronization?
|
||||
*/
|
||||
dashboardStepsStepsToGetStartedGitName = 'dashboard.steps.stepsToGetStarted.git.name',
|
||||
/**
|
||||
* Enable Git synchronization to eaily sync your changes with your repository.
|
||||
*/
|
||||
dashboardStepsStepsToGetStartedGitDescription = 'dashboard.steps.stepsToGetStarted.git.description',
|
||||
/**
|
||||
* Show the dashboard
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Position } from 'vscode';
|
||||
import { NavigationType } from '../dashboardWebView/models';
|
||||
import { BlockFieldData } from './BlockFieldData';
|
||||
import { ContentType } from '.';
|
||||
|
||||
export interface DashboardData {
|
||||
type: NavigationType;
|
||||
@@ -12,6 +13,7 @@ export interface ViewData {
|
||||
fieldName?: string;
|
||||
position?: Position;
|
||||
fileTitle?: string;
|
||||
contentType?: ContentType;
|
||||
selection?: string;
|
||||
range?: SnippetRange;
|
||||
snippetInfo?: SnippetInfo;
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface ContentType {
|
||||
|
||||
fileType?: 'md' | 'mdx' | string;
|
||||
previewPath?: string | null;
|
||||
slugTemplate?: string;
|
||||
pageBundle?: boolean;
|
||||
defaultFileName?: string;
|
||||
template?: string;
|
||||
|
||||
@@ -10,6 +10,5 @@ export enum Command {
|
||||
sendMediaUrl = 'sendMediaUrl',
|
||||
updatePlaceholder = 'updatePlaceholder',
|
||||
dataFileEntries = 'dataFileEntries',
|
||||
updatedSlug = 'updatedSlug',
|
||||
serverStarted = 'server-started'
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { LinkIcon, ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { BaseFieldProps } from '../../../models';
|
||||
import { Command } from '../../Command';
|
||||
import { CommandToCode } from '../../CommandToCode';
|
||||
import { FieldTitle } from './FieldTitle';
|
||||
import { FieldMessage } from './FieldMessage';
|
||||
@@ -14,6 +12,7 @@ import { LocalizationKey } from '../../../localization';
|
||||
export interface ISlugFieldProps extends BaseFieldProps<string> {
|
||||
titleValue: string | null;
|
||||
editable?: boolean;
|
||||
slugTemplate?: string;
|
||||
onChange: (txtValue: string) => void;
|
||||
}
|
||||
|
||||
@@ -23,6 +22,7 @@ export const SlugField: React.FunctionComponent<ISlugFieldProps> = ({
|
||||
editable,
|
||||
value,
|
||||
titleValue,
|
||||
slugTemplate,
|
||||
onChange,
|
||||
required
|
||||
}: React.PropsWithChildren<ISlugFieldProps>) => {
|
||||
@@ -38,16 +38,6 @@ export const SlugField: React.FunctionComponent<ISlugFieldProps> = ({
|
||||
Messenger.send(CommandToCode.updateSlug);
|
||||
};
|
||||
|
||||
const messageListener = useCallback(
|
||||
(message: MessageEvent<EventData<any>>) => {
|
||||
const { command, payload } = message.data;
|
||||
if (command === Command.updatedSlug) {
|
||||
setSlug(payload?.slugWithPrefixAndSuffix);
|
||||
}
|
||||
},
|
||||
[text]
|
||||
);
|
||||
|
||||
const showRequiredState = useMemo(() => {
|
||||
return required && !text;
|
||||
}, [required, text]);
|
||||
@@ -60,17 +50,18 @@ export const SlugField: React.FunctionComponent<ISlugFieldProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (titleValue) {
|
||||
Messenger.send(CommandToCode.generateSlug, titleValue);
|
||||
messageHandler.request<{ slug: string; slugWithPrefixAndSuffix: string; }>(CommandToCode.generateSlug, {
|
||||
title: titleValue,
|
||||
slugTemplate
|
||||
}).then((slug) => {
|
||||
if (slug.slugWithPrefixAndSuffix) {
|
||||
setSlug(slug.slugWithPrefixAndSuffix);
|
||||
}
|
||||
}).catch((_) => {
|
||||
setSlug(null);
|
||||
});
|
||||
}
|
||||
}, [titleValue]);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
};
|
||||
}, []);
|
||||
}, [titleValue, slugTemplate]);
|
||||
|
||||
return (
|
||||
<div className={`metadata_field`}>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { DateHelper } from '../../../helpers/DateHelper';
|
||||
import { BlockFieldData, CustomPanelViewResult, Field, PanelSettings } from '../../../models';
|
||||
import { Command } from '../../Command';
|
||||
import { BlockFieldData, ContentType, CustomPanelViewResult, Field, PanelSettings } from '../../../models';
|
||||
import { CommandToCode } from '../../CommandToCode';
|
||||
import { TagType } from '../../TagType';
|
||||
import { DataBlockField } from '../DataBlock';
|
||||
@@ -41,6 +40,7 @@ export interface IWrapperFieldProps {
|
||||
parentFields: string[];
|
||||
metadata: IMetadata;
|
||||
settings: PanelSettings;
|
||||
contentType: ContentType | null;
|
||||
blockData: BlockFieldData | undefined;
|
||||
focusElm: TagType | null;
|
||||
parentBlock: string | null | undefined;
|
||||
@@ -63,6 +63,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
parentFields,
|
||||
metadata,
|
||||
settings,
|
||||
contentType,
|
||||
blockData,
|
||||
focusElm,
|
||||
parentBlock,
|
||||
@@ -76,21 +77,6 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
html: (data: any, onChange: (value: any) => void) => Promise<CustomPanelViewResult | undefined>;
|
||||
}[]>([]);
|
||||
|
||||
const listener = useCallback(
|
||||
(event: any) => {
|
||||
const message = event.data;
|
||||
|
||||
if (message.command === Command.updatePlaceholder) {
|
||||
const data = message.payload;
|
||||
if (data.field === field.name) {
|
||||
setFieldValue(data.value);
|
||||
onSendUpdate(field.name, data.value, parentFields);
|
||||
}
|
||||
}
|
||||
},
|
||||
[field]
|
||||
);
|
||||
|
||||
const getDate = (date: string | Date): Date | null => {
|
||||
const parsedDate = DateHelper.tryParse(date, settings?.date?.format);
|
||||
return parsedDate || (date as Date | null);
|
||||
@@ -126,11 +112,18 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
|
||||
// Check if the field value contains a placeholder
|
||||
if (value && typeof value === 'string' && value.includes(`{{`) && value.includes(`}}`)) {
|
||||
window.addEventListener('message', listener);
|
||||
Messenger.send(CommandToCode.updatePlaceholder, {
|
||||
messageHandler.request<{ field: string; value: any; }>(CommandToCode.updatePlaceholder, {
|
||||
field: field.name,
|
||||
title: metadata['title'],
|
||||
value
|
||||
value,
|
||||
data: metadata,
|
||||
contentType
|
||||
}).then((data) => {
|
||||
if (data.field === field.name) {
|
||||
setFieldValue(data.value);
|
||||
onSendUpdate(field.name, data.value, parentFields);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
} else {
|
||||
// Did not contain a placeholder, so value can be set
|
||||
@@ -138,10 +131,6 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
setFieldValue(value || null);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', listener);
|
||||
};
|
||||
}, [field, parent]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -509,6 +498,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
description={field.description}
|
||||
titleValue={metadata.title as string}
|
||||
value={fieldValue}
|
||||
slugTemplate={contentType?.slugTemplate}
|
||||
required={!!field.required}
|
||||
editable={field.editable}
|
||||
onChange={onFieldChange}
|
||||
|
||||
@@ -66,6 +66,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
|
||||
parent={parent}
|
||||
parentFields={parentFields}
|
||||
metadata={metadata}
|
||||
contentType={contentType}
|
||||
settings={settings}
|
||||
blockData={blockData}
|
||||
parentBlock={parentBlock}
|
||||
|
||||
@@ -233,7 +233,10 @@ export class PagesParser {
|
||||
// Make sure these are always set
|
||||
title: escapedTitle,
|
||||
description: escapedDescription,
|
||||
slug: article?.data.slug || Article.generateSlug(escapedTitle)?.slugWithPrefixAndSuffix,
|
||||
slug:
|
||||
article?.data.slug ||
|
||||
Article.generateSlug(escapedTitle, article, contentType.slugTemplate)
|
||||
?.slugWithPrefixAndSuffix,
|
||||
date: article?.data[dateField] || '',
|
||||
draft: article?.data.draft
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user