@webbio/strapi-plugin-page-builder 0.2.2 → 0.2.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.
Files changed (93) hide show
  1. package/README.md +98 -98
  2. package/admin/src/api/collection-type.ts +110 -110
  3. package/admin/src/api/has-page-relation.ts +34 -34
  4. package/admin/src/api/page-type.ts +31 -31
  5. package/admin/src/api/template.ts +27 -27
  6. package/admin/src/components/Combobox/index.tsx +77 -77
  7. package/admin/src/components/Combobox/react-select-custom-styles.tsx +120 -111
  8. package/admin/src/components/Combobox/styles.ts +22 -22
  9. package/admin/src/components/ConfirmModal/index.tsx +90 -90
  10. package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +124 -124
  11. package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +104 -101
  12. package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/styles.ts +19 -19
  13. package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +74 -74
  14. package/admin/src/components/EditView/Details/index.tsx +47 -47
  15. package/admin/src/components/EditView/Details/styles.ts +51 -51
  16. package/admin/src/components/EditView/PageSettings/index.tsx +104 -104
  17. package/admin/src/components/EditView/Template/TemplateConfirmModal/index.tsx +36 -36
  18. package/admin/src/components/EditView/Template/TemplateSelect/index.tsx +69 -69
  19. package/admin/src/components/EditView/Template/TemplateSelect/use-template-modules.ts +38 -38
  20. package/admin/src/components/EditView/index.tsx +29 -29
  21. package/admin/src/components/EditView/page-type-select.tsx +32 -32
  22. package/admin/src/components/EditView/wrapper.tsx +35 -35
  23. package/admin/src/components/Initializer/index.tsx +24 -24
  24. package/admin/src/components/PageTypeFilter/index.tsx +17 -17
  25. package/admin/src/components/PageTypeFilter/page-type-filter.tsx +130 -130
  26. package/admin/src/components/PluginIcon/index.tsx +12 -12
  27. package/admin/src/constants.ts +3 -3
  28. package/admin/src/index.tsx +59 -59
  29. package/admin/src/middlewares/index.tsx +37 -37
  30. package/admin/src/pluginId.ts +5 -5
  31. package/admin/src/translations/en.json +6 -6
  32. package/admin/src/translations/nl.json +6 -6
  33. package/admin/src/utils/getRequestUrl.ts +11 -11
  34. package/admin/src/utils/getTrad.ts +5 -5
  35. package/admin/src/utils/hooks/useDebounce.ts +17 -17
  36. package/admin/src/utils/hooks/useGetLocaleFromUrl.ts +9 -9
  37. package/admin/src/utils/hooks/usePrevious.ts +12 -12
  38. package/admin/src/utils/sanitizeModules.ts +43 -10
  39. package/custom.d.ts +5 -5
  40. package/dist/package.json +9 -9
  41. package/dist/server/graphql/page-by-slug.js +9 -9
  42. package/dist/server/graphql/pages-by-uid.js +14 -14
  43. package/dist/server/services/builder.js +7 -4
  44. package/dist/server/services/template.js +1 -2
  45. package/dist/server/utils/graphql.js +18 -18
  46. package/dist/server/utils/strapi.js +1 -4
  47. package/dist/tsconfig.server.tsbuildinfo +1 -1
  48. package/package.json +71 -71
  49. package/server/bootstrap/collection-type-lifecycles.ts +47 -47
  50. package/server/bootstrap/permissions.ts +42 -42
  51. package/server/bootstrap.ts +198 -198
  52. package/server/config/index.ts +4 -4
  53. package/server/content-types/index.ts +1 -1
  54. package/server/controllers/collection-types.ts +27 -27
  55. package/server/controllers/index.ts +11 -11
  56. package/server/controllers/page-type.ts +13 -13
  57. package/server/controllers/page.ts +9 -9
  58. package/server/controllers/template.ts +16 -16
  59. package/server/destroy.ts +5 -5
  60. package/server/graphql/index.ts +9 -9
  61. package/server/graphql/page-by-slug.ts +98 -98
  62. package/server/graphql/page-type.ts +67 -67
  63. package/server/graphql/pages-by-uid.ts +127 -127
  64. package/server/index.ts +23 -23
  65. package/server/middlewares/index.ts +1 -1
  66. package/server/policies/index.ts +1 -1
  67. package/server/register.ts +15 -15
  68. package/server/routes/index.ts +58 -58
  69. package/server/schema/page-end.json +91 -91
  70. package/server/schema/page-start.json +87 -87
  71. package/server/schema/page-type-end.json +43 -43
  72. package/server/schema/page-type-start.json +38 -38
  73. package/server/schema/template-start.json +35 -35
  74. package/server/services/builder.ts +137 -134
  75. package/server/services/collection-types.ts +88 -88
  76. package/server/services/index.ts +13 -13
  77. package/server/services/page-type.ts +26 -26
  78. package/server/services/page.ts +24 -24
  79. package/server/services/template.ts +13 -14
  80. package/server/utils/filter-underscore-arguments.ts +12 -12
  81. package/server/utils/graphql.ts +113 -113
  82. package/server/utils/paginationValidation.ts +39 -39
  83. package/server/utils/reload-strapi-on-load.ts +13 -13
  84. package/server/utils/strapi.ts +45 -49
  85. package/shared/utils/constants.ts +4 -4
  86. package/shared/utils/sleep.ts +1 -1
  87. package/strapi-admin.js +3 -3
  88. package/strapi-server.js +3 -3
  89. package/tsconfig.json +20 -20
  90. package/tsconfig.server.json +25 -25
  91. package/dist/server/controllers/platform.js +0 -20
  92. package/dist/server/schema/platform-start.json +0 -31
  93. package/dist/server/services/platform.js +0 -36
@@ -1,77 +1,77 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { ClearIndicatorProps, DropdownIndicatorProps, GroupBase, components } from 'react-select';
3
- import AsyncSelect, { AsyncProps } from 'react-select/async';
4
-
5
- import { CarretDown, Cross } from '@strapi/icons';
6
- import { Typography } from '@strapi/design-system';
7
-
8
- import S from './styles';
9
- import { useReactSelectCustomStyles } from './react-select-custom-styles';
10
-
11
- export interface IComboboxProps extends AsyncProps<IReactSelectValue, false, GroupBase<IReactSelectValue>> {
12
- customOption?: typeof components.Option<IReactSelectValue, false, GroupBase<IReactSelectValue>>;
13
- label?: string;
14
- id: string;
15
- }
16
-
17
- export interface IReactSelectValue {
18
- value: string;
19
- label: string;
20
- initialSelected?: boolean;
21
- }
22
-
23
- const Combobox = (props: IComboboxProps) => {
24
- const { label, customOption, id, ...selectProps } = props;
25
- const styles = useReactSelectCustomStyles();
26
- const [inputValue, setInputValue] = useState<string | undefined>(props.inputValue);
27
-
28
- useEffect(() => {
29
- setInputValue(props.inputValue);
30
- }, [props.inputValue]);
31
-
32
- return (
33
- <S.Wrapper>
34
- {props.label && (
35
- <Typography as="label" htmlFor={id} variant="pi" fontWeight="bold" textColor="neutral800">
36
- {props.label}
37
- </Typography>
38
- )}
39
- <AsyncSelect
40
- {...selectProps}
41
- inputId={id}
42
- isClearable
43
- onInputChange={(value, actionMeta) => {
44
- props.onInputChange?.(value, actionMeta);
45
- setInputValue(value);
46
- }}
47
- // @ts-ignore
48
- styles={styles}
49
- inputValue={inputValue}
50
- components={{
51
- IndicatorSeparator: null,
52
- ClearIndicator,
53
- DropdownIndicator,
54
- Option: props.customOption || components.Option
55
- }}
56
- />
57
- </S.Wrapper>
58
- );
59
- };
60
-
61
- export { Combobox };
62
-
63
- const ClearIndicator = (props: ClearIndicatorProps<IReactSelectValue, false>) => {
64
- return (
65
- <components.ClearIndicator {...props}>
66
- <Cross />
67
- </components.ClearIndicator>
68
- );
69
- };
70
-
71
- const DropdownIndicator = (props: DropdownIndicatorProps<IReactSelectValue, false>) => {
72
- return (
73
- <components.DropdownIndicator {...props}>
74
- <CarretDown />
75
- </components.DropdownIndicator>
76
- );
77
- };
1
+ import React, { useEffect, useState } from 'react';
2
+ import { ClearIndicatorProps, DropdownIndicatorProps, GroupBase, components } from 'react-select';
3
+ import AsyncSelect, { AsyncProps } from 'react-select/async';
4
+
5
+ import { CarretDown, Cross } from '@strapi/icons';
6
+ import { Typography } from '@strapi/design-system';
7
+
8
+ import S from './styles';
9
+ import { useReactSelectCustomStyles } from './react-select-custom-styles';
10
+
11
+ export interface IComboboxProps extends AsyncProps<IReactSelectValue, false, GroupBase<IReactSelectValue>> {
12
+ customOption?: typeof components.Option<IReactSelectValue, false, GroupBase<IReactSelectValue>>;
13
+ label?: string;
14
+ id: string;
15
+ }
16
+
17
+ export interface IReactSelectValue {
18
+ value: string;
19
+ label: string;
20
+ initialSelected?: boolean;
21
+ }
22
+
23
+ const Combobox = (props: IComboboxProps) => {
24
+ const { label, customOption, id, ...selectProps } = props;
25
+ const styles = useReactSelectCustomStyles();
26
+ const [inputValue, setInputValue] = useState<string | undefined>(props.inputValue);
27
+
28
+ useEffect(() => {
29
+ setInputValue(props.inputValue);
30
+ }, [props.inputValue]);
31
+
32
+ return (
33
+ <S.Wrapper>
34
+ {props.label && (
35
+ <Typography as="label" htmlFor={id} variant="pi" fontWeight="bold" textColor="neutral800">
36
+ {props.label}
37
+ </Typography>
38
+ )}
39
+ <AsyncSelect
40
+ {...selectProps}
41
+ inputId={id}
42
+ isClearable
43
+ onInputChange={(value, actionMeta) => {
44
+ props.onInputChange?.(value, actionMeta);
45
+ setInputValue(value);
46
+ }}
47
+ // @ts-ignore
48
+ styles={styles}
49
+ inputValue={inputValue}
50
+ components={{
51
+ IndicatorSeparator: null,
52
+ ClearIndicator,
53
+ DropdownIndicator,
54
+ Option: props.customOption || components.Option
55
+ }}
56
+ />
57
+ </S.Wrapper>
58
+ );
59
+ };
60
+
61
+ export { Combobox };
62
+
63
+ const ClearIndicator = (props: ClearIndicatorProps<IReactSelectValue, false>) => {
64
+ return (
65
+ <components.ClearIndicator {...props}>
66
+ <Cross />
67
+ </components.ClearIndicator>
68
+ );
69
+ };
70
+
71
+ const DropdownIndicator = (props: DropdownIndicatorProps<IReactSelectValue, false>) => {
72
+ return (
73
+ <components.DropdownIndicator {...props}>
74
+ <CarretDown />
75
+ </components.DropdownIndicator>
76
+ );
77
+ };
@@ -1,111 +1,120 @@
1
- import { StylesConfig } from 'react-select';
2
- import { useTheme } from 'styled-components';
3
- import { IReactSelectValue } from '.';
4
-
5
- export const useReactSelectCustomStyles = (): StylesConfig<IReactSelectValue, false> => {
6
- const theme = useTheme() as any;
7
-
8
- return {
9
- control: (provided, { isFocused, isDisabled }) => ({
10
- ...provided,
11
- color: theme.colors.neutral800,
12
- backgroundColor: provided.backgroundColor,
13
- minHeight: '40px',
14
- lineHeight: 1.4,
15
- borderRadius: theme.borderRadius,
16
- fontSize: theme.fontSizes[2],
17
- borderColor: isFocused ? theme.colors.buttonPrimary600 : theme.colors.neutral200,
18
- boxShadow: isFocused ? `${theme.colors.buttonPrimary600} 0px 0px 0px 2px` : 'none',
19
-
20
- ':hover': {
21
- borderColor: isFocused ? theme.colors.buttonPrimary600 : theme.colors.neutral200
22
- }
23
- }),
24
- placeholder: (provided) => ({
25
- ...provided,
26
- color: theme.colors.neutral500
27
- }),
28
- menu: (provided) => ({
29
- ...provided,
30
- border: `1px solid ${theme.colors.neutral150}`,
31
- boxShadow: '0px 1px 4px rgba(33, 33, 52, 0.1)',
32
- borderRadius: theme.borderRadius,
33
- color: theme.colors.neutral800
34
- }),
35
- menuList: (provided) => ({
36
- ...provided,
37
- paddingLeft: '4px',
38
- paddingRight: '4px'
39
- }),
40
- option: (provided, { isFocused, isSelected, isDisabled }) => ({
41
- ...provided,
42
- backgroundColor: isFocused ? theme.colors.primary100 : 'transparent',
43
- fontSize: theme.fontSizes[2],
44
- borderRadius: theme.borderRadius,
45
- color: isSelected ? theme.colors.buttonPrimary600 : theme.colors.neutral800,
46
- fontWeight: isSelected ? 700 : 400,
47
- minHeight: '40px',
48
- display: 'flex',
49
- justifyContent: 'center',
50
- flexDirection: 'column',
51
- gap: '4px',
52
- opacity: isDisabled ? 0.7 : 1,
53
-
54
- 'span:not(:first-of-type)': {
55
- fontSize: theme.fontSizes[1],
56
- color: theme.colors.neutral500
57
- },
58
-
59
- '&:active': {
60
- backgroundColor: theme.colors.primary100
61
- }
62
- }),
63
- loadingIndicator: (provided) => ({
64
- ...provided,
65
- '>span': {
66
- backgroundColor: theme.colors.buttonPrimary600
67
- }
68
- }),
69
- loadingMessage: (provided) => ({
70
- ...provided,
71
- color: theme.colors.neutral500
72
- }),
73
- noOptionsMessage: (provided) => ({
74
- ...provided,
75
- color: theme.colors.neutral500
76
- }),
77
- clearIndicator: (provided, { isFocused }) => ({
78
- ...provided,
79
- cursor: 'pointer',
80
- display: 'flex',
81
- alignItems: 'center',
82
- justifyContent: 'center',
83
- paddingLeft: '6px',
84
- paddingRight: '6px',
85
- svg: {
86
- width: '0.6875rem',
87
- path: {
88
- fill: theme.colors.neutral600
89
- }
90
- },
91
- ':hover svg path': {
92
- fill: theme.colors.neutral700
93
- }
94
- }),
95
-
96
- dropdownIndicator: (provided, { isFocused }) => ({
97
- ...provided,
98
- display: 'flex',
99
- alignItems: 'center',
100
- justifyContent: 'center',
101
- paddingLeft: '6px',
102
- paddingRight: '12px',
103
- svg: {
104
- width: '0.375rem',
105
- path: {
106
- fill: theme.colors.neutral600
107
- }
108
- }
109
- })
110
- };
111
- };
1
+ import { StylesConfig } from 'react-select';
2
+ import { useTheme } from 'styled-components';
3
+ import { IReactSelectValue } from '.';
4
+
5
+ export const useReactSelectCustomStyles = (): StylesConfig<IReactSelectValue, false> => {
6
+ const theme = useTheme() as Record<string, any>;
7
+
8
+ return {
9
+ control: (provided, { isFocused, isDisabled }) => ({
10
+ ...provided,
11
+ color: theme.colors.neutral800,
12
+ backgroundColor: theme.colors.neutral0,
13
+ minHeight: '40px',
14
+ lineHeight: 1.4,
15
+ borderRadius: theme.borderRadius,
16
+ fontSize: theme.fontSizes[2],
17
+ borderColor: isFocused ? theme.colors.buttonPrimary600 : theme.colors.neutral200,
18
+ boxShadow: isFocused ? `${theme.colors.buttonPrimary600} 0px 0px 0px 2px` : 'none',
19
+
20
+ ':hover': {
21
+ borderColor: isFocused ? theme.colors.buttonPrimary600 : theme.colors.neutral200
22
+ }
23
+ }),
24
+
25
+ input: (provided, {}) => ({
26
+ ...provided,
27
+ color: theme.colors.neutral800
28
+ }),
29
+ singleValue: (provided, {}) => ({
30
+ ...provided,
31
+ color: theme.colors.neutral800
32
+ }),
33
+ placeholder: (provided) => ({
34
+ ...provided,
35
+ color: theme.colors.neutral500
36
+ }),
37
+ menu: (provided) => ({
38
+ ...provided,
39
+ border: `1px solid ${theme.colors.neutral150}`,
40
+ boxShadow: '0px 1px 4px rgba(33, 33, 52, 0.1)',
41
+ borderRadius: theme.borderRadius,
42
+ backgroundColor: theme.colors.neutral0
43
+ }),
44
+ menuList: (provided) => ({
45
+ ...provided,
46
+ paddingLeft: '4px',
47
+ paddingRight: '4px'
48
+ }),
49
+ option: (provided, { isFocused, isSelected, isDisabled }) => ({
50
+ ...provided,
51
+ backgroundColor: isFocused ? theme.colors.primary100 : 'transparent',
52
+ fontSize: theme.fontSizes[2],
53
+ borderRadius: theme.borderRadius,
54
+ color: isSelected ? theme.colors.buttonPrimary600 : theme.colors.neutral800,
55
+ fontWeight: isSelected ? 700 : 400,
56
+ minHeight: '40px',
57
+ display: 'flex',
58
+ justifyContent: 'center',
59
+ flexDirection: 'column',
60
+ gap: '4px',
61
+ opacity: isDisabled ? 0.7 : 1,
62
+
63
+ 'span:not(:first-of-type)': {
64
+ fontSize: theme.fontSizes[1],
65
+ color: theme.colors.neutral500
66
+ },
67
+
68
+ '&:active': {
69
+ backgroundColor: theme.colors.primary100
70
+ }
71
+ }),
72
+ loadingIndicator: (provided) => ({
73
+ ...provided,
74
+ '>span': {
75
+ backgroundColor: theme.colors.buttonPrimary600
76
+ }
77
+ }),
78
+ loadingMessage: (provided) => ({
79
+ ...provided,
80
+ color: theme.colors.neutral500
81
+ }),
82
+ noOptionsMessage: (provided) => ({
83
+ ...provided,
84
+ color: theme.colors.neutral500
85
+ }),
86
+ clearIndicator: (provided, { isFocused }) => ({
87
+ ...provided,
88
+ cursor: 'pointer',
89
+ display: 'flex',
90
+ alignItems: 'center',
91
+ justifyContent: 'center',
92
+ paddingLeft: '6px',
93
+ paddingRight: '6px',
94
+ svg: {
95
+ width: '0.6875rem',
96
+ path: {
97
+ fill: theme.colors.neutral600
98
+ }
99
+ },
100
+ ':hover svg path': {
101
+ fill: theme.colors.neutral700
102
+ }
103
+ }),
104
+
105
+ dropdownIndicator: (provided, { isFocused }) => ({
106
+ ...provided,
107
+ display: 'flex',
108
+ alignItems: 'center',
109
+ justifyContent: 'center',
110
+ paddingLeft: '6px',
111
+ paddingRight: '12px',
112
+ svg: {
113
+ width: '0.375rem',
114
+ path: {
115
+ fill: theme.colors.neutral600
116
+ }
117
+ }
118
+ })
119
+ };
120
+ };
@@ -1,22 +1,22 @@
1
- import styled, { css } from 'styled-components';
2
-
3
- const Wrapper = styled.div`
4
- ${({ theme }) => css`
5
- width: 100%;
6
- display: flex;
7
- flex-direction: column;
8
- gap: 4px;
9
- `}
10
- `;
11
-
12
- const Option = styled.span`
13
- display: flex;
14
- flex-direction: column;
15
- `;
16
-
17
- const ComboboxStyles = {
18
- Wrapper,
19
- Option
20
- };
21
-
22
- export default ComboboxStyles;
1
+ import styled, { css } from 'styled-components';
2
+
3
+ const Wrapper = styled.div`
4
+ ${({ theme }) => css`
5
+ width: 100%;
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 4px;
9
+ `}
10
+ `;
11
+
12
+ const Option = styled.span`
13
+ display: flex;
14
+ flex-direction: column;
15
+ `;
16
+
17
+ const ComboboxStyles = {
18
+ Wrapper,
19
+ Option
20
+ };
21
+
22
+ export default ComboboxStyles;
@@ -1,90 +1,90 @@
1
- import React, { useEffect } from 'react';
2
- import { useIntl } from 'react-intl';
3
-
4
- import { ModalLayout, ModalBody, ModalHeader, ModalFooter, Button, Typography } from '@strapi/design-system';
5
-
6
- import getTrad from '../../utils/getTrad';
7
- import { sleep } from '../../../../shared/utils/sleep';
8
-
9
- export interface IConfirmModalProps {
10
- onSubmit: () => void;
11
- closeModal: () => void;
12
- isOpen?: boolean;
13
- title?: React.ReactNode;
14
- body?: React.ReactNode;
15
- submitText?: string;
16
- }
17
-
18
- const ConfirmModal = ({
19
- isOpen,
20
- title,
21
- body,
22
- submitText,
23
- onSubmit = () => {
24
- console.warn('Modal submit function not set');
25
- },
26
- closeModal = () => {
27
- console.warn('Modal close function not set');
28
- }
29
- }: IConfirmModalProps): JSX.Element => {
30
- const { formatMessage } = useIntl();
31
-
32
- const colors = {
33
- text: 'neutral800',
34
- icon: 'neutral900'
35
- };
36
-
37
- const fixModalFocus = async () => {
38
- if (isOpen) {
39
- // To make sure the modal's focus trap works, we need to wait a short time before blurring the document
40
- await sleep(100);
41
- // @ts-ignore
42
- document?.activeElement?.blur();
43
- }
44
- };
45
-
46
- useEffect(() => {
47
- fixModalFocus();
48
- }, [isOpen]);
49
-
50
- if (!isOpen) {
51
- return <></>;
52
- }
53
-
54
- return (
55
- <ModalLayout onClose={closeModal} labelledBy="title" width="500px">
56
- <ModalHeader>
57
- <Typography fontWeight="bold" textColor={colors.text} as="h2" id="title">
58
- {title}
59
- </Typography>
60
- </ModalHeader>
61
-
62
- {/* New Strapi design system overflow styling breaks dropdowns in modals */}
63
- <ModalBody style={{ overflow: 'initial' }}>
64
- <Typography textColor={colors.text} id="body">
65
- {body}
66
- </Typography>
67
- </ModalBody>
68
-
69
- <ModalFooter
70
- startActions={
71
- <Button onClick={closeModal} variant="tertiary">
72
- {formatMessage({
73
- id: getTrad('template.confirmModal.buttons.cancel')
74
- })}
75
- </Button>
76
- }
77
- endActions={
78
- <Button onClick={onSubmit}>
79
- {submitText ||
80
- formatMessage({
81
- id: getTrad('template.confirmModal.buttons.submit')
82
- })}
83
- </Button>
84
- }
85
- />
86
- </ModalLayout>
87
- );
88
- };
89
-
90
- export default ConfirmModal;
1
+ import React, { useEffect } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+
4
+ import { ModalLayout, ModalBody, ModalHeader, ModalFooter, Button, Typography } from '@strapi/design-system';
5
+
6
+ import getTrad from '../../utils/getTrad';
7
+ import { sleep } from '../../../../shared/utils/sleep';
8
+
9
+ export interface IConfirmModalProps {
10
+ onSubmit: () => void;
11
+ closeModal: () => void;
12
+ isOpen?: boolean;
13
+ title?: React.ReactNode;
14
+ body?: React.ReactNode;
15
+ submitText?: string;
16
+ }
17
+
18
+ const ConfirmModal = ({
19
+ isOpen,
20
+ title,
21
+ body,
22
+ submitText,
23
+ onSubmit = () => {
24
+ console.warn('Modal submit function not set');
25
+ },
26
+ closeModal = () => {
27
+ console.warn('Modal close function not set');
28
+ }
29
+ }: IConfirmModalProps): JSX.Element => {
30
+ const { formatMessage } = useIntl();
31
+
32
+ const colors = {
33
+ text: 'neutral800',
34
+ icon: 'neutral900'
35
+ };
36
+
37
+ const fixModalFocus = async () => {
38
+ if (isOpen) {
39
+ // To make sure the modal's focus trap works, we need to wait a short time before blurring the document
40
+ await sleep(100);
41
+ // @ts-ignore
42
+ document?.activeElement?.blur();
43
+ }
44
+ };
45
+
46
+ useEffect(() => {
47
+ fixModalFocus();
48
+ }, [isOpen]);
49
+
50
+ if (!isOpen) {
51
+ return <></>;
52
+ }
53
+
54
+ return (
55
+ <ModalLayout onClose={closeModal} labelledBy="title" width="500px">
56
+ <ModalHeader>
57
+ <Typography fontWeight="bold" textColor={colors.text} as="h2" id="title">
58
+ {title}
59
+ </Typography>
60
+ </ModalHeader>
61
+
62
+ {/* New Strapi design system overflow styling breaks dropdowns in modals */}
63
+ <ModalBody style={{ overflow: 'initial' }}>
64
+ <Typography textColor={colors.text} id="body">
65
+ {body}
66
+ </Typography>
67
+ </ModalBody>
68
+
69
+ <ModalFooter
70
+ startActions={
71
+ <Button onClick={closeModal} variant="tertiary">
72
+ {formatMessage({
73
+ id: getTrad('template.confirmModal.buttons.cancel')
74
+ })}
75
+ </Button>
76
+ }
77
+ endActions={
78
+ <Button onClick={onSubmit}>
79
+ {submitText ||
80
+ formatMessage({
81
+ id: getTrad('template.confirmModal.buttons.submit')
82
+ })}
83
+ </Button>
84
+ }
85
+ />
86
+ </ModalLayout>
87
+ );
88
+ };
89
+
90
+ export default ConfirmModal;