mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-06-25 12:31:45 +02:00
feat: add support for additional fields in data file configuration and update related components #409
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
- Support `fieldGroup` as a single value on `fields` fields
|
||||
- Added the new content health feature to the Front Matter panel with readability scoring, link checks, and freshness warnings (`frontMatter.contentHealth.enabled`, `frontMatter.contentHealth.checkExternalLinks`, `frontMatter.contentHealth.freshnessThreshold`, `frontMatter.contentHealth.minReadability`)
|
||||
- [#409](https://github.com/estruyf/vscode-front-matter/issues/409): Added the ability to output multiple properties from a data file in the `dataFile` field type
|
||||
- [#1030](https://github.com/estruyf/vscode-front-matter/pull/1030): Add `frontMatter.file.slugSeparator` setting
|
||||
- [#1033](https://github.com/estruyf/vscode-front-matter/issues/1033): Support freeform tags and categories in the front matter validation
|
||||
- [#1036](https://github.com/estruyf/vscode-front-matter/issues/1036): Default filter, sorting, and grouping configuration for the `contents` dashboard
|
||||
|
||||
@@ -1582,6 +1582,14 @@
|
||||
"default": "",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileValue.description%"
|
||||
},
|
||||
"dataFileAdditionalFields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileAdditionalFields.description%"
|
||||
},
|
||||
"editable": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
||||
@@ -232,6 +232,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileId.description": "Specify the ID of the data file to use for this field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileKey.description": "Specify the key of the data file to use for this field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileValue.description": "Specify the property name that will be used to show the value for the field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dataFileAdditionalFields.description": "Specify additional field names from the data record to include when storing the value. When set, the frontmatter value will be an object containing the dataFileKey field plus these additional fields.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.editable.description": "Specify if the field is editable",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.encodeEmoji.description": "Specify if the field should encode emoji",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.dateFormat.description": "Specify the date format to use",
|
||||
|
||||
@@ -129,6 +129,7 @@ export interface Field {
|
||||
dataFileId?: string;
|
||||
dataFileKey?: string;
|
||||
dataFileValue?: string;
|
||||
dataFileAdditionalFields?: string[];
|
||||
|
||||
// Number field options
|
||||
numberOptions?: NumberOptions;
|
||||
|
||||
@@ -17,10 +17,11 @@ export interface IDataFileFieldProps {
|
||||
dataFileId?: string;
|
||||
dataFileKey?: string;
|
||||
dataFileValue?: string;
|
||||
selected: string | string[];
|
||||
dataFileAdditionalFields?: string[];
|
||||
selected: string | string[] | Record<string, unknown> | Record<string, unknown>[];
|
||||
multiSelect?: boolean;
|
||||
required?: boolean;
|
||||
onChange: (value: string | string[]) => void;
|
||||
onChange: (value: string | string[] | Record<string, unknown> | Record<string, unknown>[]) => void;
|
||||
}
|
||||
|
||||
export const DataFileField: React.FunctionComponent<IDataFileFieldProps> = ({
|
||||
@@ -29,6 +30,7 @@ export const DataFileField: React.FunctionComponent<IDataFileFieldProps> = ({
|
||||
dataFileId,
|
||||
dataFileKey,
|
||||
dataFileValue,
|
||||
dataFileAdditionalFields,
|
||||
selected,
|
||||
multiSelect,
|
||||
onChange,
|
||||
@@ -40,37 +42,83 @@ export const DataFileField: React.FunctionComponent<IDataFileFieldProps> = ({
|
||||
const inputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const { getDropdownStyle } = useDropdownStyle(inputRef as any);
|
||||
|
||||
const hasAdditionalFields = useMemo(
|
||||
() => !!dataFileAdditionalFields && dataFileAdditionalFields.length > 0,
|
||||
[dataFileAdditionalFields]
|
||||
);
|
||||
|
||||
// Extract the key string from a selected value (which may be a plain object when additionalFields is configured)
|
||||
const extractKey = useCallback(
|
||||
(value: unknown): string => {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value) && dataFileKey) {
|
||||
return ((value as Record<string, unknown>)[dataFileKey] as string) || '';
|
||||
}
|
||||
return (value as string) || '';
|
||||
},
|
||||
[dataFileKey]
|
||||
);
|
||||
|
||||
// Build the value to emit: an object when additionalFields is configured, otherwise just the key string
|
||||
const buildEmitValue = useCallback(
|
||||
(keyValue: string): string | Record<string, unknown> => {
|
||||
if (hasAdditionalFields && dataEntries && dataFileKey && dataFileAdditionalFields) {
|
||||
const entry = (dataEntries as any[]).find((r: any) => r[dataFileKey] === keyValue);
|
||||
if (entry) {
|
||||
const obj: Record<string, unknown> = { [dataFileKey]: entry[dataFileKey] };
|
||||
for (const field of dataFileAdditionalFields) {
|
||||
obj[field] = entry[field];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return keyValue;
|
||||
},
|
||||
[hasAdditionalFields, dataEntries, dataFileKey, dataFileAdditionalFields]
|
||||
);
|
||||
|
||||
const onValueChange = useCallback(
|
||||
(txtValue: string) => {
|
||||
if (multiSelect) {
|
||||
const newValue = [...((crntSelected || []) as string[]), txtValue];
|
||||
setCrntSelected(newValue);
|
||||
onChange(newValue);
|
||||
const newKeys = [...((crntSelected || []) as string[]), txtValue];
|
||||
setCrntSelected(newKeys);
|
||||
if (hasAdditionalFields) {
|
||||
onChange(newKeys.map(buildEmitValue) as Record<string, unknown>[]);
|
||||
} else {
|
||||
onChange(newKeys);
|
||||
}
|
||||
} else {
|
||||
setCrntSelected(txtValue);
|
||||
onChange(txtValue);
|
||||
if (hasAdditionalFields) {
|
||||
onChange(buildEmitValue(txtValue) as Record<string, unknown>);
|
||||
} else {
|
||||
onChange(txtValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
[crntSelected, multiSelect, onChange]
|
||||
[crntSelected, multiSelect, onChange, hasAdditionalFields, buildEmitValue]
|
||||
);
|
||||
|
||||
const removeSelected = useCallback(
|
||||
(txtValue: string) => {
|
||||
if (multiSelect) {
|
||||
const newValue = [...(crntSelected || [])].filter((v) => v !== txtValue);
|
||||
setCrntSelected(newValue);
|
||||
onChange(newValue);
|
||||
const newKeys = [...(crntSelected || [])].filter((v) => v !== txtValue) as string[];
|
||||
setCrntSelected(newKeys);
|
||||
if (hasAdditionalFields) {
|
||||
onChange(newKeys.map(buildEmitValue) as Record<string, unknown>[]);
|
||||
} else {
|
||||
onChange(newKeys);
|
||||
}
|
||||
} else {
|
||||
setCrntSelected('');
|
||||
onChange('');
|
||||
}
|
||||
},
|
||||
[crntSelected, multiSelect, onChange]
|
||||
[crntSelected, multiSelect, onChange, hasAdditionalFields, buildEmitValue]
|
||||
);
|
||||
|
||||
const allChoices = useMemo(() => {
|
||||
if (dataEntries && dataFileKey) {
|
||||
return dataEntries
|
||||
return (dataEntries as any[])
|
||||
.map((r: any) => ({
|
||||
id: r[dataFileKey],
|
||||
title: r[dataFileValue || dataFileKey] || r[dataFileKey]
|
||||
@@ -117,12 +165,14 @@ export const DataFileField: React.FunctionComponent<IDataFileFieldProps> = ({
|
||||
}, [required, crntSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selected) {
|
||||
if (selected !== undefined && selected !== null && selected !== '') {
|
||||
if (multiSelect) {
|
||||
setCrntSelected(typeof selected === 'string' ? [selected] : selected);
|
||||
const keys = (Array.isArray(selected) ? selected : [selected]).map(extractKey);
|
||||
setCrntSelected(keys);
|
||||
return;
|
||||
} else {
|
||||
setCrntSelected(selected instanceof Array ? selected[0] : selected);
|
||||
const key = extractKey(Array.isArray(selected) ? selected[0] : selected);
|
||||
setCrntSelected(key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,6 +511,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
dataFileId={field.dataFileId}
|
||||
dataFileKey={field.dataFileKey}
|
||||
dataFileValue={field.dataFileValue}
|
||||
dataFileAdditionalFields={field.dataFileAdditionalFields}
|
||||
selected={fieldValue as string}
|
||||
required={!!field.required}
|
||||
multiSelect={field.multiple}
|
||||
|
||||
Reference in New Issue
Block a user