@webbio/strapi-plugin-page-builder 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) 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 -120
  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 -104
  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 +93 -43
  39. package/custom.d.ts +5 -5
  40. package/dist/package.json +1 -1
  41. package/dist/server/controllers/platform.js +20 -0
  42. package/dist/server/graphql/page-by-path.js +90 -0
  43. package/dist/server/graphql/page-by-slug.js +9 -9
  44. package/dist/server/graphql/pages-by-uid.js +14 -14
  45. package/dist/server/schema/platform-start.json +31 -0
  46. package/dist/server/services/platform.js +36 -0
  47. package/dist/server/utils/graphql.js +18 -18
  48. package/dist/tsconfig.server.tsbuildinfo +1 -1
  49. package/package.json +71 -71
  50. package/server/bootstrap/collection-type-lifecycles.ts +47 -47
  51. package/server/bootstrap/permissions.ts +42 -42
  52. package/server/bootstrap.ts +198 -198
  53. package/server/config/index.ts +4 -4
  54. package/server/content-types/index.ts +1 -1
  55. package/server/controllers/collection-types.ts +27 -27
  56. package/server/controllers/index.ts +11 -11
  57. package/server/controllers/page-type.ts +13 -13
  58. package/server/controllers/page.ts +9 -9
  59. package/server/controllers/template.ts +16 -16
  60. package/server/destroy.ts +5 -5
  61. package/server/graphql/index.ts +9 -9
  62. package/server/graphql/page-by-slug.ts +98 -98
  63. package/server/graphql/page-type.ts +67 -67
  64. package/server/graphql/pages-by-uid.ts +127 -127
  65. package/server/index.ts +23 -23
  66. package/server/middlewares/index.ts +1 -1
  67. package/server/policies/index.ts +1 -1
  68. package/server/register.ts +15 -15
  69. package/server/routes/index.ts +58 -58
  70. package/server/schema/page-end.json +91 -91
  71. package/server/schema/page-start.json +87 -87
  72. package/server/schema/page-type-end.json +43 -43
  73. package/server/schema/page-type-start.json +38 -38
  74. package/server/schema/template-start.json +35 -35
  75. package/server/services/builder.ts +137 -137
  76. package/server/services/collection-types.ts +88 -88
  77. package/server/services/index.ts +13 -13
  78. package/server/services/page-type.ts +26 -26
  79. package/server/services/page.ts +24 -24
  80. package/server/services/template.ts +13 -13
  81. package/server/utils/filter-underscore-arguments.ts +12 -12
  82. package/server/utils/graphql.ts +113 -113
  83. package/server/utils/paginationValidation.ts +39 -39
  84. package/server/utils/reload-strapi-on-load.ts +13 -13
  85. package/server/utils/strapi.ts +45 -45
  86. package/shared/utils/constants.ts +4 -4
  87. package/shared/utils/sleep.ts +1 -1
  88. package/strapi-admin.js +3 -3
  89. package/strapi-server.js +3 -3
  90. package/tsconfig.json +20 -20
  91. package/tsconfig.server.json +25 -25
@@ -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,120 +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 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
+ 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;