Skip to content

Commit

Permalink
#824 - Added support in single image field
Browse files Browse the repository at this point in the history
  • Loading branch information
estruyf committed Jun 28, 2024
1 parent fe7a296 commit 4197de2
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 60 deletions.
22 changes: 13 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1550,8 +1550,12 @@
}
}
},
"action": {
"$ref": "#customscript"
"actions": {
"type": "array",
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.actions.description%",
"items": {
"$ref": "#customscript"
}
}
},
"additionalProperties": false,
Expand Down Expand Up @@ -2659,23 +2663,23 @@
"group": "navigation@0",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.collapseSections",
"group": "navigation@1",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.mode.switch",
"group": "navigation@2",
"group": "navigation@1",
"when": "view == frontMatter.explorer && frontMatter:has:modes == true"
},
{
"command": "frontMatter.project.switch",
"group": "navigation@3",
"group": "navigation@2",
"when": "view == frontMatter.explorer && frontMatter:project:switch:enabled"
},
{
"command": "frontMatter.settings.refresh",
"group": "navigation@3",
"when": "view == frontMatter.explorer"
},
{
"command": "frontMatter.collapseSections",
"group": "navigation@4",
"when": "view == frontMatter.explorer"
},
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.operator.description": "The operator to use",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.value.description": "The value to compare",
"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.fields.items.properties.actions.description": "Specify the field custom actions",
"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.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.",
Expand Down
2 changes: 1 addition & 1 deletion src/models/PanelSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export interface Field {
when?: WhenClause;

// Custom action
action?: CustomScript;
actions?: CustomScript[];
}

export interface NumberOptions {
Expand Down
103 changes: 70 additions & 33 deletions src/panelWebView/components/Fields/FieldCustomAction.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,85 @@
import * as React from 'react';
import { CustomScript } from '../../../models';
import { messageHandler } from '@estruyf/vscode/dist/client';
import { CodeBracketIcon } from '@heroicons/react/24/outline';
import { CodeBracketIcon, CommandLineIcon } from '@heroicons/react/24/solid';
import { CommandToCode } from '../../CommandToCode';
import { LocalizationKey, localize } from '../../../localization';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';

export interface IFieldCustomActionProps {
action: CustomScript;
actions: CustomScript[];
disabled?: boolean;
triggerLoading?: (message?: string) => void;
onChange: (value: any) => void;
}

export const FieldCustomAction: React.FunctionComponent<IFieldCustomActionProps> = ({ action, disabled, triggerLoading, onChange }: React.PropsWithChildren<IFieldCustomActionProps>) => {
export const FieldCustomAction: React.FunctionComponent<IFieldCustomActionProps> = ({ actions, disabled, triggerLoading, onChange }: React.PropsWithChildren<IFieldCustomActionProps>) => {

const triggerAction = React.useCallback((action: CustomScript) => {
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();
}
});
}, [triggerLoading, onChange]);

if (!actions) {
return null;
}

if (actions.length === 1) {
const action = actions[0];
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={triggerAction.bind(null, action)}
disabled={disabled}
>
<span className='sr-only'>{action?.title || localize(LocalizationKey.panelFieldsFieldCustomActionButtonTitle)}</span>
<CommandLineIcon style={{ height: "16px", width: "16px" }} aria-hidden="true" />
</button>
);
}

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));
}
<DropdownMenu>
<DropdownMenuTrigger
title={localize(LocalizationKey.commonOpenCustomActions)}
className='metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50'>
<span className="sr-only">{localize(LocalizationKey.commonOpenCustomActions)}</span>
<CommandLineIcon style={{ height: "16px", width: "16px" }} aria-hidden="true" />
</DropdownMenuTrigger>

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>
);
<DropdownMenuContent align='end' className='p-0'>
{
actions.map((action) => (
<DropdownMenuItem
key={action.id || action.title}
title={action.title}
className={`focus:bg-[var(--vscode-button-background)] focus:text-[var(--vscode-button-foreground)] focus:outline-0 rounded-none`}
onClick={(e) => triggerAction(action)}>
<CommandLineIcon className={`mr-2 h-4 w-4`} aria-hidden={true} />
<span>{action.title}</span>
</DropdownMenuItem>
))
}
</DropdownMenuContent>
</DropdownMenu>
)
};
12 changes: 6 additions & 6 deletions src/panelWebView/components/Fields/FieldTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface IFieldTitleProps {
className?: string;
required?: boolean;
actionElement?: JSX.Element;
customAction?: CustomScript;
customActions?: CustomScript[];
isDisabled?: boolean;
triggerLoading?: (message?: string) => void;
onChange?: (value: any) => void;
Expand All @@ -22,7 +22,7 @@ export const FieldTitle: React.FunctionComponent<IFieldTitleProps> = ({
className,
required,
actionElement,
customAction,
customActions,
isDisabled,
triggerLoading,
onChange,
Expand All @@ -40,17 +40,17 @@ export const FieldTitle: React.FunctionComponent<IFieldTitleProps> = ({
</label>

<div className="flex gap-4">
{actionElement}

{
customAction && onChange && (
customActions && onChange && (
<FieldCustomAction
action={customAction}
actions={customActions}
disabled={isDisabled}
triggerLoading={triggerLoading}
onChange={onChange} />
)
}

{actionElement}
</div>
</div>
);
Expand Down
14 changes: 12 additions & 2 deletions src/panelWebView/components/Fields/PreviewImageField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PhotoIcon } from '@heroicons/react/24/outline';
import * as React from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { DefaultFieldValues } from '../../../constants';
import { BaseFieldProps, BlockFieldData } from '../../../models';
import { BaseFieldProps, BlockFieldData, CustomScript } from '../../../models';
import { CommandToCode } from '../../CommandToCode';
import { FieldTitle } from './FieldTitle';
import { PreviewImage } from './PreviewImage';
Expand All @@ -23,6 +23,7 @@ export interface IPreviewImageFieldProps
parents?: string[];
multiple?: boolean;
blockData?: BlockFieldData;
actions?: CustomScript[];
onChange: (value: string | string[] | null) => void;
}

Expand All @@ -36,8 +37,10 @@ export const PreviewImageField: React.FunctionComponent<IPreviewImageFieldProps>
filePath,
multiple,
parents,
actions,
required
}: React.PropsWithChildren<IPreviewImageFieldProps>) => {
const [loading, setLoading] = React.useState<string | undefined>(undefined);
const [imageData, setImageData] = React.useState<PreviewImageValue | PreviewImageValue[] | null>(null);

const selectImage = useCallback(() => {
Expand Down Expand Up @@ -95,7 +98,14 @@ export const PreviewImageField: React.FunctionComponent<IPreviewImageFieldProps>

return (
<div className={`metadata_field`}>
<FieldTitle label={label} icon={<PhotoIcon />} required={required} />
<FieldTitle
label={label}
icon={<PhotoIcon />}
required={required}
isDisabled={!!loading}
customActions={actions}
triggerLoading={(message) => setLoading(message)}
onChange={(value: string) => onChange(value)} />

<div
className={`metadata_field__preview_image ${multiple && imageData && (imageData as PreviewImageValue[]).length > 0
Expand Down
6 changes: 3 additions & 3 deletions src/panelWebView/components/Fields/TagPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface ITagPickerProps {
limit?: number;
required?: boolean;
renderAsString?: boolean;
action?: CustomScript;
actions?: CustomScript[];
}

const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
Expand All @@ -57,7 +57,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
limit,
required,
renderAsString,
action
actions
}: React.PropsWithChildren<ITagPickerProps>) => {
const [selected, setSelected] = React.useState<string[]>([]);
const [inputValue, setInputValue] = React.useState<string>('');
Expand Down Expand Up @@ -393,7 +393,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
icon={icon}
required={required}
isDisabled={!!loading}
customAction={action}
customActions={actions}
triggerLoading={(message) => setLoading(message)}
onChange={updateTaxonomy}
/>
Expand Down
8 changes: 4 additions & 4 deletions src/panelWebView/components/Fields/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface ITextFieldProps extends BaseFieldProps<string> {
name: string;
placeholder?: string;
settings: PanelSettings;
action?: CustomScript;
actions?: CustomScript[];
onChange: (txtValue: string) => void;
}

Expand All @@ -40,7 +40,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
name,
settings,
onChange,
action,
actions,
required
}: React.PropsWithChildren<ITextFieldProps>) => {
const [, setRequiredFields] = useRecoilState(RequiredFieldsAtom);
Expand Down Expand Up @@ -145,7 +145,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
)}
</>
);
}, [settings?.aiEnabled, settings?.copilotEnabled, name, action, loading]);
}, [settings?.aiEnabled, settings?.copilotEnabled, name, actions, loading]);

useEffect(() => {
if (text !== value && (lastUpdated === null || Date.now() - DEBOUNCE_TIME > lastUpdated)) {
Expand All @@ -168,7 +168,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
icon={<PencilIcon />}
required={required}
isDisabled={!!loading}
customAction={action}
customActions={actions}
triggerLoading={(message) => setLoading(message)}
onChange={onTextChange}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/panelWebView/components/Fields/WrapperField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
value={(fieldValue as string) || null}
required={!!field.required}
settings={settings}
action={field.action}
actions={field.actions}
/>
</FieldBoundary>
);
Expand Down Expand Up @@ -252,6 +252,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
multiple={field.multiple}
blockData={blockData}
onChange={onFieldChange}
actions={field.actions}
/>
</FieldBoundary>
);
Expand Down Expand Up @@ -308,7 +309,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
limit={field.taxonomyLimit}
renderAsString={field.singleValueAsString}
required={!!field.required}
action={field.action}
actions={field.actions}
/>
</FieldBoundary>
);
Expand Down

0 comments on commit 4197de2

Please sign in to comment.