#513 - support for custom field + panel view

This commit is contained in:
Elio Struyf
2023-02-27 17:37:16 +01:00
parent cf6133b143
commit 026bcd0a84
12 changed files with 191 additions and 21 deletions
@@ -0,0 +1,43 @@
import * as React from 'react';
import { useEffect, useState } from 'react';
import { CustomPanelViewResult } from '../../../models';
import { Collapsible } from '../Collapsible';
export interface ICustomViewProps {
metadata: any;
}
export const CustomView: React.FunctionComponent<ICustomViewProps> = ({ metadata }: React.PropsWithChildren<ICustomViewProps>) => {
const [customViewTitle, setCustomViewTitle] = useState<string | undefined>(undefined);
const [customHtml, setCustomHtml] = useState<string | undefined>(undefined);
useEffect(() => {
console.log(window.fmExternal)
if (window.fmExternal && window.fmExternal.getPanelView) {
window.fmExternal.getPanelView(metadata).then((viewDetails: CustomPanelViewResult | undefined) => {
if (viewDetails && viewDetails.title && viewDetails.content) {
setCustomViewTitle(viewDetails.title);
setCustomHtml(viewDetails.content);
} else {
setCustomViewTitle(undefined);
setCustomHtml(undefined);
}
});
}
}, []);
if (!customHtml || !customViewTitle) {
return null;
}
return (
<Collapsible
id={`custom-view`}
className={`base__actions`}
title={customViewTitle}
>
<div dangerouslySetInnerHTML={{ __html: customHtml }} />
</Collapsible>
);
};
@@ -0,0 +1 @@
export * from './CustomView';
@@ -0,0 +1,58 @@
import { CodeIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useEffect, useMemo, useState } from 'react';
import { BaseFieldProps, CustomPanelViewResult } from '../../../models';
import { FieldMessage } from './FieldMessage';
import { FieldTitle } from './FieldTitle';
export interface ICustomFieldProps extends BaseFieldProps<any> {
fieldData: {
name: string,
html: (data: any, onChange: (value: any) => void) => Promise<CustomPanelViewResult | undefined>,
};
onChange: (value: any) => void;
}
export const CustomField: React.FunctionComponent<ICustomFieldProps> = ({ label, value, required, description, fieldData, onChange }: React.PropsWithChildren<ICustomFieldProps>) => {
const [customHtml, setCustomHtml] = useState<any>(null);
const internalChange = (newValue: any) => {
onChange(newValue);
};
const showRequiredState = useMemo(() => {
return required && !value;
}, [required, value]);
useEffect(() => {
if (fieldData.html) {
fieldData.html(value, internalChange).then((data) => {
if (data) {
setCustomHtml(data);
} else {
setCustomHtml(null);
}
});
}
}, [fieldData, value]);
if (!customHtml) {
return null;
}
return (
<div className={`metadata_field`}>
<FieldTitle label={label} icon={<CodeIcon />} required={required} />
<div className="metadata_field">
<div dangerouslySetInnerHTML={{ __html: customHtml }} />
</div>
<FieldMessage
name={label.toLowerCase()}
description={description}
showRequired={showRequiredState}
/>
</div>
);
};
@@ -2,7 +2,7 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
import { DateHelper } from '../../../helpers/DateHelper';
import { BlockFieldData, Field, PanelSettings, WhenOperator } from '../../../models';
import { BlockFieldData, CustomPanelViewResult, Field, PanelSettings, WhenOperator } from '../../../models';
import { Command } from '../../Command';
import { CommandToCode } from '../../CommandToCode';
import { TagType } from '../../TagType';
@@ -26,7 +26,8 @@ import {
SlugField,
PreviewImageField,
PreviewImageValue,
NumberField
NumberField,
CustomField
} from '.';
import { fieldWhenClause } from '../../../utils/fieldWhenClause';
@@ -65,6 +66,10 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
renderFields
}: React.PropsWithChildren<IWrapperFieldProps>) => {
const [fieldValue, setFieldValue] = useState<any | undefined>(undefined);
const [customFields, setCustomFields] = useState<{
name: string;
html: (data: any, onChange: (value: any) => void) => Promise<CustomPanelViewResult | undefined>;
}[]>([]);
const listener = useCallback(
(event: any) => {
@@ -132,6 +137,14 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
};
}, [field, parent]);
useEffect(() => {
if (window.fmExternal && window.fmExternal.getCustomFields) {
setCustomFields(window.fmExternal.getCustomFields || []);
} else {
setCustomFields([]);
}
}, []);
if (field.hidden || fieldValue === undefined) {
return null;
}
@@ -470,6 +483,22 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
/>
</FieldBoundary>
);
} else if (customFields.find(f => f.name === field.type)) {
const fieldData = customFields.find(f => f.name === field.type);
if (fieldData) {
return (
<CustomField
key={field.name}
label={field.title || field.name}
description={field.description}
value={fieldValue}
required={!!field.required}
onChange={(value: any) => onSendUpdate(field.name, value, parentFields)}
fieldData={fieldData} />
);
} else {
return null;
}
} else {
console.warn(`Unknown field type: ${field.type}`);
return null;
+2 -1
View File
@@ -1,8 +1,10 @@
export * from './ChoiceButton';
export * from './ChoiceField';
export * from './CustomField';
export * from './DataFileField';
export * from './DateTimeField';
export * from './DraftField';
export * from './FieldMessage';
export * from './FieldTitle';
export * from './FileField';
export * from './ImageFallback';
@@ -11,7 +13,6 @@ export * from './NumberField';
export * from './PreviewImage';
export * from './PreviewImageField';
export * from './RequiredAsterix';
export * from './FieldMessage';
export * from './SlugField';
export * from './TextField';
export * from './Toggle';