@plone/volto-slate 19.0.0-alpha.15 → 19.0.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +28 -0
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/blocks/Table/TableBlockEdit.jsx +319 -466
- package/src/blocks/Table/TableBlockEdit.test.jsx +0 -6
package/AGENTS.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file applies only to `packages/volto-slate` and its subdirectories.
|
|
4
|
+
|
|
5
|
+
## What This Package Is
|
|
6
|
+
|
|
7
|
+
- `@plone/volto-slate` provides the Slate-based editor integration used by Volto.
|
|
8
|
+
- It is tightly coupled to Volto editor behavior even though it is versioned as a separate package.
|
|
9
|
+
|
|
10
|
+
## Editing Rules
|
|
11
|
+
|
|
12
|
+
- Prefer TypeScript for all new modules and features.
|
|
13
|
+
- Refactoring touched code toward TypeScript is welcome, but not required.
|
|
14
|
+
- Treat changes here as editor-platform changes, not isolated component tweaks.
|
|
15
|
+
- Preserve serialization, deserialization, and editor schema expectations.
|
|
16
|
+
- When changing editor behavior, check whether fixtures, Cypress flows, or Volto-side block/editor integrations also need updates.
|
|
17
|
+
- Avoid introducing APIs that bypass existing Volto editor configuration patterns unless the task explicitly calls for it.
|
|
18
|
+
|
|
19
|
+
## Validation
|
|
20
|
+
|
|
21
|
+
This package does not expose a dedicated local test script in this branch.
|
|
22
|
+
Validate through Volto:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pnpm --filter @plone/volto test --run
|
|
26
|
+
pnpm --filter @plone/volto build
|
|
27
|
+
make ci-acceptance-test-run-all
|
|
28
|
+
```
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- towncrier release notes start -->
|
|
10
10
|
|
|
11
|
+
## 19.0.0-alpha.17 (2026-05-12)
|
|
12
|
+
|
|
13
|
+
### Internal
|
|
14
|
+
|
|
15
|
+
- Refactored the `TableBlockEdit` component from a class-based component to a modern functional component using React hooks. @Manik-Khajuria-5 [#7760](https://github.com/plone/volto/issues/7760)
|
|
16
|
+
|
|
17
|
+
## 19.0.0-alpha.16 (2026-05-07)
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- Added package-specific `AGENTS.md` contributor guidance for `@plone/volto-slate` maintainers.
|
|
22
|
+
|
|
11
23
|
## 19.0.0-alpha.15 (2026-04-27)
|
|
12
24
|
|
|
13
25
|
### Bugfix
|
package/package.json
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
* Slate Table block editor.
|
|
3
|
-
* @module volto-slate/blocks/Table/Edit
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { Component } from 'react';
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
7
2
|
import PropTypes from 'prop-types';
|
|
8
3
|
import isEmpty from 'lodash/isEmpty';
|
|
9
4
|
import map from 'lodash/map';
|
|
10
5
|
import remove from 'lodash/remove';
|
|
11
6
|
import { Button, Table } from 'semantic-ui-react';
|
|
12
7
|
import cx from 'classnames';
|
|
13
|
-
import { defineMessages,
|
|
8
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
9
|
+
import { useClient } from '@plone/volto/hooks/client/useClient';
|
|
14
10
|
|
|
15
11
|
import Cell from './Cell';
|
|
16
12
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
@@ -162,532 +158,389 @@ const messages = defineMessages({
|
|
|
162
158
|
|
|
163
159
|
/**
|
|
164
160
|
* Edit component for the Slate Table block type in Volto.
|
|
165
|
-
* @
|
|
166
|
-
* @
|
|
161
|
+
* @function Edit
|
|
162
|
+
* @param {Object} props
|
|
163
|
+
* @returns {JSX.Element}
|
|
167
164
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
* @property {Object} defaultProps Default properties.
|
|
193
|
-
* @static
|
|
194
|
-
*/
|
|
195
|
-
static defaultProps = {
|
|
196
|
-
detached: false,
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Constructor
|
|
201
|
-
* @method constructor
|
|
202
|
-
* @param {Object} props Component properties
|
|
203
|
-
* @constructs WysiwygEditor
|
|
204
|
-
*/
|
|
205
|
-
constructor(props) {
|
|
206
|
-
super(props);
|
|
207
|
-
this.state = {
|
|
208
|
-
headers: [],
|
|
209
|
-
rows: {},
|
|
210
|
-
selected: {
|
|
211
|
-
row: 0,
|
|
212
|
-
cell: 0,
|
|
213
|
-
},
|
|
214
|
-
isClient: false,
|
|
215
|
-
};
|
|
216
|
-
this.onChange = this.onChange.bind(this);
|
|
217
|
-
this.onSelectCell = this.onSelectCell.bind(this);
|
|
218
|
-
this.onInsertRowBefore = this.onInsertRowBefore.bind(this);
|
|
219
|
-
this.onInsertRowAfter = this.onInsertRowAfter.bind(this);
|
|
220
|
-
this.onInsertColBefore = this.onInsertColBefore.bind(this);
|
|
221
|
-
this.onInsertColAfter = this.onInsertColAfter.bind(this);
|
|
222
|
-
this.onDeleteRow = this.onDeleteRow.bind(this);
|
|
223
|
-
this.onDeleteCol = this.onDeleteCol.bind(this);
|
|
224
|
-
this.onChangeCell = this.onChangeCell.bind(this);
|
|
225
|
-
this.toggleCellType = this.toggleCellType.bind(this);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Component did mount lifecycle method
|
|
230
|
-
* @method componentDidMount
|
|
231
|
-
* @returns {undefined}
|
|
232
|
-
*/
|
|
233
|
-
componentDidMount() {
|
|
234
|
-
if (!this.props.data.table || isEmpty(this.props.data.table)) {
|
|
235
|
-
this.props.onChangeBlock(this.props.block, {
|
|
236
|
-
...this.props.data,
|
|
237
|
-
table: initialTable,
|
|
238
|
-
});
|
|
165
|
+
const Edit = (props) => {
|
|
166
|
+
const {
|
|
167
|
+
data,
|
|
168
|
+
|
|
169
|
+
index,
|
|
170
|
+
selected,
|
|
171
|
+
block,
|
|
172
|
+
onAddBlock,
|
|
173
|
+
onChangeBlock,
|
|
174
|
+
onSelectBlock,
|
|
175
|
+
blocksConfig,
|
|
176
|
+
} = props;
|
|
177
|
+
|
|
178
|
+
const intl = useIntl();
|
|
179
|
+
const isClient = useClient();
|
|
180
|
+
|
|
181
|
+
const [selectedCell, setSelectedCell] = useState({
|
|
182
|
+
row: 0,
|
|
183
|
+
cell: 0,
|
|
184
|
+
});
|
|
185
|
+
// If selected prop is unset, update the state
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (!selected) {
|
|
188
|
+
setSelectedCell(null);
|
|
239
189
|
}
|
|
240
|
-
|
|
241
|
-
}
|
|
190
|
+
}, [selected]);
|
|
242
191
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
* @returns {undefined}
|
|
248
|
-
*/
|
|
249
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
250
|
-
if (!nextProps.data.table || isEmpty(nextProps.data.table)) {
|
|
251
|
-
this.props.onChangeBlock(nextProps.block, {
|
|
252
|
-
...nextProps.data,
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (!data.table || isEmpty(data.table)) {
|
|
194
|
+
onChangeBlock(block, {
|
|
195
|
+
...data,
|
|
253
196
|
table: initialTable,
|
|
254
197
|
});
|
|
255
198
|
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
* Change cell handler
|
|
289
|
-
* @param {Number} row Row index.
|
|
290
|
-
* @param {Number} cell Cell index.
|
|
291
|
-
* @param {Array} slateValue Value of the `SlateEditor` in the cell.
|
|
292
|
-
* @returns {undefined}
|
|
293
|
-
*/
|
|
294
|
-
onChangeCell(row, cell, slateValue) {
|
|
295
|
-
const table = JSON.parse(JSON.stringify(this.props.data.table));
|
|
296
|
-
table.rows[row].cells[cell] = {
|
|
297
|
-
...table.rows[row].cells[cell],
|
|
298
|
-
value: JSON.parse(JSON.stringify(slateValue)),
|
|
299
|
-
};
|
|
300
|
-
this.props.onChangeBlock(this.props.block, {
|
|
301
|
-
...this.props.data,
|
|
302
|
-
table,
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Toggle cell type (from header to data or reverse)
|
|
308
|
-
* @method toggleCellType
|
|
309
|
-
* @returns {undefined}
|
|
310
|
-
*/
|
|
311
|
-
toggleCellType() {
|
|
312
|
-
const table = { ...this.props.data.table };
|
|
313
|
-
let type =
|
|
314
|
-
table.rows[this.state.selected.row].cells[this.state.selected.cell].type;
|
|
315
|
-
table.rows[this.state.selected.row].cells[this.state.selected.cell].type =
|
|
316
|
-
type === 'header' ? 'data' : 'header';
|
|
317
|
-
this.props.onChangeBlock(this.props.block, {
|
|
318
|
-
...this.props.data,
|
|
319
|
-
table,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
199
|
+
}, [data.table, data, block, onChangeBlock]);
|
|
200
|
+
|
|
201
|
+
const headers = useMemo(
|
|
202
|
+
() => data.table?.rows?.[0]?.cells || [],
|
|
203
|
+
[data.table],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const rows = useMemo(
|
|
207
|
+
() => data.table?.rows?.filter((_, index) => index > 0) || [],
|
|
208
|
+
[data.table],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const schema = useMemo(() => TableSchema({ ...props, intl }), [props, intl]);
|
|
212
|
+
|
|
213
|
+
const onSelectCell = useCallback((row, cell) => {
|
|
214
|
+
setSelectedCell({ row, cell });
|
|
215
|
+
}, []);
|
|
216
|
+
|
|
217
|
+
const onChangeCell = useCallback(
|
|
218
|
+
(row, cell, slateValue) => {
|
|
219
|
+
const table = JSON.parse(JSON.stringify(data.table));
|
|
220
|
+
table.rows[row].cells[cell] = {
|
|
221
|
+
...table.rows[row].cells[cell],
|
|
222
|
+
value: JSON.parse(JSON.stringify(slateValue)),
|
|
223
|
+
};
|
|
224
|
+
onChangeBlock(block, {
|
|
225
|
+
...data,
|
|
226
|
+
table,
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
[data, block, onChangeBlock],
|
|
230
|
+
);
|
|
322
231
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
*/
|
|
328
|
-
onInsertRowBefore() {
|
|
329
|
-
const table = this.props.data.table;
|
|
330
|
-
this.props.onChangeBlock(this.props.block, {
|
|
331
|
-
...this.props.data,
|
|
232
|
+
const onInsertRowBefore = useCallback(() => {
|
|
233
|
+
const table = data.table;
|
|
234
|
+
onChangeBlock(block, {
|
|
235
|
+
...data,
|
|
332
236
|
table: {
|
|
333
237
|
...table,
|
|
334
238
|
rows: [
|
|
335
|
-
...table.rows.slice(0,
|
|
239
|
+
...table.rows.slice(0, selectedCell.row),
|
|
336
240
|
emptyRow(table.rows[0].cells),
|
|
337
|
-
...table.rows.slice(
|
|
241
|
+
...table.rows.slice(selectedCell.row),
|
|
338
242
|
],
|
|
339
243
|
},
|
|
340
244
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
cell: this.state.selected.cell,
|
|
345
|
-
},
|
|
245
|
+
setSelectedCell({
|
|
246
|
+
row: selectedCell.row + 1,
|
|
247
|
+
cell: selectedCell.cell,
|
|
346
248
|
});
|
|
347
|
-
}
|
|
249
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
348
250
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
onInsertRowAfter() {
|
|
354
|
-
const table = this.props.data.table;
|
|
355
|
-
this.props.onChangeBlock(this.props.block, {
|
|
356
|
-
...this.props.data,
|
|
251
|
+
const onInsertRowAfter = useCallback(() => {
|
|
252
|
+
const table = data.table;
|
|
253
|
+
onChangeBlock(block, {
|
|
254
|
+
...data,
|
|
357
255
|
table: {
|
|
358
256
|
...table,
|
|
359
257
|
rows: [
|
|
360
|
-
...table.rows.slice(0,
|
|
258
|
+
...table.rows.slice(0, selectedCell.row + 1),
|
|
361
259
|
emptyRow(table.rows[0].cells),
|
|
362
|
-
...table.rows.slice(
|
|
260
|
+
...table.rows.slice(selectedCell.row + 1),
|
|
363
261
|
],
|
|
364
262
|
},
|
|
365
263
|
});
|
|
366
|
-
}
|
|
264
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
367
265
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
*/
|
|
373
|
-
onInsertColBefore() {
|
|
374
|
-
const table = this.props.data.table;
|
|
375
|
-
this.props.onChangeBlock(this.props.block, {
|
|
376
|
-
...this.props.data,
|
|
266
|
+
const onInsertColBefore = useCallback(() => {
|
|
267
|
+
const table = data.table;
|
|
268
|
+
onChangeBlock(block, {
|
|
269
|
+
...data,
|
|
377
270
|
table: {
|
|
378
271
|
...table,
|
|
379
272
|
rows: map(table.rows, (row, index) => ({
|
|
380
273
|
...row,
|
|
381
274
|
cells: [
|
|
382
|
-
...row.cells.slice(0,
|
|
383
|
-
emptyCell(table.rows[index].cells[
|
|
384
|
-
...row.cells.slice(
|
|
275
|
+
...row.cells.slice(0, selectedCell.cell),
|
|
276
|
+
emptyCell(table.rows[index].cells[selectedCell.cell].type),
|
|
277
|
+
...row.cells.slice(selectedCell.cell),
|
|
385
278
|
],
|
|
386
279
|
})),
|
|
387
280
|
},
|
|
388
281
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
cell: this.state.selected.cell + 1,
|
|
393
|
-
},
|
|
282
|
+
setSelectedCell({
|
|
283
|
+
row: selectedCell.row,
|
|
284
|
+
cell: selectedCell.cell + 1,
|
|
394
285
|
});
|
|
395
|
-
}
|
|
286
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
396
287
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
onInsertColAfter() {
|
|
402
|
-
const table = this.props.data.table;
|
|
403
|
-
this.props.onChangeBlock(this.props.block, {
|
|
404
|
-
...this.props.data,
|
|
288
|
+
const onInsertColAfter = useCallback(() => {
|
|
289
|
+
const table = data.table;
|
|
290
|
+
onChangeBlock(block, {
|
|
291
|
+
...data,
|
|
405
292
|
table: {
|
|
406
293
|
...table,
|
|
407
294
|
rows: map(table.rows, (row, index) => ({
|
|
408
295
|
...row,
|
|
409
296
|
cells: [
|
|
410
|
-
...row.cells.slice(0,
|
|
411
|
-
emptyCell(table.rows[index].cells[
|
|
412
|
-
...row.cells.slice(
|
|
297
|
+
...row.cells.slice(0, selectedCell.cell + 1),
|
|
298
|
+
emptyCell(table.rows[index].cells[selectedCell.cell].type),
|
|
299
|
+
...row.cells.slice(selectedCell.cell + 1),
|
|
413
300
|
],
|
|
414
301
|
})),
|
|
415
302
|
},
|
|
416
303
|
});
|
|
417
|
-
}
|
|
304
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
418
305
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
* is selected.
|
|
422
|
-
* @returns {undefined}
|
|
423
|
-
*/
|
|
424
|
-
onDeleteCol() {
|
|
425
|
-
const table = this.props.data.table;
|
|
306
|
+
const onDeleteCol = useCallback(() => {
|
|
307
|
+
const table = data.table;
|
|
426
308
|
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
cell: this.state.selected.cell - 1,
|
|
432
|
-
},
|
|
309
|
+
if (selectedCell.cell === table.rows[0].cells.length - 1) {
|
|
310
|
+
setSelectedCell({
|
|
311
|
+
row: selectedCell.row,
|
|
312
|
+
cell: selectedCell.cell - 1,
|
|
433
313
|
});
|
|
434
314
|
}
|
|
435
315
|
|
|
436
|
-
|
|
437
|
-
...
|
|
316
|
+
onChangeBlock(block, {
|
|
317
|
+
...data,
|
|
438
318
|
table: {
|
|
439
319
|
...table,
|
|
440
320
|
rows: map(table.rows, (row) => ({
|
|
441
321
|
...row,
|
|
442
322
|
cells: remove(
|
|
443
323
|
row.cells,
|
|
444
|
-
(cell, index) => index !==
|
|
324
|
+
(cell, index) => index !== selectedCell.cell,
|
|
445
325
|
),
|
|
446
326
|
})),
|
|
447
327
|
},
|
|
448
328
|
});
|
|
449
|
-
}
|
|
329
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
450
330
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
* selected.
|
|
454
|
-
* @method onDeleteRow
|
|
455
|
-
* @returns {undefined}
|
|
456
|
-
*/
|
|
457
|
-
onDeleteRow() {
|
|
458
|
-
const table = this.props.data.table;
|
|
331
|
+
const onDeleteRow = useCallback(() => {
|
|
332
|
+
const table = data.table;
|
|
459
333
|
|
|
460
|
-
if (
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
cell: this.state.selected.cell,
|
|
465
|
-
},
|
|
334
|
+
if (selectedCell.row === table.rows.length - 1) {
|
|
335
|
+
setSelectedCell({
|
|
336
|
+
row: selectedCell.row - 1,
|
|
337
|
+
cell: selectedCell.cell,
|
|
466
338
|
});
|
|
467
339
|
}
|
|
468
340
|
|
|
469
|
-
|
|
470
|
-
...
|
|
341
|
+
onChangeBlock(block, {
|
|
342
|
+
...data,
|
|
471
343
|
table: {
|
|
472
344
|
...table,
|
|
473
|
-
rows: remove(
|
|
474
|
-
table.rows,
|
|
475
|
-
(row, index) => index !== this.state.selected.row,
|
|
476
|
-
),
|
|
345
|
+
rows: remove(table.rows, (row, index) => index !== selectedCell.row),
|
|
477
346
|
},
|
|
478
347
|
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
<Button
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
compact={this.props.data.table.compact}
|
|
586
|
-
basic={this.props.data.table.basic ? 'very' : false}
|
|
587
|
-
celled={this.props.data.table.celled}
|
|
588
|
-
inverted={this.props.data.table.inverted}
|
|
589
|
-
striped={this.props.data.table.striped}
|
|
590
|
-
className="slate-table-block"
|
|
591
|
-
unstackable
|
|
592
|
-
>
|
|
593
|
-
{!this.props.data.table.hideHeaders ? (
|
|
594
|
-
<Table.Header>
|
|
595
|
-
<Table.Row textAlign="left">
|
|
596
|
-
{headers.map((cell, cellIndex) => (
|
|
597
|
-
<Table.HeaderCell
|
|
598
|
-
key={cell.key}
|
|
599
|
-
textAlign="left"
|
|
600
|
-
verticalAlign="middle"
|
|
601
|
-
>
|
|
602
|
-
<Cell
|
|
603
|
-
value={cell.value}
|
|
604
|
-
row={0}
|
|
605
|
-
cell={cellIndex}
|
|
606
|
-
onSelectCell={this.onSelectCell}
|
|
607
|
-
selected={
|
|
608
|
-
this.props.selected &&
|
|
609
|
-
this.state.selected &&
|
|
610
|
-
0 === this.state.selected.row &&
|
|
611
|
-
cellIndex === this.state.selected.cell
|
|
612
|
-
}
|
|
613
|
-
selectedCell={this.state.selected}
|
|
614
|
-
isTableBlockSelected={this.props.selected}
|
|
615
|
-
onAddBlock={this.props.onAddBlock}
|
|
616
|
-
onSelectBlock={this.props.onSelectBlock}
|
|
617
|
-
onChange={this.onChangeCell}
|
|
618
|
-
index={this.props.index}
|
|
619
|
-
/>
|
|
620
|
-
</Table.HeaderCell>
|
|
621
|
-
))}
|
|
622
|
-
</Table.Row>
|
|
623
|
-
</Table.Header>
|
|
624
|
-
) : (
|
|
625
|
-
''
|
|
626
|
-
)}
|
|
627
|
-
<Table.Body>
|
|
628
|
-
{map(rows, (row, rowIndex) => (
|
|
629
|
-
<Table.Row key={row.key}>
|
|
630
|
-
{map(row.cells, (cell, cellIndex) => (
|
|
631
|
-
<Table.Cell
|
|
632
|
-
key={cell.key}
|
|
633
|
-
textAlign="left"
|
|
634
|
-
verticalAlign="middle"
|
|
635
|
-
className={
|
|
636
|
-
this.props.selected &&
|
|
637
|
-
this.state.selected &&
|
|
638
|
-
rowIndex + 1 === this.state.selected.row &&
|
|
639
|
-
cellIndex === this.state.selected.cell &&
|
|
640
|
-
this.props.selected
|
|
641
|
-
? 'selected'
|
|
642
|
-
: ''
|
|
348
|
+
}, [data, block, onChangeBlock, selectedCell]);
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<div className={cx('block table', { selected })}>
|
|
352
|
+
{selected && (
|
|
353
|
+
<div className="toolbar">
|
|
354
|
+
<Button.Group>
|
|
355
|
+
<Button
|
|
356
|
+
icon
|
|
357
|
+
basic
|
|
358
|
+
onClick={onInsertRowBefore}
|
|
359
|
+
title={intl.formatMessage(messages.insertRowBefore)}
|
|
360
|
+
aria-label={intl.formatMessage(messages.insertRowBefore)}
|
|
361
|
+
>
|
|
362
|
+
<Icon name={rowBeforeSVG} size="24px" />
|
|
363
|
+
</Button>
|
|
364
|
+
</Button.Group>
|
|
365
|
+
<Button.Group>
|
|
366
|
+
<Button
|
|
367
|
+
icon
|
|
368
|
+
basic
|
|
369
|
+
onClick={onInsertRowAfter}
|
|
370
|
+
title={intl.formatMessage(messages.insertRowAfter)}
|
|
371
|
+
aria-label={intl.formatMessage(messages.insertRowAfter)}
|
|
372
|
+
>
|
|
373
|
+
<Icon name={rowAfterSVG} size="24px" />
|
|
374
|
+
</Button>
|
|
375
|
+
</Button.Group>
|
|
376
|
+
<Button.Group>
|
|
377
|
+
<Button
|
|
378
|
+
icon
|
|
379
|
+
basic
|
|
380
|
+
onClick={onDeleteRow}
|
|
381
|
+
disabled={data.table?.rows?.length === 1}
|
|
382
|
+
title={intl.formatMessage(messages.deleteRow)}
|
|
383
|
+
aria-label={intl.formatMessage(messages.deleteRow)}
|
|
384
|
+
>
|
|
385
|
+
<Icon name={rowDeleteSVG} size="24px" />
|
|
386
|
+
</Button>
|
|
387
|
+
</Button.Group>
|
|
388
|
+
<Button.Group>
|
|
389
|
+
<Button
|
|
390
|
+
icon
|
|
391
|
+
basic
|
|
392
|
+
onClick={onInsertColBefore}
|
|
393
|
+
title={intl.formatMessage(messages.insertColBefore)}
|
|
394
|
+
aria-label={intl.formatMessage(messages.insertColBefore)}
|
|
395
|
+
>
|
|
396
|
+
<Icon name={colBeforeSVG} size="24px" />
|
|
397
|
+
</Button>
|
|
398
|
+
</Button.Group>
|
|
399
|
+
<Button.Group>
|
|
400
|
+
<Button
|
|
401
|
+
icon
|
|
402
|
+
basic
|
|
403
|
+
onClick={onInsertColAfter}
|
|
404
|
+
title={intl.formatMessage(messages.insertColAfter)}
|
|
405
|
+
aria-label={intl.formatMessage(messages.insertColAfter)}
|
|
406
|
+
>
|
|
407
|
+
<Icon name={colAfterSVG} size="24px" />
|
|
408
|
+
</Button>
|
|
409
|
+
</Button.Group>
|
|
410
|
+
<Button.Group>
|
|
411
|
+
<Button
|
|
412
|
+
icon
|
|
413
|
+
basic
|
|
414
|
+
onClick={onDeleteCol}
|
|
415
|
+
disabled={data.table?.rows?.[0].cells.length === 1}
|
|
416
|
+
title={intl.formatMessage(messages.deleteCol)}
|
|
417
|
+
aria-label={intl.formatMessage(messages.deleteCol)}
|
|
418
|
+
>
|
|
419
|
+
<Icon name={colDeleteSVG} size="24px" />
|
|
420
|
+
</Button>
|
|
421
|
+
</Button.Group>
|
|
422
|
+
</div>
|
|
423
|
+
)}
|
|
424
|
+
{data.table && (
|
|
425
|
+
<Table
|
|
426
|
+
fixed={data.table.fixed}
|
|
427
|
+
compact={data.table.compact}
|
|
428
|
+
basic={data.table.basic ? 'very' : false}
|
|
429
|
+
celled={data.table.celled}
|
|
430
|
+
inverted={data.table.inverted}
|
|
431
|
+
striped={data.table.striped}
|
|
432
|
+
className="slate-table-block"
|
|
433
|
+
unstackable
|
|
434
|
+
>
|
|
435
|
+
{!data.table.hideHeaders ? (
|
|
436
|
+
<Table.Header>
|
|
437
|
+
<Table.Row textAlign="left">
|
|
438
|
+
{headers.map((cell, cellIndex) => (
|
|
439
|
+
<Table.HeaderCell
|
|
440
|
+
key={cell.key}
|
|
441
|
+
textAlign="left"
|
|
442
|
+
verticalAlign="middle"
|
|
443
|
+
>
|
|
444
|
+
<Cell
|
|
445
|
+
value={cell.value}
|
|
446
|
+
row={0}
|
|
447
|
+
cell={cellIndex}
|
|
448
|
+
onSelectCell={onSelectCell}
|
|
449
|
+
selected={
|
|
450
|
+
selected &&
|
|
451
|
+
selectedCell &&
|
|
452
|
+
0 === selectedCell.row &&
|
|
453
|
+
cellIndex === selectedCell.cell
|
|
643
454
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
455
|
+
selectedCell={selectedCell}
|
|
456
|
+
isTableBlockSelected={selected}
|
|
457
|
+
onAddBlock={onAddBlock}
|
|
458
|
+
onSelectBlock={onSelectBlock}
|
|
459
|
+
onChange={onChangeCell}
|
|
460
|
+
index={index}
|
|
461
|
+
/>
|
|
462
|
+
</Table.HeaderCell>
|
|
463
|
+
))}
|
|
464
|
+
</Table.Row>
|
|
465
|
+
</Table.Header>
|
|
466
|
+
) : (
|
|
467
|
+
''
|
|
468
|
+
)}
|
|
469
|
+
<Table.Body>
|
|
470
|
+
{map(rows, (row, rowIndex) => (
|
|
471
|
+
<Table.Row key={row.key}>
|
|
472
|
+
{map(row.cells, (cell, cellIndex) => (
|
|
473
|
+
<Table.Cell
|
|
474
|
+
key={cell.key}
|
|
475
|
+
textAlign="left"
|
|
476
|
+
verticalAlign="middle"
|
|
477
|
+
className={
|
|
478
|
+
selected &&
|
|
479
|
+
selectedCell &&
|
|
480
|
+
rowIndex + 1 === selectedCell.row &&
|
|
481
|
+
cellIndex === selectedCell.cell &&
|
|
482
|
+
selected
|
|
483
|
+
? 'selected'
|
|
484
|
+
: ''
|
|
485
|
+
}
|
|
486
|
+
>
|
|
487
|
+
<Cell
|
|
488
|
+
value={cell.value}
|
|
489
|
+
row={rowIndex + 1}
|
|
490
|
+
cell={cellIndex}
|
|
491
|
+
onSelectCell={onSelectCell}
|
|
492
|
+
selected={
|
|
493
|
+
selected &&
|
|
494
|
+
selectedCell &&
|
|
495
|
+
rowIndex + 1 === selectedCell.row &&
|
|
496
|
+
cellIndex === selectedCell.cell
|
|
497
|
+
}
|
|
498
|
+
selectedCell={selectedCell}
|
|
499
|
+
isTableBlockSelected={selected}
|
|
500
|
+
onAddBlock={onAddBlock}
|
|
501
|
+
onSelectBlock={onSelectBlock}
|
|
502
|
+
onChange={onChangeCell}
|
|
503
|
+
index={index}
|
|
504
|
+
/>
|
|
505
|
+
</Table.Cell>
|
|
506
|
+
))}
|
|
507
|
+
</Table.Row>
|
|
508
|
+
))}
|
|
509
|
+
</Table.Body>
|
|
510
|
+
</Table>
|
|
511
|
+
)}
|
|
512
|
+
{selected && selectedCell && isClient && (
|
|
513
|
+
<SidebarPortal selected={selected}>
|
|
514
|
+
<BlockDataForm
|
|
515
|
+
schema={schema}
|
|
516
|
+
title={schema.title}
|
|
517
|
+
onChangeField={(id, value) => {
|
|
518
|
+
onChangeBlock(block, {
|
|
519
|
+
...data,
|
|
520
|
+
[id]: value,
|
|
521
|
+
});
|
|
522
|
+
}}
|
|
523
|
+
onChangeBlock={onChangeBlock}
|
|
524
|
+
formData={data}
|
|
525
|
+
block={block}
|
|
526
|
+
blocksConfig={blocksConfig}
|
|
527
|
+
/>
|
|
528
|
+
</SidebarPortal>
|
|
529
|
+
)}
|
|
530
|
+
</div>
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
Edit.propTypes = {
|
|
535
|
+
data: PropTypes.objectOf(PropTypes.any).isRequired,
|
|
536
|
+
detached: PropTypes.bool,
|
|
537
|
+
index: PropTypes.number.isRequired,
|
|
538
|
+
selected: PropTypes.bool.isRequired,
|
|
539
|
+
block: PropTypes.string.isRequired,
|
|
540
|
+
onAddBlock: PropTypes.func.isRequired,
|
|
541
|
+
onChangeBlock: PropTypes.func.isRequired,
|
|
542
|
+
onSelectBlock: PropTypes.func.isRequired,
|
|
543
|
+
blocksConfig: PropTypes.object,
|
|
544
|
+
};
|
|
692
545
|
|
|
693
|
-
export default
|
|
546
|
+
export default Edit;
|
|
@@ -25,12 +25,6 @@ test('renders an edit table block component', () => {
|
|
|
25
25
|
onAddBlock={() => {}}
|
|
26
26
|
onChangeBlock={() => {}}
|
|
27
27
|
onSelectBlock={() => {}}
|
|
28
|
-
onDeleteBlock={() => {}}
|
|
29
|
-
onInsertBlock={() => {}}
|
|
30
|
-
onFocusPreviousBlock={() => {}}
|
|
31
|
-
onFocusNextBlock={() => {}}
|
|
32
|
-
handleKeyDown={() => {}}
|
|
33
|
-
onMutateBlock={() => {}}
|
|
34
28
|
index={1}
|
|
35
29
|
/>
|
|
36
30
|
</Provider>,
|