mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-06 17:02:54 +02:00
#824 - Field actions
This commit is contained in:
@@ -67,11 +67,11 @@ export class CustomScript {
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
private static async singleRun(
|
||||
public static async singleRun(
|
||||
wsPath: string,
|
||||
script: ICustomScript,
|
||||
path: string | null = null
|
||||
): Promise<void> {
|
||||
): Promise<any> {
|
||||
let articlePath: string | null = path;
|
||||
let article: ParsedFrontMatter | null | undefined = null;
|
||||
|
||||
@@ -99,7 +99,7 @@ export class CustomScript {
|
||||
articlePath as string,
|
||||
script
|
||||
);
|
||||
await CustomScript.showOutput(output, script, articlePath);
|
||||
return await CustomScript.showOutput(output, script, articlePath);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -133,7 +133,7 @@ export class CustomScript {
|
||||
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
|
||||
cancellable: false
|
||||
},
|
||||
async (progress, token) => {
|
||||
async (_, __) => {
|
||||
for await (const folder of folders) {
|
||||
if (folder.lastModified.length > 0) {
|
||||
for await (const file of folder.lastModified) {
|
||||
@@ -266,15 +266,21 @@ export class CustomScript {
|
||||
output: string | null,
|
||||
script: ICustomScript,
|
||||
articlePath?: string | null
|
||||
): Promise<void> {
|
||||
): Promise<any> {
|
||||
if (output) {
|
||||
try {
|
||||
const data: {
|
||||
frontmatter?: { [key: string]: any };
|
||||
fmAction?: 'open' | 'copyMediaMetadata' | 'copyMediaMetadataAndDelete' | 'deleteMedia';
|
||||
fmAction?:
|
||||
| 'open'
|
||||
| 'copyMediaMetadata'
|
||||
| 'copyMediaMetadataAndDelete'
|
||||
| 'deleteMedia'
|
||||
| 'fieldAction';
|
||||
fmPath?: string;
|
||||
fmSourcePath?: string;
|
||||
fmDestinationPath?: string;
|
||||
fmFieldValue?: any;
|
||||
} = JSON.parse(output);
|
||||
|
||||
if (data.frontmatter) {
|
||||
@@ -326,6 +332,8 @@ export class CustomScript {
|
||||
await MediaHelpers.deleteFile(data.fmSourcePath);
|
||||
} else if (data.fmAction === 'deleteMedia' && data.fmPath) {
|
||||
await MediaHelpers.deleteFile(data.fmPath);
|
||||
} else if (data.fmAction === 'fieldAction') {
|
||||
return data.fmFieldValue || undefined;
|
||||
}
|
||||
} else {
|
||||
Logger.error(`No frontmatter found.`);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Folders } from '../../commands';
|
||||
import { SETTING_CUSTOM_SCRIPTS } from '../../constants';
|
||||
import { CustomScript, Settings } from '../../helpers';
|
||||
import { CustomScript, Notifications, Settings } from '../../helpers';
|
||||
import { CustomScript as ICustomScript, PostMessageData } from '../../models';
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { BaseListener } from './BaseListener';
|
||||
@@ -16,6 +17,32 @@ export class ScriptListener extends BaseListener {
|
||||
case CommandToCode.runCustomScript:
|
||||
this.runCustomScript(msg);
|
||||
break;
|
||||
case CommandToCode.runFieldAction:
|
||||
this.runFieldAction(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async runFieldAction({ command, payload, requestId }: PostMessageData) {
|
||||
if (!payload || !requestId || !command) {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = payload as ICustomScript;
|
||||
if (script.script) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (!wsFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldValue = await CustomScript.singleRun(wsFolder.fsPath, script);
|
||||
|
||||
if (fieldValue) {
|
||||
this.sendRequest(command, requestId, fieldValue);
|
||||
} else {
|
||||
Notifications.error('The script did not return a field value');
|
||||
this.sendRequestError(command, requestId, 'The script did not return a field value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './localization.enum';
|
||||
export * from './localize';
|
||||
|
||||
@@ -1416,6 +1416,14 @@ export enum LocalizationKey {
|
||||
* Unkown field type: {0}
|
||||
*/
|
||||
panelFieldsWrapperFieldUnknown = 'panel.fields.wrapperField.unknown',
|
||||
/**
|
||||
* Custom action
|
||||
*/
|
||||
panelFieldsFieldCustomActionButtonTitle = 'panel.fields.fieldCustomAction.button.title',
|
||||
/**
|
||||
* Executing field action...
|
||||
*/
|
||||
panelFieldsFieldCustomActionExecuting = 'panel.fields.fieldCustomAction.executing',
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import * as l10n from '@vscode/l10n';
|
||||
|
||||
export const localize = (key: string, ...args: any[]): string => {
|
||||
return l10n.t(key, ...args);
|
||||
};
|
||||
@@ -141,6 +141,9 @@ export interface Field {
|
||||
|
||||
// When clause
|
||||
when?: WhenClause;
|
||||
|
||||
// Custom action
|
||||
action?: CustomScript;
|
||||
}
|
||||
|
||||
export interface NumberOptions {
|
||||
|
||||
@@ -46,5 +46,6 @@ export enum CommandToCode {
|
||||
copilotSuggestTaxonomy = 'copilot-suggest-taxonomy',
|
||||
searchByType = 'search-by-type',
|
||||
processMediaData = 'process-media-data',
|
||||
isServerStarted = 'is-server-started'
|
||||
isServerStarted = 'is-server-started',
|
||||
runFieldAction = 'run-field-action'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
import { CustomScript } from '../../../models';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { CodeBracketIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandToCode } from '../../CommandToCode';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
|
||||
export interface IFieldCustomActionProps {
|
||||
action: CustomScript;
|
||||
disabled?: boolean;
|
||||
triggerLoading?: (message?: string) => void;
|
||||
onChange: (value: any) => void;
|
||||
}
|
||||
|
||||
export const FieldCustomAction: React.FunctionComponent<IFieldCustomActionProps> = ({ action, disabled, triggerLoading, onChange }: React.PropsWithChildren<IFieldCustomActionProps>) => {
|
||||
return (
|
||||
<button
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={action?.title || localize(LocalizationKey.panelFieldsFieldCustomActionButtonTitle)}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (triggerLoading) {
|
||||
triggerLoading(localize(LocalizationKey.panelFieldsFieldCustomActionExecuting));
|
||||
}
|
||||
|
||||
messageHandler.request(CommandToCode.runFieldAction, {
|
||||
...action
|
||||
}).then((value: any) => {
|
||||
onChange(value);
|
||||
|
||||
if (triggerLoading) {
|
||||
triggerLoading();
|
||||
}
|
||||
}).catch(() => {
|
||||
console.error('Error while running the custom action');
|
||||
|
||||
if (triggerLoading) {
|
||||
triggerLoading();
|
||||
}
|
||||
});
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span className='sr-only'>{action?.title || localize(LocalizationKey.panelFieldsFieldCustomActionButtonTitle)}</span>
|
||||
<CodeBracketIcon style={{ height: "16px", width: "16px" }} aria-hidden="true" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { RequiredAsterix } from './RequiredAsterix';
|
||||
import { CustomScript } from '../../../models';
|
||||
import { FieldCustomAction } from './FieldCustomAction';
|
||||
|
||||
export interface IFieldTitleProps {
|
||||
label: string | JSX.Element;
|
||||
@@ -8,6 +10,10 @@ export interface IFieldTitleProps {
|
||||
className?: string;
|
||||
required?: boolean;
|
||||
actionElement?: JSX.Element;
|
||||
customAction?: CustomScript;
|
||||
isDisabled?: boolean;
|
||||
triggerLoading?: (message?: string) => void;
|
||||
onChange?: (value: any) => void;
|
||||
}
|
||||
|
||||
export const FieldTitle: React.FunctionComponent<IFieldTitleProps> = ({
|
||||
@@ -16,6 +22,10 @@ export const FieldTitle: React.FunctionComponent<IFieldTitleProps> = ({
|
||||
className,
|
||||
required,
|
||||
actionElement,
|
||||
customAction,
|
||||
isDisabled,
|
||||
triggerLoading,
|
||||
onChange,
|
||||
}: React.PropsWithChildren<IFieldTitleProps>) => {
|
||||
const Icon = useMemo(() => {
|
||||
return icon ? React.cloneElement(icon, { style: { width: '16px', height: '16px' } }) : null;
|
||||
@@ -29,7 +39,19 @@ export const FieldTitle: React.FunctionComponent<IFieldTitleProps> = ({
|
||||
<RequiredAsterix required={required} />
|
||||
</label>
|
||||
|
||||
{actionElement}
|
||||
<div className="flex gap-4">
|
||||
{
|
||||
customAction && onChange && (
|
||||
<FieldCustomAction
|
||||
action={customAction}
|
||||
disabled={isDisabled}
|
||||
triggerLoading={triggerLoading}
|
||||
onChange={onChange} />
|
||||
)
|
||||
}
|
||||
|
||||
{actionElement}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { CommandToCode } from '../../CommandToCode';
|
||||
import { TagType } from '../../TagType';
|
||||
import Downshift from 'downshift';
|
||||
import { AddIcon } from '../Icons/AddIcon';
|
||||
import { BlockFieldData, CustomTaxonomyData } from '../../../models';
|
||||
import { BlockFieldData, CustomScript, CustomTaxonomyData } from '../../../models';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { messageHandler, Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { FieldMessage } from '../Fields/FieldMessage';
|
||||
@@ -13,8 +13,7 @@ import { FieldTitle } from '../Fields/FieldTitle';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { PanelSettingsAtom } from '../../state';
|
||||
import { SparklesIcon } from '@heroicons/react/24/outline';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import useDropdownStyle from '../../hooks/useDropdownStyle';
|
||||
import { CopilotIcon } from '../Icons';
|
||||
|
||||
@@ -37,6 +36,7 @@ export interface ITagPickerProps {
|
||||
limit?: number;
|
||||
required?: boolean;
|
||||
renderAsString?: boolean;
|
||||
action?: CustomScript;
|
||||
}
|
||||
|
||||
const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
@@ -56,7 +56,8 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
blockData,
|
||||
limit,
|
||||
required,
|
||||
renderAsString
|
||||
renderAsString,
|
||||
action
|
||||
}: React.PropsWithChildren<ITagPickerProps>) => {
|
||||
const [selected, setSelected] = React.useState<string[]>([]);
|
||||
const [inputValue, setInputValue] = React.useState<string>('');
|
||||
@@ -65,7 +66,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
const { getDropdownStyle } = useDropdownStyle(inputRef as any);
|
||||
const dsRef = React.useRef<Downshift<string> | null>(null);
|
||||
const settings = useRecoilValue(PanelSettingsAtom);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const [loading, setLoading] = React.useState<string | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Removes an option
|
||||
@@ -249,25 +250,29 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
[options, inputRef, selected, freeform]
|
||||
);
|
||||
|
||||
const updateTaxonomy = (values: string[]) => {
|
||||
if (values && values instanceof Array && values.length > 0) {
|
||||
const uniqValues = Array.from(new Set([...selected, ...values]));
|
||||
setSelected(uniqValues);
|
||||
sendUpdate(uniqValues);
|
||||
setInputValue('');
|
||||
}
|
||||
}
|
||||
|
||||
const suggestTaxonomy = useCallback(
|
||||
(aiType: 'ai' | 'copilot', type: TagType) => {
|
||||
setLoading(true);
|
||||
setLoading(localize(LocalizationKey.panelTagPickerAiGenerating));
|
||||
|
||||
const command =
|
||||
aiType === 'ai' ? CommandToCode.aiSuggestTaxonomy : CommandToCode.copilotSuggestTaxonomy;
|
||||
messageHandler
|
||||
.request<string[]>(command, type)
|
||||
.then((values) => {
|
||||
setLoading(false);
|
||||
if (values && values instanceof Array && values.length > 0) {
|
||||
const uniqValues = Array.from(new Set([...selected, ...values]));
|
||||
setSelected(uniqValues);
|
||||
sendUpdate(uniqValues);
|
||||
setInputValue('');
|
||||
}
|
||||
setLoading(undefined);
|
||||
updateTaxonomy(values)
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
setLoading(undefined);
|
||||
});
|
||||
},
|
||||
[selected]
|
||||
@@ -286,13 +291,13 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
|
||||
const inputPlaceholder = useMemo((): string => {
|
||||
if (checkIsDisabled()) {
|
||||
return l10n.t(
|
||||
return localize(
|
||||
LocalizationKey.panelTagPickerInputPlaceholderDisabled,
|
||||
`${limit} ${label || type.toLowerCase()}`
|
||||
);
|
||||
}
|
||||
|
||||
return l10n.t(LocalizationKey.panelTagPickerInputPlaceholderEmpty, label || type.toLowerCase());
|
||||
return localize(LocalizationKey.panelTagPickerInputPlaceholderEmpty, label || type.toLowerCase());
|
||||
}, [label, type, checkIsDisabled]);
|
||||
|
||||
const showRequiredState = useMemo(() => {
|
||||
@@ -305,17 +310,17 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<>
|
||||
{settings?.aiEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action"
|
||||
title={l10n.t(
|
||||
title={localize(
|
||||
LocalizationKey.panelTagPickerAiSuggest,
|
||||
label?.toLowerCase() || type.toLowerCase()
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => suggestTaxonomy('ai', type)}
|
||||
disabled={loading}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<SparklesIcon />
|
||||
</button>
|
||||
@@ -324,18 +329,18 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
{settings?.copilotEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action"
|
||||
title={l10n.t(
|
||||
title={localize(
|
||||
LocalizationKey.panelTagPickerCopilotSuggest,
|
||||
label?.toLowerCase() || type.toLowerCase()
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => suggestTaxonomy('copilot', type)}
|
||||
disabled={loading}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<CopilotIcon />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}, [settings?.aiEnabled, settings?.copilotEnabled, label, type]);
|
||||
|
||||
@@ -376,7 +381,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
<>
|
||||
{` `}
|
||||
<span style={{ fontWeight: 'lighter' }}>
|
||||
({l10n.t(LocalizationKey.panelTagPickerLimit, limit)})
|
||||
({localize(LocalizationKey.panelTagPickerLimit, limit)})
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
@@ -387,12 +392,16 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
actionElement={actionElement}
|
||||
icon={icon}
|
||||
required={required}
|
||||
isDisabled={!!loading}
|
||||
customAction={action}
|
||||
triggerLoading={(message) => setLoading(message)}
|
||||
onChange={updateTaxonomy}
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
{loading && (
|
||||
<div className="metadata_field__loading">
|
||||
{l10n.t(LocalizationKey.panelTagPickerAiGenerating)}
|
||||
{loading}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -418,9 +427,8 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
<>
|
||||
<div
|
||||
{...getRootProps(undefined, { suppressRefError: true })}
|
||||
className={`article__tags__input ${freeform ? 'freeform' : ''} ${
|
||||
showRequiredState ? 'required' : ''
|
||||
}`}
|
||||
className={`article__tags__input ${freeform ? 'freeform' : ''} ${showRequiredState ? 'required' : ''
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
{...getInputProps({
|
||||
@@ -443,7 +451,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
{freeform && (
|
||||
<button
|
||||
className={`article__tags__input__button`}
|
||||
title={l10n.t(LocalizationKey.panelTagPickerUnkown)}
|
||||
title={localize(LocalizationKey.panelTagPickerUnkown)}
|
||||
disabled={!inputValue || checkIsDisabled()}
|
||||
onClick={() => insertUnkownTag(closeMenu)}
|
||||
>
|
||||
|
||||
@@ -2,14 +2,13 @@ import { PencilIcon, SparklesIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { BaseFieldProps, PanelSettings } from '../../../models';
|
||||
import { BaseFieldProps, CustomScript, PanelSettings } from '../../../models';
|
||||
import { RequiredFieldsAtom } from '../../state';
|
||||
import { FieldTitle } from './FieldTitle';
|
||||
import { FieldMessage } from './FieldMessage';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { CommandToCode } from '../../CommandToCode';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { LocalizationKey, localize } from '../../../localization';
|
||||
import { useDebounce } from '../../../hooks/useDebounce';
|
||||
import { CopilotIcon } from '../Icons';
|
||||
|
||||
@@ -23,6 +22,7 @@ export interface ITextFieldProps extends BaseFieldProps<string> {
|
||||
name: string;
|
||||
placeholder?: string;
|
||||
settings: PanelSettings;
|
||||
action?: CustomScript;
|
||||
onChange: (txtValue: string) => void;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
name,
|
||||
settings,
|
||||
onChange,
|
||||
action,
|
||||
required
|
||||
}: React.PropsWithChildren<ITextFieldProps>) => {
|
||||
const [, setRequiredFields] = useRecoilState(RequiredFieldsAtom);
|
||||
const [text, setText] = React.useState<string | null | undefined>(undefined);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const [loading, setLoading] = React.useState<string | undefined>(undefined);
|
||||
const [lastUpdated, setLastUpdated] = React.useState<number | null>(null);
|
||||
const debouncedText = useDebounce<string | null | undefined>(text, DEBOUNCE_TIME);
|
||||
|
||||
@@ -93,13 +94,14 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
}, [showRequiredState, isValid]);
|
||||
|
||||
const suggestDescription = (type: 'ai' | 'copilot') => {
|
||||
setLoading(true);
|
||||
setLoading(localize(LocalizationKey.panelFieldsTextFieldAiGenerate));
|
||||
|
||||
messageHandler
|
||||
.request<string>(
|
||||
type === 'copilot' ? CommandToCode.copilotSuggestDescription : CommandToCode.aiSuggestDescription
|
||||
)
|
||||
.then((suggestion) => {
|
||||
setLoading(false);
|
||||
setLoading(undefined);
|
||||
|
||||
if (suggestion) {
|
||||
setText(suggestion);
|
||||
@@ -107,7 +109,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
setLoading(undefined);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -117,14 +119,14 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<>
|
||||
{settings?.aiEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={l10n.t(LocalizationKey.panelFieldsTextFieldAiMessage, label?.toLowerCase())}
|
||||
title={localize(LocalizationKey.panelFieldsTextFieldAiMessage, label?.toLowerCase())}
|
||||
type="button"
|
||||
onClick={() => suggestDescription('ai')}
|
||||
disabled={loading}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<SparklesIcon />
|
||||
</button>
|
||||
@@ -133,17 +135,17 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
{settings?.copilotEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={l10n.t(LocalizationKey.panelFieldsTextFieldCopilotMessage, label?.toLowerCase())}
|
||||
title={localize(LocalizationKey.panelFieldsTextFieldCopilotMessage, label?.toLowerCase())}
|
||||
type="button"
|
||||
onClick={() => suggestDescription('copilot')}
|
||||
disabled={loading}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<CopilotIcon />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}, [settings?.aiEnabled, name]);
|
||||
}, [settings?.aiEnabled, settings?.copilotEnabled, name, action, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (text !== value && (lastUpdated === null || Date.now() - DEBOUNCE_TIME > lastUpdated)) {
|
||||
@@ -165,18 +167,22 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
actionElement={actionElement}
|
||||
icon={<PencilIcon />}
|
||||
required={required}
|
||||
isDisabled={!!loading}
|
||||
customAction={action}
|
||||
triggerLoading={(message) => setLoading(message)}
|
||||
onChange={onTextChange}
|
||||
/>
|
||||
|
||||
<div className='relative'>
|
||||
{loading && (
|
||||
<div className="metadata_field__loading">
|
||||
{l10n.t(LocalizationKey.panelFieldsTextFieldAiGenerate)}
|
||||
{loading}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{wysiwyg ? (
|
||||
<React.Suspense
|
||||
fallback={<div>{l10n.t(LocalizationKey.panelFieldsTextFieldLoading)}</div>}
|
||||
fallback={<div>{localize(LocalizationKey.panelFieldsTextFieldLoading)}</div>}
|
||||
>
|
||||
<WysiwygField text={text || ''} onChange={onTextChange} />
|
||||
</React.Suspense>
|
||||
@@ -206,7 +212,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
|
||||
{limit && limit > 0 && (text || '').length > limit && (
|
||||
<div className={`metadata_field__limit`}>
|
||||
{l10n.t(LocalizationKey.panelFieldsTextFieldLimit, `${(text || '').length}/${limit}`)}
|
||||
{localize(LocalizationKey.panelFieldsTextFieldLimit, `${(text || '').length}/${limit}`)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
value={(fieldValue as string) || null}
|
||||
required={!!field.required}
|
||||
settings={settings}
|
||||
action={field.action}
|
||||
/>
|
||||
</FieldBoundary>
|
||||
);
|
||||
@@ -307,6 +308,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
|
||||
limit={field.taxonomyLimit}
|
||||
renderAsString={field.singleValueAsString}
|
||||
required={!!field.required}
|
||||
action={field.action}
|
||||
/>
|
||||
</FieldBoundary>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user