From bebde4de684845c6a762c9a33156fe49c81e0d15 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 14 Feb 2022 09:18:44 -0800 Subject: [PATCH] #176 - Block field updated to support default fields --- .../uniforms-frontmatter/AutoField.tsx | 3 - src/constants/settings.ts | 1 + src/helpers/PanelSettings.ts | 5 +- src/listeners/panel/DataListener.ts | 5 + src/models/PanelSettings.ts | 8 +- .../DataBlock/DataBlockControls.tsx | 2 +- .../components/DataBlock/DataBlockField.tsx | 98 +++++++++++-------- .../components/DataBlock/DataBlockForm.tsx | 74 -------------- .../components/DataBlock/DataBlockRecord.tsx | 10 +- .../components/DataBlock/DataBlockRecords.tsx | 6 +- .../DataBlock/DataBlockSelector.tsx | 68 +++++++------ .../components/DataBlock/index.ts | 1 - .../{JsonFieldField.tsx => JsonField.tsx} | 6 +- .../JsonField/JsonFieldControls.tsx | 2 +- .../components/JsonField/JsonFieldForm.tsx | 8 -- .../components/JsonField/JsonFieldRecord.tsx | 10 +- .../components/JsonField/JsonFieldRecords.tsx | 4 +- .../JsonField/JsonFieldSelector.tsx | 2 +- .../components/JsonField/index.ts | 2 +- src/panelWebView/components/Metadata.tsx | 45 ++++++--- src/panelWebView/styles.css | 27 +++-- 21 files changed, 186 insertions(+), 201 deletions(-) delete mode 100644 src/panelWebView/components/DataBlock/DataBlockForm.tsx rename src/panelWebView/components/JsonField/{JsonFieldField.tsx => JsonField.tsx} (93%) diff --git a/src/components/uniforms-frontmatter/AutoField.tsx b/src/components/uniforms-frontmatter/AutoField.tsx index 903ea11c..ea130f2a 100644 --- a/src/components/uniforms-frontmatter/AutoField.tsx +++ b/src/components/uniforms-frontmatter/AutoField.tsx @@ -33,9 +33,6 @@ const AutoField = createAutoField(props => { return NestField; case String: return TextField; - case "image": - console.log(`You selected the image type`) - return PreviewImageField; } return invariant(false, 'Unsupported field type: %s', props.fieldType); diff --git a/src/constants/settings.ts b/src/constants/settings.ts index b35bb22e..bac61155 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -7,6 +7,7 @@ export const SETTING_GLOBAL_NOTIFICATIONS = "global.notifications"; export const SETTING_TAXONOMY_TAGS = "taxonomy.tags"; export const SETTING_TAXONOMY_CATEGORIES = "taxonomy.categories"; export const SETTING_TAXONOMY_CUSTOM = "taxonomy.customTaxonomy"; +export const SETTING_TAXONOMY_FIELD_GROUPS = "taxonomy.fieldGroups"; export const SETTING_DATE_FORMAT = "taxonomy.dateFormat"; export const SETTING_COMMA_SEPARATED_FIELDS = "taxonomy.commaSeparatedFields"; diff --git a/src/helpers/PanelSettings.ts b/src/helpers/PanelSettings.ts index f5a3c3b4..3d860625 100644 --- a/src/helpers/PanelSettings.ts +++ b/src/helpers/PanelSettings.ts @@ -3,8 +3,8 @@ import { Extension, Settings } from "." import { Dashboard } from "../commands/Dashboard" import { Preview } from "../commands/Preview" import { Template } from "../commands/Template" -import { CONTEXT, DefaultFields, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTINGS_DATA_TYPES, SETTINGS_FRAMEWORK_ID, SETTINGS_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_TAGS } from "../constants" -import { CustomScript, DataType, DraftField, PanelSettings as IPanelSettings, ScriptType } from "../models" +import { CONTEXT, DefaultFields, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTINGS_DATA_TYPES, SETTINGS_FRAMEWORK_ID, SETTINGS_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_TAGS } from "../constants" +import { CustomScript, DataType, DraftField, FieldGroup, PanelSettings as IPanelSettings, ScriptType } from "../models" export class PanelSettings { @@ -45,6 +45,7 @@ export class PanelSettings { start: Settings.get(SETTINGS_FRAMEWORK_START) }, dataTypes: Settings.get(SETTINGS_DATA_TYPES), + fieldGroups: Settings.get(SETTING_TAXONOMY_FIELD_GROUPS), } } diff --git a/src/listeners/panel/DataListener.ts b/src/listeners/panel/DataListener.ts index 7b2c64c7..0c3d2b31 100644 --- a/src/listeners/panel/DataListener.ts +++ b/src/listeners/panel/DataListener.ts @@ -123,6 +123,11 @@ export class DataListener extends BaseListener { // Support multi-level fields let parentObj = article.data; for (const parent of parents || []) { + // If parent doesn't yet exists, it needs to be created + if (!parentObj[parent]) { + parentObj[parent] = {}; + } + parentObj = parentObj[parent]; } diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index afad15e2..9e3ba636 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -25,9 +25,15 @@ export interface PanelSettings { framework: string | undefined; commands: FrameworkCommands; dataTypes: DataType[] | undefined; + fieldGroups: FieldGroup[] | undefined; commaSeparatedFields: string[]; } +export interface FieldGroup { + id: string; + fields: Field[]; +} + export interface FrameworkCommands { start: string | undefined; } @@ -55,7 +61,7 @@ export interface Field { taxonomyId?: string; default?: string; fields?: Field[]; - blockType?: string | string[]; + fieldGroup?: string | string[]; dataType?: string | string[]; } diff --git a/src/panelWebView/components/DataBlock/DataBlockControls.tsx b/src/panelWebView/components/DataBlock/DataBlockControls.tsx index e137c32a..75e49531 100644 --- a/src/panelWebView/components/DataBlock/DataBlockControls.tsx +++ b/src/panelWebView/components/DataBlock/DataBlockControls.tsx @@ -11,7 +11,7 @@ export const DataBlockControls: React.FunctionComponent const { formRef } = useForm(); return ( -
+
-
diff --git a/src/panelWebView/components/DataBlock/DataBlockRecords.tsx b/src/panelWebView/components/DataBlock/DataBlockRecords.tsx index 547110bc..d56d6be4 100644 --- a/src/panelWebView/components/DataBlock/DataBlockRecords.tsx +++ b/src/panelWebView/components/DataBlock/DataBlockRecords.tsx @@ -23,7 +23,7 @@ export const DataBlockRecords = ({ records, selectedIndex, onSort, onAdd, onEdit } return ( -
+
@@ -31,7 +31,7 @@ export const DataBlockRecords = ({ records, selectedIndex, onSort, onAdd, onEdit Records
-
@@ -44,7 +44,7 @@ export const DataBlockRecords = ({ records, selectedIndex, onSort, onAdd, onEdit key={idx} id={idx} index={idx} - label={v?.blockType ?? 'Record'} + label={v?.fieldGroup ?? 'Block'} isSelected={idx === selectedIndex} onEdit={onEdit} onDelete={onDelete} /> diff --git a/src/panelWebView/components/DataBlock/DataBlockSelector.tsx b/src/panelWebView/components/DataBlock/DataBlockSelector.tsx index ca758786..ec82135d 100644 --- a/src/panelWebView/components/DataBlock/DataBlockSelector.tsx +++ b/src/panelWebView/components/DataBlock/DataBlockSelector.tsx @@ -1,63 +1,71 @@ import * as React from 'react'; import { useCallback, useEffect, useState } from 'react'; -import { Field } from '../../../models'; -import { DataType } from '../../../models/DataType'; +import { Field, FieldGroup } from '../../../models'; import { VSCodeDropdown, VSCodeOption } from '@vscode/webview-ui-toolkit/react'; export interface IDataBlockSelectorProps { field: Field; - dataTypes: DataType[] | undefined; - selectedDataType: string | null; - onSetDataType: (dataType: string | null) => void; - onSchemaUpdate: (schema: any) => void; + fieldGroups: FieldGroup[] | undefined; + selectedGroup: string | undefined; + onGroupChange: (group: FieldGroup | undefined | null) => void; } const EMPTY_OPTION = "EMPTY_OPTION"; -export const DataBlockSelector: React.FunctionComponent = ({ field, dataTypes, selectedDataType, onSetDataType, onSchemaUpdate }: React.PropsWithChildren) => { +export const DataBlockSelector: React.FunctionComponent = ({ field, fieldGroups, selectedGroup, onGroupChange }: React.PropsWithChildren) => { const [ options, setOptions ] = useState([]); - const onSchemaSet = useCallback((blockType: string) => { - if (dataTypes && blockType !== EMPTY_OPTION) { - const schema = dataTypes.find(dataType => dataType.id === blockType); - onSchemaUpdate(schema); + const onGroupSet = useCallback((fieldGroup: string) => { + if (fieldGroups && fieldGroup !== EMPTY_OPTION) { + const group = fieldGroups.find(group => group.id === fieldGroup); + onGroupChange(group); } else { - onSchemaUpdate(null); + onGroupChange(null); } }, []); - useEffect(() => { - if (field.blockType && dataTypes) { - if (typeof field.blockType === "string") { - onSchemaSet(field.blockType); - } else if (field.blockType instanceof Array && field.blockType.length === 1) { - onSchemaSet(field.blockType[0]); - } else { - setOptions([...field.blockType]); - } + const onGroupSelect = useCallback((event: any) => { + const group = event.currentTarget.value === EMPTY_OPTION ? null : event.currentTarget.value; + + if (group) { + onGroupSet(group); + } else { + onGroupChange(null); } - }, [field.blockType, dataTypes, onSchemaUpdate]); + } , [onGroupSet]); useEffect(() => { - if (dataTypes && selectedDataType) { - if (selectedDataType !== EMPTY_OPTION) { - onSchemaSet(selectedDataType); + if (field.fieldGroup && fieldGroups) { + if (typeof field.fieldGroup === "string") { + onGroupSet(field.fieldGroup); + } else if (field.fieldGroup instanceof Array && field.fieldGroup.length === 1) { + onGroupSet(field.fieldGroup[0]); } else { - onSchemaUpdate(null); + setOptions([...field.fieldGroup]); } } - }, [selectedDataType, dataTypes, onSchemaUpdate]) + }, [field.fieldGroup, fieldGroups, onGroupChange]); + + useEffect(() => { + if (fieldGroups && selectedGroup) { + if (selectedGroup !== EMPTY_OPTION) { + onGroupSet(selectedGroup); + } else { + onGroupChange(null); + } + } + }, [selectedGroup, fieldGroups, onGroupChange]) if (options.length === 0) { return null; } return ( -
+

Block type

onSetDataType(event.currentTarget.value === EMPTY_OPTION ? null : event.currentTarget.value)} + value={selectedGroup ?? EMPTY_OPTION} + onChange={onGroupSelect} style={{ width: "100%", marginBottom: "1rem" diff --git a/src/panelWebView/components/DataBlock/index.ts b/src/panelWebView/components/DataBlock/index.ts index 8075b3ca..2d2b03cb 100644 --- a/src/panelWebView/components/DataBlock/index.ts +++ b/src/panelWebView/components/DataBlock/index.ts @@ -1,6 +1,5 @@ export * from './DataBlockControls'; export * from './DataBlockField'; -export * from './DataBlockForm'; export * from './DataBlockRecord'; export * from './DataBlockRecords'; export * from './DataBlockSelector'; diff --git a/src/panelWebView/components/JsonField/JsonFieldField.tsx b/src/panelWebView/components/JsonField/JsonField.tsx similarity index 93% rename from src/panelWebView/components/JsonField/JsonFieldField.tsx rename to src/panelWebView/components/JsonField/JsonField.tsx index 012451b1..92c205f5 100644 --- a/src/panelWebView/components/JsonField/JsonFieldField.tsx +++ b/src/panelWebView/components/JsonField/JsonField.tsx @@ -7,7 +7,7 @@ import { JsonFieldRecords, JsonFieldForm, JsonFieldSelector } from '.'; import { SortEnd } from 'react-sortable-hoc'; import { arrayMoveImmutable } from 'array-move'; -export interface IJsonFieldFieldProps { +export interface IJsonFieldProps { label: string; settings: PanelSettings; field: Field; @@ -15,7 +15,7 @@ export interface IJsonFieldFieldProps { onSubmit: (data: any) => void; } -export const JsonFieldField: React.FunctionComponent = ({ label, settings, field, value, onSubmit }: React.PropsWithChildren) => { +export const JsonField: React.FunctionComponent = ({ label, settings, field, value, onSubmit }: React.PropsWithChildren) => { const [ selectedIndex, setSelectedIndex ] = useState(null); const [ selectedDataType, setSelectedDataType ] = useState(null); const [ selectedSchema, setSelectedSchema ] = useState(null); @@ -81,7 +81,7 @@ export const JsonFieldField: React.FunctionComponent = ({ }, [selectedIndex, model, settings?.dataTypes]); return ( -
+
diff --git a/src/panelWebView/components/JsonField/JsonFieldControls.tsx b/src/panelWebView/components/JsonField/JsonFieldControls.tsx index cecd7b56..90015b78 100644 --- a/src/panelWebView/components/JsonField/JsonFieldControls.tsx +++ b/src/panelWebView/components/JsonField/JsonFieldControls.tsx @@ -11,7 +11,7 @@ export const JsonFieldControls: React.FunctionComponent const { formRef } = useForm(); return ( -
+
-
diff --git a/src/panelWebView/components/JsonField/JsonFieldRecords.tsx b/src/panelWebView/components/JsonField/JsonFieldRecords.tsx index 0d39d88b..0df9e2fb 100644 --- a/src/panelWebView/components/JsonField/JsonFieldRecords.tsx +++ b/src/panelWebView/components/JsonField/JsonFieldRecords.tsx @@ -23,7 +23,7 @@ export const JsonFieldRecords = ({ records, selectedIndex, onSort, onAdd, onEdit } return ( -
+
@@ -31,7 +31,7 @@ export const JsonFieldRecords = ({ records, selectedIndex, onSort, onAdd, onEdit Records
-
diff --git a/src/panelWebView/components/JsonField/JsonFieldSelector.tsx b/src/panelWebView/components/JsonField/JsonFieldSelector.tsx index 81308a45..32c0b95e 100644 --- a/src/panelWebView/components/JsonField/JsonFieldSelector.tsx +++ b/src/panelWebView/components/JsonField/JsonFieldSelector.tsx @@ -53,7 +53,7 @@ export const JsonFieldSelector: React.FunctionComponent } return ( -
+

Block type

= ({settings, metadata, return null; } - const renderFields = (ctFields: Field[], parent: IMetadata, parentFields: string[] = []) => { + const renderFields = (ctFields: Field[], parent: IMetadata, parentFields: string[] = [], onFieldUpdate?: (field: string | undefined, value: any, parents: string[]) => void) => { if (!ctFields) { return; } + const onSendUpdate = (field: string | undefined, value: any, parents: string[]) => { + if (onFieldUpdate) { + onFieldUpdate(field, value, parents); + } else { + sendUpdate(field, value, parents); + } + }; + return ctFields.map(field => { if (field.hidden) { return null; @@ -76,7 +84,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, label={field.title || field.name} date={dateValue} format={settings?.date?.format} - onChange={(date => sendUpdate(field.name, date, parentFields))} /> + onChange={(date => onSendUpdate(field.name, date, parentFields))} /> ); } else if (field.type === 'boolean') { @@ -86,7 +94,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, key={field.name} label={field.title || field.name} checked={!!parent[field.name] as any} - onChanged={(checked) => sendUpdate(field.name, checked, parentFields)} /> + onChanged={(checked) => onSendUpdate(field.name, checked, parentFields)} /> ); } else if (field.type === 'string') { @@ -106,7 +114,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, singleLine={field.single} limit={limit} rows={3} - onChange={(value) => sendUpdate(field.name, value, parentFields)} + onChange={(value) => onSendUpdate(field.name, value, parentFields)} value={textValue as string || null} /> ); @@ -122,7 +130,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, sendUpdate(field.name, value, parentFields)} + onChange={(value) => onSendUpdate(field.name, value, parentFields)} value={nrValue} /> ); @@ -136,7 +144,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, parents={parentFields} value={parent[field.name] as PreviewImageValue | PreviewImageValue[] | null} multiple={field.multiple} - onChange={(value) => sendUpdate(field.name, value, parentFields)} /> + onChange={(value) => onSendUpdate(field.name, value, parentFields)} /> ); } else if (field.type === 'choice') { @@ -150,7 +158,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, selected={choiceValue as string} choices={choices} multiSelect={field.multiple} - onChange={(value => sendUpdate(field.name, value, parentFields))} /> + onChange={(value => onSendUpdate(field.name, value, parentFields))} /> ); } else if (field.type === 'tags') { @@ -214,7 +222,7 @@ const Metadata: React.FunctionComponent = ({settings, metadata, type={draftField?.type || "boolean"} choices={draftField?.choices || []} value={value as boolean | string | null | undefined} - onChanged={(value: boolean | string) => sendUpdate(field.name, value, parentFields)} /> + onChanged={(value: boolean | string) => onSendUpdate(field.name, value, parentFields)} /> ); } else if (field.type === 'fields') { @@ -241,12 +249,27 @@ const Metadata: React.FunctionComponent = ({settings, metadata, return ( - sendUpdate(field.name, value, parentFields)} /> + onSubmit={(value) => onSendUpdate(field.name, value, parentFields)} /> + + ); + } else if (field.type === 'block') { + const blockData = parent[field.name]; + + return ( + + onSendUpdate(field.name, value, parentFields)} /> ); } else { diff --git a/src/panelWebView/styles.css b/src/panelWebView/styles.css index 94cd48a8..129d190b 100644 --- a/src/panelWebView/styles.css +++ b/src/panelWebView/styles.css @@ -1,5 +1,12 @@ -.data_block__field { +.block_field__form { + background-color: transparent; + border: 1px dashed var(--vscode-button-background); + padding: 1rem; + filter: brightness(85%); +} + +.json_data__field { border: 1px dashed var(--vscode-button-secondaryBackground); color: var(--vscode--settings-headerForeground); padding: .5rem 1rem; @@ -22,7 +29,7 @@ margin-bottom: .5rem; &:last-child { - .data_block__selector h3 { + .json_data__selector h3 { font-size: 1rem; font-weight: bold; text-align: center; @@ -33,7 +40,7 @@ } } -.data_block__selector { +.json_data__selector { h3 { font-size: var(--vscode-font-size); font-weight: bold; @@ -45,7 +52,7 @@ } } -.data_block__buttons { +.json_data__buttons { display: flex; justify-content: flex-end; align-items: center; @@ -79,7 +86,7 @@ } } -.data_block__list { +.json_data__list { margin-top: 1rem; label { @@ -125,7 +132,7 @@ } } -.data_block__list .metadata_field__label { +.json_data__list .metadata_field__label { justify-content: space-between; > div { @@ -138,14 +145,14 @@ } } -.data_block__record { +.json_data__record { border-bottom: 1px solid var(--vscode-button-secondaryBackground); display: flex; justify-content: space-between; align-items: center; padding: .5rem 0; - &.data_block__record_selected { + &.json_data__record_selected { background-color: rgba(255, 255, 255, .1); filter: brightness(100%); } @@ -188,7 +195,7 @@ } .sortable_item button, -.data_block__list__button { +.json_data__list__button { width: auto; background: none; color: inherit; @@ -203,7 +210,7 @@ } } -.data_block__list__button_icon { +.json_data__list__button_icon { height: 1rem; width: 1rem; }