@plone/volto 17.0.0-alpha.2 → 17.0.0-alpha.20

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 (297) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +530 -15
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +11 -14
  5. package/addon-registry.js +34 -0
  6. package/create-theme-addons-loader.js +79 -0
  7. package/cypress/support/commands.js +56 -4
  8. package/cypress/support/volto-slate.js +4 -5
  9. package/docker-compose.yml +1 -1
  10. package/locales/ca/LC_MESSAGES/volto.po +272 -6
  11. package/locales/ca.json +1 -1
  12. package/locales/de/LC_MESSAGES/volto.po +291 -25
  13. package/locales/de.json +1 -1
  14. package/locales/en/LC_MESSAGES/volto.po +271 -5
  15. package/locales/en.json +1 -1
  16. package/locales/es/LC_MESSAGES/volto.po +281 -15
  17. package/locales/es.json +1 -1
  18. package/locales/eu/LC_MESSAGES/volto.po +272 -6
  19. package/locales/eu.json +1 -1
  20. package/locales/fi/LC_MESSAGES/volto.po +4882 -0
  21. package/locales/fi.json +1 -1
  22. package/locales/fr/LC_MESSAGES/volto.po +272 -6
  23. package/locales/fr.json +1 -1
  24. package/locales/it/LC_MESSAGES/volto.po +273 -7
  25. package/locales/it.json +1 -1
  26. package/locales/ja/LC_MESSAGES/volto.po +272 -6
  27. package/locales/ja.json +1 -1
  28. package/locales/nl/LC_MESSAGES/volto.po +927 -649
  29. package/locales/nl.json +1 -1
  30. package/locales/pt/LC_MESSAGES/volto.po +272 -6
  31. package/locales/pt.json +1 -1
  32. package/locales/pt_BR/LC_MESSAGES/volto.po +281 -15
  33. package/locales/pt_BR.json +1 -1
  34. package/locales/ro/LC_MESSAGES/volto.po +272 -6
  35. package/locales/ro.json +1 -1
  36. package/locales/volto.pot +272 -6
  37. package/locales/zh_CN/LC_MESSAGES/volto.po +272 -6
  38. package/locales/zh_CN.json +1 -1
  39. package/package.json +5 -3
  40. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  41. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  42. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  43. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  44. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  45. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  46. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  47. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  48. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  49. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  50. package/packages/volto-slate/package.json +1 -1
  51. package/packages/volto-slate/src/actions/index.js +1 -1
  52. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  53. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  54. package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
  55. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  56. package/packages/volto-slate/src/blocks/Text/TextBlockView.jsx +20 -16
  57. package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
  58. package/packages/volto-slate/src/blocks/Text/index.js +10 -2
  59. package/packages/volto-slate/src/editor/config.jsx +5 -4
  60. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  61. package/packages/volto-slate/src/editor/index.js +4 -4
  62. package/packages/volto-slate/src/editor/less/slate.less +28 -0
  63. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  64. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  65. package/packages/volto-slate/src/editor/render.jsx +68 -8
  66. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  67. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  68. package/packages/volto-slate/src/index.js +2 -2
  69. package/packages/volto-slate/src/utils/blocks.js +7 -0
  70. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  71. package/razzle.config.js +28 -0
  72. package/src/actions/index.js +6 -0
  73. package/src/actions/language/language.js +9 -8
  74. package/src/actions/querystringsearch/querystringsearch.js +20 -14
  75. package/src/actions/relations/rebuild.js +25 -0
  76. package/src/actions/relations/relations.js +86 -0
  77. package/src/actions/relations/relations.test.js +15 -0
  78. package/src/components/index.js +2 -0
  79. package/src/components/manage/Add/Add.jsx +2 -2
  80. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  81. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  82. package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
  83. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  84. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  85. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  86. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  87. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  88. package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
  89. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
  90. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  91. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  92. package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
  93. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  94. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  95. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  96. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  97. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  98. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  99. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  100. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  101. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +6 -1
  102. package/src/components/manage/Blocks/Image/Edit.jsx +11 -7
  103. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
  104. package/src/components/manage/Blocks/Image/schema.js +11 -0
  105. package/src/components/manage/Blocks/Listing/DefaultTemplate.jsx +18 -3
  106. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -14
  107. package/src/components/manage/Blocks/Listing/ListingBody.jsx +30 -8
  108. package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
  109. package/src/components/manage/Blocks/Listing/getAsyncData.js +9 -3
  110. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +26 -18
  111. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
  112. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  113. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  114. package/src/components/manage/Blocks/Search/components/Facets.jsx +64 -4
  115. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
  116. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  117. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  118. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +2 -2
  119. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +43 -15
  120. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  121. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  122. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  123. package/src/components/manage/Blocks/Search/schema.js +16 -1
  124. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  125. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +20 -15
  126. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  127. package/src/components/manage/Blocks/Title/View.jsx +15 -5
  128. package/src/components/manage/Blocks/Title/View.test.jsx +16 -1
  129. package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
  130. package/src/components/manage/Blocks/ToC/View.jsx +8 -1
  131. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +17 -4
  132. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +148 -10
  133. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  134. package/src/components/manage/Contents/Contents.jsx +39 -26
  135. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  136. package/src/components/manage/Contents/ContentsUploadModal.jsx +10 -5
  137. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
  138. package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
  139. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  140. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  141. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  142. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  143. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  144. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  145. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  146. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  147. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  148. package/src/components/manage/Form/Form.jsx +5 -3
  149. package/src/components/manage/Form/InlineForm.jsx +39 -9
  150. package/src/components/manage/Form/InlineFormState.js +8 -0
  151. package/src/components/manage/History/History.jsx +11 -1
  152. package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
  153. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +97 -0
  154. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  155. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  156. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  157. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  158. package/src/components/manage/Sharing/Sharing.jsx +5 -1
  159. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  160. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  161. package/src/components/manage/TemplateChooser/template.svg +10 -0
  162. package/src/components/manage/Toast/Toast.jsx +2 -2
  163. package/src/components/manage/Toolbar/More.jsx +15 -0
  164. package/src/components/manage/Toolbar/Types.jsx +2 -2
  165. package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
  166. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
  167. package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
  168. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  169. package/src/components/manage/Widgets/FileWidget.jsx +2 -1
  170. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  171. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  172. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  173. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  174. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +8 -3
  175. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  176. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  177. package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
  178. package/src/components/theme/Anontools/Anontools.jsx +44 -72
  179. package/src/components/theme/Anontools/Anontools.stories.jsx +16 -6
  180. package/src/components/theme/Anontools/Anontools.test.jsx +16 -2
  181. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  182. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  183. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  184. package/src/components/theme/Component/Component.jsx +1 -1
  185. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  186. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  187. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  188. package/src/components/theme/Footer/Footer.jsx +2 -13
  189. package/src/components/theme/Header/Header.jsx +37 -63
  190. package/src/components/theme/Header/Header.test.jsx +18 -0
  191. package/src/components/theme/Icon/Icon.jsx +2 -2
  192. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  193. package/src/components/theme/Login/Login.jsx +1 -0
  194. package/src/components/theme/Logo/Logo.jsx +2 -1
  195. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  196. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  197. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  198. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
  199. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
  200. package/src/components/theme/SearchWidget/SearchWidget.jsx +38 -98
  201. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  202. package/src/components/theme/View/AlbumView.jsx +9 -1
  203. package/src/components/theme/View/DefaultView.jsx +1 -1
  204. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  205. package/src/components/theme/View/EventView.jsx +6 -2
  206. package/src/components/theme/View/FileView.jsx +23 -18
  207. package/src/components/theme/View/ImageView.jsx +37 -32
  208. package/src/components/theme/View/LinkView.jsx +53 -78
  209. package/src/components/theme/View/ListingView.jsx +33 -27
  210. package/src/components/theme/View/NewsItemView.jsx +10 -5
  211. package/src/components/theme/View/RenderBlocks.jsx +56 -21
  212. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  213. package/src/components/theme/View/SummaryView.jsx +47 -38
  214. package/src/components/theme/View/TabularView.jsx +59 -53
  215. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  216. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  217. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  218. package/src/config/Blocks.jsx +44 -0
  219. package/src/config/ControlPanels.js +2 -0
  220. package/src/config/NonContentRoutes.jsx +1 -0
  221. package/src/config/RichTextEditor/Blocks.jsx +2 -2
  222. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  223. package/src/config/RichTextEditor/Styles.jsx +1 -1
  224. package/src/config/Widgets.jsx +2 -0
  225. package/src/config/index.js +24 -0
  226. package/src/config/server.js +2 -0
  227. package/src/constants/ActionTypes.js +4 -0
  228. package/src/constants/Indexes.js +3 -1
  229. package/src/constants/Languages.js +8 -4
  230. package/src/express-middleware/devproxy.js +1 -1
  231. package/src/express-middleware/files.js +3 -3
  232. package/src/express-middleware/images.js +4 -4
  233. package/src/express-middleware/ok.js +16 -0
  234. package/src/express-middleware/robotstxt.js +1 -1
  235. package/src/express-middleware/sitemap.js +37 -5
  236. package/src/express-middleware/static.js +3 -3
  237. package/src/helpers/Api/Api.js +1 -1
  238. package/src/helpers/Blocks/Blocks.js +48 -0
  239. package/src/helpers/Blocks/Blocks.test.js +79 -0
  240. package/src/helpers/Extensions/index.js +2 -1
  241. package/src/helpers/Extensions/withBlockSchemaEnhancer.js +15 -11
  242. package/src/helpers/Extensions/withBlockSchemaEnhancer.test.js +145 -0
  243. package/src/helpers/FormValidation/FormValidation.js +40 -2
  244. package/src/helpers/FormValidation/FormValidation.test.js +73 -0
  245. package/src/helpers/Html/Html.jsx +3 -1
  246. package/src/helpers/Html/Html.test.jsx +5 -0
  247. package/src/helpers/MessageLabels/MessageLabels.js +80 -0
  248. package/src/helpers/Robots/Robots.js +24 -6
  249. package/src/helpers/ScrollToTop/ScrollToTop.jsx +5 -3
  250. package/src/helpers/Sitemap/Sitemap.js +44 -2
  251. package/src/helpers/Url/Url.js +27 -6
  252. package/src/helpers/Url/Url.test.js +26 -0
  253. package/src/helpers/Utils/UseDetectClickOutside.stories.jsx +191 -0
  254. package/src/helpers/Utils/Utils.js +63 -13
  255. package/src/helpers/Utils/Utils.test.js +4 -4
  256. package/src/helpers/Utils/usePagination.js +67 -14
  257. package/src/helpers/Utils/usePagination.test.js +115 -0
  258. package/src/helpers/index.js +20 -10
  259. package/src/hooks/client/useClient.js +11 -0
  260. package/src/hooks/clipboard/useClipboard.js +26 -0
  261. package/src/hooks/index.js +2 -0
  262. package/src/icons/grid-block.svg +11 -0
  263. package/src/middleware/Api.test.js +54 -0
  264. package/src/middleware/api.js +24 -6
  265. package/src/middleware/index.js +2 -2
  266. package/src/reducers/actions/actions.js +8 -6
  267. package/src/reducers/actions/actions.test.js +70 -0
  268. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  269. package/src/reducers/index.js +2 -0
  270. package/src/reducers/navigation/navigation.js +1 -1
  271. package/src/reducers/relations/relations.js +173 -0
  272. package/src/reducers/types/types.js +1 -1
  273. package/src/routes.js +14 -0
  274. package/src/server.jsx +28 -23
  275. package/src/start-server.js +2 -2
  276. package/test-setup-config.js +2 -0
  277. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  278. package/theme/themes/pastanaga/extras/contents.less +1 -0
  279. package/theme/themes/pastanaga/extras/grid.less +426 -0
  280. package/theme/themes/pastanaga/extras/main.less +3 -1
  281. package/theme/themes/pastanaga/extras/search.less +6 -0
  282. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  283. package/theme/themes/pastanaga/extras/toc.less +29 -0
  284. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
  285. package/.changelog.draft +0 -31
  286. package/.editorconfig +0 -36
  287. package/.storybook/main.js +0 -127
  288. package/.storybook/manager.js +0 -15
  289. package/.storybook/preview.js +0 -21
  290. package/.storybook/static/previewImage.svg +0 -48
  291. package/.yarnrc.yml +0 -5
  292. package/jsdoc.json +0 -16
  293. package/netlify.toml +0 -5
  294. package/pyvenv.cfg +0 -3
  295. package/share/man/man1/ttx.1 +0 -225
  296. package/src/components/theme/Header/Header.md +0 -27
  297. package/towncrier.toml +0 -33
@@ -33,17 +33,22 @@ const BlockChooser = ({
33
33
  properties = {},
34
34
  }) => {
35
35
  const intl = useIntl();
36
- const useAllowedBlocks = !isEmpty(allowedBlocks);
36
+ const hasAllowedBlocks = !isEmpty(allowedBlocks);
37
37
 
38
38
  const filteredBlocksConfig = filter(blocksConfig, (item) => {
39
+ // Check if the block is well formed (has at least id and title)
40
+ const blockIsWellFormed = Boolean(item.title && item.id);
41
+ if (!blockIsWellFormed) {
42
+ return false;
43
+ }
39
44
  if (showRestricted) {
40
- if (useAllowedBlocks) {
45
+ if (hasAllowedBlocks) {
41
46
  return allowedBlocks.includes(item.id);
42
47
  } else {
43
48
  return true;
44
49
  }
45
50
  } else {
46
- if (useAllowedBlocks) {
51
+ if (hasAllowedBlocks) {
47
52
  return allowedBlocks.includes(item.id);
48
53
  } else {
49
54
  // Overload restricted as a function, so we can decide the availability of a block
@@ -89,14 +94,18 @@ const BlockChooser = ({
89
94
  function blocksAvailableFilter(blocks) {
90
95
  return blocks.filter(
91
96
  (block) =>
92
- getFormatMessage(block.title).toLowerCase().includes(filterValue) ||
97
+ getFormatMessage(block.title)
98
+ .toLowerCase()
99
+ .includes(filterValue.toLowerCase()) ||
93
100
  filterVariations(block)?.length,
94
101
  );
95
102
  }
96
103
  function filterVariations(block) {
97
104
  return block.variations?.filter(
98
105
  (variation) =>
99
- getFormatMessage(variation.title).toLowerCase().includes(filterValue) &&
106
+ getFormatMessage(variation.title)
107
+ .toLowerCase()
108
+ .includes(filterValue.toLowerCase()) &&
100
109
  !variation.title.toLowerCase().includes('default'),
101
110
  );
102
111
  }
@@ -108,6 +108,11 @@ config.blocks.blocksConfig = {
108
108
  restricted: false,
109
109
  mostUsed: false,
110
110
  },
111
+ malformedBlock: {
112
+ icon: blockSVG,
113
+ group: 'common',
114
+ restricted: false,
115
+ },
111
116
  };
112
117
 
113
118
  const mockStore = configureStore();
@@ -40,8 +40,12 @@ const BlocksForm = (props) => {
40
40
  manage,
41
41
  children,
42
42
  isMainForm = true,
43
+ isContainer,
44
+ stopPropagation,
45
+ disableAddBlockOnEnterKey,
43
46
  blocksConfig = config.blocks.blocksConfig,
44
47
  editable = true,
48
+ direction = 'vertical',
45
49
  } = props;
46
50
 
47
51
  const blockList = getBlocks(properties);
@@ -83,7 +87,9 @@ const BlocksForm = (props) => {
83
87
  e.preventDefault();
84
88
  }
85
89
  if (e.key === 'Enter' && !disableEnter) {
86
- onSelectBlock(onAddBlock(config.settings.defaultBlockType, index + 1));
90
+ if (!disableAddBlockOnEnterKey) {
91
+ onSelectBlock(onAddBlock(config.settings.defaultBlockType, index + 1));
92
+ }
87
93
  e.preventDefault();
88
94
  }
89
95
  };
@@ -199,7 +205,16 @@ const BlocksForm = (props) => {
199
205
  });
200
206
 
201
207
  return (
202
- <div className="blocks-form" ref={ref}>
208
+ <div
209
+ className="blocks-form"
210
+ role="presentation"
211
+ ref={ref}
212
+ onKeyDown={(e) => {
213
+ if (stopPropagation) {
214
+ e.stopPropagation();
215
+ }
216
+ }}
217
+ >
203
218
  <fieldset className="invisible" disabled={!editable}>
204
219
  <DragDropList
205
220
  childList={blockList}
@@ -216,6 +231,7 @@ const BlocksForm = (props) => {
216
231
  onChangeFormData(newFormData);
217
232
  return true;
218
233
  }}
234
+ direction={direction}
219
235
  >
220
236
  {(dragProps) => {
221
237
  const { child, childId, index } = dragProps;
@@ -250,6 +266,7 @@ const BlocksForm = (props) => {
250
266
  type: child['@type'],
251
267
  editable,
252
268
  showBlockChooser: selectedBlock === childId,
269
+ detached: isContainer,
253
270
  };
254
271
  return editBlockWrapper(
255
272
  dragProps,
@@ -158,7 +158,7 @@ export class Edit extends Component {
158
158
  )
159
159
  : null
160
160
  }
161
- className={cx(`block ${type} ${this.props.data.variation ?? ''}`, {
161
+ className={cx('block', type, this.props.data.variation, {
162
162
  selected: this.props.selected || this.props.multiSelected,
163
163
  multiSelected: this.props.multiSelected,
164
164
  })}
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import cx from 'classnames';
3
3
 
4
- export default ({ data, detached, children }) => {
4
+ export default function Style({ data, detached, children }) {
5
5
  return (
6
6
  <div
7
7
  className={cx(
@@ -25,4 +25,4 @@ export default ({ data, detached, children }) => {
25
25
  </div>
26
26
  </div>
27
27
  );
28
- };
28
+ }
@@ -0,0 +1,32 @@
1
+ import { useIntl } from 'react-intl';
2
+ import { BlockDataForm } from '@plone/volto/components';
3
+
4
+ const ContainerData = (props) => {
5
+ const { block, blocksConfig, data, onChangeBlock } = props;
6
+ const intl = useIntl();
7
+
8
+ const schema = blocksConfig[data['@type']].blockSchema({ intl });
9
+ const dataAdapter = blocksConfig[data['@type']].dataAdapter;
10
+
11
+ return (
12
+ <BlockDataForm
13
+ schema={schema}
14
+ title={schema.title}
15
+ onChangeField={(id, value) => {
16
+ dataAdapter({
17
+ block,
18
+ data,
19
+ id,
20
+ onChangeBlock,
21
+ value,
22
+ });
23
+ }}
24
+ formData={data}
25
+ block={block}
26
+ onChangeBlock={onChangeBlock}
27
+ blocksConfig={blocksConfig}
28
+ />
29
+ );
30
+ };
31
+
32
+ export default ContainerData;
@@ -0,0 +1,174 @@
1
+ import { useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { pickBy } from 'lodash';
4
+ import { BlocksForm, SidebarPortal } from '@plone/volto/components';
5
+ import PropTypes from 'prop-types';
6
+ import ContainerData from './Data';
7
+ import DefaultEditBlockWrapper from './EditBlockWrapper';
8
+ import SimpleContainerToolbar from './SimpleContainerToolbar';
9
+ import { v4 as uuid } from 'uuid';
10
+ import { blocksFormGenerator } from '@plone/volto/helpers';
11
+
12
+ import DefaultTemplateChooser from '@plone/volto/components/manage/TemplateChooser/TemplateChooser';
13
+
14
+ import config from '@plone/volto/registry';
15
+
16
+ const ContainerBlockEdit = (props) => {
17
+ const {
18
+ block,
19
+ data,
20
+ direction = 'horizontal',
21
+ onChangeBlock,
22
+ onChangeField,
23
+ pathname,
24
+ selected,
25
+ manage,
26
+ } = props;
27
+
28
+ const intl = useIntl();
29
+ const blockType = data['@type'];
30
+ const metadata = props.metadata || props.properties;
31
+ const isInitialized = data?.blocks && data?.blocks_layout;
32
+ const properties = isInitialized ? data : blocksFormGenerator(0, '');
33
+ const blockConfig = config.blocks.blocksConfig[blockType];
34
+ const blocksConfig = blockConfig.blocksConfig || props.blocksConfig;
35
+ const allowedBlocks = blockConfig.allowedBlocks;
36
+ const maxLength = blockConfig.maxLength || 8;
37
+ const templates = blockConfig.templates;
38
+ const ContainerToolbar =
39
+ blockConfig.containerToolbar || SimpleContainerToolbar;
40
+
41
+ // Custom components from config or default ones
42
+ const TemplateChooser = blockConfig.templateChooser || DefaultTemplateChooser;
43
+ const EditBlockWrapper =
44
+ blockConfig.editBlockWrapper || DefaultEditBlockWrapper;
45
+
46
+ const [selectedBlock, setSelectedBlock] = useState(
47
+ properties.blocks_layout.items[0],
48
+ );
49
+
50
+ const blockState = {};
51
+
52
+ const onAddNewBlock = () => {
53
+ const newuuid = uuid();
54
+ const type = allowedBlocks?.length === 1 ? allowedBlocks[0] : null;
55
+ const blocks = data.blocks || properties.blocks;
56
+ const blocks_layout = data.blocks_layout || properties.blocks_layout;
57
+ const newFormData = {
58
+ ...data,
59
+ blocks: {
60
+ ...blocks,
61
+ [newuuid]: { '@type': type || 'empty' },
62
+ },
63
+ blocks_layout: {
64
+ items: [...blocks_layout.items, newuuid],
65
+ },
66
+ };
67
+ if (blocks_layout.items.length < maxLength) {
68
+ onChangeBlock(block, {
69
+ ...newFormData,
70
+ });
71
+ }
72
+ };
73
+
74
+ const onSelectTemplate = (templateIndex) => {
75
+ const resultantTemplates =
76
+ allowedBlocks?.length === 1 ? templates(allowedBlocks[0]) : templates();
77
+ onChangeBlock(block, {
78
+ ...data,
79
+ ...resultantTemplates(intl)[templateIndex].blocksData,
80
+ });
81
+ };
82
+
83
+ const allowedBlocksConfig = pickBy(blocksConfig, (value, key) =>
84
+ allowedBlocks.includes(key),
85
+ );
86
+
87
+ const containerProps = {
88
+ ...props,
89
+ allowedBlocks,
90
+ allowedBlocksConfig,
91
+ blocksConfig,
92
+ blockType,
93
+ maxLength,
94
+ metadata,
95
+ onAddNewBlock,
96
+ onSelectTemplate,
97
+ selectedBlock,
98
+ setSelectedBlock,
99
+ templates,
100
+ };
101
+
102
+ return (
103
+ <>
104
+ {data.headline && <h2 className="headline">{data.headline}</h2>}
105
+
106
+ {selected && <ContainerToolbar {...containerProps} />}
107
+
108
+ {!isInitialized && templates && (
109
+ <TemplateChooser
110
+ templates={
111
+ allowedBlocks?.length === 1
112
+ ? templates(allowedBlocks[0])
113
+ : templates()
114
+ }
115
+ onSelectTemplate={onSelectTemplate}
116
+ />
117
+ )}
118
+
119
+ <BlocksForm
120
+ metadata={metadata}
121
+ properties={properties}
122
+ direction={direction}
123
+ manage={manage}
124
+ selectedBlock={selected ? selectedBlock : null}
125
+ blocksConfig={allowedBlocksConfig}
126
+ title={data.placeholder}
127
+ isContainer
128
+ stopPropagation={selectedBlock}
129
+ disableAddBlockOnEnterKey
130
+ onSelectBlock={(id) => {
131
+ setSelectedBlock(id);
132
+ }}
133
+ onChangeFormData={(newFormData) => {
134
+ onChangeBlock(block, {
135
+ ...data,
136
+ ...newFormData,
137
+ });
138
+ }}
139
+ onChangeField={(id, value) => {
140
+ if (['blocks', 'blocks_layout'].includes(id)) {
141
+ blockState[id] = value;
142
+ onChangeBlock(block, {
143
+ ...data,
144
+ ...blockState,
145
+ });
146
+ } else {
147
+ onChangeField(id, value);
148
+ }
149
+ }}
150
+ pathname={pathname}
151
+ >
152
+ {({ draginfo }, editBlock, blockProps) => (
153
+ <EditBlockWrapper draginfo={draginfo} blockProps={blockProps}>
154
+ {editBlock}
155
+ </EditBlockWrapper>
156
+ )}
157
+ </BlocksForm>
158
+ <SidebarPortal selected={selected && !selectedBlock}>
159
+ <ContainerData {...props}></ContainerData>
160
+ </SidebarPortal>
161
+ </>
162
+ );
163
+ };
164
+
165
+ ContainerBlockEdit.propTypes = {
166
+ block: PropTypes.string.isRequired,
167
+ onChangeBlock: PropTypes.func.isRequired,
168
+ pathname: PropTypes.string.isRequired,
169
+ selected: PropTypes.bool.isRequired,
170
+ manage: PropTypes.bool.isRequired,
171
+ direction: PropTypes.oneOf(['horizontal', 'vertical']),
172
+ };
173
+
174
+ export default ContainerBlockEdit;
@@ -0,0 +1,120 @@
1
+ import { Icon } from '@plone/volto/components';
2
+ import { Button } from 'semantic-ui-react';
3
+ import { defineMessages, useIntl } from 'react-intl';
4
+ import NewBlockAddButton from './NewBlockAddButton';
5
+ import cx from 'classnames';
6
+ import { isInteractiveElement } from '@plone/volto/helpers';
7
+
8
+ import clearSVG from '@plone/volto/icons/clear.svg';
9
+
10
+ const messages = defineMessages({
11
+ delete: {
12
+ id: 'Remove element {index}',
13
+ defaultMessage: 'Remove element {index}',
14
+ },
15
+ reset: {
16
+ id: 'Reset element {index}',
17
+ defaultMessage: 'Reset element {index}',
18
+ },
19
+ });
20
+
21
+ const EditBlockWrapper = (props) => {
22
+ const intl = useIntl();
23
+
24
+ const { blockProps, draginfo, children } = props;
25
+ const {
26
+ block,
27
+ selected,
28
+ type,
29
+ blocksConfig,
30
+ onChangeBlock,
31
+ onDeleteBlock,
32
+ onSelectBlock,
33
+ data,
34
+ index,
35
+ } = blockProps;
36
+
37
+ function onResetBlock() {
38
+ onChangeBlock(block, { '@type': 'empty' });
39
+ }
40
+
41
+ return (
42
+ <div
43
+ ref={draginfo.innerRef}
44
+ {...draginfo.draggableProps}
45
+ {...draginfo.dragHandleProps}
46
+ className={cx(`block-editor-${data['@type']} contained`, { selected })}
47
+ >
48
+ <div
49
+ role="presentation"
50
+ className="cell-wrapper"
51
+ onClick={(e) => {
52
+ onSelectBlock(block);
53
+ }}
54
+ >
55
+ {type !== 'empty' ? (
56
+ <Button
57
+ aria-label={intl.formatMessage(messages.reset, {
58
+ index,
59
+ })}
60
+ basic
61
+ icon
62
+ onClick={(e) => onResetBlock(block, {})}
63
+ className="remove-block-button"
64
+ >
65
+ <Icon name={clearSVG} className="circled" size="24px" />
66
+ </Button>
67
+ ) : (
68
+ <Button
69
+ basic
70
+ icon
71
+ className="remove-block-button"
72
+ onClick={(e) => onDeleteBlock(block, true)}
73
+ aria-label={intl.formatMessage(messages.delete, {
74
+ index,
75
+ })}
76
+ >
77
+ <Icon
78
+ name={clearSVG}
79
+ className="circled"
80
+ size="24px"
81
+ color="#e40166"
82
+ />
83
+ </Button>
84
+ )}
85
+ {type && type !== 'empty' ? (
86
+ <div className={cx('ui drag block inner', type)}>{children}</div>
87
+ ) : (
88
+ <div
89
+ className={cx('gridBlock-empty-placeholder', {
90
+ selected: selected,
91
+ })}
92
+ role="presentation"
93
+ onClick={(e) => {
94
+ onSelectBlock(block);
95
+ // If the click is in the button, then
96
+ if (isInteractiveElement(e.target)) {
97
+ e.stopPropagation();
98
+ }
99
+ }}
100
+ >
101
+ <p>Add a new block</p>
102
+ <NewBlockAddButton
103
+ block={block}
104
+ index={index}
105
+ blocksConfig={blocksConfig}
106
+ onMutateBlock={(block, value) =>
107
+ onChangeBlock(block, {
108
+ ...data,
109
+ ...value,
110
+ })
111
+ }
112
+ />
113
+ </div>
114
+ )}
115
+ </div>
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export default EditBlockWrapper;
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { Button, Ref } from 'semantic-ui-react';
3
+ import { defineMessages, useIntl } from 'react-intl';
4
+ import { BlockChooser, Icon } from '@plone/volto/components';
5
+ import { useDetectClickOutside } from '@plone/volto/helpers';
6
+ import addSVG from '@plone/volto/icons/add.svg';
7
+ import { usePopper } from 'react-popper';
8
+ import { Portal } from 'react-portal';
9
+
10
+ const messages = defineMessages({
11
+ addBlock: {
12
+ id: 'Add block in position {index}',
13
+ defaultMessage: 'Add block in position {index}',
14
+ },
15
+ });
16
+
17
+ const NewBlockAddButton = (props) => {
18
+ const intl = useIntl();
19
+ const { blocksConfig, block, index, onMutateBlock } = props;
20
+ const [isOpenMenu, setOpenMenu] = React.useState(false);
21
+
22
+ const blockChooserRef = useDetectClickOutside({
23
+ onTriggered: () => setOpenMenu(false),
24
+ triggerKeys: ['Escape'],
25
+ });
26
+
27
+ const [referenceElement, setReferenceElement] = React.useState(null);
28
+ const [popperElement, setPopperElement] = React.useState(null);
29
+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
30
+ placement: 'bottom',
31
+ modifiers: [
32
+ {
33
+ name: 'offset',
34
+ options: {
35
+ offset: [0, -30],
36
+ },
37
+ },
38
+ {
39
+ name: 'flip',
40
+ options: {
41
+ fallbackPlacements: ['right', 'top-start'],
42
+ },
43
+ },
44
+ ],
45
+ });
46
+
47
+ return (
48
+ <>
49
+ <Ref innerRef={setReferenceElement}>
50
+ <Button
51
+ basic
52
+ icon
53
+ onClick={() => setOpenMenu(true)}
54
+ className="add-block-button"
55
+ aria-label={intl.formatMessage(messages.addBlock, {
56
+ index,
57
+ })}
58
+ >
59
+ <Icon name={addSVG} className="circled" size="24px" />
60
+ </Button>
61
+ </Ref>
62
+ {isOpenMenu ? (
63
+ <Portal node={document.getElementById('body')}>
64
+ <div
65
+ ref={setPopperElement}
66
+ style={styles.popper}
67
+ {...attributes.popper}
68
+ className="container-chooser-wrapper"
69
+ >
70
+ <BlockChooser
71
+ onMutateBlock={onMutateBlock}
72
+ currentBlock={block}
73
+ showRestricted
74
+ blocksConfig={blocksConfig}
75
+ ref={blockChooserRef}
76
+ />
77
+ </div>
78
+ </Portal>
79
+ ) : null}
80
+ </>
81
+ );
82
+ };
83
+
84
+ export default NewBlockAddButton;
@@ -0,0 +1,54 @@
1
+ import { defineMessages, useIntl } from 'react-intl';
2
+ import { Button } from 'semantic-ui-react';
3
+ import { Icon } from '@plone/volto/components';
4
+
5
+ import addSVG from '@plone/volto/icons/add.svg';
6
+ import configSVG from '@plone/volto/icons/configuration.svg';
7
+
8
+ const messages = defineMessages({
9
+ addBlock: {
10
+ id: 'Add element to container',
11
+ defaultMessage: 'Add element to container',
12
+ },
13
+ blockSettings: {
14
+ id: 'Container settings',
15
+ defaultMessage: 'Container settings',
16
+ },
17
+ });
18
+
19
+ const SimpleContainerToolbar = (props) => {
20
+ const { data, onAddNewBlock, maxLength, setSelectedBlock } = props;
21
+ const intl = useIntl();
22
+
23
+ return (
24
+ <div className="toolbar">
25
+ <Button.Group>
26
+ <Button
27
+ aria-label={intl.formatMessage(messages.addBlock)}
28
+ icon
29
+ basic
30
+ disabled={data?.blocks_layout?.items?.length >= maxLength}
31
+ onClick={(e) => onAddNewBlock()}
32
+ >
33
+ <Icon name={addSVG} size="24px" />
34
+ </Button>
35
+ </Button.Group>
36
+ <Button.Group>
37
+ <Button
38
+ aria-label={intl.formatMessage(messages.blockSettings)}
39
+ icon
40
+ basic
41
+ onClick={(e) => {
42
+ e.stopPropagation();
43
+ setSelectedBlock();
44
+ props.setSidebarTab(1);
45
+ }}
46
+ >
47
+ <Icon name={configSVG} size="24px" />
48
+ </Button>
49
+ </Button.Group>
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default SimpleContainerToolbar;
@@ -0,0 +1,33 @@
1
+ import PropTypes from 'prop-types';
2
+ import cx from 'classnames';
3
+ import ContainerEdit from '../Container/Edit';
4
+
5
+ const GridBlockEdit = (props) => {
6
+ const { data } = props;
7
+
8
+ const columnsLength = data?.blocks_layout?.items?.length || 0;
9
+
10
+ return (
11
+ <div
12
+ className={cx({
13
+ one: columnsLength === 1,
14
+ two: columnsLength === 2,
15
+ three: columnsLength === 3,
16
+ four: columnsLength >= 4,
17
+ 'grid-items': true,
18
+ })}
19
+ >
20
+ <ContainerEdit {...props} direction="horizontal" />
21
+ </div>
22
+ );
23
+ };
24
+
25
+ GridBlockEdit.propTypes = {
26
+ block: PropTypes.string.isRequired,
27
+ onChangeBlock: PropTypes.func.isRequired,
28
+ pathname: PropTypes.string.isRequired,
29
+ selected: PropTypes.bool.isRequired,
30
+ manage: PropTypes.bool.isRequired,
31
+ };
32
+
33
+ export default GridBlockEdit;
@@ -0,0 +1,43 @@
1
+ import { Grid } from 'semantic-ui-react';
2
+ import cx from 'classnames';
3
+ import { RenderBlocks } from '@plone/volto/components';
4
+ import { withBlockExtensions } from '@plone/volto/helpers';
5
+ import config from '@plone/volto/registry';
6
+
7
+ const GridBlockView = (props) => {
8
+ const { data, path, className } = props;
9
+ const metadata = props.metadata || props.properties;
10
+ const columns = data.blocks_layout.items;
11
+ const blocksConfig =
12
+ config.blocks.blocksConfig[data['@type']].blocksConfig ||
13
+ props.blocksConfig;
14
+ const location = {
15
+ pathname: path,
16
+ };
17
+ return (
18
+ <div
19
+ className={cx('block', data['@type'], className, {
20
+ one: columns?.length === 1,
21
+ two: columns?.length === 2,
22
+ three: columns?.length === 3,
23
+ four: columns?.length === 4,
24
+ })}
25
+ >
26
+ {data.headline && <h2 className="headline">{data.headline}</h2>}
27
+
28
+ <Grid stackable stretched columns={columns.length}>
29
+ <RenderBlocks
30
+ {...props}
31
+ blockWrapperTag={Grid.Column}
32
+ metadata={metadata}
33
+ content={data}
34
+ location={location}
35
+ blocksConfig={blocksConfig}
36
+ isContainer
37
+ />
38
+ </Grid>
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export default withBlockExtensions(GridBlockView);