@strapi/admin 4.14.5 → 4.15.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/admin/src/StrapiApp.js +13 -12
- package/admin/src/components/AuthenticatedApp.tsx +187 -0
- package/admin/src/components/ConfigurationProvider.tsx +2 -1
- package/admin/src/components/GuidedTour/Homepage.tsx +111 -0
- package/admin/src/components/GuidedTour/Modal.tsx +303 -0
- package/admin/src/components/GuidedTour/Ornaments.tsx +74 -0
- package/admin/src/components/GuidedTour/Provider.tsx +253 -0
- package/admin/src/components/GuidedTour/{layout.js → constants.ts} +13 -3
- package/admin/src/components/LanguageProvider.tsx +1 -0
- package/admin/src/components/Providers.tsx +125 -0
- package/admin/src/components/RBACProvider.tsx +124 -0
- package/admin/src/components/Theme.tsx +4 -2
- package/admin/src/components/ThemeToggleProvider.tsx +23 -9
- package/admin/src/components/__mocks__/{LanguageProvider.js → LanguageProvider.ts} +2 -0
- package/admin/src/{constants.js → constants.ts} +48 -0
- package/admin/src/content-manager/components/BlocksEditor/Toolbar/index.js +75 -51
- package/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js +72 -14
- package/admin/src/content-manager/pages/App/selectors.js +1 -1
- package/admin/src/content-manager/pages/App/useContentManagerInitData.js +3 -1
- package/admin/src/content-manager/pages/EditView/selectors.js +1 -1
- package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +3 -1
- package/admin/src/content-manager/pages/ListView/components/Body/index.js +53 -56
- package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +5 -3
- package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +1 -1
- package/admin/src/content-manager/pages/ListView/index.js +33 -50
- package/admin/src/content-manager/pages/ListView/selectors.js +1 -1
- package/admin/src/contexts/admin.ts +1 -0
- package/admin/src/contexts/apiTokenPermissions.tsx +64 -0
- package/admin/src/contexts/themeToggle.ts +3 -1
- package/admin/src/core/store/configure.ts +91 -0
- package/admin/src/core/store/hooks.ts +15 -0
- package/admin/src/hooks/index.js +0 -1
- package/admin/src/hooks/{useContentTypes/useContentTypes.js → useContentTypes.ts} +39 -16
- package/admin/src/hooks/useSettingsForm/index.js +14 -2
- package/admin/src/hooks/useSettingsMenu/constants.js +39 -0
- package/admin/src/index.js +2 -4
- package/admin/src/layouts/{AppLayout/index.js → AppLayout.tsx} +7 -10
- package/admin/src/layouts/UnauthenticatedLayout.tsx +77 -0
- package/admin/src/pages/Admin/index.js +11 -5
- package/admin/src/pages/App/index.js +7 -4
- package/admin/src/pages/App/selectors.js +1 -1
- package/admin/src/pages/AuthPage/components/ForgotPassword/index.js +2 -1
- package/admin/src/pages/AuthPage/components/ForgotPasswordSuccess/index.js +2 -1
- package/admin/src/pages/AuthPage/components/Login/index.js +1 -1
- package/admin/src/pages/AuthPage/components/Oops/index.js +2 -1
- package/admin/src/pages/AuthPage/components/Register/index.js +1 -1
- package/admin/src/pages/AuthPage/components/ResetPassword/index.js +2 -1
- package/admin/src/pages/AuthPage/index.js +2 -3
- package/admin/src/pages/HomePage/index.js +6 -3
- package/admin/src/pages/{InternalErrorPage/index.js → InternalErrorPage.tsx} +10 -6
- package/admin/src/pages/{NotFoundPage/index.js → NotFoundPage.tsx} +9 -7
- package/admin/src/pages/ProfilePage/components/Preferences/index.js +23 -9
- package/admin/src/pages/ProfilePage/index.js +1 -1
- package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +20 -0
- package/admin/src/pages/SettingsPage/constants.js +33 -0
- package/admin/src/pages/SettingsPage/index.js +2 -2
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +2 -2
- package/admin/src/pages/SettingsPage/pages/AuditLogs/SalesPage.js +50 -0
- package/admin/src/pages/SettingsPage/pages/ReviewWorkflows/SalesPage.js +53 -0
- package/admin/src/pages/SettingsPage/pages/SingleSignOn/SalesPage.js +53 -0
- package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +2 -1
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/DynamicTable/TableRows/index.js +1 -1
- package/admin/src/pages/{UseCasePage/index.js → UseCasePage.tsx} +10 -12
- package/admin/src/translations/en.json +5 -0
- package/admin/src/utils/createRoute.tsx +54 -0
- package/admin/src/utils/formatAPIErrors.ts +18 -0
- package/admin/src/utils/getFullName.ts +3 -0
- package/admin/src/utils/{uniqueAdminHash.js → hashAdminUserEmail.ts} +6 -3
- package/admin/src/utils/makeUniqueRoutes.ts +11 -0
- package/build/{1049.9236e785.chunk.js → 1049.ecc10c97.chunk.js} +1 -1
- package/build/1217.96155682.chunk.js +35 -0
- package/build/{1227.e0f7447b.chunk.js → 1227.947ceaf9.chunk.js} +1 -1
- package/build/1306.2699df52.chunk.js +79 -0
- package/build/{1386.07f2bbb3.chunk.js → 1386.eabd8a1e.chunk.js} +1 -1
- package/build/{2379.b0bc4013.chunk.js → 2379.7ce8e110.chunk.js} +1 -1
- package/build/{2395.d37b1025.chunk.js → 2395.acb961a8.chunk.js} +3 -3
- package/build/{2801.12522720.chunk.js → 2801.4711ea5a.chunk.js} +1 -1
- package/build/{3019.0d74d080.chunk.js → 3019.fde2e1be.chunk.js} +2 -2
- package/build/3460.8644e608.chunk.js +146 -0
- package/build/{3483.8f1b25f8.chunk.js → 3483.db8c1520.chunk.js} +1 -1
- package/build/{4174.2c4f958e.chunk.js → 4174.49cedb6a.chunk.js} +1 -1
- package/build/4732.149f5f8f.chunk.js +1 -0
- package/build/{502.b845473a.chunk.js → 502.f536f78b.chunk.js} +1 -1
- package/build/{7464.91341b4f.chunk.js → 7464.579564ac.chunk.js} +1 -1
- package/build/7811.fdbe09af.chunk.js +103 -0
- package/build/{7897.dffa5ad5.chunk.js → 7897.63ba0a00.chunk.js} +1 -1
- package/build/{8276.e9698944.chunk.js → 8276.9abe4679.chunk.js} +3 -3
- package/build/8773.ee67141c.chunk.js +48 -0
- package/build/9077.2cc01ac8.chunk.js +105 -0
- package/build/{9218.306ad178.chunk.js → 9218.b2d367f8.chunk.js} +1 -1
- package/build/Admin-authenticatedApp.059dc48f.chunk.js +79 -0
- package/build/Admin_InternalErrorPage.06eeef20.chunk.js +1 -0
- package/build/Admin_homePage.56b9eb3f.chunk.js +81 -0
- package/build/{Admin_marketplace.0db78604.chunk.js → Admin_marketplace.d693a435.chunk.js} +1 -1
- package/build/{Admin_pluginsPage.1083f7f0.chunk.js → Admin_pluginsPage.ae2c872a.chunk.js} +1 -1
- package/build/Admin_profilePage.89099d5b.chunk.js +13 -0
- package/build/Admin_settingsPage.88c45586.chunk.js +12 -0
- package/build/{Upload_ConfigureTheView.3cfeb108.chunk.js → Upload_ConfigureTheView.44f28145.chunk.js} +1 -1
- package/build/admin-app.990e112f.chunk.js +69 -0
- package/build/{admin-edit-roles-page.556fac52.chunk.js → admin-edit-roles-page.4e1eb4a9.chunk.js} +3 -3
- package/build/admin-edit-users.5b91404e.chunk.js +10 -0
- package/build/{admin-roles-list.15918328.chunk.js → admin-roles-list.89dd94fe.chunk.js} +1 -1
- package/build/{admin-users.74fddc87.chunk.js → admin-users.7be4fc5f.chunk.js} +2 -2
- package/build/{api-tokens-create-page.c08ae118.chunk.js → api-tokens-create-page.571920e5.chunk.js} +1 -1
- package/build/{api-tokens-edit-page.ce18efdc.chunk.js → api-tokens-edit-page.cbdc81b1.chunk.js} +1 -1
- package/build/{api-tokens-list-page.783b7569.chunk.js → api-tokens-list-page.de0c49e8.chunk.js} +2 -2
- package/build/audit-logs-sales-page.2955db88.chunk.js +1 -0
- package/build/{audit-logs-settings-page.12aeea8c.chunk.js → audit-logs-settings-page.b0cb5164.chunk.js} +1 -1
- package/build/content-manager.de7ae330.chunk.js +1241 -0
- package/build/{content-type-builder-list-view.38ed3935.chunk.js → content-type-builder-list-view.6c8d3213.chunk.js} +1 -1
- package/build/{content-type-builder-translation-en-json.43f9d7bc.chunk.js → content-type-builder-translation-en-json.74d80f18.chunk.js} +1 -1
- package/build/{content-type-builder.758a9d23.chunk.js → content-type-builder.0bc97051.chunk.js} +13 -23
- package/build/{email-settings-page.e08a587e.chunk.js → email-settings-page.07712efc.chunk.js} +1 -1
- package/build/en-json.5b907f67.chunk.js +1 -0
- package/build/{i18n-settings-page.3186e3e9.chunk.js → i18n-settings-page.5c34f012.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/main.f84563f1.js +2665 -0
- package/build/review-workflows-sales-page.f46a8f00.chunk.js +1 -0
- package/build/{review-workflows-settings-create-view.5cdc4d64.chunk.js → review-workflows-settings-create-view.d0544fb0.chunk.js} +1 -1
- package/build/{review-workflows-settings-edit-view.53bf7865.chunk.js → review-workflows-settings-edit-view.aabf49ef.chunk.js} +1 -1
- package/build/review-workflows-settings-list-view.8b0525ab.chunk.js +56 -0
- package/build/runtime~main.270fd45f.js +2 -0
- package/build/sso-sales-page.ef22e469.chunk.js +1 -0
- package/build/sso-settings-page.21e16ae4.chunk.js +1 -0
- package/build/{transfer-tokens-create-page.2662d519.chunk.js → transfer-tokens-create-page.3366204d.chunk.js} +1 -1
- package/build/{transfer-tokens-edit-page.f64d8d8c.chunk.js → transfer-tokens-edit-page.15cf0f73.chunk.js} +1 -1
- package/build/{transfer-tokens-list-page.e6fd5f87.chunk.js → transfer-tokens-list-page.0bc0e682.chunk.js} +2 -2
- package/build/{upload-settings.450a1de0.chunk.js → upload-settings.1319dca0.chunk.js} +1 -1
- package/build/{upload.0d53e7a3.chunk.js → upload.1ced11be.chunk.js} +1 -1
- package/build/{users-advanced-settings-page.4a1f1f6d.chunk.js → users-advanced-settings-page.8e657084.chunk.js} +1 -1
- package/build/{users-email-settings-page.ea81fe82.chunk.js → users-email-settings-page.e57745e5.chunk.js} +1 -1
- package/build/{users-providers-settings-page.10280cdb.chunk.js → users-providers-settings-page.55796d13.chunk.js} +1 -1
- package/build/{users-roles-settings-page.4a7158be.chunk.js → users-roles-settings-page.57079245.chunk.js} +1 -1
- package/build/webhook-edit-page.3a28b2e7.chunk.js +33 -0
- package/build/{webhook-list-page.f57285ca.chunk.js → webhook-list-page.ee80767b.chunk.js} +1 -1
- package/ee/admin/pages/AuthPage/components/Login/index.js +1 -1
- package/ee/admin/pages/AuthPage/components/Providers/index.js +2 -1
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/selectors.js +1 -1
- package/ee/server/bootstrap.js +1 -1
- package/ee/server/controllers/admin.js +1 -1
- package/ee/server/controllers/user.js +1 -1
- package/ee/server/destroy.js +1 -1
- package/ee/server/register.js +1 -1
- package/ee/server/routes/utils.js +1 -1
- package/ee/server/services/audit-logs.js +1 -1
- package/ee/server/services/passport/sso.js +1 -1
- package/ee/server/services/passport.js +1 -1
- package/ee/server/services/seat-enforcement.js +1 -1
- package/ee/server/utils/sso-lock.js +1 -1
- package/ee/server/validation/role.js +1 -1
- package/ee/server/validation/user.js +1 -1
- package/package.json +15 -16
- package/server/controllers/admin.js +1 -1
- package/shared/entities.ts +1 -1
- package/shared/permissions.ts +35 -35
- package/shared/schema.ts +9 -0
- package/admin/src/components/AuthenticatedApp/index.js +0 -116
- package/admin/src/components/AuthenticatedApp/utils/api.js +0 -47
- package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.ts +0 -13
- package/admin/src/components/AuthenticatedApp/utils/fetchStrapiLatestRelease.ts +0 -19
- package/admin/src/components/GuidedTour/Homepage/components/Step.js +0 -61
- package/admin/src/components/GuidedTour/Homepage/components/Stepper.js +0 -61
- package/admin/src/components/GuidedTour/Homepage/index.js +0 -71
- package/admin/src/components/GuidedTour/Modal/components/Content.js +0 -66
- package/admin/src/components/GuidedTour/Modal/components/Modal.js +0 -72
- package/admin/src/components/GuidedTour/Modal/components/StepNumberWithPadding.js +0 -26
- package/admin/src/components/GuidedTour/Modal/components/Stepper.js +0 -118
- package/admin/src/components/GuidedTour/Modal/index.js +0 -94
- package/admin/src/components/GuidedTour/Modal/reducer.js +0 -29
- package/admin/src/components/GuidedTour/Stepper/StepLine.js +0 -29
- package/admin/src/components/GuidedTour/Stepper/StepNumber.js +0 -71
- package/admin/src/components/GuidedTour/constants.js +0 -3
- package/admin/src/components/GuidedTour/index.js +0 -102
- package/admin/src/components/GuidedTour/init.js +0 -37
- package/admin/src/components/GuidedTour/reducer.js +0 -50
- package/admin/src/components/GuidedTour/utils/arePreviousSectionsDone.js +0 -13
- package/admin/src/components/GuidedTour/utils/arePreviousStepsDone.js +0 -12
- package/admin/src/components/GuidedTour/utils/isGuidedTourCompleted.js +0 -6
- package/admin/src/components/GuidedTour/utils/persistStateToLocaleStorage.js +0 -34
- package/admin/src/components/Providers/index.js +0 -156
- package/admin/src/components/RBACProvider/actions.js +0 -10
- package/admin/src/components/RBACProvider/constants.js +0 -2
- package/admin/src/components/RBACProvider/index.js +0 -39
- package/admin/src/components/RBACProvider/reducer.js +0 -51
- package/admin/src/contexts/ApiTokenPermissions/index.js +0 -25
- package/admin/src/core/store/configureStore.js +0 -47
- package/admin/src/exposedHooks.js +0 -27
- package/admin/src/hooks/useContentTypes/index.js +0 -1
- package/admin/src/injectionZones.js +0 -25
- package/admin/src/layouts/UnauthenticatedLayout/LocaleToggle/index.js +0 -29
- package/admin/src/layouts/UnauthenticatedLayout/index.js +0 -55
- package/admin/src/reducers.js +0 -23
- package/admin/src/utils/checkFormValidity.js +0 -15
- package/admin/src/utils/createRoute.js +0 -50
- package/admin/src/utils/formatAPIErrors.js +0 -17
- package/admin/src/utils/getAttributesToDisplay.js +0 -19
- package/admin/src/utils/getExistingActions.js +0 -32
- package/admin/src/utils/getFullName.js +0 -9
- package/admin/src/utils/index.js +0 -9
- package/admin/src/utils/makeUniqueRoutes.js +0 -6
- package/admin/src/utils/sortLinks.js +0 -5
- package/build/1222.fe92c653.chunk.js +0 -35
- package/build/2225.a2147b8f.chunk.js +0 -79
- package/build/3021.33ad47fb.chunk.js +0 -103
- package/build/6373.1a21d665.chunk.js +0 -105
- package/build/8894.5ca4852a.chunk.js +0 -26
- package/build/9302.550cf5b7.chunk.js +0 -146
- package/build/Admin-authenticatedApp.e897fccb.chunk.js +0 -79
- package/build/Admin_InternalErrorPage.e2431a95.chunk.js +0 -1
- package/build/Admin_homePage.71ef8d06.chunk.js +0 -81
- package/build/Admin_profilePage.61704b7d.chunk.js +0 -13
- package/build/Admin_settingsPage.39cb9fca.chunk.js +0 -111
- package/build/admin-app.06f5e70a.chunk.js +0 -69
- package/build/admin-edit-users.64fd1318.chunk.js +0 -10
- package/build/content-manager.2e3f660b.chunk.js +0 -1220
- package/build/en-json.bd611a8e.chunk.js +0 -1
- package/build/main.00ea6f5a.js +0 -2665
- package/build/review-workflows-settings-list-view.b4a8aefb.chunk.js +0 -56
- package/build/runtime~main.e3bf3980.js +0 -2
- package/build/sso-settings-page.6a35d473.chunk.js +0 -1
- package/build/webhook-edit-page.65ac30ee.chunk.js +0 -33
- /package/admin/src/hooks/{useContentTypes/__mocks__/index.js → __mocks__/useContentTypes.ts} +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Box, BoxProps, Flex, FlexProps, Typography } from '@strapi/design-system';
|
|
2
|
+
import { pxToRem } from '@strapi/helper-plugin';
|
|
3
|
+
import { Check } from '@strapi/icons';
|
|
4
|
+
|
|
5
|
+
import { STATES, States } from './constants';
|
|
6
|
+
|
|
7
|
+
/* -------------------------------------------------------------------------------------------------
|
|
8
|
+
* Number
|
|
9
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
10
|
+
|
|
11
|
+
interface NumberProps extends FlexProps {
|
|
12
|
+
children: number;
|
|
13
|
+
state: States;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Number = ({ children, state, ...props }: NumberProps) => {
|
|
17
|
+
return state === STATES.IS_DONE || state === STATES.IS_ACTIVE ? (
|
|
18
|
+
<Flex
|
|
19
|
+
background="primary600"
|
|
20
|
+
padding={2}
|
|
21
|
+
borderRadius="50%"
|
|
22
|
+
width={pxToRem(30)}
|
|
23
|
+
height={pxToRem(30)}
|
|
24
|
+
justifyContent="center"
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
{state === STATES.IS_DONE ? (
|
|
28
|
+
<Check aria-hidden width={pxToRem(16)} color="neutral0" />
|
|
29
|
+
) : (
|
|
30
|
+
<Typography fontWeight="semiBold" textColor="neutral0">
|
|
31
|
+
{children}
|
|
32
|
+
</Typography>
|
|
33
|
+
)}
|
|
34
|
+
</Flex>
|
|
35
|
+
) : (
|
|
36
|
+
<Flex
|
|
37
|
+
borderColor="neutral500"
|
|
38
|
+
borderWidth="1px"
|
|
39
|
+
borderStyle="solid"
|
|
40
|
+
padding={2}
|
|
41
|
+
borderRadius="50%"
|
|
42
|
+
width={pxToRem(30)}
|
|
43
|
+
height={pxToRem(30)}
|
|
44
|
+
justifyContent="center"
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<Typography fontWeight="semiBold" textColor="neutral600">
|
|
48
|
+
{children}
|
|
49
|
+
</Typography>
|
|
50
|
+
</Flex>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/* -------------------------------------------------------------------------------------------------
|
|
55
|
+
* VerticalDivider
|
|
56
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
57
|
+
|
|
58
|
+
interface VerticalDividerProps extends BoxProps {
|
|
59
|
+
state: States;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const VerticalDivider = ({ state, ...props }: VerticalDividerProps) => (
|
|
63
|
+
<Box
|
|
64
|
+
width={pxToRem(2)}
|
|
65
|
+
height="100%"
|
|
66
|
+
background={state === STATES.IS_NOT_DONE ? 'neutral300' : 'primary500'}
|
|
67
|
+
hasRadius
|
|
68
|
+
minHeight={state === STATES.IS_ACTIVE ? pxToRem(85) : pxToRem(65)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export { Number, VerticalDivider };
|
|
74
|
+
export type { NumberProps, VerticalDividerProps };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
GuidedTourContextValue,
|
|
5
|
+
GuidedTourProvider as GuidedTourProviderComponent,
|
|
6
|
+
GuidedTourSectionKey,
|
|
7
|
+
GuidedTourStep,
|
|
8
|
+
GuidedTourStepKey,
|
|
9
|
+
auth,
|
|
10
|
+
} from '@strapi/helper-plugin';
|
|
11
|
+
import produce from 'immer';
|
|
12
|
+
import get from 'lodash/get';
|
|
13
|
+
import set from 'lodash/set';
|
|
14
|
+
|
|
15
|
+
/* -------------------------------------------------------------------------------------------------
|
|
16
|
+
* GuidedTourProvider
|
|
17
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
18
|
+
|
|
19
|
+
interface GuidedTourProviderProps {
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const GuidedTourProvider = ({ children }: GuidedTourProviderProps) => {
|
|
24
|
+
const [{ currentStep, guidedTourState, isGuidedTourVisible, isSkipped }, dispatch] =
|
|
25
|
+
React.useReducer(reducer, initialState, initialiseState);
|
|
26
|
+
|
|
27
|
+
const setCurrentStep = (step: SetCurrentStepAction['step']) => {
|
|
28
|
+
// if step is null it is intentional, we need to dispatch it
|
|
29
|
+
if (step !== null) {
|
|
30
|
+
const isStepAlreadyDone = get(guidedTourState, step);
|
|
31
|
+
const [sectionName, stepName] = step.split('.') as [GuidedTourSectionKey, GuidedTourStepKey];
|
|
32
|
+
const sectionArray = Object.entries(guidedTourState[sectionName]);
|
|
33
|
+
|
|
34
|
+
const currentStepIndex = sectionArray.findIndex(([key]) => key === stepName);
|
|
35
|
+
const previousSteps = sectionArray.slice(0, currentStepIndex);
|
|
36
|
+
|
|
37
|
+
const isStepToShow = previousSteps.every(([, sectionValue]) => sectionValue);
|
|
38
|
+
|
|
39
|
+
if (isStepAlreadyDone || isSkipped || !isStepToShow) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
auth.set(null, 'GUIDED_TOUR_CURRENT_STEP', true);
|
|
45
|
+
|
|
46
|
+
return dispatch({
|
|
47
|
+
type: 'SET_CURRENT_STEP',
|
|
48
|
+
step,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const setGuidedTourVisibility = (value: SetGuidedTourVisibilityAction['value']) => {
|
|
53
|
+
dispatch({
|
|
54
|
+
type: 'SET_GUIDED_TOUR_VISIBILITY',
|
|
55
|
+
value,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const setStepState = (currentStep: GuidedTourStep, value: SetStepStateAction['value']) => {
|
|
60
|
+
addCompletedStep(currentStep);
|
|
61
|
+
|
|
62
|
+
dispatch({
|
|
63
|
+
type: 'SET_STEP_STATE',
|
|
64
|
+
currentStep,
|
|
65
|
+
value,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const startSection = (sectionName: GuidedTourSectionKey) => {
|
|
70
|
+
const sectionSteps = guidedTourState[sectionName];
|
|
71
|
+
|
|
72
|
+
if (sectionSteps) {
|
|
73
|
+
const guidedTourArray = Object.entries(guidedTourState);
|
|
74
|
+
|
|
75
|
+
// Find current section position in the guidedTourArray
|
|
76
|
+
// Get only previous sections based on current section position
|
|
77
|
+
const currentSectionIndex = guidedTourArray.findIndex(([key]) => key === sectionName);
|
|
78
|
+
const previousSections = guidedTourArray.slice(0, currentSectionIndex);
|
|
79
|
+
|
|
80
|
+
// Check if every steps from previous section are done
|
|
81
|
+
const isSectionToShow = previousSections.every(([, sectionValue]) =>
|
|
82
|
+
Object.values(sectionValue).every(Boolean)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const [firstStep] = Object.keys(sectionSteps) as [GuidedTourStepKey];
|
|
86
|
+
const isFirstStepDone = sectionSteps[firstStep];
|
|
87
|
+
|
|
88
|
+
if (isSectionToShow && !currentStep && !isFirstStepDone) {
|
|
89
|
+
setCurrentStep(`${sectionName}.${firstStep}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const setSkipped = (value: SetSkippedAction['value']) => {
|
|
95
|
+
auth.set(value, 'GUIDED_TOUR_SKIPPED', true);
|
|
96
|
+
|
|
97
|
+
dispatch({
|
|
98
|
+
type: 'SET_SKIPPED',
|
|
99
|
+
value,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<GuidedTourProviderComponent
|
|
105
|
+
guidedTourState={guidedTourState}
|
|
106
|
+
currentStep={currentStep}
|
|
107
|
+
setCurrentStep={setCurrentStep}
|
|
108
|
+
setGuidedTourVisibility={setGuidedTourVisibility}
|
|
109
|
+
setSkipped={setSkipped}
|
|
110
|
+
setStepState={setStepState}
|
|
111
|
+
startSection={startSection}
|
|
112
|
+
isGuidedTourVisible={isGuidedTourVisible}
|
|
113
|
+
isSkipped={isSkipped}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</GuidedTourProviderComponent>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
type State = Pick<
|
|
121
|
+
GuidedTourContextValue,
|
|
122
|
+
'guidedTourState' | 'currentStep' | 'isGuidedTourVisible' | 'isSkipped'
|
|
123
|
+
>;
|
|
124
|
+
|
|
125
|
+
const initialState = {
|
|
126
|
+
currentStep: null,
|
|
127
|
+
guidedTourState: {
|
|
128
|
+
contentTypeBuilder: {
|
|
129
|
+
create: false,
|
|
130
|
+
success: false,
|
|
131
|
+
},
|
|
132
|
+
contentManager: {
|
|
133
|
+
create: false,
|
|
134
|
+
success: false,
|
|
135
|
+
},
|
|
136
|
+
apiTokens: {
|
|
137
|
+
create: false,
|
|
138
|
+
success: false,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
isGuidedTourVisible: false,
|
|
142
|
+
isSkipped: false,
|
|
143
|
+
} satisfies State;
|
|
144
|
+
|
|
145
|
+
interface SetCurrentStepAction {
|
|
146
|
+
type: 'SET_CURRENT_STEP';
|
|
147
|
+
step: GuidedTourStep | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface SetStepStateAction {
|
|
151
|
+
type: 'SET_STEP_STATE';
|
|
152
|
+
currentStep: GuidedTourStep;
|
|
153
|
+
value: boolean;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface SetSkippedAction {
|
|
157
|
+
type: 'SET_SKIPPED';
|
|
158
|
+
value: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface SetGuidedTourVisibilityAction {
|
|
162
|
+
type: 'SET_GUIDED_TOUR_VISIBILITY';
|
|
163
|
+
value: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type Action =
|
|
167
|
+
| SetCurrentStepAction
|
|
168
|
+
| SetStepStateAction
|
|
169
|
+
| SetSkippedAction
|
|
170
|
+
| SetGuidedTourVisibilityAction;
|
|
171
|
+
|
|
172
|
+
const reducer: React.Reducer<State, Action> = (state: State = initialState, action: Action) =>
|
|
173
|
+
produce(state, (draftState) => {
|
|
174
|
+
switch (action.type) {
|
|
175
|
+
case 'SET_CURRENT_STEP': {
|
|
176
|
+
draftState.currentStep = action.step;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case 'SET_STEP_STATE': {
|
|
180
|
+
const [section, step] = action.currentStep.split('.') as [
|
|
181
|
+
GuidedTourSectionKey,
|
|
182
|
+
GuidedTourStepKey
|
|
183
|
+
];
|
|
184
|
+
draftState.guidedTourState[section][step] = action.value;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case 'SET_SKIPPED': {
|
|
188
|
+
draftState.isSkipped = action.value;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case 'SET_GUIDED_TOUR_VISIBILITY': {
|
|
192
|
+
draftState.isGuidedTourVisible = action.value;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
default: {
|
|
196
|
+
return draftState;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const initialiseState = (initialState: State) => {
|
|
202
|
+
const copyInitialState = { ...initialState };
|
|
203
|
+
const guidedTourLocaleStorage = auth.get('GUIDED_TOUR_COMPLETED_STEPS');
|
|
204
|
+
const currentStepLocaleStorage = auth.get('GUIDED_TOUR_CURRENT_STEP');
|
|
205
|
+
const skippedLocaleStorage = auth.get('GUIDED_TOUR_SKIPPED');
|
|
206
|
+
|
|
207
|
+
if (Array.isArray(guidedTourLocaleStorage)) {
|
|
208
|
+
guidedTourLocaleStorage.forEach((step) => {
|
|
209
|
+
const [sectionName, stepName] = step.split('.');
|
|
210
|
+
set(copyInitialState, ['guidedTourState', sectionName, stepName], true);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// if current step when initializing mark it as done
|
|
215
|
+
if (currentStepLocaleStorage) {
|
|
216
|
+
const [sectionName, stepName] = currentStepLocaleStorage.split('.') as [
|
|
217
|
+
GuidedTourSectionKey,
|
|
218
|
+
GuidedTourStepKey
|
|
219
|
+
];
|
|
220
|
+
set(copyInitialState, ['guidedTourState', sectionName, stepName], true);
|
|
221
|
+
|
|
222
|
+
addCompletedStep(currentStepLocaleStorage as GuidedTourStep);
|
|
223
|
+
|
|
224
|
+
auth.set(null, 'GUIDED_TOUR_CURRENT_STEP', true);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (skippedLocaleStorage !== null) {
|
|
228
|
+
set(copyInitialState, 'isSkipped', skippedLocaleStorage);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return copyInitialState;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @description Add a completed step to the local storage if it does not already exist.
|
|
236
|
+
*/
|
|
237
|
+
const addCompletedStep = (completedStep: GuidedTourStep) => {
|
|
238
|
+
const currentSteps = auth.get('GUIDED_TOUR_COMPLETED_STEPS') ?? [];
|
|
239
|
+
|
|
240
|
+
if (!Array.isArray(currentSteps)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const isAlreadyStored = currentSteps.includes(completedStep);
|
|
245
|
+
|
|
246
|
+
if (isAlreadyStored) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
auth.set([...currentSteps, completedStep], 'GUIDED_TOUR_COMPLETED_STEPS', true);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export { GuidedTourProvider };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const LAYOUT_DATA = {
|
|
2
2
|
contentTypeBuilder: {
|
|
3
3
|
home: {
|
|
4
4
|
title: {
|
|
@@ -157,6 +157,16 @@ const layout = {
|
|
|
157
157
|
trackingEvent: 'didGenerateGuidedTourApiTokens',
|
|
158
158
|
},
|
|
159
159
|
},
|
|
160
|
-
};
|
|
160
|
+
} as const;
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
const STATES = {
|
|
163
|
+
IS_DONE: 'IS_DONE',
|
|
164
|
+
IS_ACTIVE: 'IS_ACTIVE',
|
|
165
|
+
IS_NOT_DONE: 'IS_NOT_DONE',
|
|
166
|
+
} as const;
|
|
167
|
+
|
|
168
|
+
type LayoutData = typeof LAYOUT_DATA;
|
|
169
|
+
type States = keyof typeof STATES;
|
|
170
|
+
|
|
171
|
+
export { LAYOUT_DATA, STATES };
|
|
172
|
+
export type { LayoutData, States };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AutoReloadOverlayBlockerProvider,
|
|
5
|
+
CustomFieldsProvider,
|
|
6
|
+
CustomFieldsProviderProps,
|
|
7
|
+
LibraryProvider,
|
|
8
|
+
LibraryProviderProps,
|
|
9
|
+
NotificationsProvider,
|
|
10
|
+
OverlayBlockerProvider,
|
|
11
|
+
StrapiAppProvider,
|
|
12
|
+
StrapiAppProviderProps,
|
|
13
|
+
} from '@strapi/helper-plugin';
|
|
14
|
+
import { QueryClient, QueryClientProvider } from 'react-query';
|
|
15
|
+
import { Provider } from 'react-redux';
|
|
16
|
+
|
|
17
|
+
import { AdminContext, AdminContextValue } from '../contexts/admin';
|
|
18
|
+
|
|
19
|
+
import { ConfigurationProvider, ConfigurationProviderProps } from './ConfigurationProvider';
|
|
20
|
+
import { GuidedTourProvider } from './GuidedTour/Provider';
|
|
21
|
+
import { LanguageProvider, LanguageProviderProps } from './LanguageProvider';
|
|
22
|
+
import { Theme } from './Theme';
|
|
23
|
+
import { ThemeToggleProvider, ThemeToggleProviderProps } from './ThemeToggleProvider';
|
|
24
|
+
|
|
25
|
+
import type { Store } from '../core/store/configure';
|
|
26
|
+
|
|
27
|
+
const queryClient = new QueryClient({
|
|
28
|
+
defaultOptions: {
|
|
29
|
+
queries: {
|
|
30
|
+
refetchOnWindowFocus: false,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
interface ProvidersProps
|
|
36
|
+
extends Pick<ThemeToggleProviderProps, 'themes'>,
|
|
37
|
+
Pick<LanguageProviderProps, 'messages' | 'localeNames'>,
|
|
38
|
+
Pick<
|
|
39
|
+
ConfigurationProviderProps,
|
|
40
|
+
'authLogo' | 'menuLogo' | 'showReleaseNotification' | 'showTutorials'
|
|
41
|
+
>,
|
|
42
|
+
Pick<AdminContextValue, 'getAdminInjectedComponents'>,
|
|
43
|
+
Pick<CustomFieldsProviderProps, 'customFields'>,
|
|
44
|
+
Pick<LibraryProviderProps, 'components' | 'fields'>,
|
|
45
|
+
Pick<
|
|
46
|
+
StrapiAppProviderProps,
|
|
47
|
+
| 'getPlugin'
|
|
48
|
+
| 'menu'
|
|
49
|
+
| 'plugins'
|
|
50
|
+
| 'runHookParallel'
|
|
51
|
+
| 'runHookSeries'
|
|
52
|
+
| 'runHookWaterfall'
|
|
53
|
+
| 'settings'
|
|
54
|
+
> {
|
|
55
|
+
children: React.ReactNode;
|
|
56
|
+
store: Store;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const Providers = ({
|
|
60
|
+
authLogo,
|
|
61
|
+
children,
|
|
62
|
+
components,
|
|
63
|
+
customFields,
|
|
64
|
+
fields,
|
|
65
|
+
getAdminInjectedComponents,
|
|
66
|
+
getPlugin,
|
|
67
|
+
localeNames,
|
|
68
|
+
menu,
|
|
69
|
+
menuLogo,
|
|
70
|
+
messages,
|
|
71
|
+
plugins,
|
|
72
|
+
runHookParallel,
|
|
73
|
+
runHookSeries,
|
|
74
|
+
runHookWaterfall,
|
|
75
|
+
settings,
|
|
76
|
+
showReleaseNotification,
|
|
77
|
+
showTutorials,
|
|
78
|
+
store,
|
|
79
|
+
themes,
|
|
80
|
+
}: ProvidersProps) => {
|
|
81
|
+
return (
|
|
82
|
+
<LanguageProvider messages={messages} localeNames={localeNames}>
|
|
83
|
+
<ThemeToggleProvider themes={themes}>
|
|
84
|
+
<Theme>
|
|
85
|
+
<QueryClientProvider client={queryClient}>
|
|
86
|
+
<Provider store={store}>
|
|
87
|
+
<AdminContext.Provider value={{ getAdminInjectedComponents }}>
|
|
88
|
+
<ConfigurationProvider
|
|
89
|
+
authLogo={authLogo}
|
|
90
|
+
menuLogo={menuLogo}
|
|
91
|
+
showReleaseNotification={showReleaseNotification}
|
|
92
|
+
showTutorials={showTutorials}
|
|
93
|
+
>
|
|
94
|
+
<StrapiAppProvider
|
|
95
|
+
getPlugin={getPlugin}
|
|
96
|
+
menu={menu}
|
|
97
|
+
plugins={plugins}
|
|
98
|
+
runHookParallel={runHookParallel}
|
|
99
|
+
runHookWaterfall={runHookWaterfall}
|
|
100
|
+
runHookSeries={runHookSeries}
|
|
101
|
+
settings={settings}
|
|
102
|
+
>
|
|
103
|
+
<LibraryProvider components={components} fields={fields}>
|
|
104
|
+
<CustomFieldsProvider customFields={customFields}>
|
|
105
|
+
<AutoReloadOverlayBlockerProvider>
|
|
106
|
+
<OverlayBlockerProvider>
|
|
107
|
+
<GuidedTourProvider>
|
|
108
|
+
<NotificationsProvider>{children}</NotificationsProvider>
|
|
109
|
+
</GuidedTourProvider>
|
|
110
|
+
</OverlayBlockerProvider>
|
|
111
|
+
</AutoReloadOverlayBlockerProvider>
|
|
112
|
+
</CustomFieldsProvider>
|
|
113
|
+
</LibraryProvider>
|
|
114
|
+
</StrapiAppProvider>
|
|
115
|
+
</ConfigurationProvider>
|
|
116
|
+
</AdminContext.Provider>
|
|
117
|
+
</Provider>
|
|
118
|
+
</QueryClientProvider>
|
|
119
|
+
</Theme>
|
|
120
|
+
</ThemeToggleProvider>
|
|
121
|
+
</LanguageProvider>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export { Providers };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
LoadingIndicatorPage,
|
|
5
|
+
Permission,
|
|
6
|
+
RBACContext,
|
|
7
|
+
RBACContextValue,
|
|
8
|
+
} from '@strapi/helper-plugin';
|
|
9
|
+
import produce from 'immer';
|
|
10
|
+
|
|
11
|
+
import { useTypedSelector, useTypedDispatch } from '../core/store/hooks';
|
|
12
|
+
|
|
13
|
+
/* -------------------------------------------------------------------------------------------------
|
|
14
|
+
* RBACProvider
|
|
15
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
16
|
+
|
|
17
|
+
interface RBACProviderProps {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
permissions: Permission[];
|
|
20
|
+
refetchPermissions: RBACContextValue['refetchPermissions'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const RBACProvider = ({ children, permissions, refetchPermissions }: RBACProviderProps) => {
|
|
24
|
+
const allPermissions = useTypedSelector((state) => state.rbacProvider.allPermissions);
|
|
25
|
+
|
|
26
|
+
const dispatch = useTypedDispatch();
|
|
27
|
+
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
dispatch(setPermissionsAction(permissions));
|
|
30
|
+
|
|
31
|
+
return () => {
|
|
32
|
+
dispatch(resetStoreAction());
|
|
33
|
+
};
|
|
34
|
+
}, [permissions, dispatch]);
|
|
35
|
+
|
|
36
|
+
if (!allPermissions) {
|
|
37
|
+
return <LoadingIndicatorPage />;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<RBACContext.Provider value={{ allPermissions, refetchPermissions }}>
|
|
42
|
+
{children}
|
|
43
|
+
</RBACContext.Provider>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/* -------------------------------------------------------------------------------------------------
|
|
48
|
+
* RBACReducer
|
|
49
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
50
|
+
|
|
51
|
+
interface RBACState {
|
|
52
|
+
allPermissions: null | Permission[];
|
|
53
|
+
collectionTypesRelatedPermissions: Record<string, Record<string, Permission[]>>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const initialState = {
|
|
57
|
+
allPermissions: null,
|
|
58
|
+
collectionTypesRelatedPermissions: {},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const RESET_STORE = 'StrapiAdmin/RBACProvider/RESET_STORE';
|
|
62
|
+
const SET_PERMISSIONS = 'StrapiAdmin/RBACProvider/SET_PERMISSIONS';
|
|
63
|
+
|
|
64
|
+
interface ResetStoreAction {
|
|
65
|
+
type: typeof RESET_STORE;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const resetStoreAction = (): ResetStoreAction => ({ type: RESET_STORE });
|
|
69
|
+
|
|
70
|
+
interface SetPermissionsAction {
|
|
71
|
+
type: typeof SET_PERMISSIONS;
|
|
72
|
+
permissions: Permission[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const setPermissionsAction = (
|
|
76
|
+
permissions: SetPermissionsAction['permissions']
|
|
77
|
+
): SetPermissionsAction => ({
|
|
78
|
+
type: SET_PERMISSIONS,
|
|
79
|
+
permissions,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
type Actions = ResetStoreAction | SetPermissionsAction;
|
|
83
|
+
|
|
84
|
+
const RBACReducer = (state: RBACState = initialState, action: Actions) =>
|
|
85
|
+
produce(state, (draftState) => {
|
|
86
|
+
switch (action.type) {
|
|
87
|
+
case SET_PERMISSIONS: {
|
|
88
|
+
draftState.allPermissions = action.permissions;
|
|
89
|
+
draftState.collectionTypesRelatedPermissions = action.permissions
|
|
90
|
+
.filter((perm) => perm.subject)
|
|
91
|
+
.reduce<Record<string, Record<string, Permission[]>>>((acc, current) => {
|
|
92
|
+
const { subject, action } = current;
|
|
93
|
+
|
|
94
|
+
if (!subject) return acc;
|
|
95
|
+
|
|
96
|
+
if (!acc[subject]) {
|
|
97
|
+
acc[subject] = {};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
acc[subject] = acc[subject][action]
|
|
101
|
+
? { ...acc[subject], [action]: [...acc[subject][action], current] }
|
|
102
|
+
: { ...acc[subject], [action]: [current] };
|
|
103
|
+
|
|
104
|
+
return acc;
|
|
105
|
+
}, {});
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case RESET_STORE: {
|
|
109
|
+
return initialState;
|
|
110
|
+
}
|
|
111
|
+
default:
|
|
112
|
+
return state;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export { RBACProvider, RBACReducer, resetStoreAction, setPermissionsAction };
|
|
117
|
+
export type {
|
|
118
|
+
RBACState,
|
|
119
|
+
Actions,
|
|
120
|
+
RBACProviderProps,
|
|
121
|
+
ResetStoreAction,
|
|
122
|
+
SetPermissionsAction,
|
|
123
|
+
Permission,
|
|
124
|
+
};
|
|
@@ -11,9 +11,11 @@ interface ThemeProps {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const Theme = ({ children }: ThemeProps) => {
|
|
14
|
-
const { currentTheme, themes } = useThemeToggle();
|
|
14
|
+
const { currentTheme, themes, systemTheme } = useThemeToggle();
|
|
15
15
|
const { locale } = useIntl();
|
|
16
16
|
|
|
17
|
+
const computedThemeName = currentTheme === 'system' ? systemTheme : currentTheme;
|
|
18
|
+
|
|
17
19
|
return (
|
|
18
20
|
<DesignSystemProvider
|
|
19
21
|
locale={locale}
|
|
@@ -22,7 +24,7 @@ const Theme = ({ children }: ThemeProps) => {
|
|
|
22
24
|
* if it can't find it, that way the type is always fully defined and we're
|
|
23
25
|
* not checking it all the time...
|
|
24
26
|
*/
|
|
25
|
-
theme={
|
|
27
|
+
theme={themes?.[computedThemeName || 'light']}
|
|
26
28
|
>
|
|
27
29
|
{children}
|
|
28
30
|
<GlobalStyle />
|
|
@@ -2,19 +2,14 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { DefaultTheme } from 'styled-components';
|
|
4
4
|
|
|
5
|
-
import { ThemeToggleContext, ThemeName } from '../contexts/themeToggle';
|
|
5
|
+
import { ThemeToggleContext, ThemeName, NonSystemThemeName } from '../contexts/themeToggle';
|
|
6
6
|
|
|
7
7
|
const THEME_KEY = 'STRAPI_THEME';
|
|
8
8
|
|
|
9
9
|
const getDefaultTheme = () => {
|
|
10
|
-
const browserTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
11
10
|
const persistedTheme = localStorage.getItem(THEME_KEY) as ThemeName | null;
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
localStorage.setItem(THEME_KEY, browserTheme);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return persistedTheme || browserTheme;
|
|
12
|
+
return persistedTheme || 'system';
|
|
18
13
|
};
|
|
19
14
|
|
|
20
15
|
interface ThemeToggleProviderProps {
|
|
@@ -26,7 +21,8 @@ interface ThemeToggleProviderProps {
|
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
const ThemeToggleProvider = ({ children, themes }: ThemeToggleProviderProps) => {
|
|
29
|
-
const [currentTheme, setCurrentTheme] = React.useState(getDefaultTheme());
|
|
24
|
+
const [currentTheme, setCurrentTheme] = React.useState<ThemeName>(getDefaultTheme());
|
|
25
|
+
const [systemTheme, setSystemTheme] = React.useState<NonSystemThemeName>();
|
|
30
26
|
|
|
31
27
|
const handleChangeTheme = React.useCallback(
|
|
32
28
|
(nextTheme: ThemeName) => {
|
|
@@ -36,15 +32,33 @@ const ThemeToggleProvider = ({ children, themes }: ThemeToggleProviderProps) =>
|
|
|
36
32
|
[setCurrentTheme]
|
|
37
33
|
);
|
|
38
34
|
|
|
35
|
+
// Listen to changes in the system theme
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
const themeWatcher = window.matchMedia('(prefers-color-scheme: dark)');
|
|
38
|
+
setSystemTheme(themeWatcher.matches ? 'dark' : 'light');
|
|
39
|
+
|
|
40
|
+
const listener = (event: MediaQueryListEvent) => {
|
|
41
|
+
setSystemTheme(event.matches ? 'dark' : 'light');
|
|
42
|
+
};
|
|
43
|
+
themeWatcher.addEventListener('change', listener);
|
|
44
|
+
|
|
45
|
+
// Remove listener on cleanup
|
|
46
|
+
return () => {
|
|
47
|
+
themeWatcher.removeEventListener('change', listener);
|
|
48
|
+
};
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
39
51
|
const themeValues = React.useMemo(() => {
|
|
40
52
|
return {
|
|
41
53
|
currentTheme,
|
|
42
54
|
onChangeTheme: handleChangeTheme,
|
|
43
55
|
themes,
|
|
56
|
+
systemTheme,
|
|
44
57
|
};
|
|
45
|
-
}, [currentTheme, handleChangeTheme, themes]);
|
|
58
|
+
}, [currentTheme, handleChangeTheme, themes, systemTheme]);
|
|
46
59
|
|
|
47
60
|
return <ThemeToggleContext.Provider value={themeValues}>{children}</ThemeToggleContext.Provider>;
|
|
48
61
|
};
|
|
49
62
|
|
|
50
63
|
export { ThemeToggleProvider };
|
|
64
|
+
export type { ThemeToggleProviderProps };
|