@plone/volto-slate 18.0.0-alpha.8 → 18.0.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.
Files changed (33) hide show
  1. package/.stylelintrc +14 -0
  2. package/CHANGELOG.md +106 -0
  3. package/locales/pt_BR/LC_MESSAGES/volto.po +205 -0
  4. package/package.json +13 -2
  5. package/src/blocks/Table/TableBlockEdit.jsx +2 -1
  6. package/src/blocks/Table/TableBlockView.jsx +2 -2
  7. package/src/blocks/Table/index.js +0 -1
  8. package/src/blocks/Text/DefaultTextBlockEditor.jsx +2 -5
  9. package/src/blocks/Text/DetachedTextBlockEditor.jsx +12 -1
  10. package/src/blocks/Text/SlashMenu.jsx +16 -2
  11. package/src/blocks/Text/extensions/breakList.js +24 -9
  12. package/src/blocks/Text/extensions/insertBreak.js +1 -1
  13. package/src/blocks/Text/extensions/withDeserializers.js +1 -4
  14. package/src/blocks/Text/{index.js → index.jsx} +6 -22
  15. package/src/blocks/Text/keyboard/indentListItems.js +2 -2
  16. package/src/constants.js +3 -0
  17. package/src/editor/less/editor.less +14 -5
  18. package/src/editor/less/slate.less +3 -1
  19. package/src/editor/plugins/Markdown/constants.js +9 -9
  20. package/src/editor/plugins/Markdown/extensions.js +2 -2
  21. package/src/editor/ui/PositionedToolbar.jsx +9 -2
  22. package/src/elementEditor/PluginEditor.jsx +2 -1
  23. package/src/elementEditor/utils.js +24 -5
  24. package/src/utils/blocks.js +16 -1
  25. package/src/utils/volto-blocks.js +15 -2
  26. package/src/widgets/HtmlSlateWidget.jsx +1 -1
  27. package/src/widgets/ObjectByTypeWidget.jsx +2 -1
  28. package/src/widgets/RichTextWidget.jsx +1 -1
  29. /package/src/editor/plugins/Blockquote/{index.js → index.jsx} +0 -0
  30. /package/src/editor/plugins/Callout/{index.js → index.jsx} +0 -0
  31. /package/src/editor/plugins/Link/{index.js → index.jsx} +0 -0
  32. /package/src/editor/plugins/StyleMenu/{index.js → index.jsx} +0 -0
  33. /package/src/editor/plugins/Table/{index.js → index.jsx} +0 -0
package/.stylelintrc ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": ["stylelint-config-idiomatic-order"],
3
+ "plugins": ["stylelint-prettier"],
4
+ "overrides": [
5
+ {
6
+ "files": ["**/*.less"],
7
+ "customSyntax": "postcss-less"
8
+ }
9
+ ],
10
+ "rules": {
11
+ "prettier/prettier": true,
12
+ "order/properties-alphabetical-order": null
13
+ }
14
+ }
package/CHANGELOG.md CHANGED
@@ -8,6 +8,112 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 18.0.0 (2024-10-31)
12
+
13
+ ### Internal
14
+
15
+ - Release 18.0.0 final @sneridagh
16
+
17
+ ## 18.0.0-alpha.20 (2024-10-30)
18
+
19
+ ### Bugfix
20
+
21
+ - Fix slight CSS lint violation in volto-slate @sneridagh [#6444](https://github.com/plone/volto/issues/6444)
22
+
23
+ ## 18.0.0-alpha.19 (2024-10-03)
24
+
25
+ ### Feature
26
+
27
+ - Update Brazilian Portuguese translations. @ericof [#6292](https://github.com/plone/volto/issues/6292)
28
+
29
+ ### Bugfix
30
+
31
+ - Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
32
+
33
+ ## 18.0.0-alpha.18 (2024-09-13)
34
+
35
+ ### Feature
36
+
37
+ - Pass `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
38
+
39
+ ## 18.0.0-alpha.17 (2024-07-05)
40
+
41
+ ### Feature
42
+
43
+ - Use the unused `toggleButton` prop in `PositionedToolbar` to render the toolbar in a different `portal` target falling back to `document.body` if `toggleButton` is not provided. @ichim-david
44
+
45
+ When `toggleButton` is provided as a `portal` target, allow negative left positioning except when the target is `document.body` to prevent the toolbar going off-screen and avoid breaking changes. @ichim-david [#6159](https://github.com/plone/volto/issues/6159)
46
+
47
+ ## 18.0.0-alpha.16 (2024-07-03)
48
+
49
+ ### Internal
50
+
51
+ - Fix dependencies for the package @sneridagh [#6148](https://github.com/plone/volto/issues/6148)
52
+
53
+ ## 18.0.0-alpha.15 (2024-06-28)
54
+
55
+ ### Internal
56
+
57
+ - Add proper dependencies to `volto-slate`. @sneridagh [#6130](https://github.com/plone/volto/issues/6130)
58
+
59
+ ## 18.0.0-alpha.14 (2024-06-26)
60
+
61
+ ### Feature
62
+
63
+ - Added `link-form-container` styles for `AddLinkForm` component. @ichim-david [#5607](https://github.com/plone/volto/issues/5607)
64
+ - Handle breakList in DetachedTextBlockEditor. @giuliaghisini [#6106](https://github.com/plone/volto/issues/6106)
65
+
66
+ ### Bugfix
67
+
68
+ - In the Slate text block, the markup shortcuts for heading and blockquote work again. @kHAPPY2004 [#5452](https://github.com/plone/volto/issues/5452)
69
+ - When pasting an image into a Slate block, now only an image block is created, instead of both a Slate block and an image block. @aryan7081 [#5818](https://github.com/plone/volto/issues/5818)
70
+
71
+ ### Internal
72
+
73
+ - Rename files with wrong extension `js->jsx` when they contain JSX. @sneridagh [#6114](https://github.com/plone/volto/issues/6114)
74
+
75
+ ## 18.0.0-alpha.13 (2024-06-13)
76
+
77
+ ### Bugfix
78
+
79
+ - Fix removal of slate formatting applied to text when toggling the list buttons @robgietema [#6080](https://github.com/plone/volto/issues/6080)
80
+
81
+ ## 18.0.0-alpha.12 (2024-04-23)
82
+
83
+ ### Bugfix
84
+
85
+ - In the Slate text block, the markup shortcuts for bold, italic and strikethrough work again. @kHAPPY2004 [#5605](https://github.com/plone/volto/issues/5605)
86
+
87
+ ### Internal
88
+
89
+ - Update imports to work with the new code split components in Volto. @pnicolli [#5295](https://github.com/plone/volto/issues/5295)
90
+
91
+ ## 18.0.0-alpha.11 (2024-04-03)
92
+
93
+ ### Bugfix
94
+
95
+ - Fix removing an element in slate when cursor is at the end of the element to be removed; Fix losing selection when adding an element. @razvanMiu [#5928](https://github.com/plone/volto/issues/5928)
96
+
97
+ ## 18.0.0-alpha.10 (2024-03-14)
98
+
99
+ ### Breaking
100
+
101
+ - Remove legacy `text`, `table` and `hero` blocks based in `draftJS` @sneridagh [#5846](https://github.com/plone/volto/issues/5846)
102
+
103
+ ### Bugfix
104
+
105
+ - Fix other occurrences of mutable (referenced) objects when assigning the default inner `blocksConfig` object for the `grid` block, pass by value instead. sneridagh [#5859](https://github.com/plone/volto/issues/5859)
106
+
107
+ ### Internal
108
+
109
+ - Fix CSS lint @sneridagh [#5849](https://github.com/plone/volto/issues/5849)
110
+
111
+ ## 18.0.0-alpha.9 (2024-03-02)
112
+
113
+ ### Internal
114
+
115
+ - Update dependencies @sneridagh [#5815](https://github.com/plone/volto/issues/5815)
116
+
11
117
  ## 18.0.0-alpha.8 (2024-03-01)
12
118
 
13
119
  ### Feature
@@ -0,0 +1,205 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Plone\n"
4
+ "POT-Creation-Date: 2022-07-20T12:45:09.681Z\n"
5
+ "PO-Revision-Date: \n"
6
+ "Last-Translator: Érico Andrei <ericof@plone.org>\n"
7
+ "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
8
+ "Language: pt_BR\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=utf-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "Plural-Forms: nplurals=2; plural=(n > 1);\n"
13
+ "Language-Code: en\n"
14
+ "Language-Name: English\n"
15
+ "Preferred-Encodings: utf-8\n"
16
+ "Domain: volto\n"
17
+ "X-Generator: Poedit 3.5\n"
18
+
19
+ # defaultMessage: Add link
20
+ #: editor/plugins/Link/index
21
+ msgid "Add link"
22
+ msgstr "Adicionar link"
23
+
24
+ # defaultMessage: An error has occurred while editing "{name}" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator.
25
+ #: widgets/HtmlSlateWidget
26
+ #, fuzzy
27
+ msgid "An error has occurred while editing \"{name}\" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator."
28
+ msgstr "Ocorreu um erro ao editar o campo \"{name}\". Fomos notificados e estamos analisando o problema. Salve seu trabalho e tente novamente. Se o problema persistir, entre em contato com o administrador do site."
29
+
30
+ # defaultMessage: An error has occurred while rendering "{name}" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator.
31
+ #: widgets/RichTextWidgetView
32
+ #, fuzzy
33
+ msgid "An error has occurred while rendering \"{name}\" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator."
34
+ msgstr "Ocorreu um erro ao renderizar o campo \"{name}\". Fomos notificados e estamos analisando o problema. Se o problema persistir, entre em contato com o administrador do site."
35
+
36
+ # defaultMessage: Bottom
37
+ #: blocks/Table/TableBlockEdit
38
+ msgid "Bottom"
39
+ msgstr "Base"
40
+
41
+ # defaultMessage: Center
42
+ #: blocks/Table/TableBlockEdit
43
+ #, fuzzy
44
+ msgid "Center"
45
+ msgstr "Centro"
46
+
47
+ # defaultMessage: Delete col
48
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
49
+ #, fuzzy
50
+ msgid "Delete col"
51
+ msgstr "Excluir coluna"
52
+
53
+ # defaultMessage: Delete row
54
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
55
+ #, fuzzy
56
+ msgid "Delete row"
57
+ msgstr "Excluir linha"
58
+
59
+ # defaultMessage: Delete table
60
+ #: editor/plugins/Table/index
61
+ #, fuzzy
62
+ msgid "Delete table"
63
+ msgstr "Excluir tabela"
64
+
65
+ # defaultMessage: Divide each row into separate cells
66
+ #: blocks/Table/TableBlockEdit
67
+ #, fuzzy
68
+ msgid "Divide each row into separate cells"
69
+ msgstr "Dividir cada linha em células separadas"
70
+
71
+ # defaultMessage: Edit element
72
+ #: elementEditor/messages
73
+ #, fuzzy
74
+ msgid "Edit element"
75
+ msgstr "Editar elemento"
76
+
77
+ # defaultMessage: Edit link
78
+ #: editor/plugins/AdvancedLink/index editor/plugins/Link/index
79
+ #, fuzzy
80
+ msgid "Edit link"
81
+ msgstr "Editar link"
82
+
83
+ # defaultMessage: Fixed width table cells
84
+ #: blocks/Table/TableBlockEdit
85
+ #, fuzzy
86
+ msgid "Fixed width table cells"
87
+ msgstr "Células de tabela de largura fixa"
88
+
89
+ # defaultMessage: Hide headers
90
+ #: blocks/Table/TableBlockEdit
91
+ #, fuzzy
92
+ msgid "Hide headers"
93
+ msgstr "Ocultar cabeçalhos"
94
+
95
+ # defaultMessage: Insert col after
96
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
97
+ #, fuzzy
98
+ msgid "Insert col after"
99
+ msgstr "Inserir coluna após"
100
+
101
+ # defaultMessage: Insert col before
102
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
103
+ #, fuzzy
104
+ msgid "Insert col before"
105
+ msgstr "Inserir coluna antes"
106
+
107
+ # defaultMessage: Insert row after
108
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
109
+ #, fuzzy
110
+ msgid "Insert row after"
111
+ msgstr "Inserir linha após"
112
+
113
+ # defaultMessage: Insert row before
114
+ #: blocks/Table/TableBlockEdit editor/plugins/Table/index
115
+ #, fuzzy
116
+ msgid "Insert row before"
117
+ msgstr "Inserir linha antes"
118
+
119
+ # defaultMessage: Left
120
+ #: blocks/Table/TableBlockEdit
121
+ #, fuzzy
122
+ msgid "Left"
123
+ msgstr "Esquerda"
124
+
125
+ # defaultMessage: Make the table compact
126
+ #: blocks/Table/TableBlockEdit
127
+ #, fuzzy
128
+ msgid "Make the table compact"
129
+ msgstr "Compactar a tabela"
130
+
131
+ # defaultMessage: Make the table sortable
132
+ #: blocks/Table/TableBlockEdit
133
+ msgid "Make the table sortable"
134
+ msgstr "Tornar a tabela ordenável"
135
+
136
+ # defaultMessage: Middle
137
+ #: blocks/Table/TableBlockEdit
138
+ msgid "Middle"
139
+ msgstr "Meio"
140
+
141
+ # defaultMessage: No matching blocks
142
+ #: blocks/Text/SlashMenu
143
+ #, fuzzy
144
+ msgid "No matching blocks"
145
+ msgstr "Não há blocos correspondentes"
146
+
147
+ # defaultMessage: Reduce complexity
148
+ #: blocks/Table/TableBlockEdit
149
+ #, fuzzy
150
+ msgid "Reduce complexity"
151
+ msgstr "Reduzir complexidade"
152
+
153
+ # defaultMessage: Remove element
154
+ #: elementEditor/messages
155
+ #, fuzzy
156
+ msgid "Remove element"
157
+ msgstr "Remover elemento"
158
+
159
+ # defaultMessage: Remove link
160
+ #: editor/plugins/AdvancedLink/index
161
+ #, fuzzy
162
+ msgid "Remove link"
163
+ msgstr "Remover link"
164
+
165
+ # defaultMessage: Right
166
+ #: blocks/Table/TableBlockEdit
167
+ #, fuzzy
168
+ msgid "Right"
169
+ msgstr "Direita"
170
+
171
+ # defaultMessage: Stripe alternate rows with color
172
+ #: blocks/Table/TableBlockEdit
173
+ #, fuzzy
174
+ msgid "Stripe alternate rows with color"
175
+ msgstr "Alternar cores das linhas"
176
+
177
+ # defaultMessage: Table
178
+ #: blocks/Table/TableBlockEdit
179
+ #, fuzzy
180
+ msgid "Table"
181
+ msgstr "Tabela"
182
+
183
+ # defaultMessage: Table color inverted
184
+ #: blocks/Table/TableBlockEdit
185
+ #, fuzzy
186
+ msgid "Table color inverted"
187
+ msgstr "Cor da tabela invertida"
188
+
189
+ # defaultMessage: Top
190
+ #: blocks/Table/TableBlockEdit
191
+ #, fuzzy
192
+ msgid "Top"
193
+ msgstr "Topo"
194
+
195
+ # defaultMessage: Type text…
196
+ #: blocks/Text/DefaultTextBlockEditor blocks/Text/DetachedTextBlockEditor
197
+ #, fuzzy
198
+ msgid "Type text…"
199
+ msgstr "Digite texto…"
200
+
201
+ # defaultMessage: Visible only in view mode
202
+ #: blocks/Table/TableBlockEdit
203
+ #, fuzzy
204
+ msgid "Visible only in view mode"
205
+ msgstr "Visível apenas no modo de visualização"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "18.0.0-alpha.8",
3
+ "version": "18.0.0",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -18,16 +18,27 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
+ "classnames": "2.2.6",
22
+ "github-slugger": "1.4.0",
21
23
  "image-extensions": "1.1.0",
24
+ "is-hotkey": "0.2.0",
22
25
  "is-url": "1.2.4",
23
26
  "jsdom": "^16.6.0",
27
+ "lodash": "4.17.21",
24
28
  "react": "18.2.0",
25
- "react-intersection-observer": "9.1.0",
26
29
  "react-dom": "18.2.0",
30
+ "react-intersection-observer": "9.1.0",
31
+ "react-intl": "3.12.1",
32
+ "react-redux": "8.1.2",
33
+ "react-router-dom": "5.2.0",
34
+ "react-toastify": "5.5.0",
35
+ "redux-mock-store": "1.5.4",
36
+ "semantic-ui-react": "2.1.5",
27
37
  "slate": "0.100.0",
28
38
  "slate-history": "0.100.0",
29
39
  "slate-hyperscript": "0.100.0",
30
40
  "slate-react": "0.98.4",
41
+ "uuid": "^9.0.1",
31
42
  "weak-key": "^1.0.2"
32
43
  },
33
44
  "devDependencies": {
@@ -11,7 +11,8 @@ import cx from 'classnames';
11
11
  import { defineMessages, injectIntl } from 'react-intl';
12
12
 
13
13
  import Cell from './Cell';
14
- import { BlockDataForm, Icon, SidebarPortal } from '@plone/volto/components';
14
+ import { Icon, SidebarPortal } from '@plone/volto/components';
15
+ import { BlockDataForm } from '@plone/volto/components/manage/Form';
15
16
  import TableSchema from './schema';
16
17
 
17
18
  import rowBeforeSVG from '@plone/volto/icons/row-before.svg';
@@ -101,8 +101,8 @@ const View = ({ data }) => {
101
101
  state.column !== index
102
102
  ? 'ascending'
103
103
  : state.direction === 'ascending'
104
- ? 'descending'
105
- : 'ascending',
104
+ ? 'descending'
105
+ : 'ascending',
106
106
  });
107
107
  }}
108
108
  >
@@ -50,7 +50,6 @@ export default function install(config) {
50
50
  // },
51
51
  };
52
52
 
53
- config.blocks.blocksConfig.table.restricted = true;
54
53
  config.blocks.blocksConfig.slateTable = {
55
54
  ...tableBlockConfig,
56
55
  id: 'slateTable',
@@ -12,11 +12,8 @@ import {
12
12
  validateFileUploadSize,
13
13
  } from '@plone/volto/helpers';
14
14
  import config from '@plone/volto/registry';
15
- import {
16
- BlockDataForm,
17
- SidebarPortal,
18
- BlockChooserButton,
19
- } from '@plone/volto/components';
15
+ import { SidebarPortal, BlockChooserButton } from '@plone/volto/components';
16
+ import { BlockDataForm } from '@plone/volto/components/manage/Form';
20
17
 
21
18
  import { SlateEditor } from '@plone/volto-slate/editor';
22
19
  import { serializeNodesToText } from '@plone/volto-slate/editor/render';
@@ -4,6 +4,7 @@ import { useInView } from 'react-intersection-observer';
4
4
  import { SlateEditor } from '@plone/volto-slate/editor';
5
5
  import { serializeNodesToText } from '@plone/volto-slate/editor/render';
6
6
  import { handleKeyDetached } from './keyboard';
7
+ import config from '@plone/volto/registry';
7
8
 
8
9
  const DEBUG = false;
9
10
 
@@ -29,6 +30,7 @@ export const DetachedTextBlockEditor = (props) => {
29
30
  const { value } = data;
30
31
 
31
32
  const intl = useIntl();
33
+ const { textblockExtensions } = config.settings.slate;
32
34
  const placeholder =
33
35
  data.placeholder || formTitle || intl.formatMessage(messages.text);
34
36
  let instructions = data?.instructions?.data || data?.instructions;
@@ -41,13 +43,22 @@ export const DetachedTextBlockEditor = (props) => {
41
43
  rootMargin: '0px 0px 200px 0px',
42
44
  });
43
45
 
46
+ const withBlockProperties = React.useCallback(
47
+ (editor) => {
48
+ editor.getBlockProps = () => props;
49
+ return editor;
50
+ },
51
+ [props],
52
+ );
53
+
44
54
  return (
45
55
  <div className="text-slate-editor-inner detached-slate-editor" ref={ref}>
46
56
  <SlateEditor
47
57
  index={index}
48
58
  readOnly={!inView}
49
59
  properties={properties}
50
- renderExtensions={[]}
60
+ extensions={textblockExtensions}
61
+ renderExtensions={[withBlockProperties]}
51
62
  value={value}
52
63
  block={block /* is this needed? */}
53
64
  debug={DEBUG}
@@ -4,6 +4,7 @@ import { filter, isEmpty } from 'lodash';
4
4
  import { Menu } from 'semantic-ui-react';
5
5
  import { useIntl, FormattedMessage } from 'react-intl';
6
6
  import { Icon } from '@plone/volto/components';
7
+ import { useUser } from '@plone/volto/hooks';
7
8
 
8
9
  const emptySlateBlock = () => ({
9
10
  value: [
@@ -105,9 +106,13 @@ const PersistentSlashMenu = ({ editor }) => {
105
106
  selected,
106
107
  allowedBlocks,
107
108
  detached,
109
+ navRoot,
110
+ contentType,
108
111
  } = props;
109
112
  const disableNewBlocks = data?.disableNewBlocks || detached;
110
113
 
114
+ const user = useUser();
115
+
111
116
  const [slashMenuSelected, setSlashMenuSelected] = React.useState(0);
112
117
 
113
118
  const hasAllowedBlocks = !isEmpty(allowedBlocks);
@@ -122,8 +127,14 @@ const PersistentSlashMenu = ({ editor }) => {
122
127
  hasAllowedBlocks
123
128
  ? allowedBlocks.includes(item.id)
124
129
  : typeof item.restricted === 'function'
125
- ? !item.restricted({ properties, block: item })
126
- : !item.restricted,
130
+ ? !item.restricted({
131
+ properties,
132
+ block: item,
133
+ navRoot,
134
+ contentType,
135
+ user,
136
+ })
137
+ : !item.restricted,
127
138
  )
128
139
  .filter((block) => Boolean(block.title && block.id))
129
140
  .filter((block) => {
@@ -152,6 +163,9 @@ const PersistentSlashMenu = ({ editor }) => {
152
163
  properties,
153
164
  slashCommand,
154
165
  hasAllowedBlocks,
166
+ navRoot,
167
+ contentType,
168
+ user,
155
169
  ],
156
170
  );
157
171
 
@@ -35,6 +35,8 @@ export const breakList = (editor) => {
35
35
 
36
36
  const { slate } = config.settings;
37
37
  const { anchor } = editor.selection;
38
+ const blockProps = editor.getBlockProps();
39
+ const detached = blockProps.detached;
38
40
 
39
41
  const ref = Editor.rangeRef(editor, editor.selection, {
40
42
  affinity: 'inward',
@@ -65,8 +67,7 @@ export const breakList = (editor) => {
65
67
  return; // applies default behaviour, as defined in insertBreak.js extension
66
68
  }
67
69
 
68
- if (parent) {
69
- const blockProps = editor.getBlockProps();
70
+ if (parent && !detached) {
70
71
  const { data } = blockProps;
71
72
  // Don't add new block if not allowed
72
73
  if (data?.disableNewBlocks) {
@@ -77,22 +78,36 @@ export const breakList = (editor) => {
77
78
  Editor.deleteBackward(editor, { unit: 'line' });
78
79
  // also account for empty nodes [{text: ''}]
79
80
  if (Editor.isEmpty(editor, parent)) {
80
- createAndSelectNewBlockAfter(editor, [createEmptyParagraph()]);
81
- Transforms.removeNodes(editor, { at: ref.current });
81
+ if (detached) {
82
+ Transforms.removeNodes(editor, { at: ref.current });
83
+
84
+ Transforms.insertNodes(editor, createEmptyParagraph(), {
85
+ at: [editor.children.length],
86
+ });
87
+ Transforms.select(editor, Editor.end(editor, []));
88
+ } else {
89
+ createAndSelectNewBlockAfter(editor, [createEmptyParagraph()]);
90
+ Transforms.removeNodes(editor, { at: ref.current });
91
+ }
82
92
  return true;
83
93
  }
84
94
 
85
95
  Transforms.removeNodes(editor, { at: ref.current });
86
96
 
87
97
  if (isCursorAtBlockEnd(editor)) {
88
- createAndSelectNewBlockAfter(editor, [createEmptyParagraph()]);
98
+ if (detached) {
99
+ Editor.insertNode(editor, createEmptyParagraph());
100
+ } else {
101
+ createAndSelectNewBlockAfter(editor, [createEmptyParagraph()]);
102
+ }
89
103
  return true;
90
104
  }
91
105
 
92
- const [top, bottom] = splitEditorInTwoFragments(editor, ref.current);
93
- setEditorContent(editor, top);
94
- createAndSelectNewBlockAfter(editor, bottom);
95
-
106
+ if (!detached) {
107
+ const [top, bottom] = splitEditorInTwoFragments(editor, ref.current);
108
+ setEditorContent(editor, top);
109
+ createAndSelectNewBlockAfter(editor, bottom);
110
+ }
96
111
  return true;
97
112
  };
98
113
 
@@ -34,7 +34,7 @@ export const withSplitBlocksOnBreak = (editor) => {
34
34
  const { data } = blockProps;
35
35
 
36
36
  // Don't add new block if not allowed
37
- if (data?.disableNewBlocks) {
37
+ if (data?.disableNewBlocks || blockProps.detached) {
38
38
  return insertBreak();
39
39
  }
40
40
 
@@ -46,10 +46,7 @@ export const onImageLoad = (editor, reader) => () => {
46
46
  },
47
47
  };
48
48
 
49
- uploadContent(url, content, block).then((data) => {
50
- const dlUrl = data.image.download;
51
- insertImage(editor, dlUrl);
52
- });
49
+ uploadContent(url, content, block);
53
50
  };
54
51
 
55
52
  export const withDeserializers = (editor) => {
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import redraft from 'redraft';
3
2
  import TextBlockView from './TextBlockView';
4
3
  import TextBlockEdit from './TextBlockEdit';
5
4
  import TextBlockSchema from './TextBlockSchema';
5
+ import { cloneDeep } from 'lodash';
6
6
 
7
7
  import {
8
8
  goDown,
@@ -128,26 +128,8 @@ export default function applyConfig(config) {
128
128
  return override_toc && level
129
129
  ? [parseInt(level.slice(1)), entry_text]
130
130
  : config.settings.slate.topLevelTargetElements.includes(type)
131
- ? [parseInt(type.slice(1)), plaintext]
132
- : null;
133
- },
134
- };
135
-
136
- // Make draft js compatible with ToC
137
- config.blocks.blocksConfig.text = {
138
- ...config.blocks.blocksConfig.text,
139
- restricted: true,
140
- tocEntry: (block = {}) => {
141
- const draft = redraft(
142
- block.text,
143
- config.settings.richtextViewSettings.ToHTMLRenderers,
144
- config.settings.richtextViewSettings.ToHTMLOptions,
145
- );
146
- const type = draft?.[0]?.[0]?.type;
147
-
148
- return config.settings.slate.topLevelTargetElements.includes(type)
149
- ? [parseInt(type.slice(1)), block.text.blocks[0].text]
150
- : null;
131
+ ? [parseInt(type.slice(1)), plaintext]
132
+ : null;
151
133
  },
152
134
  };
153
135
 
@@ -164,7 +146,9 @@ export default function applyConfig(config) {
164
146
  // This is required in order to initialize the inner blocksConfig
165
147
  // for the grid block, since the slate block lives in an add-on
166
148
  // it should be responsible to fill itself into the grid configuration
167
- config.blocks.blocksConfig.gridBlock.blocksConfig.slate = slateBlockConfig;
149
+ // The cloneDeep is mandatory in order to not mess with pass by reference problems
150
+ config.blocks.blocksConfig.gridBlock.blocksConfig.slate =
151
+ cloneDeep(slateBlockConfig);
168
152
  }
169
153
 
170
154
  return config;
@@ -55,8 +55,8 @@ export function indentListItems({ editor, event }) {
55
55
  ? decreaseMultipleItemsDepth(editor, event)
56
56
  : decreaseItemDepth(editor, event)
57
57
  : event.ctrlKey
58
- ? increaseMultipleItemDepth(editor, event)
59
- : increaseItemDepth(editor, event);
58
+ ? increaseMultipleItemDepth(editor, event)
59
+ : increaseItemDepth(editor, event);
60
60
  }
61
61
  }
62
62
 
package/src/constants.js CHANGED
@@ -22,6 +22,9 @@ export const P = 'p';
22
22
  export const LI = 'li';
23
23
  export const UL = 'ul';
24
24
  export const OL = 'ol';
25
+ export const H2 = 'h2';
26
+ export const H3 = 'h3';
27
+ export const BLOCKQUOTE = 'blockquote';
25
28
 
26
29
  // dom parsing node information
27
30
  export const TEXT_NODE = 3;
@@ -25,6 +25,12 @@
25
25
  &.upper {
26
26
  transform: translateY(-100%);
27
27
  }
28
+
29
+ .link-form-container {
30
+ margin-left: 2px;
31
+ display: flex;
32
+ align-items: center;
33
+ }
28
34
  }
29
35
 
30
36
  .toolbar-wrapper.active {
@@ -41,12 +47,13 @@
41
47
  box-sizing: border-box;
42
48
  padding: 3px;
43
49
  border: none;
44
- background: #fff;
45
50
  border-radius: 2px;
51
+ background: #fff;
46
52
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
47
53
  font-family: 'Poppins', 'Helvetica Neue', Arial, Helvetica, sans-serif;
48
54
  font-size: 1rem;
49
55
  font-weight: normal;
56
+ min-width: 300px; // needed for image widget slate toolbar
50
57
 
51
58
  .expando {
52
59
  flex-grow: 1;
@@ -74,8 +81,8 @@
74
81
  box-sizing: border-box;
75
82
  padding: 4px !important;
76
83
  border: 0;
77
- background: rgba(255, 255, 255, 0.975);
78
84
  border-radius: 1px;
85
+ background: rgba(255, 255, 255, 0.975);
79
86
  color: @brown;
80
87
  font-size: 18px;
81
88
  vertical-align: bottom;
@@ -91,9 +98,9 @@
91
98
  }
92
99
 
93
100
  &.active {
101
+ border-radius: 3px;
94
102
  // TODO: stop using !important, use smth better
95
103
  background: #efefef !important;
96
- border-radius: 3px;
97
104
  box-shadow: inset 0 0 0 1px @blue !important;
98
105
  color: @blue !important;
99
106
 
@@ -151,9 +158,11 @@
151
158
  top: 29px;
152
159
  left: -9px;
153
160
  width: 210px;
154
- background-color: rgba(255, 255, 255, 0.975);
155
161
  border-radius: 2px;
156
- box-shadow: 0 0 8px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.05);
162
+ background-color: rgba(255, 255, 255, 0.975);
163
+ box-shadow:
164
+ 0 0 8px rgba(0, 0, 0, 0.1),
165
+ 0 2px 4px rgba(0, 0, 0, 0.05);
157
166
 
158
167
  .ui.menu {
159
168
  border: 0;
@@ -22,7 +22,9 @@ h4 {
22
22
  fill: #42526e;
23
23
  opacity: 0;
24
24
  transform: rotate(15deg) translate(-8px, 2px);
25
- transition: opacity 0.2s ease 0s, transform 0.2s ease 0s;
25
+ transition:
26
+ opacity 0.2s ease 0s,
27
+ transform 0.2s ease 0s;
26
28
  }
27
29
  }
28
30
  }
@@ -1,6 +1,6 @@
1
1
  import { toggleList } from './utils';
2
2
  import { isBlockActive } from '@plone/volto-slate/utils';
3
- import { UL, OL, LI } from '@plone/volto-slate/constants';
3
+ import { UL, OL, LI, H2, H3, BLOCKQUOTE } from '@plone/volto-slate/constants';
4
4
 
5
5
  /**
6
6
  * Uses the old toggleList function to toggle lists on or off or from a type to another.
@@ -22,11 +22,11 @@ export const localToggleList = (editor, format) => {
22
22
  */
23
23
  export const autoformatRules = [
24
24
  {
25
- type: 'h2',
25
+ type: H2,
26
26
  markup: '#',
27
27
  },
28
28
  {
29
- type: 'h3',
29
+ type: H3,
30
30
  markup: '##',
31
31
  },
32
32
  {
@@ -44,36 +44,36 @@ export const autoformatRules = [
44
44
  },
45
45
  },
46
46
  {
47
- type: 'blockquote',
47
+ type: BLOCKQUOTE,
48
48
  markup: ['>'],
49
49
  // preFormat,
50
50
  },
51
51
  {
52
- type: 'bold',
52
+ type: 'strong',
53
53
  between: ['**', '**'],
54
54
  mode: 'inline',
55
55
  insertTrigger: true,
56
56
  },
57
57
  {
58
- type: 'bold',
58
+ type: 'strong',
59
59
  between: ['__', '__'],
60
60
  mode: 'inline',
61
61
  insertTrigger: true,
62
62
  },
63
63
  {
64
- type: 'italic',
64
+ type: 'em',
65
65
  between: ['*', '*'],
66
66
  mode: 'inline',
67
67
  insertTrigger: true,
68
68
  },
69
69
  {
70
- type: 'italic',
70
+ type: 'em',
71
71
  between: ['_', '_'],
72
72
  mode: 'inline',
73
73
  insertTrigger: true,
74
74
  },
75
75
  {
76
- type: 'strikethrough',
76
+ type: 'del',
77
77
  between: ['~~', '~~'],
78
78
  mode: 'inline',
79
79
  insertTrigger: true,
@@ -170,9 +170,8 @@ export const autoformatInline = (
170
170
 
171
171
  // add mark to the text between the markups
172
172
  Transforms.select(editor, markupRange);
173
- editor.addMark(type, true);
173
+ Transforms.wrapNodes(editor, { type, children: [] }, { split: true });
174
174
  Transforms.collapse(editor, { edge: 'end' });
175
- editor.removeMark(type);
176
175
 
177
176
  // delete start markup
178
177
  const startMarkupPointBefore = getPointBefore(editor, selection, {
@@ -200,6 +199,7 @@ export const autoformatBlock = (editor, type, at, { preFormat, format }) => {
200
199
  Transforms.setNodes(
201
200
  editor,
202
201
  { type },
202
+ { at },
203
203
  { match: (n) => Editor.isBlock(editor, n) },
204
204
  );
205
205
  } else {
@@ -7,12 +7,19 @@ const PositionedToolbar = ({ toggleButton, className, children, position }) => {
7
7
  // TODO: "position" is actually an object like `{ style: {} }`
8
8
  // To be renamed as "attributes" or "attrs"
9
9
  const ref = React.useRef();
10
+ const portalTarget = toggleButton || document.body;
10
11
 
11
12
  React.useEffect(() => {
12
13
  const el = ref.current;
13
14
 
14
15
  const { style } = position || {};
15
- const left = `${Math.max(style.left - el.offsetWidth / 2, 0)}px`;
16
+ // allow negative left positioning when portal isn't document.body
17
+ // we need to use Math.max to avoid Slate AddLink popup going off screen
18
+ // and it adds the toolbar to the body
19
+ const left =
20
+ portalTarget === document.body
21
+ ? `${Math.max(style.left - el.offsetWidth / 2, 0)}px`
22
+ : `${style.left - el.offsetWidth / 2}px`;
16
23
  const top = `${style.top - el.offsetHeight}px`;
17
24
 
18
25
  el.style.opacity = style.opacity;
@@ -24,7 +31,7 @@ const PositionedToolbar = ({ toggleButton, className, children, position }) => {
24
31
  <BasicToolbar className={`slate-inline-toolbar ${className}`} ref={ref}>
25
32
  {children}
26
33
  </BasicToolbar>,
27
- document.body,
34
+ portalTarget,
28
35
  );
29
36
  };
30
37
 
@@ -3,7 +3,8 @@ import { isEqual } from 'lodash';
3
3
  import React from 'react';
4
4
  import { useDispatch } from 'react-redux';
5
5
  import { ReactEditor } from 'slate-react';
6
- import { Icon as VoltoIcon, BlockDataForm } from '@plone/volto/components';
6
+ import { Icon as VoltoIcon } from '@plone/volto/components';
7
+ import { BlockDataForm } from '@plone/volto/components/manage/Form';
7
8
  import { setPluginOptions } from '@plone/volto-slate/actions';
8
9
  import BaseSchemaProvider from './SchemaProvider';
9
10
 
@@ -45,13 +45,16 @@ export const _insertElement = (elementType) => (editor, data) => {
45
45
  match: (node) => {
46
46
  return Node.string(node).length !== 0;
47
47
  },
48
- }, //,
48
+ },
49
49
  );
50
50
  }
51
51
 
52
52
  const sel = JSON.parse(JSON.stringify(rangeRef.current));
53
- Transforms.select(editor, sel);
54
- editor.setSavedSelection(sel);
53
+
54
+ setTimeout(() => {
55
+ Transforms.select(editor, sel);
56
+ editor.setSavedSelection(sel);
57
+ });
55
58
 
56
59
  return true;
57
60
  }
@@ -69,10 +72,26 @@ export const _insertElement = (elementType) => (editor, data) => {
69
72
  * @returns {Object|null} - current node
70
73
  */
71
74
  export const _unwrapElement = (elementType) => (editor) => {
72
- const [link] = Editor.nodes(editor, {
73
- at: editor.selection,
75
+ const selection = editor.selection || editor.getSavedSelection();
76
+ let [link] = Editor.nodes(editor, {
77
+ at: selection,
74
78
  match: (node) => node?.type === elementType,
75
79
  });
80
+ const isAtStart =
81
+ selection.anchor.offset === 0 && selection.focus.offset === 0;
82
+
83
+ if (!link && !isAtStart) return false;
84
+
85
+ if (!link) {
86
+ try {
87
+ link = Editor.previous(editor, {
88
+ at: selection.anchor.path,
89
+ });
90
+ } catch (ex) {
91
+ link = [];
92
+ }
93
+ }
94
+
76
95
  const [, path] = link;
77
96
  const [start, end] = Editor.edges(editor, path);
78
97
  const range = { anchor: start, focus: end };
@@ -264,7 +264,7 @@ export const toggleBlock = (editor, format, allowedChildren) => {
264
264
  } else if (!isListItem && !wantsList) {
265
265
  toggleFormat(editor, format, allowedChildren);
266
266
  } else if (isListItem && wantsList && isActive) {
267
- clearFormatting(editor);
267
+ clearList(editor);
268
268
  } else {
269
269
  console.warn('toggleBlock case not covered, please examine:', {
270
270
  wantsList,
@@ -298,6 +298,21 @@ export const switchListType = (editor, format) => {
298
298
  Transforms.wrapNodes(editor, block);
299
299
  };
300
300
 
301
+ /*
302
+ * Clear list by exploding the block
303
+ */
304
+ export const clearList = (editor) => {
305
+ const { slate } = config.settings;
306
+ Transforms.unwrapNodes(editor, {
307
+ match: (n) => slate.listTypes.includes(n.type),
308
+ split: true,
309
+ });
310
+ Transforms.setNodes(editor, {
311
+ type: 'p',
312
+ });
313
+ Editor.normalize(editor);
314
+ };
315
+
301
316
  export const changeBlockToList = (editor, format) => {
302
317
  const { slate } = config.settings;
303
318
  const [match] = Editor.nodes(editor, {
@@ -3,6 +3,8 @@ import { v4 as uuid } from 'uuid';
3
3
  import {
4
4
  addBlock,
5
5
  changeBlock,
6
+ insertBlock,
7
+ blockHasValue,
6
8
  getBlocksFieldname,
7
9
  getBlocksLayoutFieldname,
8
10
  } from '@plone/volto/helpers';
@@ -121,8 +123,19 @@ export function createImageBlock(url, index, props) {
121
123
  const blocksFieldname = getBlocksFieldname(properties);
122
124
  const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
123
125
 
124
- const [id, formData] = addBlock(properties, 'image', index + 1);
125
- const newFormData = changeBlock(formData, id, { '@type': 'image', url });
126
+ const currBlockId = properties.blocks_layout.items[index];
127
+ const currBlockHasValue = blockHasValue(properties.blocks[currBlockId]);
128
+ let id, newFormData;
129
+
130
+ if (currBlockHasValue) {
131
+ [id, newFormData] = addBlock(properties, 'image', index + 1);
132
+ newFormData = changeBlock(newFormData, id, { '@type': 'image', url });
133
+ } else {
134
+ [id, newFormData] = insertBlock(properties, currBlockId, {
135
+ '@type': 'image',
136
+ url,
137
+ });
138
+ }
126
139
 
127
140
  ReactDOM.unstable_batchedUpdates(() => {
128
141
  onChangeField(blocksFieldname, newFormData[blocksFieldname]);
@@ -9,7 +9,7 @@ import { MemoryRouter } from 'react-router-dom';
9
9
  import { Provider, useSelector } from 'react-redux';
10
10
  import { defineMessages, injectIntl } from 'react-intl';
11
11
 
12
- import { FormFieldWrapper } from '@plone/volto/components';
12
+ import { FormFieldWrapper } from '@plone/volto/components/manage/Widgets';
13
13
  import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
14
14
  import { serializeNodes } from '@plone/volto-slate/editor/render';
15
15
  import { makeEditor } from '@plone/volto-slate/utils';
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Menu, Tab } from 'semantic-ui-react';
3
- import { Icon, ObjectWidget } from '@plone/volto/components';
3
+ import { Icon } from '@plone/volto/components';
4
+ import { ObjectWidget } from '@plone/volto/components/manage/Widgets';
4
5
 
5
6
  export const ObjectByTypeWidget = (props) => {
6
7
  const { schemas, value = {}, onChange, errors = {}, id } = props;
@@ -6,7 +6,7 @@
6
6
  import React from 'react';
7
7
  import isUndefined from 'lodash/isUndefined';
8
8
  import isString from 'lodash/isString';
9
- import { FormFieldWrapper } from '@plone/volto/components';
9
+ import { FormFieldWrapper } from '@plone/volto/components/manage/Widgets';
10
10
  import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
11
11
 
12
12
  import { createEmptyParagraph, createParagraph } from '../utils/blocks';
File without changes