strapi-plugin-navigation 2.0.0-beta.1 → 2.0.0-beta.5
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 +62 -104
- package/admin/src/components/Item/ItemCardHeader/index.js +2 -2
- package/admin/src/components/Item/index.js +3 -1
- package/admin/src/components/NavigationItemList/index.js +2 -0
- package/admin/src/components/Search/index.js +49 -0
- package/admin/src/pages/DataManagerProvider/index.js +6 -9
- package/admin/src/pages/DataManagerProvider/reducer.js +58 -69
- package/admin/src/pages/View/index.js +16 -4
- package/admin/src/pages/View/utils/parsers.js +1 -1
- package/admin/src/pluginId.js +2 -2
- package/package.json +7 -3
- package/server/config/index.js +8 -0
- package/server/controllers/navigation.js +21 -0
- package/server/graphql/index.js +23 -0
- package/server/graphql/queries/index.js +17 -0
- package/server/graphql/queries/render-navigation-child.js +16 -0
- package/server/graphql/queries/render-navigation.js +15 -0
- package/server/graphql/resolvers-config.js +4 -0
- package/server/graphql/types/content-types-name-fields.js +8 -0
- package/server/graphql/types/content-types.js +16 -0
- package/server/graphql/types/create-navigation-item.js +17 -0
- package/server/graphql/types/create-navigation-related.js +8 -0
- package/server/graphql/types/create-navigation.js +7 -0
- package/server/graphql/types/index.js +15 -0
- package/server/graphql/types/navigation-config.js +9 -0
- package/server/graphql/types/navigation-details.js +10 -0
- package/server/graphql/types/navigation-item.js +29 -0
- package/server/graphql/types/navigation-related.js +23 -0
- package/server/graphql/types/navigation-render-type.js +4 -0
- package/server/graphql/types/navigation.js +9 -0
- package/server/register.js +5 -0
- package/server/routes/client.js +21 -0
- package/server/routes/index.js +2 -1
- package/server/services/navigation.js +272 -6
- package/server/services/utils/functions.js +84 -2
- package/strapi-server.js +3 -1
|
@@ -14,10 +14,8 @@ import { ContentLayout } from '@strapi/design-system/Layout';
|
|
|
14
14
|
import { Button } from '@strapi/design-system/Button';
|
|
15
15
|
import { LoadingIndicatorPage } from "@strapi/helper-plugin";
|
|
16
16
|
import { EmptyStateLayout } from '@strapi/design-system/EmptyStateLayout';
|
|
17
|
-
import { IconButton } from '@strapi/design-system/IconButton';
|
|
18
17
|
import EmptyDocumentsIcon from '@strapi/icons/EmptyDocuments';
|
|
19
18
|
import PlusIcon from "@strapi/icons/Plus";
|
|
20
|
-
import SearchIcon from "@strapi/icons/Search";
|
|
21
19
|
|
|
22
20
|
// Components
|
|
23
21
|
import List from '../../components/NavigationItemList';
|
|
@@ -32,6 +30,7 @@ import {
|
|
|
32
30
|
usedContentTypes,
|
|
33
31
|
validateNavigationStructure,
|
|
34
32
|
} from './utils/parsers';
|
|
33
|
+
import Search from '../../components/Search';
|
|
35
34
|
|
|
36
35
|
const View = () => {
|
|
37
36
|
const {
|
|
@@ -55,6 +54,9 @@ const View = () => {
|
|
|
55
54
|
const [activeNavigationItem, setActiveNavigationItemState] = useState({});
|
|
56
55
|
const { formatMessage } = useIntl();
|
|
57
56
|
|
|
57
|
+
const [searchValue, setSearchValue] = useState('');
|
|
58
|
+
const isSearchEmpty = isEmpty(searchValue);
|
|
59
|
+
|
|
58
60
|
const structureHasErrors = !validateNavigationStructure((changedActiveNavigation || {}).items);
|
|
59
61
|
const navigationSelectValue = get(activeNavigation, "id", null);
|
|
60
62
|
const handleSave = () => isLoadingForSubmit || structureHasErrors
|
|
@@ -104,6 +106,15 @@ const View = () => {
|
|
|
104
106
|
handleChangeNavigationData(changedStructure, true);
|
|
105
107
|
};
|
|
106
108
|
|
|
109
|
+
const filteredListFactory = (items, filterFunction) => items.reduce((acc, item) => {
|
|
110
|
+
const subItems = !isEmpty(item.items) ? filteredListFactory(item.items, filterFunction) : [];
|
|
111
|
+
if (filterFunction(item))
|
|
112
|
+
return [item, ...subItems, ...acc];
|
|
113
|
+
else
|
|
114
|
+
return [...subItems, ...acc];
|
|
115
|
+
}, []);
|
|
116
|
+
const filteredList = !isSearchEmpty ? filteredListFactory(changedActiveNavigation.items, (item) => item?.title.includes(searchValue)) : [];
|
|
117
|
+
|
|
107
118
|
const handleItemRemove = (item) => {
|
|
108
119
|
handleSubmitNavigationItem({
|
|
109
120
|
...item,
|
|
@@ -147,7 +158,7 @@ const View = () => {
|
|
|
147
158
|
{changedActiveNavigation && (
|
|
148
159
|
<>
|
|
149
160
|
<NavigationContentHeader
|
|
150
|
-
startActions={<
|
|
161
|
+
startActions={<Search value={searchValue} setValue={setSearchValue}/>}
|
|
151
162
|
endActions={<Button
|
|
152
163
|
onClick={addNewNavigationItem}
|
|
153
164
|
startIcon={<PlusIcon />}
|
|
@@ -176,11 +187,12 @@ const View = () => {
|
|
|
176
187
|
{
|
|
177
188
|
!isEmpty(changedActiveNavigation.items || [])
|
|
178
189
|
&& <List
|
|
179
|
-
items={changedActiveNavigation.items || []}
|
|
190
|
+
items={isSearchEmpty ? changedActiveNavigation.items || [] : filteredList}
|
|
180
191
|
onItemLevelAdd={addNewNavigationItem}
|
|
181
192
|
onItemRemove={handleItemRemove}
|
|
182
193
|
onItemEdit={handleItemEdit}
|
|
183
194
|
onItemRestore={handleItemRestore}
|
|
195
|
+
displayFlat={!isSearchEmpty}
|
|
184
196
|
root
|
|
185
197
|
error={error}
|
|
186
198
|
allowedLevels={config.allowedLevels}
|
|
@@ -28,7 +28,7 @@ export const transformItemToRESTPayload = (
|
|
|
28
28
|
const { contentTypes = [] } = config;
|
|
29
29
|
|
|
30
30
|
const parsedRelated = Number(related);
|
|
31
|
-
const relatedId = isExternal || isNaN(parsedRelated) ? related
|
|
31
|
+
const relatedId = isExternal || isNaN(parsedRelated) ? related?.value || related : parsedRelated;
|
|
32
32
|
|
|
33
33
|
const relatedContentType = relatedType ?
|
|
34
34
|
find(contentTypes,
|
package/admin/src/pluginId.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-navigation",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.5",
|
|
4
4
|
"description": "Strapi - Navigation plugin",
|
|
5
5
|
"strapi": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "navigation",
|
|
7
7
|
"icon": "hamburger",
|
|
8
8
|
"description": "UI navigation management",
|
|
9
9
|
"kind": "plugin"
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"test:unit": "jest --verbose --coverage"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@
|
|
19
|
+
"@strapi/utils": "^4.0.5",
|
|
20
|
+
"uuid": "^8.3.0",
|
|
20
21
|
"bad-words": "^3.0.3",
|
|
21
22
|
"lodash": "^4.17.11",
|
|
22
23
|
"react": "^16.9.0",
|
|
@@ -38,6 +39,9 @@
|
|
|
38
39
|
"jest-styled-components": "^7.0.2",
|
|
39
40
|
"codecov": "^3.7.2"
|
|
40
41
|
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@strapi/strapi": "4.x"
|
|
44
|
+
},
|
|
41
45
|
"author": {
|
|
42
46
|
"name": "VirtusLab // Mateusz Ziarko",
|
|
43
47
|
"email": "mziarko@virtuslab.com",
|
|
@@ -48,4 +48,25 @@ module.exports = {
|
|
|
48
48
|
return getService().put(id, body, auditLog)
|
|
49
49
|
.catch(errorHandler(ctx));
|
|
50
50
|
},
|
|
51
|
+
async render(ctx) {
|
|
52
|
+
const { params, query = {} } = ctx;
|
|
53
|
+
const { type, menu: menuOnly } = query;
|
|
54
|
+
const { idOrSlug } = parseParams(params);
|
|
55
|
+
return getService().render(
|
|
56
|
+
idOrSlug,
|
|
57
|
+
type,
|
|
58
|
+
menuOnly,
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
async renderChild(ctx) {
|
|
62
|
+
const { params, query = {} } = ctx;
|
|
63
|
+
const { type, menu: menuOnly } = query;
|
|
64
|
+
const { idOrSlug, childUIKey } = parseParams(params);
|
|
65
|
+
return getService().renderChildren(
|
|
66
|
+
idOrSlug,
|
|
67
|
+
childUIKey,
|
|
68
|
+
type,
|
|
69
|
+
menuOnly
|
|
70
|
+
);
|
|
71
|
+
},
|
|
51
72
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const getTypes = require('./types');
|
|
2
|
+
const getQueries = require('./queries');
|
|
3
|
+
const getResolversConfig = require('./resolvers-config');
|
|
4
|
+
|
|
5
|
+
module.exports = () => {
|
|
6
|
+
const extensionService = strapi.plugin('graphql').service('extension');
|
|
7
|
+
|
|
8
|
+
extensionService.shadowCRUD('plugin::navigation.audience').disable();
|
|
9
|
+
extensionService.shadowCRUD('plugin::navigation.navigation').disable();
|
|
10
|
+
extensionService.shadowCRUD('plugin::navigation.navigation-item').disable();
|
|
11
|
+
extensionService.shadowCRUD('plugin::navigation.navigations-items-related').disable();
|
|
12
|
+
|
|
13
|
+
extensionService.use(({ nexus }) => {
|
|
14
|
+
const types = getTypes({ strapi, nexus });
|
|
15
|
+
const queries = getQueries({ strapi, nexus });
|
|
16
|
+
const resolversConfig = getResolversConfig({ strapi });
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
types: [types, queries],
|
|
20
|
+
resolversConfig,
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports = (context) => {
|
|
2
|
+
const queries = {
|
|
3
|
+
renderNavigationChild: require('./render-navigation-child'),
|
|
4
|
+
renderNavigation: require('./render-navigation'),
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return context.nexus.extendType({
|
|
8
|
+
type: 'Query',
|
|
9
|
+
definition(t) {
|
|
10
|
+
for (const [name, configFactory] of Object.entries(queries)) {
|
|
11
|
+
const config = configFactory(context);
|
|
12
|
+
|
|
13
|
+
t.field(name, config);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = ({ strapi, nexus }) => {
|
|
2
|
+
const { nonNull, list, stringArg, booleanArg } = nexus;
|
|
3
|
+
return {
|
|
4
|
+
type: nonNull(list('NavigationItem')),
|
|
5
|
+
args: {
|
|
6
|
+
id: nonNull(stringArg()),
|
|
7
|
+
childUiKey: nonNull(stringArg()),
|
|
8
|
+
type: 'NavigationRenderType',
|
|
9
|
+
menuOnly: booleanArg()
|
|
10
|
+
},
|
|
11
|
+
resolve(obj, args) {
|
|
12
|
+
const { id, childUIKey, type, menuOnly } = args;
|
|
13
|
+
return strapi.plugin('navigation').service('navigation').renderChildren(id, childUIKey, type, menuOnly);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module.exports = ({ strapi, nexus }) => {
|
|
2
|
+
const { nonNull, list, stringArg, booleanArg } = nexus;
|
|
3
|
+
return {
|
|
4
|
+
type: nonNull(list('NavigationItem')),
|
|
5
|
+
args: {
|
|
6
|
+
navigationIdOrSlug: nonNull(stringArg()),
|
|
7
|
+
type: 'NavigationRenderType',
|
|
8
|
+
menuOnly: booleanArg()
|
|
9
|
+
},
|
|
10
|
+
resolve(obj, args) {
|
|
11
|
+
const { navigationIdOrSlug, type, menuOnly } = args;
|
|
12
|
+
return strapi.plugin('navigation').service('navigation').render(navigationIdOrSlug, type, menuOnly);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
|
+
name: "ContentTypesNameFields",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.nonNull.list.nonNull.string("default")
|
|
5
|
+
const contentTypesNameFields = strapi.plugin('navigation').config('contentTypesNameFields')
|
|
6
|
+
Object.keys(contentTypesNameFields || {}).forEach(key => t.nonNull.list.string(key))
|
|
7
|
+
}
|
|
8
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
|
+
name: "ContentTypes",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.nonNull.string("uid")
|
|
5
|
+
t.nonNull.string("name")
|
|
6
|
+
t.nonNull.boolean("isSingle")
|
|
7
|
+
t.nonNull.string("collectionName")
|
|
8
|
+
t.nonNull.string("contentTypeName")
|
|
9
|
+
t.nonNull.string("label")
|
|
10
|
+
t.nonNull.string("relatedField")
|
|
11
|
+
t.nonNull.string("labelSingular")
|
|
12
|
+
t.nonNull.string("endpoint")
|
|
13
|
+
t.nonNull.boolean("available")
|
|
14
|
+
t.nonNull.boolean("visible")
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.inputObjectType({
|
|
2
|
+
name: "CreateNavigationItem",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.nonNull.string("title")
|
|
5
|
+
t.nonNull.string("type")
|
|
6
|
+
t.string("path")
|
|
7
|
+
t.string("externalPath")
|
|
8
|
+
t.nonNull.string("uiRouterKey")
|
|
9
|
+
t.nonNull.boolean("menuAttached")
|
|
10
|
+
t.nonNull.int("order")
|
|
11
|
+
t.int("parent")
|
|
12
|
+
t.int("master")
|
|
13
|
+
t.list.field("items", { type: 'CreateNavigationItem' })
|
|
14
|
+
t.list.string("audience")
|
|
15
|
+
t.field("related", { type: 'CreateNavigationRelated' })
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const typesFactories = [
|
|
2
|
+
require('./navigation-item'),
|
|
3
|
+
require('./navigation-related'),
|
|
4
|
+
require('./navigation-render-type'),
|
|
5
|
+
require('./navigation'),
|
|
6
|
+
require('./navigation-details'),
|
|
7
|
+
require('./content-types-name-fields'),
|
|
8
|
+
require('./content-types'),
|
|
9
|
+
require('./navigation-config'),
|
|
10
|
+
require('./create-navigation-related'),
|
|
11
|
+
require('./create-navigation-item'),
|
|
12
|
+
require('./create-navigation'),
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
module.exports = context => typesFactories.map(factory => factory(context));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
|
+
name: "NavigationConfig",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.int("allowedLevels");
|
|
5
|
+
t.nonNull.list.string("additionalFields");
|
|
6
|
+
t.field("contentTypesNameFields", { type: 'ContentTypesNameFields' });
|
|
7
|
+
t.list.field("contentTypes", { type: 'ContentTypes' });
|
|
8
|
+
}
|
|
9
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
|
+
name: "NavigationDetails",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.nonNull.string("id")
|
|
5
|
+
t.nonNull.string("name")
|
|
6
|
+
t.nonNull.string("slug")
|
|
7
|
+
t.nonNull.boolean("visible")
|
|
8
|
+
t.nonNull.list.field("items", { type: 'NavigationItem' })
|
|
9
|
+
}
|
|
10
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = ({ nexus }) =>
|
|
2
|
+
nexus.objectType({
|
|
3
|
+
name: "NavigationItem",
|
|
4
|
+
definition(t) {
|
|
5
|
+
t.nonNull.int("id")
|
|
6
|
+
t.nonNull.string("title")
|
|
7
|
+
t.nonNull.string("type")
|
|
8
|
+
t.string("path")
|
|
9
|
+
t.string("externalPath")
|
|
10
|
+
t.nonNull.string("uiRouterKey")
|
|
11
|
+
t.nonNull.boolean("menuAttached")
|
|
12
|
+
t.nonNull.int("order")
|
|
13
|
+
t.int("parent")
|
|
14
|
+
t.int("master")
|
|
15
|
+
t.list.field("items", { type: 'NavigationItem' })
|
|
16
|
+
t.list.field("related", { type: 'NavigationRelated' })
|
|
17
|
+
t.list.string("audience")
|
|
18
|
+
// SQL
|
|
19
|
+
t.string("created_at")
|
|
20
|
+
t.string("updated_at")
|
|
21
|
+
t.string("created_by")
|
|
22
|
+
t.string("updated_by")
|
|
23
|
+
// MONGO
|
|
24
|
+
t.string("createdAt")
|
|
25
|
+
t.string("updatedAt")
|
|
26
|
+
t.string("createdBy")
|
|
27
|
+
t.string("updatedBy")
|
|
28
|
+
}
|
|
29
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module.exports = ({ strapi, nexus }) => {
|
|
2
|
+
const related = strapi.plugin('navigation').config('gql').navigationItemRelated;
|
|
3
|
+
const name = "NavigationRelated";
|
|
4
|
+
|
|
5
|
+
if (related?.length) {
|
|
6
|
+
return nexus.unionType({
|
|
7
|
+
name,
|
|
8
|
+
definition(t) {
|
|
9
|
+
t.members(...related)
|
|
10
|
+
},
|
|
11
|
+
resolveType: (item) => strapi.contentTypes[item.__contentType]?.globalId
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return nexus.objectType({
|
|
16
|
+
name,
|
|
17
|
+
definition(t) {
|
|
18
|
+
t.int("id")
|
|
19
|
+
t.string("title")
|
|
20
|
+
t.string("name")
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
type: 'content-api',
|
|
3
|
+
routes: [
|
|
4
|
+
{
|
|
5
|
+
method: "GET",
|
|
6
|
+
path: "/render/:idOrSlug",
|
|
7
|
+
handler: "navigation.render",
|
|
8
|
+
config: {
|
|
9
|
+
policies: []
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
method: "GET",
|
|
14
|
+
path: "/render/:idOrSlug/:childUIKey",
|
|
15
|
+
handler: "navigation.renderChild",
|
|
16
|
+
config: {
|
|
17
|
+
policies: []
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
package/server/routes/index.js
CHANGED