@wordpress/views 1.6.0 → 1.6.1-next.v.20260206T143.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -20
- package/build/filter-utils.cjs +84 -0
- package/build/filter-utils.cjs.map +7 -0
- package/build/load-view.cjs +11 -6
- package/build/load-view.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/use-view.cjs +39 -11
- package/build/use-view.cjs.map +2 -2
- package/build-module/filter-utils.mjs +58 -0
- package/build-module/filter-utils.mjs.map +7 -0
- package/build-module/load-view.mjs +11 -6
- package/build-module/load-view.mjs.map +2 -2
- package/build-module/use-view.mjs +42 -11
- package/build-module/use-view.mjs.map +2 -2
- package/build-types/filter-utils.d.ts +32 -0
- package/build-types/filter-utils.d.ts.map +1 -0
- package/build-types/load-view.d.ts +10 -190
- package/build-types/load-view.d.ts.map +1 -1
- package/build-types/types.d.ts +10 -1
- package/build-types/types.d.ts.map +1 -1
- package/build-types/use-view.d.ts +2 -8
- package/build-types/use-view.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/filter-utils.ts +117 -0
- package/src/load-view.ts +20 -27
- package/src/types.ts +11 -1
- package/src/use-view.ts +53 -25
package/README.md
CHANGED
|
@@ -23,20 +23,7 @@ npm install @wordpress/views --save
|
|
|
23
23
|
|
|
24
24
|
### loadView
|
|
25
25
|
|
|
26
|
-
Async function for loading view state in route loaders
|
|
27
|
-
|
|
28
|
-
_Usage_
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// In route loader
|
|
32
|
-
const view = await loadView( {
|
|
33
|
-
kind: 'taxonomy',
|
|
34
|
-
name: 'category',
|
|
35
|
-
slug: 'all',
|
|
36
|
-
defaultView,
|
|
37
|
-
queryParams: { page: search.page, search: search.search },
|
|
38
|
-
} );
|
|
39
|
-
```
|
|
26
|
+
Async function for loading view state in route loaders.
|
|
40
27
|
|
|
41
28
|
_Parameters_
|
|
42
29
|
|
|
@@ -45,6 +32,7 @@ _Parameters_
|
|
|
45
32
|
- _config.name_ `ViewConfig`: Specific entity name.
|
|
46
33
|
- _config.slug_ `ViewConfig`: View identifier.
|
|
47
34
|
- _config.defaultView_ `ViewConfig`: Default view configuration.
|
|
35
|
+
- _config.activeViewOverrides_ `ViewConfig`: View overrides applied on top but never persisted.
|
|
48
36
|
- _config.queryParams_ `ViewConfig`: Object with `page` and/or `search` from URL.
|
|
49
37
|
|
|
50
38
|
_Returns_
|
|
@@ -58,12 +46,6 @@ Hook for managing DataViews view state with local persistence.
|
|
|
58
46
|
_Parameters_
|
|
59
47
|
|
|
60
48
|
- _config_ `ViewConfig`: Configuration object for loading the view.
|
|
61
|
-
- _config.kind_ `ViewConfig`: Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
62
|
-
- _config.name_ `ViewConfig`: Specific entity name.
|
|
63
|
-
- _config.slug_ `ViewConfig`: View identifier.
|
|
64
|
-
- _config.defaultView_ `ViewConfig`: Default view configuration.
|
|
65
|
-
- _config.queryParams_ `ViewConfig`: Object with `page` and/or `search` from URL.
|
|
66
|
-
- _config.onChangeQueryParams_ `ViewConfig`: Optional callback to update URL parameters.
|
|
67
49
|
|
|
68
50
|
_Returns_
|
|
69
51
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/views/src/filter-utils.ts
|
|
21
|
+
var filter_utils_exports = {};
|
|
22
|
+
__export(filter_utils_exports, {
|
|
23
|
+
mergeActiveViewOverrides: () => mergeActiveViewOverrides,
|
|
24
|
+
stripActiveViewOverrides: () => stripActiveViewOverrides
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(filter_utils_exports);
|
|
27
|
+
function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
|
|
28
|
+
if (!activeViewOverrides) {
|
|
29
|
+
return view;
|
|
30
|
+
}
|
|
31
|
+
let result = view;
|
|
32
|
+
if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
|
|
33
|
+
const activeFields = new Set(
|
|
34
|
+
activeViewOverrides.filters.map((f) => f.field)
|
|
35
|
+
);
|
|
36
|
+
const preserved = (view.filters ?? []).filter(
|
|
37
|
+
(f) => !activeFields.has(f.field)
|
|
38
|
+
);
|
|
39
|
+
result = {
|
|
40
|
+
...result,
|
|
41
|
+
filters: [...preserved, ...activeViewOverrides.filters]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (activeViewOverrides.sort) {
|
|
45
|
+
const isDefaultSort = defaultView && view.sort?.field === defaultView.sort?.field && view.sort?.direction === defaultView.sort?.direction;
|
|
46
|
+
if (isDefaultSort) {
|
|
47
|
+
result = {
|
|
48
|
+
...result,
|
|
49
|
+
sort: activeViewOverrides.sort
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
|
|
56
|
+
if (!activeViewOverrides) {
|
|
57
|
+
return view;
|
|
58
|
+
}
|
|
59
|
+
let result = view;
|
|
60
|
+
if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
|
|
61
|
+
const activeFields = new Set(
|
|
62
|
+
activeViewOverrides.filters.map((f) => f.field)
|
|
63
|
+
);
|
|
64
|
+
result = {
|
|
65
|
+
...result,
|
|
66
|
+
filters: (view.filters ?? []).filter(
|
|
67
|
+
(f) => !activeFields.has(f.field)
|
|
68
|
+
)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (activeViewOverrides.sort && view.sort?.field === activeViewOverrides.sort.field && view.sort?.direction === activeViewOverrides.sort.direction) {
|
|
72
|
+
result = {
|
|
73
|
+
...result,
|
|
74
|
+
sort: defaultView?.sort
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
80
|
+
0 && (module.exports = {
|
|
81
|
+
mergeActiveViewOverrides,
|
|
82
|
+
stripActiveViewOverrides
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=filter-utils.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/filter-utils.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\ntype ActiveViewOverrides = {\n\tfilters?: Filter[];\n\tsort?: View[ 'sort' ];\n};\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\treturn result;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,UAAM,aAAc,KAAK,WAAW,CAAC,GAAI;AAAA,MACxC,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,IAC9C;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,SAAS,CAAE,GAAG,WAAW,GAAG,oBAAoB,OAAQ;AAAA,IACzD;AAAA,EACD;AAGA,MAAK,oBAAoB,MAAO;AAC/B,UAAM,gBACL,eACA,KAAK,MAAM,UAAU,YAAY,MAAM,SACvC,KAAK,MAAM,cAAc,YAAY,MAAM;AAE5C,QAAK,eAAgB;AACpB,eAAS;AAAA,QACR,GAAG;AAAA,QACH,MAAM,oBAAoB;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAYO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,UAAW,KAAK,WAAW,CAAC,GAAI;AAAA,QAC/B,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,MAC9C;AAAA,IACD;AAAA,EACD;AAGA,MACC,oBAAoB,QACpB,KAAK,MAAM,UAAU,oBAAoB,KAAK,SAC9C,KAAK,MAAM,cAAc,oBAAoB,KAAK,WACjD;AACD,aAAS;AAAA,MACR,GAAG;AAAA,MACH,MAAM,aAAa;AAAA,IACpB;AAAA,EACD;AAEA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/build/load-view.cjs
CHANGED
|
@@ -26,8 +26,9 @@ module.exports = __toCommonJS(load_view_exports);
|
|
|
26
26
|
var import_data = require("@wordpress/data");
|
|
27
27
|
var import_preferences = require("@wordpress/preferences");
|
|
28
28
|
var import_preference_keys = require("./preference-keys.cjs");
|
|
29
|
+
var import_filter_utils = require("./filter-utils.cjs");
|
|
29
30
|
async function loadView(config) {
|
|
30
|
-
const { kind, name, slug, defaultView, queryParams } = config;
|
|
31
|
+
const { kind, name, slug, defaultView, activeViewOverrides, queryParams } = config;
|
|
31
32
|
const preferenceKey = (0, import_preference_keys.generatePreferenceKey)(kind, name, slug);
|
|
32
33
|
const persistedView = (0, import_data.select)(import_preferences.store).get(
|
|
33
34
|
"core/views",
|
|
@@ -36,11 +37,15 @@ async function loadView(config) {
|
|
|
36
37
|
const baseView = persistedView ?? defaultView;
|
|
37
38
|
const page = queryParams?.page ?? 1;
|
|
38
39
|
const search = queryParams?.search ?? "";
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
return (0, import_filter_utils.mergeActiveViewOverrides)(
|
|
41
|
+
{
|
|
42
|
+
...baseView,
|
|
43
|
+
page,
|
|
44
|
+
search
|
|
45
|
+
},
|
|
46
|
+
activeViewOverrides,
|
|
47
|
+
defaultView
|
|
48
|
+
);
|
|
44
49
|
}
|
|
45
50
|
// Annotate the CommonJS export names for ESM import in node:
|
|
46
51
|
0 && (module.exports = {
|
package/build/load-view.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/load-view.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { select } from '@wordpress/data';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { View } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport type { ViewConfig } from './types';\n\n/**\n * Async function for loading view state in route loaders
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAuB;AAEvB,yBAA0C;AAM1C,6BAAsC;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { select } from '@wordpress/data';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { View } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport { mergeActiveViewOverrides } from './filter-utils';\nimport type { ViewConfig } from './types';\n\n/**\n * Async function for loading view state in route loaders.\n *\n * @param config Configuration object for loading the view.\n * @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').\n * @param config.name Specific entity name.\n * @param config.slug View identifier.\n * @param config.defaultView Default view configuration.\n * @param config.activeViewOverrides View overrides applied on top but never persisted.\n * @param config.queryParams Object with `page` and/or `search` from URL.\n * @return Promise resolving to the loaded view object.\n */\nexport async function loadView( config: ViewConfig ) {\n\tconst { kind, name, slug, defaultView, activeViewOverrides, queryParams } =\n\t\tconfig;\n\tconst preferenceKey = generatePreferenceKey( kind, name, slug );\n\tconst persistedView: View | undefined = select( preferencesStore ).get(\n\t\t'core/views',\n\t\tpreferenceKey\n\t) as View | undefined;\n\n\tconst baseView = persistedView ?? defaultView;\n\tconst page = queryParams?.page ?? 1;\n\tconst search = queryParams?.search ?? '';\n\n\treturn mergeActiveViewOverrides(\n\t\t{\n\t\t\t...baseView,\n\t\t\tpage,\n\t\t\tsearch,\n\t\t},\n\t\tactiveViewOverrides,\n\t\tdefaultView\n\t);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAuB;AAEvB,yBAA0C;AAM1C,6BAAsC;AACtC,0BAAyC;AAezC,eAAsB,SAAU,QAAqB;AACpD,QAAM,EAAE,MAAM,MAAM,MAAM,aAAa,qBAAqB,YAAY,IACvE;AACD,QAAM,oBAAgB,8CAAuB,MAAM,MAAM,IAAK;AAC9D,QAAM,oBAAkC,oBAAQ,mBAAAA,KAAiB,EAAE;AAAA,IAClE;AAAA,IACA;AAAA,EACD;AAEA,QAAM,WAAW,iBAAiB;AAClC,QAAM,OAAO,aAAa,QAAQ;AAClC,QAAM,SAAS,aAAa,UAAU;AAEtC,aAAO;AAAA,IACN;AAAA,MACC,GAAG;AAAA,MACH;AAAA,MACA;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": ["preferencesStore"]
|
|
7
7
|
}
|
package/build/types.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/types.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View } from '@wordpress/dataviews';\n\nexport interface ViewConfig {\n\t/**\n\t * Entity kind (e.g. postType, root).\n\t * Similar to core-data entity kinds.\n\t */\n\tkind: string;\n\n\t/**\n\t * Entity name (e.g. post, page, user, site).\n\t * Similar to core-data entity names.\n\t */\n\tname: string;\n\n\t/**\n\t * Identifier for the current view (all, published, mine, etc.)\n\t */\n\tslug: string;\n\n\t/**\n\t * Default view configuration\n\t */\n\tdefaultView: View;\n\n\t/**\n\t * Optional query parameters from URL (page, search)\n\t */\n\tqueryParams?: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t};\n\n\t/**\n\t * Callback for when URL query parameters should change\n\t */\n\tonChangeQueryParams?: ( params: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t} ) => void;\n}\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\nexport interface ViewConfig {\n\t/**\n\t * Entity kind (e.g. postType, root).\n\t * Similar to core-data entity kinds.\n\t */\n\tkind: string;\n\n\t/**\n\t * Entity name (e.g. post, page, user, site).\n\t * Similar to core-data entity names.\n\t */\n\tname: string;\n\n\t/**\n\t * Identifier for the current view (all, published, mine, etc.)\n\t */\n\tslug: string;\n\n\t/**\n\t * Default view configuration\n\t */\n\tdefaultView: View;\n\n\t/**\n\t * View overrides applied on top of the persisted view but never persisted.\n\t * These represent tab-specific configuration (filters, sort) that should\n\t * override the persisted view settings.\n\t */\n\tactiveViewOverrides?: {\n\t\tfilters?: Filter[];\n\t\tsort?: View[ 'sort' ];\n\t};\n\n\t/**\n\t * Optional query parameters from URL (page, search)\n\t */\n\tqueryParams?: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t};\n\n\t/**\n\t * Callback for when URL query parameters should change\n\t */\n\tonChangeQueryParams?: ( params: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t} ) => void;\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/use-view.cjs
CHANGED
|
@@ -24,10 +24,11 @@ __export(use_view_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(use_view_exports);
|
|
26
26
|
var import_dequal = require("dequal");
|
|
27
|
-
var import_preference_keys = require("./preference-keys.cjs");
|
|
28
27
|
var import_element = require("@wordpress/element");
|
|
29
28
|
var import_data = require("@wordpress/data");
|
|
30
29
|
var import_preferences = require("@wordpress/preferences");
|
|
30
|
+
var import_preference_keys = require("./preference-keys.cjs");
|
|
31
|
+
var import_filter_utils = require("./filter-utils.cjs");
|
|
31
32
|
function omit(obj, keys) {
|
|
32
33
|
const result = { ...obj };
|
|
33
34
|
for (const key of keys) {
|
|
@@ -36,7 +37,15 @@ function omit(obj, keys) {
|
|
|
36
37
|
return result;
|
|
37
38
|
}
|
|
38
39
|
function useView(config) {
|
|
39
|
-
const {
|
|
40
|
+
const {
|
|
41
|
+
kind,
|
|
42
|
+
name,
|
|
43
|
+
slug,
|
|
44
|
+
defaultView,
|
|
45
|
+
activeViewOverrides,
|
|
46
|
+
queryParams,
|
|
47
|
+
onChangeQueryParams
|
|
48
|
+
} = config;
|
|
40
49
|
const preferenceKey = (0, import_preference_keys.generatePreferenceKey)(kind, name, slug);
|
|
41
50
|
const persistedView = (0, import_data.useSelect)(
|
|
42
51
|
(select) => {
|
|
@@ -52,12 +61,16 @@ function useView(config) {
|
|
|
52
61
|
const page = Number(queryParams?.page ?? baseView.page ?? 1);
|
|
53
62
|
const search = queryParams?.search ?? baseView.search ?? "";
|
|
54
63
|
const view = (0, import_element.useMemo)(() => {
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
return (0, import_filter_utils.mergeActiveViewOverrides)(
|
|
65
|
+
{
|
|
66
|
+
...baseView,
|
|
67
|
+
page,
|
|
68
|
+
search
|
|
69
|
+
},
|
|
70
|
+
activeViewOverrides,
|
|
71
|
+
defaultView
|
|
72
|
+
);
|
|
73
|
+
}, [baseView, page, search, activeViewOverrides, defaultView]);
|
|
61
74
|
const isModified = !!persistedView;
|
|
62
75
|
const updateView = (0, import_element.useCallback)(
|
|
63
76
|
(newView) => {
|
|
@@ -65,12 +78,26 @@ function useView(config) {
|
|
|
65
78
|
page: newView?.page,
|
|
66
79
|
search: newView?.search
|
|
67
80
|
};
|
|
68
|
-
const preferenceView =
|
|
81
|
+
const preferenceView = (0, import_filter_utils.stripActiveViewOverrides)(
|
|
82
|
+
omit(newView, ["page", "search"]),
|
|
83
|
+
activeViewOverrides,
|
|
84
|
+
defaultView
|
|
85
|
+
);
|
|
69
86
|
if (onChangeQueryParams && !(0, import_dequal.dequal)(urlParams, { page, search })) {
|
|
70
87
|
onChangeQueryParams(urlParams);
|
|
71
88
|
}
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
const comparableBaseView = (0, import_filter_utils.stripActiveViewOverrides)(
|
|
90
|
+
baseView,
|
|
91
|
+
activeViewOverrides,
|
|
92
|
+
defaultView
|
|
93
|
+
);
|
|
94
|
+
const comparableDefaultView = (0, import_filter_utils.stripActiveViewOverrides)(
|
|
95
|
+
defaultView,
|
|
96
|
+
activeViewOverrides,
|
|
97
|
+
defaultView
|
|
98
|
+
);
|
|
99
|
+
if (!(0, import_dequal.dequal)(comparableBaseView, preferenceView)) {
|
|
100
|
+
if ((0, import_dequal.dequal)(preferenceView, comparableDefaultView)) {
|
|
74
101
|
set("core/views", preferenceKey, void 0);
|
|
75
102
|
} else {
|
|
76
103
|
set("core/views", preferenceKey, preferenceView);
|
|
@@ -83,6 +110,7 @@ function useView(config) {
|
|
|
83
110
|
search,
|
|
84
111
|
baseView,
|
|
85
112
|
defaultView,
|
|
113
|
+
activeViewOverrides,
|
|
86
114
|
set,
|
|
87
115
|
preferenceKey
|
|
88
116
|
]
|
package/build/use-view.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/use-view.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport { dequal } from 'dequal';\n\n/**\n *
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAuB;AAKvB,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport { dequal } from 'dequal';\n\n/**\n * WordPress dependencies\n */\nimport { useCallback, useMemo } from '@wordpress/element';\nimport { useDispatch, useSelect } from '@wordpress/data';\nimport type { View } from '@wordpress/dataviews';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport {\n\tmergeActiveViewOverrides,\n\tstripActiveViewOverrides,\n} from './filter-utils';\nimport type { ViewConfig } from './types';\n\ninterface UseViewReturn {\n\tview: View;\n\tisModified: boolean;\n\tupdateView: ( newView: View ) => void;\n\tresetToDefault: () => void;\n}\n\nfunction omit< T extends object, K extends keyof T >(\n\tobj: T,\n\tkeys: K[]\n): Omit< T, K > {\n\tconst result = { ...obj };\n\tfor ( const key of keys ) {\n\t\tdelete result[ key ];\n\t}\n\treturn result;\n}\n\n/**\n * Hook for managing DataViews view state with local persistence.\n *\n * @param config Configuration object for loading the view.\n *\n * @return Object with current view, modification state, and update functions.\n */\nexport function useView( config: ViewConfig ): UseViewReturn {\n\tconst {\n\t\tkind,\n\t\tname,\n\t\tslug,\n\t\tdefaultView,\n\t\tactiveViewOverrides,\n\t\tqueryParams,\n\t\tonChangeQueryParams,\n\t} = config;\n\n\tconst preferenceKey = generatePreferenceKey( kind, name, slug );\n\tconst persistedView: View | undefined = useSelect(\n\t\t( select ) => {\n\t\t\treturn select( preferencesStore ).get(\n\t\t\t\t'core/views',\n\t\t\t\tpreferenceKey\n\t\t\t) as View | undefined;\n\t\t},\n\t\t[ preferenceKey ]\n\t);\n\tconst { set } = useDispatch( preferencesStore );\n\n\tconst baseView: View = persistedView ?? defaultView;\n\tconst page = Number( queryParams?.page ?? baseView.page ?? 1 );\n\tconst search = queryParams?.search ?? baseView.search ?? '';\n\n\t// Merge URL query parameters (page, search) and activeViewOverrides into the view\n\tconst view: View = useMemo( () => {\n\t\treturn mergeActiveViewOverrides(\n\t\t\t{\n\t\t\t\t...baseView,\n\t\t\t\tpage,\n\t\t\t\tsearch,\n\t\t\t},\n\t\t\tactiveViewOverrides,\n\t\t\tdefaultView\n\t\t);\n\t}, [ baseView, page, search, activeViewOverrides, defaultView ] );\n\n\tconst isModified = !! persistedView;\n\n\tconst updateView = useCallback(\n\t\t( newView: View ) => {\n\t\t\t// Extract URL params (page, search) from the new view\n\t\t\tconst urlParams: { page?: number; search?: string } = {\n\t\t\t\tpage: newView?.page,\n\t\t\t\tsearch: newView?.search,\n\t\t\t};\n\t\t\t// Strip activeViewOverrides and URL params before persisting\n\t\t\t// Cast is safe: omitting page/search doesn't change the discriminant (type field)\n\t\t\tconst preferenceView = stripActiveViewOverrides(\n\t\t\t\tomit( newView, [ 'page', 'search' ] ) as View,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\n\t\t\t// If we have URL handling enabled, separate URL state from preference state\n\t\t\tif (\n\t\t\t\tonChangeQueryParams &&\n\t\t\t\t! dequal( urlParams, { page, search } )\n\t\t\t) {\n\t\t\t\tonChangeQueryParams( urlParams );\n\t\t\t}\n\n\t\t\t// Compare with baseView and defaultView after stripping activeViewOverrides\n\t\t\tconst comparableBaseView = stripActiveViewOverrides(\n\t\t\t\tbaseView,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\t\t\tconst comparableDefaultView = stripActiveViewOverrides(\n\t\t\t\tdefaultView,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\n\t\t\t// Only persist non-URL preferences if different from baseView\n\t\t\tif ( ! dequal( comparableBaseView, preferenceView ) ) {\n\t\t\t\tif ( dequal( preferenceView, comparableDefaultView ) ) {\n\t\t\t\t\tset( 'core/views', preferenceKey, undefined );\n\t\t\t\t} else {\n\t\t\t\t\tset( 'core/views', preferenceKey, preferenceView );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[\n\t\t\tonChangeQueryParams,\n\t\t\tpage,\n\t\t\tsearch,\n\t\t\tbaseView,\n\t\t\tdefaultView,\n\t\t\tactiveViewOverrides,\n\t\t\tset,\n\t\t\tpreferenceKey,\n\t\t]\n\t);\n\n\tconst resetToDefault = useCallback( () => {\n\t\tset( 'core/views', preferenceKey, undefined );\n\t}, [ preferenceKey, set ] );\n\n\treturn {\n\t\tview,\n\t\tisModified,\n\t\tupdateView,\n\t\tresetToDefault,\n\t};\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAuB;AAKvB,qBAAqC;AACrC,kBAAuC;AAGvC,yBAA0C;AAK1C,6BAAsC;AACtC,0BAGO;AAUP,SAAS,KACR,KACA,MACe;AACf,QAAM,SAAS,EAAE,GAAG,IAAI;AACxB,aAAY,OAAO,MAAO;AACzB,WAAO,OAAQ,GAAI;AAAA,EACpB;AACA,SAAO;AACR;AASO,SAAS,QAAS,QAAoC;AAC5D,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI;AAEJ,QAAM,oBAAgB,8CAAuB,MAAM,MAAM,IAAK;AAC9D,QAAM,oBAAkC;AAAA,IACvC,CAAE,WAAY;AACb,aAAO,OAAQ,mBAAAA,KAAiB,EAAE;AAAA,QACjC;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAAA,IACA,CAAE,aAAc;AAAA,EACjB;AACA,QAAM,EAAE,IAAI,QAAI,yBAAa,mBAAAA,KAAiB;AAE9C,QAAM,WAAiB,iBAAiB;AACxC,QAAM,OAAO,OAAQ,aAAa,QAAQ,SAAS,QAAQ,CAAE;AAC7D,QAAM,SAAS,aAAa,UAAU,SAAS,UAAU;AAGzD,QAAM,WAAa,wBAAS,MAAM;AACjC,eAAO;AAAA,MACN;AAAA,QACC,GAAG;AAAA,QACH;AAAA,QACA;AAAA,MACD;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD,GAAG,CAAE,UAAU,MAAM,QAAQ,qBAAqB,WAAY,CAAE;AAEhE,QAAM,aAAa,CAAC,CAAE;AAEtB,QAAM,iBAAa;AAAA,IAClB,CAAE,YAAmB;AAEpB,YAAM,YAAgD;AAAA,QACrD,MAAM,SAAS;AAAA,QACf,QAAQ,SAAS;AAAA,MAClB;AAGA,YAAM,qBAAiB;AAAA,QACtB,KAAM,SAAS,CAAE,QAAQ,QAAS,CAAE;AAAA,QACpC;AAAA,QACA;AAAA,MACD;AAGA,UACC,uBACA,KAAE,sBAAQ,WAAW,EAAE,MAAM,OAAO,CAAE,GACrC;AACD,4BAAqB,SAAU;AAAA,MAChC;AAGA,YAAM,yBAAqB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,MACD;AACA,YAAM,4BAAwB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAGA,UAAK,KAAE,sBAAQ,oBAAoB,cAAe,GAAI;AACrD,gBAAK,sBAAQ,gBAAgB,qBAAsB,GAAI;AACtD,cAAK,cAAc,eAAe,MAAU;AAAA,QAC7C,OAAO;AACN,cAAK,cAAc,eAAe,cAAe;AAAA,QAClD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,QAAM,qBAAiB,4BAAa,MAAM;AACzC,QAAK,cAAc,eAAe,MAAU;AAAA,EAC7C,GAAG,CAAE,eAAe,GAAI,CAAE;AAE1B,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": ["preferencesStore"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// packages/views/src/filter-utils.ts
|
|
2
|
+
function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
|
|
3
|
+
if (!activeViewOverrides) {
|
|
4
|
+
return view;
|
|
5
|
+
}
|
|
6
|
+
let result = view;
|
|
7
|
+
if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
|
|
8
|
+
const activeFields = new Set(
|
|
9
|
+
activeViewOverrides.filters.map((f) => f.field)
|
|
10
|
+
);
|
|
11
|
+
const preserved = (view.filters ?? []).filter(
|
|
12
|
+
(f) => !activeFields.has(f.field)
|
|
13
|
+
);
|
|
14
|
+
result = {
|
|
15
|
+
...result,
|
|
16
|
+
filters: [...preserved, ...activeViewOverrides.filters]
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (activeViewOverrides.sort) {
|
|
20
|
+
const isDefaultSort = defaultView && view.sort?.field === defaultView.sort?.field && view.sort?.direction === defaultView.sort?.direction;
|
|
21
|
+
if (isDefaultSort) {
|
|
22
|
+
result = {
|
|
23
|
+
...result,
|
|
24
|
+
sort: activeViewOverrides.sort
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
|
|
31
|
+
if (!activeViewOverrides) {
|
|
32
|
+
return view;
|
|
33
|
+
}
|
|
34
|
+
let result = view;
|
|
35
|
+
if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
|
|
36
|
+
const activeFields = new Set(
|
|
37
|
+
activeViewOverrides.filters.map((f) => f.field)
|
|
38
|
+
);
|
|
39
|
+
result = {
|
|
40
|
+
...result,
|
|
41
|
+
filters: (view.filters ?? []).filter(
|
|
42
|
+
(f) => !activeFields.has(f.field)
|
|
43
|
+
)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (activeViewOverrides.sort && view.sort?.field === activeViewOverrides.sort.field && view.sort?.direction === activeViewOverrides.sort.direction) {
|
|
47
|
+
result = {
|
|
48
|
+
...result,
|
|
49
|
+
sort: defaultView?.sort
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
mergeActiveViewOverrides,
|
|
56
|
+
stripActiveViewOverrides
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=filter-utils.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/filter-utils.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\ntype ActiveViewOverrides = {\n\tfilters?: Filter[];\n\tsort?: View[ 'sort' ];\n};\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\treturn result;\n}\n"],
|
|
5
|
+
"mappings": ";AAoBO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,UAAM,aAAc,KAAK,WAAW,CAAC,GAAI;AAAA,MACxC,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,IAC9C;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,SAAS,CAAE,GAAG,WAAW,GAAG,oBAAoB,OAAQ;AAAA,IACzD;AAAA,EACD;AAGA,MAAK,oBAAoB,MAAO;AAC/B,UAAM,gBACL,eACA,KAAK,MAAM,UAAU,YAAY,MAAM,SACvC,KAAK,MAAM,cAAc,YAAY,MAAM;AAE5C,QAAK,eAAgB;AACpB,eAAS;AAAA,QACR,GAAG;AAAA,QACH,MAAM,oBAAoB;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAYO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,UAAW,KAAK,WAAW,CAAC,GAAI;AAAA,QAC/B,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,MAC9C;AAAA,IACD;AAAA,EACD;AAGA,MACC,oBAAoB,QACpB,KAAK,MAAM,UAAU,oBAAoB,KAAK,SAC9C,KAAK,MAAM,cAAc,oBAAoB,KAAK,WACjD;AACD,aAAS;AAAA,MACR,GAAG;AAAA,MACH,MAAM,aAAa;AAAA,IACpB;AAAA,EACD;AAEA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import { select } from "@wordpress/data";
|
|
3
3
|
import { store as preferencesStore } from "@wordpress/preferences";
|
|
4
4
|
import { generatePreferenceKey } from "./preference-keys.mjs";
|
|
5
|
+
import { mergeActiveViewOverrides } from "./filter-utils.mjs";
|
|
5
6
|
async function loadView(config) {
|
|
6
|
-
const { kind, name, slug, defaultView, queryParams } = config;
|
|
7
|
+
const { kind, name, slug, defaultView, activeViewOverrides, queryParams } = config;
|
|
7
8
|
const preferenceKey = generatePreferenceKey(kind, name, slug);
|
|
8
9
|
const persistedView = select(preferencesStore).get(
|
|
9
10
|
"core/views",
|
|
@@ -12,11 +13,15 @@ async function loadView(config) {
|
|
|
12
13
|
const baseView = persistedView ?? defaultView;
|
|
13
14
|
const page = queryParams?.page ?? 1;
|
|
14
15
|
const search = queryParams?.search ?? "";
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
return mergeActiveViewOverrides(
|
|
17
|
+
{
|
|
18
|
+
...baseView,
|
|
19
|
+
page,
|
|
20
|
+
search
|
|
21
|
+
},
|
|
22
|
+
activeViewOverrides,
|
|
23
|
+
defaultView
|
|
24
|
+
);
|
|
20
25
|
}
|
|
21
26
|
export {
|
|
22
27
|
loadView
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/load-view.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { select } from '@wordpress/data';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { View } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport type { ViewConfig } from './types';\n\n/**\n * Async function for loading view state in route loaders
|
|
5
|
-
"mappings": ";AAGA,SAAS,cAAc;AAEvB,SAAS,SAAS,wBAAwB;AAM1C,SAAS,6BAA6B;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { select } from '@wordpress/data';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { View } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport { mergeActiveViewOverrides } from './filter-utils';\nimport type { ViewConfig } from './types';\n\n/**\n * Async function for loading view state in route loaders.\n *\n * @param config Configuration object for loading the view.\n * @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').\n * @param config.name Specific entity name.\n * @param config.slug View identifier.\n * @param config.defaultView Default view configuration.\n * @param config.activeViewOverrides View overrides applied on top but never persisted.\n * @param config.queryParams Object with `page` and/or `search` from URL.\n * @return Promise resolving to the loaded view object.\n */\nexport async function loadView( config: ViewConfig ) {\n\tconst { kind, name, slug, defaultView, activeViewOverrides, queryParams } =\n\t\tconfig;\n\tconst preferenceKey = generatePreferenceKey( kind, name, slug );\n\tconst persistedView: View | undefined = select( preferencesStore ).get(\n\t\t'core/views',\n\t\tpreferenceKey\n\t) as View | undefined;\n\n\tconst baseView = persistedView ?? defaultView;\n\tconst page = queryParams?.page ?? 1;\n\tconst search = queryParams?.search ?? '';\n\n\treturn mergeActiveViewOverrides(\n\t\t{\n\t\t\t...baseView,\n\t\t\tpage,\n\t\t\tsearch,\n\t\t},\n\t\tactiveViewOverrides,\n\t\tdefaultView\n\t);\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,cAAc;AAEvB,SAAS,SAAS,wBAAwB;AAM1C,SAAS,6BAA6B;AACtC,SAAS,gCAAgC;AAezC,eAAsB,SAAU,QAAqB;AACpD,QAAM,EAAE,MAAM,MAAM,MAAM,aAAa,qBAAqB,YAAY,IACvE;AACD,QAAM,gBAAgB,sBAAuB,MAAM,MAAM,IAAK;AAC9D,QAAM,gBAAkC,OAAQ,gBAAiB,EAAE;AAAA,IAClE;AAAA,IACA;AAAA,EACD;AAEA,QAAM,WAAW,iBAAiB;AAClC,QAAM,OAAO,aAAa,QAAQ;AAClC,QAAM,SAAS,aAAa,UAAU;AAEtC,SAAO;AAAA,IACN;AAAA,MACC,GAAG;AAAA,MACH;AAAA,MACA;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// packages/views/src/use-view.ts
|
|
2
2
|
import { dequal } from "dequal";
|
|
3
|
-
import { generatePreferenceKey } from "./preference-keys.mjs";
|
|
4
3
|
import { useCallback, useMemo } from "@wordpress/element";
|
|
5
4
|
import { useDispatch, useSelect } from "@wordpress/data";
|
|
6
5
|
import { store as preferencesStore } from "@wordpress/preferences";
|
|
6
|
+
import { generatePreferenceKey } from "./preference-keys.mjs";
|
|
7
|
+
import {
|
|
8
|
+
mergeActiveViewOverrides,
|
|
9
|
+
stripActiveViewOverrides
|
|
10
|
+
} from "./filter-utils.mjs";
|
|
7
11
|
function omit(obj, keys) {
|
|
8
12
|
const result = { ...obj };
|
|
9
13
|
for (const key of keys) {
|
|
@@ -12,7 +16,15 @@ function omit(obj, keys) {
|
|
|
12
16
|
return result;
|
|
13
17
|
}
|
|
14
18
|
function useView(config) {
|
|
15
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
kind,
|
|
21
|
+
name,
|
|
22
|
+
slug,
|
|
23
|
+
defaultView,
|
|
24
|
+
activeViewOverrides,
|
|
25
|
+
queryParams,
|
|
26
|
+
onChangeQueryParams
|
|
27
|
+
} = config;
|
|
16
28
|
const preferenceKey = generatePreferenceKey(kind, name, slug);
|
|
17
29
|
const persistedView = useSelect(
|
|
18
30
|
(select) => {
|
|
@@ -28,12 +40,16 @@ function useView(config) {
|
|
|
28
40
|
const page = Number(queryParams?.page ?? baseView.page ?? 1);
|
|
29
41
|
const search = queryParams?.search ?? baseView.search ?? "";
|
|
30
42
|
const view = useMemo(() => {
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
return mergeActiveViewOverrides(
|
|
44
|
+
{
|
|
45
|
+
...baseView,
|
|
46
|
+
page,
|
|
47
|
+
search
|
|
48
|
+
},
|
|
49
|
+
activeViewOverrides,
|
|
50
|
+
defaultView
|
|
51
|
+
);
|
|
52
|
+
}, [baseView, page, search, activeViewOverrides, defaultView]);
|
|
37
53
|
const isModified = !!persistedView;
|
|
38
54
|
const updateView = useCallback(
|
|
39
55
|
(newView) => {
|
|
@@ -41,12 +57,26 @@ function useView(config) {
|
|
|
41
57
|
page: newView?.page,
|
|
42
58
|
search: newView?.search
|
|
43
59
|
};
|
|
44
|
-
const preferenceView =
|
|
60
|
+
const preferenceView = stripActiveViewOverrides(
|
|
61
|
+
omit(newView, ["page", "search"]),
|
|
62
|
+
activeViewOverrides,
|
|
63
|
+
defaultView
|
|
64
|
+
);
|
|
45
65
|
if (onChangeQueryParams && !dequal(urlParams, { page, search })) {
|
|
46
66
|
onChangeQueryParams(urlParams);
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
const comparableBaseView = stripActiveViewOverrides(
|
|
69
|
+
baseView,
|
|
70
|
+
activeViewOverrides,
|
|
71
|
+
defaultView
|
|
72
|
+
);
|
|
73
|
+
const comparableDefaultView = stripActiveViewOverrides(
|
|
74
|
+
defaultView,
|
|
75
|
+
activeViewOverrides,
|
|
76
|
+
defaultView
|
|
77
|
+
);
|
|
78
|
+
if (!dequal(comparableBaseView, preferenceView)) {
|
|
79
|
+
if (dequal(preferenceView, comparableDefaultView)) {
|
|
50
80
|
set("core/views", preferenceKey, void 0);
|
|
51
81
|
} else {
|
|
52
82
|
set("core/views", preferenceKey, preferenceView);
|
|
@@ -59,6 +89,7 @@ function useView(config) {
|
|
|
59
89
|
search,
|
|
60
90
|
baseView,
|
|
61
91
|
defaultView,
|
|
92
|
+
activeViewOverrides,
|
|
62
93
|
set,
|
|
63
94
|
preferenceKey
|
|
64
95
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/use-view.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport { dequal } from 'dequal';\n\n/**\n *
|
|
5
|
-
"mappings": ";AAGA,SAAS,cAAc;AAKvB,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport { dequal } from 'dequal';\n\n/**\n * WordPress dependencies\n */\nimport { useCallback, useMemo } from '@wordpress/element';\nimport { useDispatch, useSelect } from '@wordpress/data';\nimport type { View } from '@wordpress/dataviews';\n// @ts-ignore - Preferences package is not typed\nimport { store as preferencesStore } from '@wordpress/preferences';\n\n/**\n * Internal dependencies\n */\nimport { generatePreferenceKey } from './preference-keys';\nimport {\n\tmergeActiveViewOverrides,\n\tstripActiveViewOverrides,\n} from './filter-utils';\nimport type { ViewConfig } from './types';\n\ninterface UseViewReturn {\n\tview: View;\n\tisModified: boolean;\n\tupdateView: ( newView: View ) => void;\n\tresetToDefault: () => void;\n}\n\nfunction omit< T extends object, K extends keyof T >(\n\tobj: T,\n\tkeys: K[]\n): Omit< T, K > {\n\tconst result = { ...obj };\n\tfor ( const key of keys ) {\n\t\tdelete result[ key ];\n\t}\n\treturn result;\n}\n\n/**\n * Hook for managing DataViews view state with local persistence.\n *\n * @param config Configuration object for loading the view.\n *\n * @return Object with current view, modification state, and update functions.\n */\nexport function useView( config: ViewConfig ): UseViewReturn {\n\tconst {\n\t\tkind,\n\t\tname,\n\t\tslug,\n\t\tdefaultView,\n\t\tactiveViewOverrides,\n\t\tqueryParams,\n\t\tonChangeQueryParams,\n\t} = config;\n\n\tconst preferenceKey = generatePreferenceKey( kind, name, slug );\n\tconst persistedView: View | undefined = useSelect(\n\t\t( select ) => {\n\t\t\treturn select( preferencesStore ).get(\n\t\t\t\t'core/views',\n\t\t\t\tpreferenceKey\n\t\t\t) as View | undefined;\n\t\t},\n\t\t[ preferenceKey ]\n\t);\n\tconst { set } = useDispatch( preferencesStore );\n\n\tconst baseView: View = persistedView ?? defaultView;\n\tconst page = Number( queryParams?.page ?? baseView.page ?? 1 );\n\tconst search = queryParams?.search ?? baseView.search ?? '';\n\n\t// Merge URL query parameters (page, search) and activeViewOverrides into the view\n\tconst view: View = useMemo( () => {\n\t\treturn mergeActiveViewOverrides(\n\t\t\t{\n\t\t\t\t...baseView,\n\t\t\t\tpage,\n\t\t\t\tsearch,\n\t\t\t},\n\t\t\tactiveViewOverrides,\n\t\t\tdefaultView\n\t\t);\n\t}, [ baseView, page, search, activeViewOverrides, defaultView ] );\n\n\tconst isModified = !! persistedView;\n\n\tconst updateView = useCallback(\n\t\t( newView: View ) => {\n\t\t\t// Extract URL params (page, search) from the new view\n\t\t\tconst urlParams: { page?: number; search?: string } = {\n\t\t\t\tpage: newView?.page,\n\t\t\t\tsearch: newView?.search,\n\t\t\t};\n\t\t\t// Strip activeViewOverrides and URL params before persisting\n\t\t\t// Cast is safe: omitting page/search doesn't change the discriminant (type field)\n\t\t\tconst preferenceView = stripActiveViewOverrides(\n\t\t\t\tomit( newView, [ 'page', 'search' ] ) as View,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\n\t\t\t// If we have URL handling enabled, separate URL state from preference state\n\t\t\tif (\n\t\t\t\tonChangeQueryParams &&\n\t\t\t\t! dequal( urlParams, { page, search } )\n\t\t\t) {\n\t\t\t\tonChangeQueryParams( urlParams );\n\t\t\t}\n\n\t\t\t// Compare with baseView and defaultView after stripping activeViewOverrides\n\t\t\tconst comparableBaseView = stripActiveViewOverrides(\n\t\t\t\tbaseView,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\t\t\tconst comparableDefaultView = stripActiveViewOverrides(\n\t\t\t\tdefaultView,\n\t\t\t\tactiveViewOverrides,\n\t\t\t\tdefaultView\n\t\t\t);\n\n\t\t\t// Only persist non-URL preferences if different from baseView\n\t\t\tif ( ! dequal( comparableBaseView, preferenceView ) ) {\n\t\t\t\tif ( dequal( preferenceView, comparableDefaultView ) ) {\n\t\t\t\t\tset( 'core/views', preferenceKey, undefined );\n\t\t\t\t} else {\n\t\t\t\t\tset( 'core/views', preferenceKey, preferenceView );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[\n\t\t\tonChangeQueryParams,\n\t\t\tpage,\n\t\t\tsearch,\n\t\t\tbaseView,\n\t\t\tdefaultView,\n\t\t\tactiveViewOverrides,\n\t\t\tset,\n\t\t\tpreferenceKey,\n\t\t]\n\t);\n\n\tconst resetToDefault = useCallback( () => {\n\t\tset( 'core/views', preferenceKey, undefined );\n\t}, [ preferenceKey, set ] );\n\n\treturn {\n\t\tview,\n\t\tisModified,\n\t\tupdateView,\n\t\tresetToDefault,\n\t};\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,cAAc;AAKvB,SAAS,aAAa,eAAe;AACrC,SAAS,aAAa,iBAAiB;AAGvC,SAAS,SAAS,wBAAwB;AAK1C,SAAS,6BAA6B;AACtC;AAAA,EACC;AAAA,EACA;AAAA,OACM;AAUP,SAAS,KACR,KACA,MACe;AACf,QAAM,SAAS,EAAE,GAAG,IAAI;AACxB,aAAY,OAAO,MAAO;AACzB,WAAO,OAAQ,GAAI;AAAA,EACpB;AACA,SAAO;AACR;AASO,SAAS,QAAS,QAAoC;AAC5D,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI;AAEJ,QAAM,gBAAgB,sBAAuB,MAAM,MAAM,IAAK;AAC9D,QAAM,gBAAkC;AAAA,IACvC,CAAE,WAAY;AACb,aAAO,OAAQ,gBAAiB,EAAE;AAAA,QACjC;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAAA,IACA,CAAE,aAAc;AAAA,EACjB;AACA,QAAM,EAAE,IAAI,IAAI,YAAa,gBAAiB;AAE9C,QAAM,WAAiB,iBAAiB;AACxC,QAAM,OAAO,OAAQ,aAAa,QAAQ,SAAS,QAAQ,CAAE;AAC7D,QAAM,SAAS,aAAa,UAAU,SAAS,UAAU;AAGzD,QAAM,OAAa,QAAS,MAAM;AACjC,WAAO;AAAA,MACN;AAAA,QACC,GAAG;AAAA,QACH;AAAA,QACA;AAAA,MACD;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD,GAAG,CAAE,UAAU,MAAM,QAAQ,qBAAqB,WAAY,CAAE;AAEhE,QAAM,aAAa,CAAC,CAAE;AAEtB,QAAM,aAAa;AAAA,IAClB,CAAE,YAAmB;AAEpB,YAAM,YAAgD;AAAA,QACrD,MAAM,SAAS;AAAA,QACf,QAAQ,SAAS;AAAA,MAClB;AAGA,YAAM,iBAAiB;AAAA,QACtB,KAAM,SAAS,CAAE,QAAQ,QAAS,CAAE;AAAA,QACpC;AAAA,QACA;AAAA,MACD;AAGA,UACC,uBACA,CAAE,OAAQ,WAAW,EAAE,MAAM,OAAO,CAAE,GACrC;AACD,4BAAqB,SAAU;AAAA,MAChC;AAGA,YAAM,qBAAqB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,MACD;AACA,YAAM,wBAAwB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAGA,UAAK,CAAE,OAAQ,oBAAoB,cAAe,GAAI;AACrD,YAAK,OAAQ,gBAAgB,qBAAsB,GAAI;AACtD,cAAK,cAAc,eAAe,MAAU;AAAA,QAC7C,OAAO;AACN,cAAK,cAAc,eAAe,cAAe;AAAA,QAClD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,QAAM,iBAAiB,YAAa,MAAM;AACzC,QAAK,cAAc,eAAe,MAAU;AAAA,EAC7C,GAAG,CAAE,eAAe,GAAI,CAAE;AAE1B,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { View, Filter } from '@wordpress/dataviews';
|
|
5
|
+
type ActiveViewOverrides = {
|
|
6
|
+
filters?: Filter[];
|
|
7
|
+
sort?: View['sort'];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Merges activeViewOverrides into a view.
|
|
11
|
+
* Filters: Active filters take precedence; same-field filters are replaced.
|
|
12
|
+
* Sort: Active sort is applied only if current sort matches the default.
|
|
13
|
+
*
|
|
14
|
+
* @param view The view to merge overrides into.
|
|
15
|
+
* @param activeViewOverrides The tab-specific overrides to apply.
|
|
16
|
+
* @param defaultView The default view configuration.
|
|
17
|
+
* @return A new view with merged overrides, or the original view if no overrides.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mergeActiveViewOverrides(view: View, activeViewOverrides?: ActiveViewOverrides, defaultView?: View): View;
|
|
20
|
+
/**
|
|
21
|
+
* Strips overrides before persisting.
|
|
22
|
+
* Filters: Removes filters on fields managed by activeViewOverrides.
|
|
23
|
+
* Sort: If sort matches the override, restores the default sort.
|
|
24
|
+
*
|
|
25
|
+
* @param view The view to strip overrides from.
|
|
26
|
+
* @param activeViewOverrides The tab-specific override definitions.
|
|
27
|
+
* @param defaultView The default view configuration.
|
|
28
|
+
* @return A new view with overrides stripped, or the original view if no overrides.
|
|
29
|
+
*/
|
|
30
|
+
export declare function stripActiveViewOverrides(view: View, activeViewOverrides?: ActiveViewOverrides, defaultView?: View): View;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=filter-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-utils.d.ts","sourceRoot":"","sources":["../src/filter-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEzD,KAAK,mBAAmB,GAAG;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,CAAE,MAAM,CAAE,CAAC;CACtB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CAwCN;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CAoCN"}
|
|
@@ -1,196 +1,16 @@
|
|
|
1
|
+
import type { View } from '@wordpress/dataviews';
|
|
1
2
|
import type { ViewConfig } from './types';
|
|
2
3
|
/**
|
|
3
|
-
* Async function for loading view state in route loaders
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
*
|
|
7
|
-
* ```typescript
|
|
8
|
-
* // In route loader
|
|
9
|
-
* const view = await loadView( {
|
|
10
|
-
* kind: 'taxonomy',
|
|
11
|
-
* name: 'category',
|
|
12
|
-
* slug: 'all',
|
|
13
|
-
* defaultView,
|
|
14
|
-
* queryParams: { page: search.page, search: search.search },
|
|
15
|
-
* } );
|
|
16
|
-
* ```
|
|
17
|
-
*
|
|
18
|
-
* @param config Configuration object for loading the view.
|
|
19
|
-
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
20
|
-
* @param config.name Specific entity name.
|
|
21
|
-
* @param config.slug View identifier.
|
|
22
|
-
* @param config.defaultView Default view configuration.
|
|
23
|
-
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
4
|
+
* Async function for loading view state in route loaders.
|
|
24
5
|
*
|
|
6
|
+
* @param config Configuration object for loading the view.
|
|
7
|
+
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
8
|
+
* @param config.name Specific entity name.
|
|
9
|
+
* @param config.slug View identifier.
|
|
10
|
+
* @param config.defaultView Default view configuration.
|
|
11
|
+
* @param config.activeViewOverrides View overrides applied on top but never persisted.
|
|
12
|
+
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
25
13
|
* @return Promise resolving to the loaded view object.
|
|
26
14
|
*/
|
|
27
|
-
export declare function loadView(config: ViewConfig): Promise<
|
|
28
|
-
page: number;
|
|
29
|
-
search: string;
|
|
30
|
-
type: "list";
|
|
31
|
-
layout?: {
|
|
32
|
-
density?: import("@wordpress/dataviews").Density;
|
|
33
|
-
};
|
|
34
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
35
|
-
sort?: {
|
|
36
|
-
field: string;
|
|
37
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
38
|
-
};
|
|
39
|
-
perPage?: number;
|
|
40
|
-
fields?: string[];
|
|
41
|
-
titleField?: string;
|
|
42
|
-
mediaField?: string;
|
|
43
|
-
descriptionField?: string;
|
|
44
|
-
showTitle?: boolean;
|
|
45
|
-
showMedia?: boolean;
|
|
46
|
-
showDescription?: boolean;
|
|
47
|
-
showLevels?: boolean;
|
|
48
|
-
groupBy?: {
|
|
49
|
-
field: string;
|
|
50
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
51
|
-
showLabel?: boolean;
|
|
52
|
-
};
|
|
53
|
-
infiniteScrollEnabled?: boolean;
|
|
54
|
-
} | {
|
|
55
|
-
page: number;
|
|
56
|
-
search: string;
|
|
57
|
-
type: "grid";
|
|
58
|
-
layout?: {
|
|
59
|
-
badgeFields?: string[];
|
|
60
|
-
previewSize?: number;
|
|
61
|
-
};
|
|
62
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
63
|
-
sort?: {
|
|
64
|
-
field: string;
|
|
65
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
66
|
-
};
|
|
67
|
-
perPage?: number;
|
|
68
|
-
fields?: string[];
|
|
69
|
-
titleField?: string;
|
|
70
|
-
mediaField?: string;
|
|
71
|
-
descriptionField?: string;
|
|
72
|
-
showTitle?: boolean;
|
|
73
|
-
showMedia?: boolean;
|
|
74
|
-
showDescription?: boolean;
|
|
75
|
-
showLevels?: boolean;
|
|
76
|
-
groupBy?: {
|
|
77
|
-
field: string;
|
|
78
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
79
|
-
showLabel?: boolean;
|
|
80
|
-
};
|
|
81
|
-
infiniteScrollEnabled?: boolean;
|
|
82
|
-
} | {
|
|
83
|
-
page: number;
|
|
84
|
-
search: string;
|
|
85
|
-
type: "table";
|
|
86
|
-
layout?: {
|
|
87
|
-
styles?: Record<string, import("@wordpress/dataviews").ColumnStyle>;
|
|
88
|
-
density?: import("@wordpress/dataviews").Density;
|
|
89
|
-
enableMoving?: boolean;
|
|
90
|
-
};
|
|
91
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
92
|
-
sort?: {
|
|
93
|
-
field: string;
|
|
94
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
95
|
-
};
|
|
96
|
-
perPage?: number;
|
|
97
|
-
fields?: string[];
|
|
98
|
-
titleField?: string;
|
|
99
|
-
mediaField?: string;
|
|
100
|
-
descriptionField?: string;
|
|
101
|
-
showTitle?: boolean;
|
|
102
|
-
showMedia?: boolean;
|
|
103
|
-
showDescription?: boolean;
|
|
104
|
-
showLevels?: boolean;
|
|
105
|
-
groupBy?: {
|
|
106
|
-
field: string;
|
|
107
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
108
|
-
showLabel?: boolean;
|
|
109
|
-
};
|
|
110
|
-
infiniteScrollEnabled?: boolean;
|
|
111
|
-
} | {
|
|
112
|
-
page: number;
|
|
113
|
-
search: string;
|
|
114
|
-
type: "pickerGrid";
|
|
115
|
-
layout?: {
|
|
116
|
-
badgeFields?: string[];
|
|
117
|
-
previewSize?: number;
|
|
118
|
-
};
|
|
119
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
120
|
-
sort?: {
|
|
121
|
-
field: string;
|
|
122
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
123
|
-
};
|
|
124
|
-
perPage?: number;
|
|
125
|
-
fields?: string[];
|
|
126
|
-
titleField?: string;
|
|
127
|
-
mediaField?: string;
|
|
128
|
-
descriptionField?: string;
|
|
129
|
-
showTitle?: boolean;
|
|
130
|
-
showMedia?: boolean;
|
|
131
|
-
showDescription?: boolean;
|
|
132
|
-
showLevels?: boolean;
|
|
133
|
-
groupBy?: {
|
|
134
|
-
field: string;
|
|
135
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
136
|
-
showLabel?: boolean;
|
|
137
|
-
};
|
|
138
|
-
infiniteScrollEnabled?: boolean;
|
|
139
|
-
} | {
|
|
140
|
-
page: number;
|
|
141
|
-
search: string;
|
|
142
|
-
type: "pickerTable";
|
|
143
|
-
layout?: {
|
|
144
|
-
styles?: Record<string, import("@wordpress/dataviews").ColumnStyle>;
|
|
145
|
-
density?: import("@wordpress/dataviews").Density;
|
|
146
|
-
enableMoving?: boolean;
|
|
147
|
-
};
|
|
148
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
149
|
-
sort?: {
|
|
150
|
-
field: string;
|
|
151
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
152
|
-
};
|
|
153
|
-
perPage?: number;
|
|
154
|
-
fields?: string[];
|
|
155
|
-
titleField?: string;
|
|
156
|
-
mediaField?: string;
|
|
157
|
-
descriptionField?: string;
|
|
158
|
-
showTitle?: boolean;
|
|
159
|
-
showMedia?: boolean;
|
|
160
|
-
showDescription?: boolean;
|
|
161
|
-
showLevels?: boolean;
|
|
162
|
-
groupBy?: {
|
|
163
|
-
field: string;
|
|
164
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
165
|
-
showLabel?: boolean;
|
|
166
|
-
};
|
|
167
|
-
infiniteScrollEnabled?: boolean;
|
|
168
|
-
} | {
|
|
169
|
-
page: number;
|
|
170
|
-
search: string;
|
|
171
|
-
type: "activity";
|
|
172
|
-
layout?: {
|
|
173
|
-
density?: import("@wordpress/dataviews").Density;
|
|
174
|
-
};
|
|
175
|
-
filters?: import("@wordpress/dataviews").Filter[];
|
|
176
|
-
sort?: {
|
|
177
|
-
field: string;
|
|
178
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
179
|
-
};
|
|
180
|
-
perPage?: number;
|
|
181
|
-
fields?: string[];
|
|
182
|
-
titleField?: string;
|
|
183
|
-
mediaField?: string;
|
|
184
|
-
descriptionField?: string;
|
|
185
|
-
showTitle?: boolean;
|
|
186
|
-
showMedia?: boolean;
|
|
187
|
-
showDescription?: boolean;
|
|
188
|
-
showLevels?: boolean;
|
|
189
|
-
groupBy?: {
|
|
190
|
-
field: string;
|
|
191
|
-
direction: import("@wordpress/dataviews").SortDirection;
|
|
192
|
-
showLabel?: boolean;
|
|
193
|
-
};
|
|
194
|
-
infiniteScrollEnabled?: boolean;
|
|
195
|
-
}>;
|
|
15
|
+
export declare function loadView(config: ViewConfig): Promise<View>;
|
|
196
16
|
//# sourceMappingURL=load-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-view.d.ts","sourceRoot":"","sources":["../src/load-view.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-view.d.ts","sourceRoot":"","sources":["../src/load-view.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAOjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAE,MAAM,EAAE,UAAU,iBAsBjD"}
|
package/build-types/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type { View } from '@wordpress/dataviews';
|
|
4
|
+
import type { View, Filter } from '@wordpress/dataviews';
|
|
5
5
|
export interface ViewConfig {
|
|
6
6
|
/**
|
|
7
7
|
* Entity kind (e.g. postType, root).
|
|
@@ -21,6 +21,15 @@ export interface ViewConfig {
|
|
|
21
21
|
* Default view configuration
|
|
22
22
|
*/
|
|
23
23
|
defaultView: View;
|
|
24
|
+
/**
|
|
25
|
+
* View overrides applied on top of the persisted view but never persisted.
|
|
26
|
+
* These represent tab-specific configuration (filters, sort) that should
|
|
27
|
+
* override the persisted view settings.
|
|
28
|
+
*/
|
|
29
|
+
activeViewOverrides?: {
|
|
30
|
+
filters?: Filter[];
|
|
31
|
+
sort?: View['sort'];
|
|
32
|
+
};
|
|
24
33
|
/**
|
|
25
34
|
* Optional query parameters from URL (page, search)
|
|
26
35
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,IAAI,CAAE,MAAM,CAAE,CAAC;KACtB,CAAC;IAEF;;OAEG;IACH,WAAW,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAE,MAAM,EAAE;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,KAAM,IAAI,CAAC;CACZ"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ViewConfig } from './types';
|
|
2
1
|
import type { View } from '@wordpress/dataviews';
|
|
2
|
+
import type { ViewConfig } from './types';
|
|
3
3
|
interface UseViewReturn {
|
|
4
4
|
view: View;
|
|
5
5
|
isModified: boolean;
|
|
@@ -9,13 +9,7 @@ interface UseViewReturn {
|
|
|
9
9
|
/**
|
|
10
10
|
* Hook for managing DataViews view state with local persistence.
|
|
11
11
|
*
|
|
12
|
-
* @param config
|
|
13
|
-
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
14
|
-
* @param config.name Specific entity name.
|
|
15
|
-
* @param config.slug View identifier.
|
|
16
|
-
* @param config.defaultView Default view configuration.
|
|
17
|
-
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
18
|
-
* @param config.onChangeQueryParams Optional callback to update URL parameters.
|
|
12
|
+
* @param config Configuration object for loading the view.
|
|
19
13
|
*
|
|
20
14
|
* @return Object with current view, modification state, and update functions.
|
|
21
15
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-view.d.ts","sourceRoot":"","sources":["../src/use-view.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-view.d.ts","sourceRoot":"","sources":["../src/use-view.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAYjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,UAAU,aAAa;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,CAAE,OAAO,EAAE,IAAI,KAAM,IAAI,CAAC;IACtC,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B;AAaD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAE,MAAM,EAAE,UAAU,GAAI,aAAa,CA4G3D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/views",
|
|
3
|
-
"version": "1.6.0",
|
|
3
|
+
"version": "1.6.1-next.v.20260206T143.0+81f8de885",
|
|
4
4
|
"description": "View persistence and management for WordPress DataViews.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
},
|
|
42
42
|
"types": "build-types",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@wordpress/data": "^10.39.0",
|
|
45
|
-
"@wordpress/dataviews": "^
|
|
46
|
-
"@wordpress/element": "^6.39.0",
|
|
47
|
-
"@wordpress/preferences": "^4.39.0",
|
|
44
|
+
"@wordpress/data": "^10.39.1-next.v.20260206T143.0+81f8de885",
|
|
45
|
+
"@wordpress/dataviews": "^12.0.1-next.v.20260206T143.0+81f8de885",
|
|
46
|
+
"@wordpress/element": "^6.39.1-next.v.20260206T143.0+81f8de885",
|
|
47
|
+
"@wordpress/preferences": "^4.39.1-next.v.20260206T143.0+81f8de885",
|
|
48
48
|
"dequal": "^2.0.3"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "6ae3fbb907e6c309ff6a0685e5e5ff0c2ee23b15"
|
|
54
54
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { View, Filter } from '@wordpress/dataviews';
|
|
5
|
+
|
|
6
|
+
type ActiveViewOverrides = {
|
|
7
|
+
filters?: Filter[];
|
|
8
|
+
sort?: View[ 'sort' ];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Merges activeViewOverrides into a view.
|
|
13
|
+
* Filters: Active filters take precedence; same-field filters are replaced.
|
|
14
|
+
* Sort: Active sort is applied only if current sort matches the default.
|
|
15
|
+
*
|
|
16
|
+
* @param view The view to merge overrides into.
|
|
17
|
+
* @param activeViewOverrides The tab-specific overrides to apply.
|
|
18
|
+
* @param defaultView The default view configuration.
|
|
19
|
+
* @return A new view with merged overrides, or the original view if no overrides.
|
|
20
|
+
*/
|
|
21
|
+
export function mergeActiveViewOverrides(
|
|
22
|
+
view: View,
|
|
23
|
+
activeViewOverrides?: ActiveViewOverrides,
|
|
24
|
+
defaultView?: View
|
|
25
|
+
): View {
|
|
26
|
+
if ( ! activeViewOverrides ) {
|
|
27
|
+
return view;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let result = view;
|
|
31
|
+
|
|
32
|
+
// Merge filters
|
|
33
|
+
if (
|
|
34
|
+
activeViewOverrides.filters &&
|
|
35
|
+
activeViewOverrides.filters.length > 0
|
|
36
|
+
) {
|
|
37
|
+
const activeFields = new Set(
|
|
38
|
+
activeViewOverrides.filters.map( ( f ) => f.field )
|
|
39
|
+
);
|
|
40
|
+
const preserved = ( view.filters ?? [] ).filter(
|
|
41
|
+
( f: Filter ) => ! activeFields.has( f.field )
|
|
42
|
+
);
|
|
43
|
+
result = {
|
|
44
|
+
...result,
|
|
45
|
+
filters: [ ...preserved, ...activeViewOverrides.filters ],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Merge sort - only apply if the current sort matches the default
|
|
50
|
+
if ( activeViewOverrides.sort ) {
|
|
51
|
+
const isDefaultSort =
|
|
52
|
+
defaultView &&
|
|
53
|
+
view.sort?.field === defaultView.sort?.field &&
|
|
54
|
+
view.sort?.direction === defaultView.sort?.direction;
|
|
55
|
+
|
|
56
|
+
if ( isDefaultSort ) {
|
|
57
|
+
result = {
|
|
58
|
+
...result,
|
|
59
|
+
sort: activeViewOverrides.sort,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Strips overrides before persisting.
|
|
69
|
+
* Filters: Removes filters on fields managed by activeViewOverrides.
|
|
70
|
+
* Sort: If sort matches the override, restores the default sort.
|
|
71
|
+
*
|
|
72
|
+
* @param view The view to strip overrides from.
|
|
73
|
+
* @param activeViewOverrides The tab-specific override definitions.
|
|
74
|
+
* @param defaultView The default view configuration.
|
|
75
|
+
* @return A new view with overrides stripped, or the original view if no overrides.
|
|
76
|
+
*/
|
|
77
|
+
export function stripActiveViewOverrides(
|
|
78
|
+
view: View,
|
|
79
|
+
activeViewOverrides?: ActiveViewOverrides,
|
|
80
|
+
defaultView?: View
|
|
81
|
+
): View {
|
|
82
|
+
if ( ! activeViewOverrides ) {
|
|
83
|
+
return view;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let result = view;
|
|
87
|
+
|
|
88
|
+
// Strip managed filters
|
|
89
|
+
if (
|
|
90
|
+
activeViewOverrides.filters &&
|
|
91
|
+
activeViewOverrides.filters.length > 0
|
|
92
|
+
) {
|
|
93
|
+
const activeFields = new Set(
|
|
94
|
+
activeViewOverrides.filters.map( ( f ) => f.field )
|
|
95
|
+
);
|
|
96
|
+
result = {
|
|
97
|
+
...result,
|
|
98
|
+
filters: ( view.filters ?? [] ).filter(
|
|
99
|
+
( f: Filter ) => ! activeFields.has( f.field )
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Strip sort if it matches the override (restore to default)
|
|
105
|
+
if (
|
|
106
|
+
activeViewOverrides.sort &&
|
|
107
|
+
view.sort?.field === activeViewOverrides.sort.field &&
|
|
108
|
+
view.sort?.direction === activeViewOverrides.sort.direction
|
|
109
|
+
) {
|
|
110
|
+
result = {
|
|
111
|
+
...result,
|
|
112
|
+
sort: defaultView?.sort,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
package/src/load-view.ts
CHANGED
|
@@ -10,35 +10,24 @@ import type { View } from '@wordpress/dataviews';
|
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
12
12
|
import { generatePreferenceKey } from './preference-keys';
|
|
13
|
+
import { mergeActiveViewOverrides } from './filter-utils';
|
|
13
14
|
import type { ViewConfig } from './types';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* Async function for loading view state in route loaders
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
*
|
|
20
|
-
* ```typescript
|
|
21
|
-
* // In route loader
|
|
22
|
-
* const view = await loadView( {
|
|
23
|
-
* kind: 'taxonomy',
|
|
24
|
-
* name: 'category',
|
|
25
|
-
* slug: 'all',
|
|
26
|
-
* defaultView,
|
|
27
|
-
* queryParams: { page: search.page, search: search.search },
|
|
28
|
-
* } );
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* @param config Configuration object for loading the view.
|
|
32
|
-
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
33
|
-
* @param config.name Specific entity name.
|
|
34
|
-
* @param config.slug View identifier.
|
|
35
|
-
* @param config.defaultView Default view configuration.
|
|
36
|
-
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
17
|
+
* Async function for loading view state in route loaders.
|
|
37
18
|
*
|
|
19
|
+
* @param config Configuration object for loading the view.
|
|
20
|
+
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
21
|
+
* @param config.name Specific entity name.
|
|
22
|
+
* @param config.slug View identifier.
|
|
23
|
+
* @param config.defaultView Default view configuration.
|
|
24
|
+
* @param config.activeViewOverrides View overrides applied on top but never persisted.
|
|
25
|
+
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
38
26
|
* @return Promise resolving to the loaded view object.
|
|
39
27
|
*/
|
|
40
28
|
export async function loadView( config: ViewConfig ) {
|
|
41
|
-
const { kind, name, slug, defaultView, queryParams } =
|
|
29
|
+
const { kind, name, slug, defaultView, activeViewOverrides, queryParams } =
|
|
30
|
+
config;
|
|
42
31
|
const preferenceKey = generatePreferenceKey( kind, name, slug );
|
|
43
32
|
const persistedView: View | undefined = select( preferencesStore ).get(
|
|
44
33
|
'core/views',
|
|
@@ -49,9 +38,13 @@ export async function loadView( config: ViewConfig ) {
|
|
|
49
38
|
const page = queryParams?.page ?? 1;
|
|
50
39
|
const search = queryParams?.search ?? '';
|
|
51
40
|
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
return mergeActiveViewOverrides(
|
|
42
|
+
{
|
|
43
|
+
...baseView,
|
|
44
|
+
page,
|
|
45
|
+
search,
|
|
46
|
+
},
|
|
47
|
+
activeViewOverrides,
|
|
48
|
+
defaultView
|
|
49
|
+
);
|
|
57
50
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type { View } from '@wordpress/dataviews';
|
|
4
|
+
import type { View, Filter } from '@wordpress/dataviews';
|
|
5
5
|
|
|
6
6
|
export interface ViewConfig {
|
|
7
7
|
/**
|
|
@@ -26,6 +26,16 @@ export interface ViewConfig {
|
|
|
26
26
|
*/
|
|
27
27
|
defaultView: View;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* View overrides applied on top of the persisted view but never persisted.
|
|
31
|
+
* These represent tab-specific configuration (filters, sort) that should
|
|
32
|
+
* override the persisted view settings.
|
|
33
|
+
*/
|
|
34
|
+
activeViewOverrides?: {
|
|
35
|
+
filters?: Filter[];
|
|
36
|
+
sort?: View[ 'sort' ];
|
|
37
|
+
};
|
|
38
|
+
|
|
29
39
|
/**
|
|
30
40
|
* Optional query parameters from URL (page, search)
|
|
31
41
|
*/
|
package/src/use-view.ts
CHANGED
|
@@ -3,12 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { dequal } from 'dequal';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Internal dependencies
|
|
8
|
-
*/
|
|
9
|
-
import { generatePreferenceKey } from './preference-keys';
|
|
10
|
-
import type { ViewConfig } from './types';
|
|
11
|
-
|
|
12
6
|
/**
|
|
13
7
|
* WordPress dependencies
|
|
14
8
|
*/
|
|
@@ -18,6 +12,16 @@ import type { View } from '@wordpress/dataviews';
|
|
|
18
12
|
// @ts-ignore - Preferences package is not typed
|
|
19
13
|
import { store as preferencesStore } from '@wordpress/preferences';
|
|
20
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Internal dependencies
|
|
17
|
+
*/
|
|
18
|
+
import { generatePreferenceKey } from './preference-keys';
|
|
19
|
+
import {
|
|
20
|
+
mergeActiveViewOverrides,
|
|
21
|
+
stripActiveViewOverrides,
|
|
22
|
+
} from './filter-utils';
|
|
23
|
+
import type { ViewConfig } from './types';
|
|
24
|
+
|
|
21
25
|
interface UseViewReturn {
|
|
22
26
|
view: View;
|
|
23
27
|
isModified: boolean;
|
|
@@ -39,19 +43,20 @@ function omit< T extends object, K extends keyof T >(
|
|
|
39
43
|
/**
|
|
40
44
|
* Hook for managing DataViews view state with local persistence.
|
|
41
45
|
*
|
|
42
|
-
* @param config
|
|
43
|
-
* @param config.kind Entity kind (e.g., 'postType', 'taxonomy', 'root').
|
|
44
|
-
* @param config.name Specific entity name.
|
|
45
|
-
* @param config.slug View identifier.
|
|
46
|
-
* @param config.defaultView Default view configuration.
|
|
47
|
-
* @param config.queryParams Object with `page` and/or `search` from URL.
|
|
48
|
-
* @param config.onChangeQueryParams Optional callback to update URL parameters.
|
|
46
|
+
* @param config Configuration object for loading the view.
|
|
49
47
|
*
|
|
50
48
|
* @return Object with current view, modification state, and update functions.
|
|
51
49
|
*/
|
|
52
50
|
export function useView( config: ViewConfig ): UseViewReturn {
|
|
53
|
-
const {
|
|
54
|
-
|
|
51
|
+
const {
|
|
52
|
+
kind,
|
|
53
|
+
name,
|
|
54
|
+
slug,
|
|
55
|
+
defaultView,
|
|
56
|
+
activeViewOverrides,
|
|
57
|
+
queryParams,
|
|
58
|
+
onChangeQueryParams,
|
|
59
|
+
} = config;
|
|
55
60
|
|
|
56
61
|
const preferenceKey = generatePreferenceKey( kind, name, slug );
|
|
57
62
|
const persistedView: View | undefined = useSelect(
|
|
@@ -69,14 +74,18 @@ export function useView( config: ViewConfig ): UseViewReturn {
|
|
|
69
74
|
const page = Number( queryParams?.page ?? baseView.page ?? 1 );
|
|
70
75
|
const search = queryParams?.search ?? baseView.search ?? '';
|
|
71
76
|
|
|
72
|
-
// Merge URL query parameters (page, search) into the view
|
|
77
|
+
// Merge URL query parameters (page, search) and activeViewOverrides into the view
|
|
73
78
|
const view: View = useMemo( () => {
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
return mergeActiveViewOverrides(
|
|
80
|
+
{
|
|
81
|
+
...baseView,
|
|
82
|
+
page,
|
|
83
|
+
search,
|
|
84
|
+
},
|
|
85
|
+
activeViewOverrides,
|
|
86
|
+
defaultView
|
|
87
|
+
);
|
|
88
|
+
}, [ baseView, page, search, activeViewOverrides, defaultView ] );
|
|
80
89
|
|
|
81
90
|
const isModified = !! persistedView;
|
|
82
91
|
|
|
@@ -87,7 +96,13 @@ export function useView( config: ViewConfig ): UseViewReturn {
|
|
|
87
96
|
page: newView?.page,
|
|
88
97
|
search: newView?.search,
|
|
89
98
|
};
|
|
90
|
-
|
|
99
|
+
// Strip activeViewOverrides and URL params before persisting
|
|
100
|
+
// Cast is safe: omitting page/search doesn't change the discriminant (type field)
|
|
101
|
+
const preferenceView = stripActiveViewOverrides(
|
|
102
|
+
omit( newView, [ 'page', 'search' ] ) as View,
|
|
103
|
+
activeViewOverrides,
|
|
104
|
+
defaultView
|
|
105
|
+
);
|
|
91
106
|
|
|
92
107
|
// If we have URL handling enabled, separate URL state from preference state
|
|
93
108
|
if (
|
|
@@ -97,9 +112,21 @@ export function useView( config: ViewConfig ): UseViewReturn {
|
|
|
97
112
|
onChangeQueryParams( urlParams );
|
|
98
113
|
}
|
|
99
114
|
|
|
115
|
+
// Compare with baseView and defaultView after stripping activeViewOverrides
|
|
116
|
+
const comparableBaseView = stripActiveViewOverrides(
|
|
117
|
+
baseView,
|
|
118
|
+
activeViewOverrides,
|
|
119
|
+
defaultView
|
|
120
|
+
);
|
|
121
|
+
const comparableDefaultView = stripActiveViewOverrides(
|
|
122
|
+
defaultView,
|
|
123
|
+
activeViewOverrides,
|
|
124
|
+
defaultView
|
|
125
|
+
);
|
|
126
|
+
|
|
100
127
|
// Only persist non-URL preferences if different from baseView
|
|
101
|
-
if ( ! dequal(
|
|
102
|
-
if ( dequal( preferenceView,
|
|
128
|
+
if ( ! dequal( comparableBaseView, preferenceView ) ) {
|
|
129
|
+
if ( dequal( preferenceView, comparableDefaultView ) ) {
|
|
103
130
|
set( 'core/views', preferenceKey, undefined );
|
|
104
131
|
} else {
|
|
105
132
|
set( 'core/views', preferenceKey, preferenceView );
|
|
@@ -112,6 +139,7 @@ export function useView( config: ViewConfig ): UseViewReturn {
|
|
|
112
139
|
search,
|
|
113
140
|
baseView,
|
|
114
141
|
defaultView,
|
|
142
|
+
activeViewOverrides,
|
|
115
143
|
set,
|
|
116
144
|
preferenceKey,
|
|
117
145
|
]
|