strapi-plugin-navigation 2.0.3 → 2.0.6

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 CHANGED
@@ -81,7 +81,7 @@ Complete installation requirements are exact same as for Strapi itself and can b
81
81
 
82
82
  **Supported Strapi versions**:
83
83
 
84
- - Strapi v4.1.0 (recently tested)
84
+ - Strapi v4.1.5 (recently tested)
85
85
  - Strapi v4.x
86
86
 
87
87
  > This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-navigation/tree/strapi-v3).
@@ -4,13 +4,13 @@ import { Flex } from '@strapi/design-system/Flex';
4
4
  import { IconButton } from '@strapi/design-system/IconButton';
5
5
  import { Typography } from '@strapi/design-system/Typography';
6
6
  import { Icon } from '@strapi/design-system/Icon';
7
- import { Pencil, Trash, Refresh } from '@strapi/icons/';
7
+ import { Pencil, Trash, Refresh, Drag } from '@strapi/icons/';
8
8
 
9
9
  import Wrapper from './Wrapper';
10
10
  import ItemCardBadge from '../ItemCardBadge';
11
11
  import { getMessage } from '../../../utils';
12
12
 
13
- const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit, onItemRestore }) => {
13
+ const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit, onItemRestore, dragRef }) => {
14
14
  return (
15
15
  <Wrapper>
16
16
  <Flex alignItems="center">
@@ -36,7 +36,10 @@ const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit,
36
36
  <IconButton disabled={removed} onClick={onItemEdit} label="Edit" icon={<Pencil />} />
37
37
  {removed ?
38
38
  <IconButton onClick={onItemRestore} label="Restore" icon={<Refresh />} /> :
39
- <IconButton onClick={onItemRemove} label="Remove" icon={<Trash />} />
39
+ <>
40
+ <IconButton ref={dragRef} label="Drag" icon={<Drag />} />
41
+ <IconButton onClick={onItemRemove} label="Remove" icon={<Trash />} />
42
+ </>
40
43
  }
41
44
  </Flex>
42
45
  </Wrapper>
@@ -1,6 +1,8 @@
1
+ import React, { useRef, useEffect } from 'react';
1
2
  import PropTypes from 'prop-types';
2
- import React from 'react';
3
- import { isEmpty, isNumber } from 'lodash';
3
+ import { useDrag, useDrop } from 'react-dnd';
4
+ import { getEmptyImage } from 'react-dnd-html5-backend';
5
+ import { drop, isEmpty, isNumber } from 'lodash';
4
6
 
5
7
  import { Box } from '@strapi/design-system/Box';
6
8
  import { Card, CardBody } from '@strapi/design-system/Card';
@@ -18,7 +20,7 @@ import Wrapper from './Wrapper';
18
20
  import { extractRelatedItemLabel } from '../../pages/View/utils/parsers';
19
21
  import ItemCardBadge from './ItemCardBadge';
20
22
  import { ItemCardRemovedOverlay } from './ItemCardRemovedOverlay';
21
- import { getMessage } from '../../utils';
23
+ import { getMessage, ItemTypes } from '../../utils';
22
24
 
23
25
  const Item = (props) => {
24
26
  const {
@@ -33,6 +35,7 @@ const Item = (props) => {
33
35
  onItemRemove,
34
36
  onItemRestore,
35
37
  onItemEdit,
38
+ onItemReOrder,
36
39
  error,
37
40
  displayChildren,
38
41
  config = {},
@@ -60,73 +63,124 @@ const Item = (props) => {
60
63
  const relatedTypeLabel = relatedRef?.labelSingular;
61
64
  const relatedBadgeColor = isPublished ? 'success' : 'secondary';
62
65
 
66
+ const dragRef = useRef(null);
67
+ const dropRef = useRef(null);
68
+ const previewRef = useRef(null);
69
+
70
+ const [, drop] = useDrop({
71
+ accept: `${ItemTypes.NAVIGATION_ITEM}_${levelPath}`,
72
+ hover(hoveringItem, monitor) {
73
+ const dragIndex = hoveringItem.order;
74
+ const dropIndex = item.order;
75
+
76
+ // Don't replace items with themselves
77
+ if (dragIndex === dropIndex) {
78
+ return;
79
+ }
80
+
81
+ const hoverBoundingRect = dropRef.current.getBoundingClientRect();
82
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
83
+ const clientOffset = monitor.getClientOffset();
84
+ const hoverClientY = clientOffset.y - hoverBoundingRect.top;
85
+
86
+ // Place the hovering item before or after the drop target
87
+ const isAfter = hoverClientY > hoverMiddleY;
88
+ const newOrder = isAfter ? item.order + 0.5 : item.order - 0.5;
89
+
90
+ onItemReOrder({ ...hoveringItem }, newOrder);
91
+ },
92
+ collect: monitor => ({
93
+ isOverCurrent: monitor.isOver({ shallow: true }),
94
+ })
95
+ });
96
+
97
+ const [{ isDragging }, drag, dragPreview] = useDrag({
98
+ type: `${ItemTypes.NAVIGATION_ITEM}_${levelPath}`,
99
+ item: () => {
100
+ return { ...item };
101
+ },
102
+ collect: monitor => ({
103
+ isDragging: monitor.isDragging(),
104
+ }),
105
+ });
106
+
107
+ const refs = {
108
+ dragRef: drag(dragRef),
109
+ dropRef: drop(dropRef),
110
+ previewRef: dragPreview(previewRef),
111
+ }
112
+
63
113
  return (
64
- <Wrapper level={level} isLast={isLast}>
114
+ <Wrapper level={level} isLast={isLast} style={{ opacity: isDragging ? 0.2 : 1 }} ref={refs ? refs.dropRef : null} >
65
115
  <Card style={{ width: "728px", zIndex: 1, position: "relative", overflow: 'hidden' }}>
66
- { removed && (<ItemCardRemovedOverlay />) }
67
- <CardBody>
68
- <ItemCardHeader
69
- title={title}
70
- path={isExternal ? externalPath : absolutePath}
71
- icon={isExternal ? Earth : LinkIcon }
72
- onItemRemove={() => onItemRemove({
73
- ...item,
74
- relatedRef,
75
- })}
76
- onItemEdit={() => onItemEdit({
77
- ...item,
78
- isMenuAllowedLevel,
79
- isParentAttachedToMenu,
80
- }, levelPath, isParentAttachedToMenu)}
81
- onItemRestore={() => onItemRestore({
82
- ...item,
83
- relatedRef,
84
- })}
85
- removed={removed}
86
- />
87
- </CardBody>
88
- <Divider />
89
- { !isExternal && (<CardBody style={{ margin: '8px' }}>
90
- <Flex style={{ width: '100%' }} direction="row" alignItems="center" justifyContent="space-between">
91
- <TextButton
92
- disabled={removed}
93
- startIcon={<Plus />}
94
- onClick={(e) => onItemLevelAdd(e, viewId, isNextMenuAllowedLevel, absolutePath, menuAttached)}
95
- >
96
- <Typography variant="pi" fontWeight="bold" textColor={removed ? "neutral600" : "primary600"}>
97
- {getMessage("components.navigationItem.action.newItem")}
98
- </Typography>
99
- </TextButton>
100
- { relatedItemLabel && (<Box>
116
+ {removed && (<ItemCardRemovedOverlay />)}
117
+ <div ref={refs.previewRef}>
118
+ <CardBody>
119
+ <ItemCardHeader
120
+ title={title}
121
+ path={isExternal ? externalPath : absolutePath}
122
+ icon={isExternal ? Earth : LinkIcon}
123
+ onItemRemove={() => onItemRemove({
124
+ ...item,
125
+ relatedRef,
126
+ })}
127
+ onItemEdit={() => onItemEdit({
128
+ ...item,
129
+ isMenuAllowedLevel,
130
+ isParentAttachedToMenu,
131
+ }, levelPath, isParentAttachedToMenu)}
132
+ onItemRestore={() => onItemRestore({
133
+ ...item,
134
+ relatedRef,
135
+ })}
136
+ dragRef={refs.dragRef}
137
+ removed={removed}
138
+ />
139
+ </CardBody>
140
+ <Divider />
141
+ {!isExternal && (<CardBody style={{ margin: '8px' }}>
142
+ <Flex style={{ width: '100%' }} direction="row" alignItems="center" justifyContent="space-between">
143
+ <TextButton
144
+ disabled={removed}
145
+ startIcon={<Plus />}
146
+ onClick={(e) => onItemLevelAdd(e, viewId, isNextMenuAllowedLevel, absolutePath, menuAttached)}
147
+ >
148
+ <Typography variant="pi" fontWeight="bold" textColor={removed ? "neutral600" : "primary600"}>
149
+ {getMessage("components.navigationItem.action.newItem")}
150
+ </Typography>
151
+ </TextButton>
152
+ {relatedItemLabel && (<Box>
101
153
  <ItemCardBadge
102
154
  borderColor={`${relatedBadgeColor}200`}
103
155
  backgroundColor={`${relatedBadgeColor}100`}
104
156
  textColor={`${relatedBadgeColor}600`}
105
157
  className="action"
106
158
  small
107
- >
108
- {getMessage({
109
- id: `components.navigationItem.badge.${isPublished ? 'published' : 'draft'}`, props: {
110
- type: relatedTypeLabel
111
- }
112
- })}
159
+ >
160
+ {getMessage({
161
+ id: `components.navigationItem.badge.${isPublished ? 'published' : 'draft'}`, props: {
162
+ type: relatedTypeLabel
163
+ }
164
+ })}
113
165
  </ItemCardBadge>
114
166
  <Typography variant="pi" fontWeight="bold" textColor="neutral600">
115
- { relatedItemLabel }
116
- <Link
117
- to={`/content-manager/collectionType/${relatedRef?.__collectionUid}/${relatedRef?.id}`}
167
+ {relatedItemLabel}
168
+ <Link
169
+ to={`/content-manager/collectionType/${relatedRef?.__collectionUid}/${relatedRef?.id}`}
118
170
  endIcon={<ArrowRight />}>&nbsp;</Link>
119
171
  </Typography>
120
172
  </Box>)
121
- }
122
- </Flex>
123
- </CardBody>)}
173
+ }
174
+ </Flex>
175
+ </CardBody>)}
176
+ </div>
124
177
  </Card>
125
178
  {hasChildren && !removed && <List
126
179
  onItemLevelAdd={onItemLevelAdd}
127
180
  onItemRemove={onItemRemove}
128
181
  onItemEdit={onItemEdit}
129
182
  onItemRestore={onItemRestore}
183
+ onItemReOrder={onItemReOrder}
130
184
  error={error}
131
185
  allowedLevels={allowedLevels}
132
186
  isParentAttachedToMenu={menuAttached}
@@ -159,9 +213,10 @@ Item.propTypes = {
159
213
  onItemRestore: PropTypes.func.isRequired,
160
214
  onItemLevelAdd: PropTypes.func.isRequired,
161
215
  onItemRemove: PropTypes.func.isRequired,
216
+ onItemReOrder: PropTypes.func.isRequired,
162
217
  config: PropTypes.shape({
163
- contentTypes: PropTypes.array.isRequired,
164
- contentTypesNameFields: PropTypes.object.isRequired,
218
+ contentTypes: PropTypes.array.isRequired,
219
+ contentTypesNameFields: PropTypes.object.isRequired,
165
220
  }).isRequired
166
221
  };
167
222
 
@@ -15,6 +15,7 @@ const List = ({
15
15
  onItemLevelAdd,
16
16
  onItemRemove,
17
17
  onItemRestore,
18
+ onItemReOrder,
18
19
  displayFlat,
19
20
  contentTypes,
20
21
  contentTypesNameFields,
@@ -36,6 +37,7 @@ const List = ({
36
37
  onItemLevelAdd={onItemLevelAdd}
37
38
  onItemRemove={onItemRemove}
38
39
  onItemEdit={onItemEdit}
40
+ onItemReOrder={onItemReOrder}
39
41
  error={error}
40
42
  displayChildren={displayFlat}
41
43
  config={{
@@ -57,6 +59,7 @@ List.propTypes = {
57
59
  onItemRemove: PropTypes.func.isRequired,
58
60
  onItemRestore: PropTypes.func.isRequired,
59
61
  onItemRestore: PropTypes.func.isRequired,
62
+ onItemReOrder: PropTypes.func.isRequired,
60
63
  contentTypes: PropTypes.array.isRequired,
61
64
  contentTypesNameFields: PropTypes.object.isRequired
62
65
  };
@@ -29,7 +29,7 @@ const Search = ({ value, setValue }) => {
29
29
  onChange={(e) => setValue(e.target.value)}
30
30
  clearLabel="Clearing the search"
31
31
  placeholder={formatMessage({
32
- id: getTradId('popup.item.form.audience.placeholder'),
32
+ id: getTradId('pages.main.search.placeholder'),
33
33
  defaultMessage: 'Type to start searching...',
34
34
  })}
35
35
  >
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Formik } from 'formik';
3
- import { isEmpty, capitalize, isEqual } from 'lodash';
3
+ import { isEmpty, capitalize, isEqual, orderBy } from 'lodash';
4
4
 
5
5
  import {
6
6
  CheckPermissions,
@@ -8,9 +8,11 @@ import {
8
8
  Form,
9
9
  useOverlayBlocker,
10
10
  useAutoReloadOverlayBlocker,
11
+ SettingsPageTitle,
11
12
  } from '@strapi/helper-plugin';
12
13
  import { Main } from '@strapi/design-system/Main';
13
14
  import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
15
+ import { Accordion, AccordionToggle, AccordionContent, AccordionGroup } from '@strapi/design-system/Accordion';
14
16
  import { Button } from '@strapi/design-system/Button';
15
17
  import { Box } from '@strapi/design-system/Box';
16
18
  import { Stack } from '@strapi/design-system/Stack';
@@ -19,13 +21,8 @@ import { Grid, GridItem } from '@strapi/design-system/Grid';
19
21
  import { ToggleInput } from '@strapi/design-system/ToggleInput';
20
22
  import { NumberInput } from '@strapi/design-system/NumberInput';
21
23
  import { Select, Option } from '@strapi/design-system/Select';
22
- import { Check, Refresh, Play } from '@strapi/icons';
23
- import { SettingsPageTitle } from '@strapi/helper-plugin';
24
- import {
25
- Card,
26
- CardBody,
27
- CardContent,
28
- } from '@strapi/design-system/Card';
24
+ import { Tooltip } from '@strapi/design-system/Tooltip';
25
+ import { Check, Refresh, Play, Information } from '@strapi/icons';
29
26
 
30
27
  import permissions from '../../permissions';
31
28
  import useNavigationConfig from '../../hooks/useNavigationConfig';
@@ -40,20 +37,28 @@ const SettingsPage = () => {
40
37
  const { lockAppWithAutoreload, unlockAppWithAutoreload } = useAutoReloadOverlayBlocker();
41
38
  const [isRestorePopupOpen, setIsRestorePopupOpen] = useState(false);
42
39
  const [isRestartRequired, setIsRestartRequired] = useState(false);
40
+ const [contentTypeExpanded, setContentTypeExpanded] = useState(undefined);
43
41
  const { data: navigationConfigData, isLoading: isConfigLoading, err: configErr, submitMutation, restoreMutation, restartMutation } = useNavigationConfig();
44
42
  const { data: allContentTypesData, isLoading: isContentTypesLoading, err: contentTypesErr } = useAllContentTypes();
45
43
  const isLoading = isConfigLoading || isContentTypesLoading;
46
44
  const isError = configErr || contentTypesErr;
47
-
45
+ const boxDefaultProps = {
46
+ background: "neutral0",
47
+ hasRadius: true,
48
+ shadow: "filterShadow",
49
+ padding: 6,
50
+ };
51
+
48
52
  const preparePayload = ({ selectedContentTypes, nameFields, audienceFieldChecked, allowedLevels }) => ({
49
53
  contentTypes: selectedContentTypes,
50
54
  contentTypesNameFields: nameFields,
51
55
  additionalFields: audienceFieldChecked ? [navigationItemAdditionalFields.AUDIENCE] : [],
52
56
  allowedLevels: allowedLevels,
53
57
  gql: {
54
- navigationItemRelated: selectedContentTypes.map(uid => allContentTypes.find(ct => ct.uid === uid).info.displayName)
58
+ navigationItemRelated: selectedContentTypes.map(uid => allContentTypes.find(ct => ct.uid === uid).info.displayName.replace(/\s+/g, ''))
55
59
  }
56
- })
60
+ });
61
+
57
62
  const onSave = async (form) => {
58
63
  lockApp();
59
64
  const payload = preparePayload(form);
@@ -82,6 +87,7 @@ const SettingsPage = () => {
82
87
  unlockAppWithAutoreload();
83
88
  };
84
89
  const handleRestartDiscard = () => setIsRestartRequired(false);
90
+ const handleSetContentTypeExpanded = key => setContentTypeExpanded(key === contentTypeExpanded ? undefined : key);
85
91
 
86
92
  const prepareNameFieldFor = (uid, current, value) => ({
87
93
  ...current,
@@ -145,12 +151,7 @@ const SettingsPage = () => {
145
151
  onClose={handleRestartDiscard}>
146
152
  {getMessage('pages.settings.actions.restart.alert.description')}
147
153
  </RestartAlert>)}
148
- <Box
149
- background="neutral0"
150
- hasRadius
151
- shadow="filterShadow"
152
- padding={6}
153
- >
154
+ <Box {...boxDefaultProps} >
154
155
  <Stack size={4}>
155
156
  <Typography variant="delta" as="h2">
156
157
  {getMessage('pages.settings.general.title')}
@@ -172,31 +173,69 @@ const SettingsPage = () => {
172
173
  {allContentTypes.map((item) => <Option key={item.uid} value={item.uid}>{item.info.displayName}</Option>)}
173
174
  </Select>
174
175
  </GridItem>
175
- <GridItem col={3} s={6} xs={12}>
176
- <NumberInput
177
- name="allowedLevels"
178
- label={getMessage('pages.settings.form.allowedLevels.label')}
179
- placeholder={getMessage('pages.settings.form.allowedLevels.placeholder')}
180
- hint={getMessage('pages.settings.form.allowedLevels.hint')}
181
- onValueChange={(value) => setFieldValue('allowedLevels', value, false)}
182
- value={values.allowedLevels}
183
- disabled={isRestartRequired}
184
- />
185
- </GridItem>
176
+ {!isEmpty(values.selectedContentTypes) && (
177
+ <GridItem col={12}>
178
+ <AccordionGroup
179
+ label={getMessage('pages.settings.form.contentTypesSettings.label')}
180
+ labelAction={<Tooltip description={getMessage('pages.settings.form.contentTypesSettings.tooltip')}>
181
+ <Information aria-hidden={true} />
182
+ </Tooltip>}>
183
+ {orderBy(values.selectedContentTypes).map(uid => {
184
+ const { attributes, info: { displayName } } = allContentTypes.find(item => item.uid == uid);
185
+ const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
186
+ const key = `collectionSettings-${uid}`;
187
+ return (<Accordion
188
+ expanded={contentTypeExpanded === key}
189
+ toggle={() => handleSetContentTypeExpanded(key)}
190
+ key={key}
191
+ id={key}
192
+ size="S">
193
+ <AccordionToggle title={displayName} togglePosition="left" />
194
+ <AccordionContent>
195
+ <Box padding={6}>
196
+ <Stack size={4}>
197
+ <Select
198
+ name={`collectionSettings-${uid}-entryLabel`}
199
+ label={getMessage('pages.settings.form.nameField.label')}
200
+ hint={getMessage('pages.settings.form.nameField.hint')}
201
+ placeholder={getMessage('pages.settings.form.nameField.placeholder')}
202
+ onClear={() => null}
203
+ value={values.nameFields[uid] || []}
204
+ onChange={(value) => setFieldValue('nameFields', prepareNameFieldFor(uid, values.nameFields, value))}
205
+ multi
206
+ withTags
207
+ disabled={isRestartRequired}
208
+ >
209
+ {stringAttributes.map(key =>
210
+ (<Option key={uid + key} value={key}>{capitalize(key.split('_').join(' '))}</Option>))}
211
+ </Select>
212
+ </Stack>
213
+ </Box>
214
+ </AccordionContent>
215
+ </Accordion>);
216
+ })}
217
+ </AccordionGroup>
218
+ </GridItem>)}
186
219
  </Grid>
187
220
  </Stack>
188
221
  </Box>
189
- <Box
190
- background="neutral0"
191
- hasRadius
192
- shadow="filterShadow"
193
- padding={6}
194
- >
222
+ <Box {...boxDefaultProps} >
195
223
  <Stack size={4}>
196
224
  <Typography variant="delta" as="h2">
197
225
  {getMessage('pages.settings.additional.title')}
198
226
  </Typography>
199
227
  <Grid gap={4}>
228
+ <GridItem col={3} s={6} xs={12}>
229
+ <NumberInput
230
+ name="allowedLevels"
231
+ label={getMessage('pages.settings.form.allowedLevels.label')}
232
+ placeholder={getMessage('pages.settings.form.allowedLevels.placeholder')}
233
+ hint={getMessage('pages.settings.form.allowedLevels.hint')}
234
+ onValueChange={(value) => setFieldValue('allowedLevels', value, false)}
235
+ value={values.allowedLevels}
236
+ disabled={isRestartRequired}
237
+ />
238
+ </GridItem>
200
239
  <GridItem col={6} s={12} xs={12}>
201
240
  <ToggleInput
202
241
  name="audienceFieldChecked"
@@ -212,62 +251,7 @@ const SettingsPage = () => {
212
251
  </Grid>
213
252
  </Stack>
214
253
  </Box>
215
- {!isEmpty(values.selectedContentTypes) && (
216
- <Box
217
- background="neutral0"
218
- hasRadius
219
- shadow="filterShadow"
220
- padding={6}
221
- >
222
- <Stack size={4}>
223
- <Typography variant="delta" as="h2">
224
- {getMessage('pages.settings.nameField.title')}
225
- </Typography>
226
- <Grid gap={4}>
227
- {values.selectedContentTypes.map(uid => {
228
- const { attributes, info: { displayName } } = allContentTypes.find(item => item.uid == uid);
229
- const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
230
-
231
- return !isEmpty(stringAttributes) && (
232
- <GridItem key={`collectionSettings-${uid}`} col={6} s={12} xs={12}>
233
- <Card background="primary100" borderColor="primary200">
234
- <CardBody>
235
- <CardContent style={{ width: '100%' }}>
236
- <Stack size={4}>
237
- <Typography variant="epsilon" fontWeight="semibold" as="h3">{displayName}</Typography>
238
- <Select
239
- name={`collectionSettings-${uid}-entryLabel`}
240
- label={getMessage('pages.settings.form.nameField.label')}
241
- hint={getMessage('pages.settings.form.nameField.hint')}
242
- placeholder={getMessage('pages.settings.form.nameField.placeholder')}
243
- onClear={() => null}
244
- value={values.nameFields[uid] || []}
245
- onChange={(value) => setFieldValue('nameFields', prepareNameFieldFor(uid, values.nameFields, value))}
246
- multi
247
- withTags
248
- disabled={isRestartRequired}
249
- >
250
- {stringAttributes.map(key =>
251
- (<Option key={uid + key} value={key}>{capitalize(key.split('_').join(' '))}</Option>))}
252
- </Select>
253
- </Stack>
254
- </CardContent>
255
- </CardBody>
256
- </Card>
257
- </GridItem>
258
- );
259
- })
260
- }
261
- </Grid>
262
- </Stack>
263
- </Box>
264
- )}
265
- <Box
266
- background="neutral0"
267
- hasRadius
268
- shadow="filterShadow"
269
- padding={6}
270
- >
254
+ <Box {...boxDefaultProps} >
271
255
  <Stack size={4}>
272
256
  <Typography variant="delta" as="h2">
273
257
  {getMessage('pages.settings.restoring.title')}
@@ -308,4 +292,4 @@ const SettingsPage = () => {
308
292
  }
309
293
 
310
294
 
311
- export default SettingsPage;
295
+ export default SettingsPage;
@@ -80,8 +80,10 @@ const NavigationItemForm = ({
80
80
  const sanitizedType = purePayload.type || navigationItemType.INTERNAL;
81
81
  const relatedId = related
82
82
  const relatedCollectionType = relatedType;
83
+ const title = payload.title || relatedSelectOptions.find(v => v.key == relatedId)?.label
83
84
  return {
84
85
  ...purePayload,
86
+ title,
85
87
  menuAttached: isNil(menuAttached) ? false : menuAttached,
86
88
  type: sanitizedType,
87
89
  path: sanitizedType === navigationItemType.INTERNAL ? purePayload.path : undefined,
@@ -1,16 +1,16 @@
1
1
 
2
2
 
3
3
  import React from 'react';
4
- import { ButtonText } from '@strapi/design-system/Text';
4
+ import { Typography } from '@strapi/design-system/Typography';
5
5
  import { ModalHeader } from '@strapi/design-system/ModalLayout';
6
6
  import { getMessage } from '../../../../utils';
7
7
 
8
8
  export const NavigationItemPopupHeader = ({isNewItem}) => {
9
9
  return (
10
10
  <ModalHeader>
11
- <ButtonText textColor="neutral800" as="h2" id="asset-dialog-title">
11
+ <Typography variant="omega" fontWeight="bold" textColor="neutral800" as="h2" id="asset-dialog-title">
12
12
  {getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
13
- </ButtonText>
13
+ </Typography>
14
14
  </ModalHeader>
15
15
  );
16
16
  };
@@ -120,6 +120,13 @@ const View = () => {
120
120
  }, []);
121
121
  const filteredList = !isSearchEmpty ? filteredListFactory(changedActiveNavigation.items, (item) => item?.title.includes(searchValue)) : [];
122
122
 
123
+ const handleItemReOrder = (item, newOrder) => {
124
+ handleSubmitNavigationItem({
125
+ ...item,
126
+ order: newOrder,
127
+ })
128
+ }
129
+
123
130
  const handleItemRemove = (item) => {
124
131
  handleSubmitNavigationItem({
125
132
  ...item,
@@ -206,6 +213,7 @@ const View = () => {
206
213
  onItemRemove={handleItemRemove}
207
214
  onItemEdit={handleItemEdit}
208
215
  onItemRestore={handleItemRestore}
216
+ onItemReOrder={handleItemReOrder}
209
217
  displayFlat={!isSearchEmpty}
210
218
  root
211
219
  error={error}
@@ -43,6 +43,7 @@
43
43
  "notification.navigation.item.relation": "Entity relation does not exist!",
44
44
  "notification.navigation.item.relation.status.draft": "draft",
45
45
  "notification.navigation.item.relation.status.published": "published",
46
+ "pages.main.search.placeholder": "Type to start searching...",
46
47
  "pages.settings.general.title": "General settings",
47
48
  "pages.settings.additional.title": "Additional settings",
48
49
  "pages.settings.nameField.title": "Content types settings",
@@ -80,6 +81,8 @@
80
81
  "pages.settings.form.nameField.label": "Name fields",
81
82
  "pages.settings.form.nameField.placeholder": "Select at least one or leave empty to apply defaults",
82
83
  "pages.settings.form.nameField.hint": "If left empty name field is going to take following ordered fields: \"title\", \"subject\" and \"name\"",
84
+ "pages.settings.form.contentTypesSettings.label": "Content types",
85
+ "pages.settings.form.contentTypesSettings.tooltip": "Custom configuration per content type",
83
86
  "components.navigationItem.action.newItem": "Add nested item",
84
87
  "components.navigationItem.badge.removed": "Removed",
85
88
  "components.navigationItem.badge.draft": "{type}: Draft",
@@ -3,7 +3,7 @@ import { isString } from 'lodash';
3
3
 
4
4
  import pluginId from '../pluginId';
5
5
 
6
- const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
6
+ export const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
7
7
  const { formatMessage } = useIntl();
8
8
  let formattedId = ''
9
9
  if (isString(input)) {
@@ -17,4 +17,6 @@ const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
17
17
  }, input?.props || undefined)
18
18
  };
19
19
 
20
- export { getMessage };
20
+ export const ItemTypes = {
21
+ NAVIGATION_ITEM: 'navigationItem'
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.0.3",
3
+ "version": "2.0.6",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
6
  "name": "navigation",
@@ -17,9 +17,8 @@
17
17
  "test:unit": "jest --verbose --coverage"
18
18
  },
19
19
  "dependencies": {
20
- "@strapi/utils": "^4.1.0",
20
+ "@strapi/utils": "^4.1.5",
21
21
  "uuid": "^8.3.0",
22
- "bad-words": "^3.0.3",
23
22
  "lodash": "^4.17.11",
24
23
  "react": "^16.9.0",
25
24
  "react-dom": "^16.9.0",
@@ -13,7 +13,7 @@ module.exports = ({ nexus }) =>
13
13
  t.int("parent")
14
14
  t.int("master")
15
15
  t.list.field("items", { type: 'NavigationItem' })
16
- t.list.field("related", { type: 'NavigationRelated' })
16
+ t.field("related", { type: 'NavigationRelated' })
17
17
  t.list.string("audience")
18
18
  // SQL
19
19
  t.string("created_at")