From 39704f3a558c3414cfc68bbc783704b14130f94b Mon Sep 17 00:00:00 2001 From: Dennis Zoma Date: Mon, 7 Oct 2024 10:54:20 +0200 Subject: [PATCH] feat: Add option to filter content relationship options by active locale --- package.json | 5 + package.nls.json | 1 + src/listeners/dashboard/PagesListener.ts | 2 +- src/listeners/panel/FieldsListener.ts | 27 +- src/models/PanelSettings.ts | 1 + .../Fields/ContentTypeRelationshipField.tsx | 231 +++++++++--------- .../components/Fields/WrapperField.tsx | 1 + 7 files changed, 148 insertions(+), 120 deletions(-) diff --git a/package.json b/package.json index e094473e..8b420807 100644 --- a/package.json +++ b/package.json @@ -1543,6 +1543,11 @@ "default": "path", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeValue.description%" }, + "sameContentLocale": { + "type": "boolean", + "default": true, + "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.sameContentLocale.description%" + }, "when": { "type": "object", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.description%", diff --git a/package.nls.json b/package.nls.json index 7fc0537f..906586c1 100644 --- a/package.nls.json +++ b/package.nls.json @@ -228,6 +228,7 @@ "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.required.description": "Specify if the field is required", "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeName.description": "Specify the content type name to filter content for the contentRelationship field", "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.contentTypeValue.description": "Specify the value to insert for the contentRelationship field", + "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.sameContentLocale.description": "Specify if you only want to show the content with the same locale", "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.description": "Specify the conditions to show the field", "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.fieldRef.description": "The field ID to use", "setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.operator.description": "The operator to use", diff --git a/src/listeners/dashboard/PagesListener.ts b/src/listeners/dashboard/PagesListener.ts index 26d12e22..7456a5e4 100644 --- a/src/listeners/dashboard/PagesListener.ts +++ b/src/listeners/dashboard/PagesListener.ts @@ -257,7 +257,7 @@ export class PagesListener extends BaseListener { */ private static async createSearchIndex(pages: Page[]) { const pagesIndex = Fuse.createIndex( - ['title', 'slug', 'description', 'fmBody', 'type', 'fmContentType'], + ['title', 'slug', 'description', 'fmBody', 'type', 'fmContentType', 'fmLocale.locale'], pages ); await Extension.getInstance().setState( diff --git a/src/listeners/panel/FieldsListener.ts b/src/listeners/panel/FieldsListener.ts index 5181cbea..6ebc62e0 100644 --- a/src/listeners/panel/FieldsListener.ts +++ b/src/listeners/panel/FieldsListener.ts @@ -1,3 +1,4 @@ +import { i18n } from '../../commands'; import { ExtensionState } from '../../constants'; import { Page } from '../../dashboardWebView/models'; import { Extension } from '../../helpers'; @@ -29,15 +30,28 @@ export class FieldsListener extends BaseListener { * @param payload * @returns */ - private static async searchByType(command: string, requestId?: string, type?: string) { - if (!type || !requestId) { + private static async searchByType( + command: string, + requestId?: string, + data?: { type?: string; sameLocale?: boolean; activePath?: string } + ) { + if (!data?.type || !data?.activePath || !requestId) { + return; + } + + const activeLocale = await i18n.getLocale(data.activePath); + if (!activeLocale?.locale) { return; } PagesListener.getPagesData(false, async (pages) => { const fuseOptions: Fuse.IFuseOptions = { - keys: [{ name: 'fmContentType', weight: 1 }], - threshold: 0, + keys: [ + { name: 'fmContentType', weight: 1 }, + ...(data.sameLocale ? [{ name: 'fmLocale.locale', weight: 1 }] : []) + ], + findAllMatches: true, + threshold: 0 }; const pagesIndex = await Extension.getInstance().getState>( @@ -48,9 +62,8 @@ export class FieldsListener extends BaseListener { const fuse = new Fuse(pages || [], fuseOptions, fuseIndex); const results = fuse.search({ $and: [ - { - fmContentType: type - } + { fmContentType: data.type! }, + ...(data.sameLocale ? [{ 'fmLocale.locale': activeLocale.locale }] : []) ] }); const pageResults = results.map((page) => page.item); diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 12fe7bdf..e175f35c 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -135,6 +135,7 @@ export interface Field { // Content relationship contentTypeName?: string; contentTypeValue?: 'path' | 'slug'; + sameContentLocale?: boolean; // Custom field customType?: string; diff --git a/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx b/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx index bcedd18e..eb7d019c 100644 --- a/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx +++ b/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx @@ -12,20 +12,25 @@ import { FieldTitle } from './FieldTitle'; import { FieldMessage } from './FieldMessage'; import { ChoiceButton } from './ChoiceButton'; import useDropdownStyle from '../../hooks/useDropdownStyle'; +import useMessages from '../../hooks/useMessages'; export interface IContentTypeRelationshipFieldProps extends BaseFieldProps { contentTypeName?: string; contentTypeValue?: string; + sameContentLocale?: boolean; multiSelect?: boolean; onChange: (value: string | string[]) => void; } -export const ContentTypeRelationshipField: React.FunctionComponent = ({ +export const ContentTypeRelationshipField: React.FunctionComponent< + IContentTypeRelationshipFieldProps +> = ({ label, description, value, contentTypeName, contentTypeValue, + sameContentLocale, multiSelect, onChange, required @@ -37,6 +42,7 @@ export const ContentTypeRelationshipField: React.FunctionComponent(undefined); const inputRef = React.useRef(null); const { getDropdownStyle } = useDropdownStyle(inputRef as any, '6px'); + const { metadata } = useMessages(); /** * Check the required state @@ -49,11 +55,11 @@ export const ContentTypeRelationshipField: React.FunctionComponent { + const getValue = (value: Page, type: string = 'path') => { if (type === 'path') { return value.fmRelFilePath || value.fmFilePath; } @@ -64,20 +70,21 @@ export const ContentTypeRelationshipField: React.FunctionComponent { - const choice = pages.find( - (p: Page) => getValue(p, contentTypeValue) === value - ); + const getChoiceValue = useCallback( + (value: string) => { + const choice = pages.find((p: Page) => getValue(p, contentTypeValue) === value); - if (choice) { - return choice.title; - } - return ''; - }, [pages, choices, contentTypeValue]); + if (choice) { + return choice.title; + } + return ''; + }, + [pages, choices, contentTypeValue] + ); /** * On selecting an option - * @param txtValue + * @param txtValue */ const onSelect = (option: string) => { setFilter(undefined); @@ -98,25 +105,28 @@ export const ContentTypeRelationshipField: React.FunctionComponent { - if (multiSelect) { - const newValue = [...(crntSelected || [])].filter((v) => v !== txtValue); - setCrntSelected(newValue); - onChange(newValue); - } else { - setCrntSelected(''); - onChange(''); - } - }, [multiSelect, crntSelected, onChange]); + const removeSelected = useCallback( + (txtValue: string) => { + if (multiSelect) { + const newValue = [...(crntSelected || [])].filter((v) => v !== txtValue); + setCrntSelected(newValue); + onChange(newValue); + } else { + setCrntSelected(''); + onChange(''); + } + }, + [multiSelect, crntSelected, onChange] + ); /** * Retrieve the available choices */ const availableChoices = useMemo(() => { return pages.filter((page: Page) => { - const value = contentTypeValue === "slug" ? page.slug : page.fmFilePath; + const value = contentTypeValue === 'slug' ? page.slug : page.fmFilePath; let toShow = true; @@ -148,93 +158,93 @@ export const ContentTypeRelationshipField: React.FunctionComponent { - if (contentTypeName) { + if (contentTypeName && metadata?.filePath) { setLoading(true); messageHandler - .request(CommandToCode.searchByType, contentTypeName) + .request(CommandToCode.searchByType, { + type: contentTypeName, + sameLocale: sameContentLocale ?? true, + activePath: metadata?.filePath + }) .then((pages: Page[]) => { setPages(pages || []); - setChoices((pages || []).map(page => page.title)) - }).finally(() => { + setChoices((pages || []).map((page) => page.title)); + }) + .finally(() => { setLoading(false); }); } - }, [contentTypeName]); + }, [contentTypeName, sameContentLocale, metadata?.filePath]); return (
- } - required={required} /> + } required={required} /> - { - loading ? ( -
-
- {l10n.t(LocalizationKey.panelFieldsContentTypeRelationshipFieldLoading)} -
+ {loading ? ( +
+
+ {l10n.t(LocalizationKey.panelFieldsContentTypeRelationshipFieldLoading)}
- ) : ( - - {({ open }) => ( -
+
+ ) : ( + + {({ open }) => ( +
+
+ setFilter(e.target.value)} + value={filter || ''} + placeholder={l10n.t(LocalizationKey.panelFieldsChoiceFieldSelect, label)} + ref={inputRef} + /> -
- setFilter(e.target.value)} - value={filter || ""} - placeholder={l10n.t(LocalizationKey.panelFieldsChoiceFieldSelect, label)} - ref={inputRef} /> - - - -
- - setFilter('')} - > - - {availableChoices.map((choice) => ( - `py-[var(--input-padding-vertical)] px-[var(--input-padding-horizontal)] list-none cursor-pointer hover:text-[var(--vscode-button-foreground)] hover:bg-[var(--vscode-button-hoverBackground)] ${active ? "text-[var(--vscode-button-foreground)] bg-[var(--vscode-button-hoverBackground)] " : ""}`}> - {choice.title} -
{choice.slug}
-
- ))} - - {availableChoices.length === 0 ? ( -
- {l10n.t(LocalizationKey.commonNoResults)} -
- ) : null} -
-
+ +
- )} - - ) - } + + setFilter('')} + > + + {availableChoices.map((choice) => ( + + `cursor-pointer list-none px-[var(--input-padding-horizontal)] py-[var(--input-padding-vertical)] hover:bg-[var(--vscode-button-hoverBackground)] hover:text-[var(--vscode-button-foreground)] ${ + active + ? 'bg-[var(--vscode-button-hoverBackground)] text-[var(--vscode-button-foreground)] ' + : '' + }` + } + > + {choice.title} +
{choice.slug}
+
+ ))} + + {availableChoices.length === 0 ? ( +
+ {l10n.t(LocalizationKey.commonNoResults)} +
+ ) : null} +
+
+
+ )} +
+ )} - { - pages.length > 0 && ( - crntSelected instanceof Array - ? crntSelected.map((value: string) => ( + {pages.length > 0 && + (crntSelected instanceof Array + ? crntSelected.map((value: string) => ( )) - : crntSelected && ( + : crntSelected && ( - ) - ) - } + ))}
- ) -}; \ No newline at end of file + ); +}; diff --git a/src/panelWebView/components/Fields/WrapperField.tsx b/src/panelWebView/components/Fields/WrapperField.tsx index b3510614..b76cccd1 100644 --- a/src/panelWebView/components/Fields/WrapperField.tsx +++ b/src/panelWebView/components/Fields/WrapperField.tsx @@ -497,6 +497,7 @@ export const WrapperField: React.FunctionComponent = ({ required={!!field.required} contentTypeName={field.contentTypeName} contentTypeValue={field.contentTypeValue} + sameContentLocale={field.sameContentLocale} multiSelect={field.multiple} onChange={onFieldChange} />