@strapi/admin 4.14.2 → 4.14.4
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/.eslintrc.js +4 -1
- package/admin/.eslintrc.js +16 -0
- package/admin/custom.d.ts +8 -0
- package/admin/src/components/AuthenticatedApp/index.js +3 -7
- package/admin/src/components/AuthenticatedApp/utils/api.js +1 -39
- package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.ts +13 -0
- package/admin/src/{hooks/useReleaseNotification/utils/api.js → components/AuthenticatedApp/utils/fetchStrapiLatestRelease.ts} +2 -3
- package/admin/src/components/{DragLayer/DragLayer.js → DragLayer.tsx} +18 -10
- package/admin/src/components/PrivateRoute.tsx +42 -0
- package/admin/src/components/Providers/index.js +2 -2
- package/admin/src/components/Theme.tsx +39 -0
- package/admin/src/components/ThemeToggleProvider.tsx +50 -0
- package/admin/src/components/{UnauthenticatedLogo/index.js → UnauthenticatedLogo.tsx} +2 -4
- package/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js +22 -3
- package/admin/src/content-manager/components/BlocksEditor/Toolbar/index.js +263 -134
- package/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js +362 -95
- package/admin/src/content-manager/components/BlocksEditor/hooks/useModifiersStore.js +15 -0
- package/admin/src/content-manager/components/BlocksEditor/index.js +99 -9
- package/admin/src/content-manager/components/BlocksEditor/plugins/index.js +4 -0
- package/admin/src/content-manager/components/BlocksEditor/plugins/withLinks.js +61 -0
- package/admin/src/content-manager/components/BlocksEditor/plugins/withStrapiSchema.js +33 -0
- package/admin/src/content-manager/components/BlocksEditor/utils/links.js +90 -0
- package/admin/src/content-manager/components/InputUID/index.js +1 -1
- package/admin/src/content-manager/hooks/useAllowedAttributes.js +9 -1
- package/admin/src/content-manager/hooks/useRelation/useRelation.js +1 -0
- package/admin/src/content-manager/pages/EditSettingsView/index.js +1 -0
- package/admin/src/content-manager/pages/EditSettingsView/utils/createPossibleMainFieldsForModelsAndComponents.js +1 -0
- package/admin/src/content-manager/pages/ListSettingsView/constants.js +1 -0
- package/admin/src/content-manager/pages/ListView/index.js +2 -1
- package/admin/src/content-manager/utils/checkIfAttributeIsDisplayable.js +1 -1
- package/admin/src/content-manager/utils/schema.js +2 -2
- package/admin/src/contexts/configuration.ts +15 -0
- package/admin/src/contexts/index.js +1 -2
- package/admin/src/contexts/themeToggle.ts +16 -0
- package/admin/src/hooks/{useConfigurations/__mocks__/index.js → __mocks__/useConfigurations.ts} +4 -2
- package/admin/src/hooks/index.js +1 -5
- package/admin/src/hooks/useConfigurations.ts +5 -0
- package/admin/src/hooks/useDebounce.ts +17 -0
- package/admin/src/hooks/useLicenseLimitNotification.ts +3 -0
- package/admin/src/hooks/useThemeToggle.ts +9 -0
- package/admin/src/pages/App/index.js +1 -1
- package/admin/src/pages/AuthPage/components/ForgotPassword/index.js +1 -1
- package/admin/src/pages/AuthPage/components/ForgotPasswordSuccess/index.js +1 -1
- package/admin/src/pages/AuthPage/components/Login/BaseLogin.js +1 -1
- package/admin/src/pages/AuthPage/components/Oops/index.js +1 -1
- package/admin/src/pages/AuthPage/components/Register/index.js +1 -1
- package/admin/src/pages/AuthPage/components/ResetPassword/index.js +1 -1
- package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +0 -2
- package/admin/src/pages/MarketplacePage/hooks/__mocks__/useNavigatorOnline.ts +1 -0
- package/admin/src/{hooks/useNavigatorOnLine/index.js → pages/MarketplacePage/hooks/useNavigatorOnline.ts} +4 -6
- package/admin/src/pages/MarketplacePage/index.js +3 -3
- package/admin/src/pages/ProfilePage/index.js +1 -1
- package/admin/src/pages/SettingsPage/components/Tokens/Regenerate/index.js +1 -1
- package/admin/src/{hooks/useRegenerate/index.js → pages/SettingsPage/hooks/useRegenerate.ts} +13 -7
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +1 -1
- package/admin/src/pages/UseCasePage/index.js +1 -1
- package/admin/src/translations/en.json +8 -0
- package/admin/tsconfig.json +5 -0
- package/build/1049.f7aed23d.chunk.js +1 -0
- package/build/{1227.969e24e6.chunk.js → 1227.f9c74718.chunk.js} +1 -1
- package/build/{1386.db9a2795.chunk.js → 1386.6b8819c6.chunk.js} +2 -2
- package/build/2224.8af54440.chunk.js +138 -0
- package/build/2225.d1bcf7e3.chunk.js +79 -0
- package/build/2379.f0baf826.chunk.js +1 -0
- package/build/{2395.f6ac2863.chunk.js → 2395.aca6ce66.chunk.js} +1 -1
- package/build/2421.a478ba24.chunk.js +105 -0
- package/build/2801.c49f88a1.chunk.js +1 -0
- package/build/{3483.f6b2439f.chunk.js → 3483.5df8e010.chunk.js} +1 -1
- package/build/3911.d4fada48.chunk.js +95 -0
- package/build/412.72afdf0c.chunk.js +689 -0
- package/build/{4174.3e13fb26.chunk.js → 4174.df9aa09a.chunk.js} +1 -1
- package/build/502.8666bbef.chunk.js +25 -0
- package/build/570.2f3b4c56.chunk.js +1 -0
- package/build/5702.5b433d50.chunk.js +1 -0
- package/build/6186.c33ce082.chunk.js +116 -0
- package/build/7464.43a4527c.chunk.js +1 -0
- package/build/7818.d2196a53.chunk.js +29 -0
- package/build/7897.5c03247b.chunk.js +25 -0
- package/build/{8276.951e198e.chunk.js → 8276.d4426fd8.chunk.js} +3 -3
- package/build/8690.33243bba.chunk.js +38 -0
- package/build/{9832.65ed5a44.chunk.js → 8743.31c921b1.chunk.js} +139 -123
- package/build/9218.8bc01ab9.chunk.js +1 -0
- package/build/Admin-authenticatedApp.27545a1b.chunk.js +112 -0
- package/build/{Admin_InternalErrorPage.b3163562.chunk.js → Admin_InternalErrorPage.b66ee9c1.chunk.js} +1 -1
- package/build/Admin_homePage.a6281dd6.chunk.js +124 -0
- package/build/Admin_marketplace.31b962b8.chunk.js +44 -0
- package/build/{Admin_pluginsPage.b9fa2947.chunk.js → Admin_pluginsPage.9217101d.chunk.js} +1 -1
- package/build/{Admin_profilePage.a4d41380.chunk.js → Admin_profilePage.680123d9.chunk.js} +2 -2
- package/build/{Admin_settingsPage.6dc2af9f.chunk.js → Admin_settingsPage.33378310.chunk.js} +1 -1
- package/build/{Upload_ConfigureTheView.cc7ca628.chunk.js → Upload_ConfigureTheView.b40eea4d.chunk.js} +1 -1
- package/build/admin-app.e8c52c37.chunk.js +36 -0
- package/build/admin-edit-roles-page.fcf056bf.chunk.js +275 -0
- package/build/{admin-edit-users.9b42cc9e.chunk.js → admin-edit-users.89efe3c4.chunk.js} +2 -2
- package/build/{admin-roles-list.cf964578.chunk.js → admin-roles-list.8b77704a.chunk.js} +3 -3
- package/build/admin-users.e3f1be14.chunk.js +19 -0
- package/build/{api-tokens-create-page.2f25ddf6.chunk.js → api-tokens-create-page.0dd63e91.chunk.js} +1 -1
- package/build/{api-tokens-edit-page.45faac16.chunk.js → api-tokens-edit-page.78d877f8.chunk.js} +1 -1
- package/build/{api-tokens-list-page.5baabf1a.chunk.js → api-tokens-list-page.ae13346c.chunk.js} +2 -2
- package/build/audit-logs-settings-page.e9c92a75.chunk.js +9 -0
- package/build/content-manager.5849dbe3.chunk.js +1226 -0
- package/build/{content-type-builder-list-view.aa8a5d1a.chunk.js → content-type-builder-list-view.3fffae65.chunk.js} +1 -1
- package/build/{content-type-builder-translation-en-json.b9e5cacd.chunk.js → content-type-builder-translation-en-json.43f9d7bc.chunk.js} +1 -1
- package/build/{content-type-builder.885f2cad.chunk.js → content-type-builder.98c71164.chunk.js} +14 -14
- package/build/{email-settings-page.6bd7b280.chunk.js → email-settings-page.ecfec9b3.chunk.js} +1 -1
- package/build/{en-json.a3973ff5.chunk.js → en-json.bd611a8e.chunk.js} +1 -1
- package/build/{i18n-settings-page.6c0157e7.chunk.js → i18n-settings-page.a9708926.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/main.3abb6f34.js +3278 -0
- package/build/{review-workflows-settings-create-view.ae369a88.chunk.js → review-workflows-settings-create-view.b7b0c6c5.chunk.js} +1 -1
- package/build/{review-workflows-settings-edit-view.9a61c69f.chunk.js → review-workflows-settings-edit-view.c331b3fe.chunk.js} +1 -1
- package/build/review-workflows-settings-list-view.70218dc1.chunk.js +75 -0
- package/build/{runtime~main.cec66cd9.js → runtime~main.450561b1.js} +1 -1
- package/build/{sso-settings-page.a29e6c38.chunk.js → sso-settings-page.1a9e7f8f.chunk.js} +1 -1
- package/build/{transfer-tokens-create-page.6e1b8cee.chunk.js → transfer-tokens-create-page.e7f541d3.chunk.js} +1 -1
- package/build/{transfer-tokens-edit-page.10bb22e2.chunk.js → transfer-tokens-edit-page.bd1276c2.chunk.js} +1 -1
- package/build/{transfer-tokens-list-page.0306652c.chunk.js → transfer-tokens-list-page.5de6bb9f.chunk.js} +2 -2
- package/build/upload-settings.97ef4c92.chunk.js +14 -0
- package/build/{upload.19e14c8e.chunk.js → upload.f08715a1.chunk.js} +1 -1
- package/build/{users-advanced-settings-page.ed69812f.chunk.js → users-advanced-settings-page.36a3c363.chunk.js} +1 -1
- package/build/users-email-settings-page.47b47962.chunk.js +149 -0
- package/build/users-providers-settings-page.1e0c8376.chunk.js +154 -0
- package/build/{users-roles-settings-page.afab5a0d.chunk.js → users-roles-settings-page.d5a8e8a1.chunk.js} +4 -4
- package/build/{webhook-edit-page.4c037da4.chunk.js → webhook-edit-page.87456194.chunk.js} +3 -3
- package/build/{webhook-list-page.56c82f4a.chunk.js → webhook-list-page.c88a382b.chunk.js} +3 -3
- package/ee/admin/hooks/{useLicenseLimitNotification.js → useLicenseLimitNotification.ts} +4 -4
- package/ee/admin/pages/AuthPage/components/Providers/index.js +1 -1
- package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +1 -3
- package/package.json +13 -12
- package/scripts/build.js +6 -2
- package/webpack.config.js +1 -0
- package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.js +0 -11
- package/admin/src/components/DragLayer/index.js +0 -1
- package/admin/src/components/GlobalStyle/index.js +0 -9
- package/admin/src/components/PrivateRoute/index.js +0 -46
- package/admin/src/components/Theme/index.js +0 -26
- package/admin/src/components/ThemeToggleProvider/index.js +0 -79
- package/admin/src/contexts/Configurations/index.js +0 -5
- package/admin/src/contexts/ThemeToggle/index.js +0 -5
- package/admin/src/hooks/useConfigurations/index.js +0 -11
- package/admin/src/hooks/useDebounce/index.js +0 -19
- package/admin/src/hooks/useLicenseLimitNotification/index.js +0 -5
- package/admin/src/hooks/useReleaseNotification/index.js +0 -31
- package/admin/src/hooks/useReleaseNotification/utils/checkLatestStrapiVersion.js +0 -11
- package/admin/src/hooks/useThemeToggle/index.js +0 -11
- package/admin/src/tsconfig.json +0 -10
- package/build/1049.acb0e730.chunk.js +0 -1
- package/build/2225.78fb9b89.chunk.js +0 -79
- package/build/2379.906334f0.chunk.js +0 -1
- package/build/2614.3e088d3e.chunk.js +0 -35
- package/build/2659.cb94f1e7.chunk.js +0 -105
- package/build/2801.2afb4757.chunk.js +0 -1
- package/build/2950.216f2e89.chunk.js +0 -1
- package/build/3021.33ad47fb.chunk.js +0 -103
- package/build/3911.488fbde3.chunk.js +0 -95
- package/build/4546.1203ac95.chunk.js +0 -1
- package/build/502.9918bff7.chunk.js +0 -1
- package/build/5158.c85f841a.chunk.js +0 -1
- package/build/6266.e8990811.chunk.js +0 -146
- package/build/7464.0280cf59.chunk.js +0 -1
- package/build/7897.4a39de37.chunk.js +0 -6
- package/build/Admin-authenticatedApp.08f32723.chunk.js +0 -112
- package/build/Admin_homePage.6cb51f18.chunk.js +0 -81
- package/build/Admin_marketplace.3eb5e132.chunk.js +0 -55
- package/build/admin-app.98cdf43a.chunk.js +0 -36
- package/build/admin-edit-roles-page.418bb1c5.chunk.js +0 -267
- package/build/admin-users.8385dd73.chunk.js +0 -11
- package/build/audit-logs-settings-page.91489670.chunk.js +0 -1
- package/build/content-manager.0d2b4a60.chunk.js +0 -1199
- package/build/main.105dcf23.js +0 -2665
- package/build/review-workflows-settings-list-view.067e0c35.chunk.js +0 -56
- package/build/upload-settings.0af6edc5.chunk.js +0 -14
- package/build/users-email-settings-page.131a00fb.chunk.js +0 -9
- package/build/users-providers-settings-page.b3dca41d.chunk.js +0 -14
|
@@ -8,20 +8,28 @@ import { withHistory } from 'slate-history';
|
|
|
8
8
|
import { Slate, withReact, ReactEditor } from 'slate-react';
|
|
9
9
|
import styled from 'styled-components';
|
|
10
10
|
|
|
11
|
+
import Hint from '../Hint';
|
|
12
|
+
|
|
11
13
|
import BlocksInput from './BlocksInput';
|
|
14
|
+
import { withLinks, withStrapiSchema } from './plugins';
|
|
12
15
|
import { BlocksToolbar } from './Toolbar';
|
|
13
16
|
|
|
14
17
|
const TypographyAsterisk = styled(Typography)`
|
|
15
18
|
line-height: 0;
|
|
16
19
|
`;
|
|
17
20
|
|
|
21
|
+
const LabelAction = styled(Box)`
|
|
22
|
+
svg path {
|
|
23
|
+
fill: ${({ theme }) => theme.colors.neutral500};
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
18
27
|
const EditorDivider = styled(Divider)`
|
|
19
28
|
background: ${({ theme }) => theme.colors.neutral200};
|
|
20
29
|
`;
|
|
21
30
|
|
|
22
31
|
const Wrapper = styled(Box)`
|
|
23
32
|
width: 100%;
|
|
24
|
-
max-height: 512px;
|
|
25
33
|
overflow: auto;
|
|
26
34
|
padding: ${({ theme }) => `${theme.spaces[3]} ${theme.spaces[4]}`};
|
|
27
35
|
font-size: ${({ theme }) => theme.fontSizes[2]};
|
|
@@ -31,10 +39,72 @@ const Wrapper = styled(Box)`
|
|
|
31
39
|
border-radius: ${({ theme }) => theme.borderRadius};
|
|
32
40
|
`;
|
|
33
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Images are void elements. They handle the rendering of their children instead of Slate.
|
|
44
|
+
* See the Slate documentation for more information:
|
|
45
|
+
* - https://docs.slatejs.org/api/nodes/element#void-vs-not-void
|
|
46
|
+
* - https://docs.slatejs.org/api/nodes/element#rendering-void-elements
|
|
47
|
+
*
|
|
48
|
+
* @param {import('slate').Editor} editor
|
|
49
|
+
*/
|
|
50
|
+
const withImages = (editor) => {
|
|
51
|
+
const { isVoid } = editor;
|
|
52
|
+
|
|
53
|
+
editor.isVoid = (element) => {
|
|
54
|
+
return element.type === 'image' ? true : isVoid(element);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return editor;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Forces an update of the Slate editor when the value prop changes from outside of Slate.
|
|
62
|
+
* The root cause is that Slate is not a controlled component: https://github.com/ianstormtaylor/slate/issues/4612
|
|
63
|
+
* Why not use JSON.stringify(value) as the key?
|
|
64
|
+
* Because it would force a rerender of the entire editor every time the user types a character.
|
|
65
|
+
* Why not use the entity id as the key, since it's unique for each locale?
|
|
66
|
+
* Because it would not solve the problem when using the "fill in from other locale" feature
|
|
67
|
+
*
|
|
68
|
+
* @param {import('slate').Descendant[]} value
|
|
69
|
+
* @returns {{
|
|
70
|
+
* key: number,
|
|
71
|
+
* incrementSlateUpdatesCount: () => void
|
|
72
|
+
* }}
|
|
73
|
+
*/
|
|
74
|
+
function useResetKey(value) {
|
|
75
|
+
// Keep track how how many times Slate detected a change from a user interaction in the editor
|
|
76
|
+
const slateUpdatesCount = React.useRef(0);
|
|
77
|
+
// Keep track of how many times the value prop was updated, whether from within editor or from outside
|
|
78
|
+
const valueUpdatesCount = React.useRef(0);
|
|
79
|
+
// Use a key to force a rerender of the Slate editor when needed
|
|
80
|
+
const [key, setKey] = React.useState(0);
|
|
81
|
+
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
valueUpdatesCount.current += 1;
|
|
84
|
+
|
|
85
|
+
// If the 2 refs are not equal, it means the value was updated from outside
|
|
86
|
+
if (valueUpdatesCount.current !== slateUpdatesCount.current) {
|
|
87
|
+
// So we change the key to force a rerender of the Slate editor,
|
|
88
|
+
// which will pick up the new value through its initialValue prop
|
|
89
|
+
setKey((previousKey) => previousKey + 1);
|
|
90
|
+
|
|
91
|
+
// Then bring the 2 refs back in sync
|
|
92
|
+
slateUpdatesCount.current = valueUpdatesCount.current;
|
|
93
|
+
}
|
|
94
|
+
}, [value]);
|
|
95
|
+
|
|
96
|
+
return { key, incrementSlateUpdatesCount: () => (slateUpdatesCount.current += 1) };
|
|
97
|
+
}
|
|
98
|
+
|
|
34
99
|
const BlocksEditor = React.forwardRef(
|
|
35
|
-
(
|
|
100
|
+
(
|
|
101
|
+
{ intlLabel, labelAction, name, disabled, required, error, value, onChange, placeholder, hint },
|
|
102
|
+
ref
|
|
103
|
+
) => {
|
|
36
104
|
const { formatMessage } = useIntl();
|
|
37
|
-
const [editor] = React.useState(() =>
|
|
105
|
+
const [editor] = React.useState(() =>
|
|
106
|
+
withReact(withStrapiSchema(withLinks(withImages(withHistory(createEditor())))))
|
|
107
|
+
);
|
|
38
108
|
|
|
39
109
|
const label = intlLabel.id
|
|
40
110
|
? formatMessage(
|
|
@@ -43,6 +113,10 @@ const BlocksEditor = React.forwardRef(
|
|
|
43
113
|
)
|
|
44
114
|
: name;
|
|
45
115
|
|
|
116
|
+
const formattedPlaceholder = placeholder
|
|
117
|
+
? formatMessage({ id: placeholder.id, defaultMessage: placeholder.defaultMessage })
|
|
118
|
+
: null;
|
|
119
|
+
|
|
46
120
|
/** Editable is not able to hold the ref, https://github.com/ianstormtaylor/slate/issues/4082
|
|
47
121
|
* so with "useImperativeHandle" we can use ReactEditor methods to expose to the parent above
|
|
48
122
|
* also not passing forwarded ref here, gives console warning.
|
|
@@ -57,10 +131,14 @@ const BlocksEditor = React.forwardRef(
|
|
|
57
131
|
[editor]
|
|
58
132
|
);
|
|
59
133
|
|
|
134
|
+
const { key, incrementSlateUpdatesCount } = useResetKey(value);
|
|
135
|
+
|
|
60
136
|
const handleSlateChange = (state) => {
|
|
61
137
|
const isAstChange = editor.operations.some((op) => op.type !== 'set_selection');
|
|
62
138
|
|
|
63
139
|
if (isAstChange) {
|
|
140
|
+
incrementSlateUpdatesCount();
|
|
141
|
+
|
|
64
142
|
onChange({
|
|
65
143
|
target: { name, value: state, type: 'blocks' },
|
|
66
144
|
});
|
|
@@ -75,20 +153,23 @@ const BlocksEditor = React.forwardRef(
|
|
|
75
153
|
{label}
|
|
76
154
|
{required && <TypographyAsterisk textColor="danger600">*</TypographyAsterisk>}
|
|
77
155
|
</Typography>
|
|
156
|
+
{labelAction && <LabelAction paddingLeft={1}>{labelAction}</LabelAction>}
|
|
78
157
|
</Flex>
|
|
79
158
|
<Slate
|
|
80
159
|
editor={editor}
|
|
81
160
|
initialValue={value || [{ type: 'paragraph', children: [{ type: 'text', text: '' }] }]}
|
|
82
161
|
onChange={handleSlateChange}
|
|
162
|
+
key={key}
|
|
83
163
|
>
|
|
84
|
-
<InputWrapper direction="column" alignItems="flex-start">
|
|
85
|
-
<BlocksToolbar />
|
|
164
|
+
<InputWrapper direction="column" alignItems="flex-start" height="512px">
|
|
165
|
+
<BlocksToolbar disabled={disabled} />
|
|
86
166
|
<EditorDivider width="100%" />
|
|
87
|
-
<Wrapper>
|
|
88
|
-
<BlocksInput
|
|
167
|
+
<Wrapper grow={1}>
|
|
168
|
+
<BlocksInput disabled={disabled} placeholder={formattedPlaceholder} />
|
|
89
169
|
</Wrapper>
|
|
90
170
|
</InputWrapper>
|
|
91
171
|
</Slate>
|
|
172
|
+
<Hint hint={hint} name={name} error={error} />
|
|
92
173
|
</Flex>
|
|
93
174
|
{error && (
|
|
94
175
|
<Box paddingTop={1}>
|
|
@@ -103,10 +184,13 @@ const BlocksEditor = React.forwardRef(
|
|
|
103
184
|
);
|
|
104
185
|
|
|
105
186
|
BlocksEditor.defaultProps = {
|
|
187
|
+
labelAction: null,
|
|
188
|
+
disabled: false,
|
|
106
189
|
required: false,
|
|
107
|
-
readOnly: false,
|
|
108
190
|
error: '',
|
|
109
191
|
value: null,
|
|
192
|
+
placeholder: null,
|
|
193
|
+
hint: null,
|
|
110
194
|
};
|
|
111
195
|
|
|
112
196
|
BlocksEditor.propTypes = {
|
|
@@ -115,12 +199,18 @@ BlocksEditor.propTypes = {
|
|
|
115
199
|
defaultMessage: PropTypes.string.isRequired,
|
|
116
200
|
values: PropTypes.object,
|
|
117
201
|
}).isRequired,
|
|
202
|
+
labelAction: PropTypes.element,
|
|
118
203
|
name: PropTypes.string.isRequired,
|
|
119
204
|
required: PropTypes.bool,
|
|
120
|
-
|
|
205
|
+
disabled: PropTypes.bool,
|
|
121
206
|
error: PropTypes.string,
|
|
122
207
|
onChange: PropTypes.func.isRequired,
|
|
123
208
|
value: PropTypes.array,
|
|
209
|
+
placeholder: PropTypes.shape({
|
|
210
|
+
id: PropTypes.string.isRequired,
|
|
211
|
+
defaultMessage: PropTypes.string.isRequired,
|
|
212
|
+
}),
|
|
213
|
+
hint: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
|
124
214
|
};
|
|
125
215
|
|
|
126
216
|
export default BlocksEditor;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Path, Transforms, Range, Point, Editor } from 'slate';
|
|
2
|
+
|
|
3
|
+
const withLinks = (editor) => {
|
|
4
|
+
const { isInline, apply, insertText } = editor;
|
|
5
|
+
|
|
6
|
+
// Links are inline elements, so we need to override the isInline method for slate
|
|
7
|
+
editor.isInline = (element) => {
|
|
8
|
+
return element.type === 'link' ? true : isInline(element);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// We keep a track of the last inserted link path
|
|
12
|
+
// So we can show the popover on the link component if that link is the last one inserted
|
|
13
|
+
editor.lastInsertedLinkPath = null;
|
|
14
|
+
|
|
15
|
+
// We intercept the apply method, so everytime we insert a new link, we save its path
|
|
16
|
+
editor.apply = (operation) => {
|
|
17
|
+
if (operation.type === 'insert_node') {
|
|
18
|
+
if (operation.node.type === 'link') {
|
|
19
|
+
editor.lastInsertedLinkPath = operation.path;
|
|
20
|
+
}
|
|
21
|
+
} else if (operation.type === 'move_node') {
|
|
22
|
+
// We need to update the last inserted link path when link is moved
|
|
23
|
+
// If link is the first word in the paragraph we dont need to update the path
|
|
24
|
+
if (Path.hasPrevious(operation.path)) {
|
|
25
|
+
editor.lastInsertedLinkPath = Path.transform(editor.lastInsertedLinkPath, operation);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
apply(operation);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
editor.insertText = (text) => {
|
|
33
|
+
// When selection is at the end of a link and user types a space, we want to break the link
|
|
34
|
+
if (Range.isCollapsed(editor.selection) && text === ' ') {
|
|
35
|
+
const linksInSelection = Array.from(
|
|
36
|
+
Editor.nodes(editor, { at: editor.selection, match: (node) => node.type === 'link' })
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const selectionIsInLink = editor.selection && linksInSelection.length > 0;
|
|
40
|
+
const selectionIsAtEndOfLink =
|
|
41
|
+
selectionIsInLink &&
|
|
42
|
+
Point.equals(editor.selection.anchor, Editor.end(editor, linksInSelection[0][1]));
|
|
43
|
+
|
|
44
|
+
if (selectionIsAtEndOfLink) {
|
|
45
|
+
Transforms.insertNodes(
|
|
46
|
+
editor,
|
|
47
|
+
{ text: ' ', type: 'text' },
|
|
48
|
+
{ at: Path.next(linksInSelection[0][1]), select: true }
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
insertText(text);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return editor;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { withLinks };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Element, Transforms } from 'slate';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This plugin is used to normalize the Slate document to match the Strapi schema.
|
|
5
|
+
*
|
|
6
|
+
* @param {import('slate').Editor} editor
|
|
7
|
+
*/
|
|
8
|
+
const withStrapiSchema = (editor) => {
|
|
9
|
+
const { normalizeNode } = editor;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* On the strapi schema, we want text nodes to have type: text
|
|
13
|
+
* By default, Slate add text nodes without type: text
|
|
14
|
+
* So we add this normalization for the cases when Slate add text nodes automatically
|
|
15
|
+
*/
|
|
16
|
+
editor.normalizeNode = (entry) => {
|
|
17
|
+
const [node, path] = entry;
|
|
18
|
+
|
|
19
|
+
if (!Element.isElement(node)) {
|
|
20
|
+
if (node.type !== 'text') {
|
|
21
|
+
Transforms.setNodes(editor, { type: 'text' }, { at: path });
|
|
22
|
+
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
normalizeNode(entry);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return editor;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { withStrapiSchema };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Transforms, Editor, Element as SlateElement, Node, Range } from 'slate';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {string} url
|
|
6
|
+
* @param {string} protocol
|
|
7
|
+
*/
|
|
8
|
+
const addProtocol = (url, protocol = 'https://') => {
|
|
9
|
+
const allowedProtocols = ['http://', 'https://', 'mailto:', 'tel:'];
|
|
10
|
+
|
|
11
|
+
if (allowedProtocols.some((allowedProtocol) => url.startsWith(allowedProtocol))) {
|
|
12
|
+
return url;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return `${protocol}${url}`;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {import('slate').Editor} editor
|
|
21
|
+
*/
|
|
22
|
+
const removeLink = (editor) => {
|
|
23
|
+
Transforms.unwrapNodes(editor, {
|
|
24
|
+
match: (node) => !Editor.isEditor(node) && SlateElement.isElement(node) && node.type === 'link',
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {import('slate').Editor} editor
|
|
31
|
+
* @param {object} link
|
|
32
|
+
* @param {string} link.url
|
|
33
|
+
*/
|
|
34
|
+
const insertLink = (editor, { url }) => {
|
|
35
|
+
if (editor.selection) {
|
|
36
|
+
// We want to remove all link on the selection
|
|
37
|
+
const linkNodes = Array.from(
|
|
38
|
+
Editor.nodes(editor, {
|
|
39
|
+
at: editor.selection,
|
|
40
|
+
match: (node) => node.type === 'link',
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
linkNodes.forEach(([, path]) => {
|
|
45
|
+
Transforms.unwrapNodes(editor, { at: path });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (Range.isCollapsed(editor.selection)) {
|
|
49
|
+
const link = { type: 'link', url: url ? addProtocol(url) : '', children: [{ text: url }] };
|
|
50
|
+
|
|
51
|
+
Transforms.insertNodes(editor, link);
|
|
52
|
+
} else {
|
|
53
|
+
Transforms.wrapNodes(
|
|
54
|
+
editor,
|
|
55
|
+
{ type: 'link', url: url ? addProtocol(url) : '' },
|
|
56
|
+
{ split: true }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {import('slate').Editor} editor
|
|
65
|
+
* @param {object} link
|
|
66
|
+
* @param {string} link.url
|
|
67
|
+
* @param {string} link.text
|
|
68
|
+
*/
|
|
69
|
+
const editLink = (editor, { url, text }) => {
|
|
70
|
+
if (editor.selection) {
|
|
71
|
+
const [linkNode, linkPath] = Editor.above(editor, { match: (node) => node.type === 'link' });
|
|
72
|
+
|
|
73
|
+
if (linkNode) {
|
|
74
|
+
Transforms.setNodes(editor, { url: addProtocol(url) }, { at: linkPath });
|
|
75
|
+
|
|
76
|
+
// If link text is different, we remove the old text and insert the new one
|
|
77
|
+
if (text !== '' && text !== Editor.string(editor, linkPath)) {
|
|
78
|
+
const linkNodeChildrens = Array.from(Node.children(editor, linkPath, { reverse: true }));
|
|
79
|
+
|
|
80
|
+
linkNodeChildrens.forEach(([, childPath]) => {
|
|
81
|
+
Transforms.removeNodes(editor, { at: childPath });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
Transforms.insertNodes(editor, [{ text }], { at: linkPath.concat(0) });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { insertLink, editLink, removeLink };
|
|
@@ -12,7 +12,7 @@ import PropTypes from 'prop-types';
|
|
|
12
12
|
import { useIntl } from 'react-intl';
|
|
13
13
|
import { useMutation, useQuery } from 'react-query';
|
|
14
14
|
|
|
15
|
-
import useDebounce from '../../../hooks/useDebounce';
|
|
15
|
+
import { useDebounce } from '../../../hooks/useDebounce';
|
|
16
16
|
|
|
17
17
|
import { FieldActionWrapper, LoadingWrapper, TextValidation } from './endActionStyle';
|
|
18
18
|
import UID_REGEX from './regex';
|
|
@@ -2,7 +2,15 @@ import { useRBACProvider, findMatchingPermissions } from '@strapi/helper-plugin'
|
|
|
2
2
|
|
|
3
3
|
import { CREATOR_FIELDS } from '../constants/attributes';
|
|
4
4
|
|
|
5
|
-
const NOT_ALLOWED_FILTERS = [
|
|
5
|
+
const NOT_ALLOWED_FILTERS = [
|
|
6
|
+
'json',
|
|
7
|
+
'component',
|
|
8
|
+
'media',
|
|
9
|
+
'richtext',
|
|
10
|
+
'dynamiczone',
|
|
11
|
+
'password',
|
|
12
|
+
'blocks',
|
|
13
|
+
];
|
|
6
14
|
const TIMESTAMPS = ['createdAt', 'updatedAt'];
|
|
7
15
|
|
|
8
16
|
export const useAllowedAttributes = (contentType, slug) => {
|
|
@@ -185,6 +185,7 @@ function ListView({
|
|
|
185
185
|
};
|
|
186
186
|
|
|
187
187
|
filter.fieldSchema.mainField = {
|
|
188
|
+
...mainField,
|
|
188
189
|
name: 'id',
|
|
189
190
|
};
|
|
190
191
|
}
|
|
@@ -720,7 +721,7 @@ function ListView({
|
|
|
720
721
|
{/* Empty content */}
|
|
721
722
|
<Table.EmptyBody
|
|
722
723
|
contentType={headerLayoutTitle}
|
|
723
|
-
|
|
724
|
+
action={getCreateAction({ variant: 'secondary' })}
|
|
724
725
|
/>
|
|
725
726
|
{/* Content */}
|
|
726
727
|
<Body.Root
|
|
@@ -5,7 +5,7 @@ const checkIfAttributeIsDisplayable = (attribute) => {
|
|
|
5
5
|
return !(attribute?.relationType ?? '').toLowerCase().includes('morph');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
return !['json', 'dynamiczone', 'richtext', 'password'].includes(type) && !!type;
|
|
8
|
+
return !['json', 'dynamiczone', 'richtext', 'password', 'blocks'].includes(type) && !!type;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export default checkIfAttributeIsDisplayable;
|
|
@@ -228,8 +228,8 @@ const createYupSchemaAttribute = (type, validations, options) => {
|
|
|
228
228
|
return true;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
// The backend validates the actual schema
|
|
232
|
-
if (!Array.isArray(value)) {
|
|
231
|
+
// The backend validates the actual schema, check if a value different than null is not an array
|
|
232
|
+
if (value && !Array.isArray(value)) {
|
|
233
233
|
return false;
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ConfigurationsContextValue {
|
|
4
|
+
logos: {
|
|
5
|
+
auth: { custom?: string; default: string };
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ConfigurationsContext = createContext<ConfigurationsContextValue>({
|
|
10
|
+
logos: {
|
|
11
|
+
auth: { default: '' },
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export { ConfigurationsContext };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { default as AdminContext } from './Admin';
|
|
2
|
-
export {
|
|
2
|
+
export { ConfigurationsContext } from './configuration';
|
|
3
3
|
export { default as PermissionsDataManagerContext } from './PermisssionsDataManagerContext';
|
|
4
|
-
export { default as ThemeToggleContext } from './ThemeToggle';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import { DefaultTheme } from 'styled-components';
|
|
4
|
+
|
|
5
|
+
export type ThemeName = 'light' | 'dark';
|
|
6
|
+
|
|
7
|
+
interface ThemeToggleContextContextValue {
|
|
8
|
+
currentTheme?: ThemeName;
|
|
9
|
+
onChangeTheme?: (nextTheme: ThemeName) => void;
|
|
10
|
+
themes?: {
|
|
11
|
+
dark: DefaultTheme;
|
|
12
|
+
light: DefaultTheme;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ThemeToggleContext = createContext<ThemeToggleContextContextValue>({});
|
package/admin/src/hooks/index.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { useConfigurations } from './useConfigurations';
|
|
2
2
|
export { useContentTypes } from './useContentTypes';
|
|
3
|
-
export { default as useLicenseLimitNotification } from './useLicenseLimitNotification';
|
|
4
3
|
export { default as useMenu } from './useMenu';
|
|
5
4
|
export { default as usePermissionsDataManager } from './usePermissionsDataManager';
|
|
6
|
-
export { default as useRegenerate } from './useRegenerate';
|
|
7
|
-
export { default as useReleaseNotification } from './useReleaseNotification';
|
|
8
5
|
export { default as useSettingsForm } from './useSettingsForm';
|
|
9
6
|
export { default as useSettingsMenu } from './useSettingsMenu';
|
|
10
|
-
export { default as useThemeToggle } from './useThemeToggle';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounce<TValue>(value: TValue, delay: number): TValue {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = React.useState(value);
|
|
5
|
+
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
const handler = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
}, delay);
|
|
10
|
+
|
|
11
|
+
return () => {
|
|
12
|
+
clearTimeout(handler);
|
|
13
|
+
};
|
|
14
|
+
}, [value, delay]);
|
|
15
|
+
|
|
16
|
+
return debouncedValue;
|
|
17
|
+
}
|
|
@@ -21,7 +21,7 @@ import { useIntl } from 'react-intl';
|
|
|
21
21
|
import { useDispatch } from 'react-redux';
|
|
22
22
|
import { Route, Switch } from 'react-router-dom';
|
|
23
23
|
|
|
24
|
-
import PrivateRoute from '../../components/PrivateRoute';
|
|
24
|
+
import { PrivateRoute } from '../../components/PrivateRoute';
|
|
25
25
|
import { ADMIN_PERMISSIONS_CE } from '../../constants';
|
|
26
26
|
import { useConfigurations } from '../../hooks';
|
|
27
27
|
import { useEnterprise } from '../../hooks/useEnterprise';
|
|
@@ -6,7 +6,7 @@ import { Formik } from 'formik';
|
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import { useIntl } from 'react-intl';
|
|
8
8
|
|
|
9
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
9
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
10
10
|
import UnauthenticatedLayout, {
|
|
11
11
|
Column,
|
|
12
12
|
LayoutContent,
|
|
@@ -4,7 +4,7 @@ import { Box, Flex, Main, Typography } from '@strapi/design-system';
|
|
|
4
4
|
import { Link } from '@strapi/helper-plugin';
|
|
5
5
|
import { useIntl } from 'react-intl';
|
|
6
6
|
|
|
7
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
7
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
8
8
|
import UnauthenticatedLayout, {
|
|
9
9
|
Column,
|
|
10
10
|
LayoutContent,
|
|
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
|
|
|
8
8
|
import { useIntl } from 'react-intl';
|
|
9
9
|
import styled from 'styled-components';
|
|
10
10
|
|
|
11
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
11
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
12
12
|
import { Column, LayoutContent } from '../../../../layouts/UnauthenticatedLayout';
|
|
13
13
|
import FieldActionWrapper from '../FieldActionWrapper';
|
|
14
14
|
|
|
@@ -4,7 +4,7 @@ import { Box, Flex, Main, Typography } from '@strapi/design-system';
|
|
|
4
4
|
import { Link, useQuery } from '@strapi/helper-plugin';
|
|
5
5
|
import { useIntl } from 'react-intl';
|
|
6
6
|
|
|
7
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
7
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
8
8
|
import UnauthenticatedLayout, {
|
|
9
9
|
Column,
|
|
10
10
|
LayoutContent,
|
|
@@ -30,7 +30,7 @@ import { useHistory } from 'react-router-dom';
|
|
|
30
30
|
import styled from 'styled-components';
|
|
31
31
|
|
|
32
32
|
import { useNpsSurveySettings } from '../../../../components/NpsSurvey/hooks/useNpsSurveySettings';
|
|
33
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
33
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
34
34
|
import UnauthenticatedLayout, { LayoutContent } from '../../../../layouts/UnauthenticatedLayout';
|
|
35
35
|
import FieldActionWrapper from '../FieldActionWrapper';
|
|
36
36
|
|
|
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
|
|
|
8
8
|
import { useIntl } from 'react-intl';
|
|
9
9
|
import styled from 'styled-components';
|
|
10
10
|
|
|
11
|
-
import Logo from '../../../../components/UnauthenticatedLogo';
|
|
11
|
+
import { Logo } from '../../../../components/UnauthenticatedLogo';
|
|
12
12
|
import UnauthenticatedLayout, {
|
|
13
13
|
Column,
|
|
14
14
|
LayoutContent,
|