From 2f76de2a28ada1c9354c4bf0c1d31e6df9e1ed2c Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 27 Nov 2024 16:00:34 +0100 Subject: [PATCH] #405 - Implement custom grouping option --- CHANGELOG.md | 1 + package.json | 28 ++++++++++++++ package.nls.json | 4 ++ src/constants/settings.ts | 1 + .../components/Contents/Overview.tsx | 38 ++++++++++++++----- .../components/Header/Grouping.tsx | 23 ++++++----- src/dashboardWebView/models/Settings.ts | 1 + src/helpers/DashboardSettings.ts | 3 ++ src/helpers/SettingsHelper.ts | 6 ++- 9 files changed, 85 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a18b36a..2f16d44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 🎨 Enhancements +- [#405](https://github.com/estruyf/vscode-front-matter/issues/405): Added new `frontMatter.content.grouping` setting which allows you to define custom "group by" options - [#705](https://github.com/estruyf/vscode-front-matter/issues/705): UX improvements for the panel view - [#887](https://github.com/estruyf/vscode-front-matter/issues/887): Added new `frontMatter.global.timezone` setting, by default it is set to `UTC` for date formatting - [#888](https://github.com/estruyf/vscode-front-matter/issues/888): Added the ability to prompt GitHub Copilot from a custom script/action diff --git a/package.json b/package.json index 173832cc..24b23768 100644 --- a/package.json +++ b/package.json @@ -484,6 +484,34 @@ "additionalProperties": false } }, + "frontMatter.content.grouping": { + "type": "array", + "default": [], + "markdownDescription": "%setting.frontMatter.content.grouping.markdownDescription%", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "%setting.frontMatter.content.grouping.items.properties.id.description%" + }, + "title": { + "type": "string", + "description": "%setting.frontMatter.content.grouping.items.properties.title.description%" + }, + "name": { + "type": "string", + "description": "%setting.frontMatter.content.grouping.items.properties.name.description%" + } + }, + "additionalProperties": false, + "required": [ + "title", + "name" + ] + }, + "scope": "Content" + }, "frontMatter.content.sorting": { "type": "array", "default": [], diff --git a/package.nls.json b/package.nls.json index e69768d3..f88d1f05 100644 --- a/package.nls.json +++ b/package.nls.json @@ -96,6 +96,10 @@ "setting.frontMatter.content.publicFolder.properties.relative.description": "Defines if the path to your media files be relative to the content file?", "setting.frontMatter.snippets.wrapper.enabled.markdownDescription": "Specify if you want to wrap the snippets. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.snippets.wrapper.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.snippets.wrapper.enabled%22%5D)", "setting.frontMatter.content.snippets.markdownDescription": "Define the snippets you want to use in your content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.snippets) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.snippets%22%5D)", + "setting.frontMatter.content.grouping.markdownDescription": "Specify the grouping options for your dashboard content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.grouping) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.grouping%22%5D)", + "setting.frontMatter.content.grouping.items.properties.id.description": "The ID of the grouping option.", + "setting.frontMatter.content.grouping.items.properties.title.description": "Title of the grouping which will be shown in the UI.", + "setting.frontMatter.content.grouping.items.properties.name.description": "Name of the content-type field to group by.", "setting.frontMatter.content.sorting.markdownDescription": "Define the sorting options for your dashboard content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.sorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.sorting%22%5D)", "setting.frontMatter.content.sorting.items.properties.id.description": "The ID of the sorting option. This will be used for the storing the last used sorting option or the default option.", "setting.frontMatter.content.sorting.items.properties.title.description": "Name of the sorting label", diff --git a/src/constants/settings.ts b/src/constants/settings.ts index 852c12b2..b2f92afd 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -62,6 +62,7 @@ export const SETTING_CONTENT_STATIC_FOLDER = 'content.publicFolder'; export const SETTING_CONTENT_FRONTMATTER_HIGHLIGHT = 'content.fmHighlight'; export const SETTING_CONTENT_DRAFT_FIELD = 'content.draftField'; export const SETTING_CONTENT_SORTING = 'content.sorting'; +export const SETTING_CONTENT_GROUPING = 'content.grouping'; export const SETTING_CONTENT_FILTERS = 'content.filters'; export const SETTING_CONTENT_WYSIWYG = 'content.wysiwyg'; export const SETTING_CONTENT_PLACEHOLDERS = 'content.placeholders'; diff --git a/src/dashboardWebView/components/Contents/Overview.tsx b/src/dashboardWebView/components/Contents/Overview.tsx index 47012869..ddeb8955 100644 --- a/src/dashboardWebView/components/Contents/Overview.tsx +++ b/src/dashboardWebView/components/Contents/Overview.tsx @@ -10,8 +10,7 @@ import { GroupingSelector, PageAtom, PagedItems, ViewSelector } from '../../stat import { Item } from './Item'; import { List } from './List'; import usePagination from '../../hooks/usePagination'; -import * as l10n from '@vscode/l10n'; -import { LocalizationKey } from '../../../localization'; +import { LocalizationKey, localize } from '../../../localization'; import { PinnedItemsAtom } from '../../state/atom/PinnedItems'; import { messageHandler } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../../DashboardMessage'; @@ -54,13 +53,18 @@ export const Overview: React.FunctionComponent = ({ const groupName = useCallback( (groupId, groupedPages) => { + const count = groupedPages[groupId].length; if (grouping === GroupOption.Draft) { - return `${groupId} (${groupedPages[groupId].length})`; + return `${groupId} (${count})`; + } else if (typeof grouping === 'string') { + const group = settings?.grouping?.find((g) => g.name === grouping); + const prefix = group?.title ? `${group.title}: ` : ''; + return `${prefix}${groupId} (${count})`; } - return `${GroupOption[grouping]}: ${groupId} (${groupedPages[groupId].length})`; + return `${GroupOption[grouping]}: ${groupId} (${count})`; }, - [grouping] + [grouping, settings?.grouping] ); const { groupKeys, groupedPages } = useMemo(() => { @@ -68,7 +72,18 @@ export const Overview: React.FunctionComponent = ({ return { groupKeys: [], groupedPages: {} }; } - let groupedPages = groupBy(pages, grouping === GroupOption.Year ? 'fmYear' : 'fmDraft'); + let groupName: string | undefined; + if (grouping === GroupOption.Year) { + groupName = 'fmYear'; + } else if (grouping === GroupOption.Draft) { + groupName = 'fmDraft'; + } else if (typeof grouping === 'string') { + groupName = grouping; + } else { + return { groupKeys: [], groupedPages: {} }; + } + + let groupedPages = groupBy(pages, groupName); let groupKeys = Object.keys(groupedPages); if (grouping === GroupOption.Year) { @@ -96,6 +111,8 @@ export const Overview: React.FunctionComponent = ({ ...groupedPages, } } + } else { + groupKeys = groupKeys.sort(); } return { groupKeys, groupedPages }; @@ -127,9 +144,11 @@ export const Overview: React.FunctionComponent = ({ className={`h-32 mx-auto opacity-90 mb-8 text-[var(--vscode-editor-foreground)]`} /> {settings && settings?.contentFolders?.length > 0 ? ( -

{l10n.t(LocalizationKey.dashboardContentsOverviewNoMarkdown)}

+

{localize(LocalizationKey.dashboardContentsOverviewNoMarkdown)}

+ ) : ( -

{l10n.t(LocalizationKey.dashboardContentsOverviewNoFolders)}

+

{localize(LocalizationKey.dashboardContentsOverviewNoFolders)}

+ )} @@ -176,7 +195,8 @@ export const Overview: React.FunctionComponent = ({

- {l10n.t(LocalizationKey.dashboardContentsOverviewPinned)} + {localize(LocalizationKey.dashboardContentsOverviewPinned)} +

{pinnedPages.map((page, idx) => ( diff --git a/src/dashboardWebView/components/Header/Grouping.tsx b/src/dashboardWebView/components/Header/Grouping.tsx index 8f2cfdac..11209320 100644 --- a/src/dashboardWebView/components/Header/Grouping.tsx +++ b/src/dashboardWebView/components/Header/Grouping.tsx @@ -1,10 +1,9 @@ import * as React from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { GroupOption } from '../../constants/GroupOption'; -import { AllPagesAtom, GroupingAtom } from '../../state'; +import { AllPagesAtom, GroupingAtom, SettingsAtom } from '../../state'; import { MenuButton, MenuItem } from '../Menu'; -import * as l10n from '@vscode/l10n'; -import { LocalizationKey } from '../../../localization'; +import { LocalizationKey, localize } from '../../../localization'; import { DropdownMenu, DropdownMenuContent } from '../../../components/shadcn/Dropdown'; export interface IGroupingProps { } @@ -12,28 +11,34 @@ export interface IGroupingProps { } export const Grouping: React.FunctionComponent< IGroupingProps > = () => { + const settings = useRecoilValue(SettingsAtom); const [group, setGroup] = useRecoilState(GroupingAtom); const pages = useRecoilValue(AllPagesAtom); const GROUP_OPTIONS = React.useMemo(() => { - const options: { name: string, id: GroupOption }[] = []; + const options: { name: string, id?: GroupOption | string }[] = []; if (pages.length > 0) { + if (settings?.grouping) { + const groups = settings.grouping.map((g) => ({ name: g.title, id: g.name })); + options.push(...groups); + } + if (pages.some((x) => x.fmYear)) { - options.push({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionYear), id: GroupOption.Year }) + options.push({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionYear), id: GroupOption.Year }) } if (pages.some((x) => x.fmDraft)) { - options.push({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionDraft), id: GroupOption.Draft }) + options.push({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionDraft), id: GroupOption.Draft }) } } if (options.length > 0) { - options.unshift({ name: l10n.t(LocalizationKey.dashboardHeaderGroupingOptionNone), id: GroupOption.none }) + options.unshift({ name: localize(LocalizationKey.dashboardHeaderGroupingOptionNone), id: GroupOption.none }) } return options; - }, [pages]) + }, [pages, settings?.grouping]) const crntGroup = GROUP_OPTIONS.find((x) => x.id === group); @@ -43,7 +48,7 @@ export const Grouping: React.FunctionComponent< return ( - + {GROUP_OPTIONS.map((option) => ( diff --git a/src/dashboardWebView/models/Settings.ts b/src/dashboardWebView/models/Settings.ts index 159cfc4e..c197f2c8 100644 --- a/src/dashboardWebView/models/Settings.ts +++ b/src/dashboardWebView/models/Settings.ts @@ -41,6 +41,7 @@ export interface Settings { draftField: DraftField | null | undefined; customSorting: SortingSetting[] | undefined; filters: (FilterType | { title: string; name: string })[] | undefined; + grouping: { title: string; name: string }[] | undefined; dashboardState: DashboardState; scripts: CustomScript[]; dataFiles: DataFile[] | undefined; diff --git a/src/helpers/DashboardSettings.ts b/src/helpers/DashboardSettings.ts index c26a8477..3665589a 100644 --- a/src/helpers/DashboardSettings.ts +++ b/src/helpers/DashboardSettings.ts @@ -8,6 +8,7 @@ import { ExtensionState, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_FILTERS, + SETTING_CONTENT_GROUPING, SETTING_CONTENT_SORTING, SETTING_CONTENT_SORTING_DEFAULT, SETTING_DASHBOARD_OPENONSTART, @@ -118,6 +119,8 @@ export class DashboardSettings { contentFolders: await Folders.get(), filters: Settings.get<(FilterType | { title: string; name: string })[]>(SETTING_CONTENT_FILTERS), + grouping: + Settings.get<{ title: string; name: string }[]>(SETTING_CONTENT_GROUPING), crntFramework: Settings.get(SETTING_FRAMEWORK_ID), framework: !isInitialized && wsFolder ? await FrameworkDetector.get(wsFolder.fsPath) : null, scripts: Settings.get(SETTING_CUSTOM_SCRIPTS) || [], diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index f226d290..58973966 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -43,7 +43,8 @@ import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, SETTING_CONTENT_FILTERS, - SETTING_CONTENT_I18N + SETTING_CONTENT_I18N, + SETTING_CONTENT_GROUPING } from '../constants'; import { Folders } from '../commands/Folders'; import { join, basename, dirname, parse } from 'path'; @@ -867,7 +868,8 @@ ${JSON.stringify(value, null, 2)}`, settingName === SETTING_GLOBAL_NOTIFICATIONS_DISABLED || settingName === SETTING_MEDIA_SUPPORTED_MIMETYPES || settingName === SETTING_COMMA_SEPARATED_FIELDS || - settingName === SETTING_CONTENT_FILTERS + settingName === SETTING_CONTENT_FILTERS || + settingName === SETTING_CONTENT_GROUPING ) { if (typeof originalConfig[key] === 'undefined') { Settings.globalConfig[key] = value;