diff --git a/packages/dataviews/src/index.ts b/packages/dataviews/src/index.ts index 233ecd6864a5d5..31f44e5ed97502 100644 --- a/packages/dataviews/src/index.ts +++ b/packages/dataviews/src/index.ts @@ -1,3 +1,4 @@ export { default as DataViews } from './dataviews'; export { VIEW_LAYOUTS } from './layouts'; export { filterSortAndPaginate } from './filter-and-sort-data-view'; +export type * from './types'; diff --git a/packages/editor/README.md b/packages/editor/README.md index b9106822272424..8279ec3e743605 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -1504,6 +1504,18 @@ _Returns_ Undocumented declaration. +### registerEntityAction + +Registers a new DataViews action. + +This is an experimental API and is subject to change. it's only available in the Gutenberg plugin for now. + +_Parameters_ + +- _kind_ `string`: Entity kind. +- _name_ `string`: Entity name. +- _config_ `Action`: Action configuration. + ### RichText > **Deprecated** since 5.3, use `wp.blockEditor.RichText` instead. @@ -1589,6 +1601,18 @@ Undocumented declaration. Undocumented declaration. +### unregisterEntityAction + +Unregisters a DataViews action. + +This is an experimental API and is subject to change. it's only available in the Gutenberg plugin for now. + +_Parameters_ + +- _kind_ `string`: Entity kind. +- _name_ `string`: Entity name. +- _actionId_ `string`: Action ID. + ### UnsavedChangesWarning Warns the user if there are unsaved changes before leaving the editor. Compatible with Post Editor and Site Editor. diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index f376d0e8f969ec..6dcc7f6cb66708 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -1030,11 +1030,13 @@ export const duplicateTemplatePartAction = { }; export function usePostActions( postType, onActionPerformed ) { - const { postTypeObject } = useSelect( + const { defaultActions, postTypeObject } = useSelect( ( select ) => { const { getPostType } = select( coreStore ); + const { getEntityActions } = unlock( select( editorStore ) ); return { postTypeObject: getPostType( postType ), + defaultActions: getEntityActions( 'postType', postType ), }; }, [ postType ] @@ -1072,6 +1074,7 @@ export function usePostActions( postType, onActionPerformed ) { ? deletePostAction : trashPostAction, ! isTemplateOrTemplatePart && permanentlyDeletePostAction, + ...defaultActions, ].filter( Boolean ); if ( onActionPerformed ) { @@ -1117,6 +1120,7 @@ export function usePostActions( postType, onActionPerformed ) { return actions; }, [ + defaultActions, isTemplateOrTemplatePart, isPattern, postTypeObject?.viewable, diff --git a/packages/editor/src/dataviews/api.js b/packages/editor/src/dataviews/api.js new file mode 100644 index 00000000000000..130a69bba754c7 --- /dev/null +++ b/packages/editor/src/dataviews/api.js @@ -0,0 +1,55 @@ +/** + * WordPress dependencies + */ +import { dispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; +import { store as editorStore } from '../store'; + +/** + * @typedef {import('@wordpress/dataviews').Action} Action + */ + +/** + * Registers a new DataViews action. + * + * This is an experimental API and is subject to change. + * it's only available in the Gutenberg plugin for now. + * + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * @param {Action} config Action configuration. + */ + +export function registerEntityAction( kind, name, config ) { + const { registerEntityAction: _registerEntityAction } = unlock( + dispatch( editorStore ) + ); + + if ( globalThis.IS_GUTENBERG_PLUGIN ) { + _registerEntityAction( kind, name, config ); + } +} + +/** + * Unregisters a DataViews action. + * + * This is an experimental API and is subject to change. + * it's only available in the Gutenberg plugin for now. + * + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * @param {string} actionId Action ID. + */ +export function unregisterEntityAction( kind, name, actionId ) { + const { unregisterEntityAction: _unregisterEntityAction } = unlock( + dispatch( editorStore ) + ); + + if ( globalThis.IS_GUTENBERG_PLUGIN ) { + _unregisterEntityAction( kind, name, actionId ); + } +} diff --git a/packages/editor/src/dataviews/store/private-actions.ts b/packages/editor/src/dataviews/store/private-actions.ts new file mode 100644 index 00000000000000..a74e1b5e79844a --- /dev/null +++ b/packages/editor/src/dataviews/store/private-actions.ts @@ -0,0 +1,30 @@ +/** + * WordPress dependencies + */ +import type { Action, AnyItem } from '@wordpress/dataviews'; + +export function registerEntityAction< Item extends AnyItem >( + kind: string, + name: string, + config: Action< Item > +) { + return { + type: 'REGISTER_ENTITY_ACTION' as const, + kind, + name, + config, + }; +} + +export function unregisterEntityAction( + kind: string, + name: string, + actionId: string +) { + return { + type: 'UNREGISTER_ENTITY_ACTION' as const, + kind, + name, + actionId, + }; +} diff --git a/packages/editor/src/dataviews/store/private-selectors.ts b/packages/editor/src/dataviews/store/private-selectors.ts new file mode 100644 index 00000000000000..938228ad97ed72 --- /dev/null +++ b/packages/editor/src/dataviews/store/private-selectors.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +import type { State } from './reducer'; + +export function getEntityActions( state: State, kind: string, name: string ) { + return state.actions[ kind ]?.[ name ] ?? []; +} diff --git a/packages/editor/src/dataviews/store/reducer.ts b/packages/editor/src/dataviews/store/reducer.ts new file mode 100644 index 00000000000000..e3b400faabe934 --- /dev/null +++ b/packages/editor/src/dataviews/store/reducer.ts @@ -0,0 +1,44 @@ +/** + * WordPress dependencies + */ +import { combineReducers } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import type { Action } from '@wordpress/dataviews'; + +type ReduxAction = + | ReturnType< typeof import('./private-actions').registerEntityAction > + | ReturnType< typeof import('./private-actions').unregisterEntityAction >; + +export type ActionState = Record< string, Record< string, Action< any >[] > >; +export type State = { + actions: ActionState; +}; + +function actions( state: ActionState = {}, action: ReduxAction ) { + switch ( action.type ) { + case 'REGISTER_ENTITY_ACTION': + return { + ...state, + [ action.kind ]: { + [ action.name ]: [ + ...( state[ action.kind ]?.[ action.name ] ?? [] ), + action.config, + ], + }, + }; + case 'UNREGISTER_ENTITY_ACTION': { + return { + ...state, + [ action.kind ]: ( + state[ action.kind ]?.[ action.name ] ?? [] + ).filter( ( _action ) => _action.id !== action.actionId ), + }; + } + } + + return state; +} + +export default combineReducers( { + actions, +} ); diff --git a/packages/editor/src/index.js b/packages/editor/src/index.js index 3f6d7a78d837c0..1f7bb7699c7040 100644 --- a/packages/editor/src/index.js +++ b/packages/editor/src/index.js @@ -8,6 +8,7 @@ export { storeConfig, store } from './store'; export * from './components'; export * from './utils'; export * from './private-apis'; +export * from './dataviews/api'; /* * Backward compatibility diff --git a/packages/editor/src/store/private-actions.js b/packages/editor/src/store/private-actions.js index 9304a2fe2c0579..bde803962f5d3a 100644 --- a/packages/editor/src/store/private-actions.js +++ b/packages/editor/src/store/private-actions.js @@ -15,6 +15,7 @@ import { decodeEntities } from '@wordpress/html-entities'; * Internal dependencies */ import isTemplateRevertable from './utils/is-template-revertable'; +export * from '../dataviews/store/private-actions'; /** * Returns an action object used to set which template is currently being used/edited. diff --git a/packages/editor/src/store/private-selectors.js b/packages/editor/src/store/private-selectors.js index 8a866b46a6cdd7..f56e1e8c9e81fb 100644 --- a/packages/editor/src/store/private-selectors.js +++ b/packages/editor/src/store/private-selectors.js @@ -27,6 +27,7 @@ import { } from './selectors'; import { TEMPLATE_PART_POST_TYPE } from './constants'; import { getFilteredTemplatePartBlocks } from './utils/get-filtered-template-parts'; +import { getEntityActions as _getEntityActions } from '../dataviews/store/private-selectors'; const EMPTY_INSERTION_POINT = { rootClientId: undefined, @@ -180,3 +181,7 @@ export const hasPostMetaChanges = createRegistrySelector( ); } ); + +export function getEntityActions( state, ...args ) { + return _getEntityActions( state.dataviews, ...args ); +} diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index f9b4e05ffa8e5e..a165d90e296d6f 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -7,6 +7,7 @@ import { combineReducers } from '@wordpress/data'; * Internal dependencies */ import { EDITOR_SETTINGS_DEFAULTS } from './defaults'; +import dataviewsReducer from '../dataviews/store/reducer'; /** * Returns a post attribute value, flattening nested rendered content using its @@ -402,4 +403,5 @@ export default combineReducers( { listViewPanel, listViewToggleRef, publishSidebarActive, + dataviews: dataviewsReducer, } ); diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json new file mode 100644 index 00000000000000..1177a1040b117b --- /dev/null +++ b/packages/editor/tsconfig.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig.json", + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "declarationDir": "build-types" + }, + "references": [ + { "path": "../a11y" }, + { "path": "../api-fetch" }, + { "path": "../blob" }, + { "path": "../block-editor" }, + { "path": "../components" }, + { "path": "../compose" }, + { "path": "../core-data" }, + { "path": "../data" }, + { "path": "../dataviews" }, + { "path": "../date" }, + { "path": "../deprecated" }, + { "path": "../dom" }, + { "path": "../element" }, + { "path": "../hooks" }, + { "path": "../html-entities" }, + { "path": "../i18n" }, + { "path": "../icons" }, + { "path": "../keycodes" }, + { "path": "../notices" }, + { "path": "../plugins" }, + { "path": "../private-apis" }, + { "path": "../rich-text" }, + { "path": "../url" }, + { "path": "../warning" }, + { "path": "../wordcount" } + ], + "include": [ "src/**/*.ts", "src/**/*.tsx" ] +} diff --git a/tsconfig.json b/tsconfig.json index 4add5beed2f436..cf986ddbee72bf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,7 @@ { "path": "packages/dom" }, { "path": "packages/dom-ready" }, { "path": "packages/e2e-test-utils-playwright" }, + { "path": "packages/editor" }, { "path": "packages/element" }, { "path": "packages/escape-html" }, { "path": "packages/eslint-plugin" },