Skip to content

Commit ffdb663

Browse files
authored
Merge pull request jdorn#702 from JElchison/dev-table-copy-button
Add copy button to arrays in table format
2 parents c3cb832 + c36e348 commit ffdb663

File tree

5 files changed

+130
-82
lines changed

5 files changed

+130
-82
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- Fix of #701 - editors/number.js and editors/integer.js don't change values when validation is failed
1515
- Fix of #716 - add ignore for allOf to fall in line with existing ignores of anyOf/oneOf for additionalProperties validation
1616
- Fix of #714 - Checkboxes inside object tables duplicate labels from heading
17+
- Added copy button to arrays in table format
1718

1819
### 2.0.0-dev
1920
- Fix of #643 - Allow use of themes not compiled directly into the build

src/defaults.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ languages.en = {
225225
*/
226226
button_delete_row_title_short: 'Delete',
227227
/**
228+
* Title on Copy Row buttons, short version (no parameter with the object title)
229+
*/
230+
button_copy_row_title_short: 'Copy',
231+
/**
228232
* Title on Collapse buttons
229233
*/
230234
button_collapse: 'Collapse',

src/editors/table.js

Lines changed: 92 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -207,31 +207,49 @@ export class TableEditor extends ArrayEditor {
207207
refreshRowButtons () {
208208
/* If we currently have minItems items in the array */
209209
const minItems = this.schema.minItems && this.schema.minItems >= this.rows.length
210+
/* If we currently have maxItems items in the array */
211+
const maxItems = this.schema.maxItems && this.schema.maxItems <= this.rows.length
210212

211213
let needRowButtons = false
212214
this.rows.forEach((editor, i) => {
213-
/* Hide the move down button for the last row */
214-
if (editor.movedown_button) {
215-
if (i === this.rows.length - 1) {
216-
editor.movedown_button.style.display = 'none'
215+
if (editor.delete_button) {
216+
/* Hide the delete button if we have minItems items */
217+
if (minItems) {
218+
editor.delete_button.style.display = 'none'
217219
} else {
218220
needRowButtons = true
219-
editor.movedown_button.style.display = ''
221+
editor.delete_button.style.display = ''
220222
}
221223
}
222224

223-
/* Hide the delete button if we have minItems items */
224-
if (editor.delete_button) {
225-
if (minItems) {
226-
editor.delete_button.style.display = 'none'
225+
if (editor.copy_button) {
226+
/* Hide the copy button if we have maxItems items */
227+
if (maxItems) {
228+
editor.copy_button.style.display = 'none'
227229
} else {
228230
needRowButtons = true
229-
editor.delete_button.style.display = ''
231+
editor.copy_button.style.display = ''
230232
}
231233
}
232234

233235
if (editor.moveup_button) {
234-
needRowButtons = true
236+
/* Hide the moveup button for the first row */
237+
if (i === 0) {
238+
editor.moveup_button.style.display = 'none'
239+
} else {
240+
needRowButtons = true
241+
editor.moveup_button.style.display = ''
242+
}
243+
}
244+
245+
if (editor.movedown_button) {
246+
/* Hide the movedown button for the last row */
247+
if (i === this.rows.length - 1) {
248+
editor.movedown_button.style.display = 'none'
249+
} else {
250+
needRowButtons = true
251+
editor.movedown_button.style.display = ''
252+
}
235253
}
236254
})
237255

@@ -249,49 +267,38 @@ export class TableEditor extends ArrayEditor {
249267
this.controls_header_cell.style.display = 'none'
250268
}
251269

252-
let controlsNeeded = false
253-
254270
if (!this.value.length) {
255-
this.delete_last_row_button.style.display = 'none'
256-
this.remove_all_rows_button.style.display = 'none'
257271
this.table.style.display = 'none'
258-
} else if (this.value.length === 1) {
259-
this.table.style.display = ''
260-
this.remove_all_rows_button.style.display = 'none'
261-
262-
/* If there are minItems items in the array, or configured to hide the delete_last_row button, hide the delete button beneath the rows */
263-
if (minItems || this.hide_delete_last_row_buttons) {
264-
this.delete_last_row_button.style.display = 'none'
265-
} else {
266-
this.delete_last_row_button.style.display = ''
267-
controlsNeeded = true
268-
}
269272
} else {
270273
this.table.style.display = ''
271-
272-
if (minItems || this.hide_delete_last_row_buttons) {
273-
this.delete_last_row_button.style.display = 'none'
274-
} else {
275-
this.delete_last_row_button.style.display = ''
276-
controlsNeeded = true
277-
}
278-
279-
if (minItems || this.hide_delete_all_rows_buttons) {
280-
this.remove_all_rows_button.style.display = 'none'
281-
} else {
282-
this.remove_all_rows_button.style.display = ''
283-
controlsNeeded = true
284-
}
285274
}
286275

287-
/* If there are maxItems in the array, hide the add button beneath the rows */
288-
if ((this.schema.maxItems && this.schema.maxItems <= this.rows.length) || this.hide_add_button) {
276+
let controlsNeeded = false
277+
278+
/* If there are maxItems items in the array, or configured to hide the add_row_button button, hide the button beneath the rows */
279+
if (maxItems || this.hide_add_button) {
289280
this.add_row_button.style.display = 'none'
290281
} else {
291282
this.add_row_button.style.display = ''
292283
controlsNeeded = true
293284
}
294285

286+
/* If there are minItems items in the array, or configured to hide the delete_last_row button, hide the button beneath the rows */
287+
if (!this.value.length || minItems || this.hide_delete_last_row_buttons) {
288+
this.delete_last_row_button.style.display = 'none'
289+
} else {
290+
this.delete_last_row_button.style.display = ''
291+
controlsNeeded = true
292+
}
293+
294+
/* If there are minItems items in the array, or configured to hide the remove_all_rows_button button, hide the button beneath the rows */
295+
if (this.value.length <= 1 || minItems || this.hide_delete_all_rows_buttons) {
296+
this.remove_all_rows_button.style.display = 'none'
297+
} else {
298+
this.remove_all_rows_button.style.display = ''
299+
controlsNeeded = true
300+
}
301+
295302
if (!controlsNeeded) {
296303
this.controls.style.display = 'none'
297304
} else {
@@ -316,7 +323,7 @@ export class TableEditor extends ArrayEditor {
316323

317324
const controlsHolder = this.rows[i].table_controls
318325

319-
/* Buttons to delete row, move row up, and move row down */
326+
/* Buttons to delete row, copy row, move row up, and move row down */
320327
if (!this.hide_delete_buttons) {
321328
this.rows[i].delete_button = this.getButton('', 'delete', this.translate('button_delete_row_title_short'))
322329
this.rows[i].delete_button.classList.add('delete', 'json-editor-btntype-delete')
@@ -329,33 +336,54 @@ export class TableEditor extends ArrayEditor {
329336
return false
330337
}
331338

332-
const i = e.currentTarget.getAttribute('data-i') * 1
333-
const newval = this.getValue().filter((row, j) => j !== i) /* If this is the one we're deleting */
334-
this.setValue(newval)
339+
const j = e.currentTarget.getAttribute('data-i') * 1
340+
const value = this.getValue()
341+
342+
value.splice(j, 1)
343+
344+
this.setValue(value)
335345
this.onChange(true)
336-
this.jsoneditor.trigger('deleteRow', this.rows[i])
346+
this.jsoneditor.trigger('deleteRow', this.rows[j])
337347
})
338348
controlsHolder.appendChild(this.rows[i].delete_button)
339349
}
340350

341-
if (i && !this.hide_move_buttons) {
351+
if (this.show_copy_button) {
352+
this.rows[i].copy_button = this.getButton('', 'copy', this.translate('button_copy_row_title_short'))
353+
this.rows[i].copy_button.classList.add('copy', 'json-editor-btntype-copy')
354+
this.rows[i].copy_button.setAttribute('data-i', i)
355+
this.rows[i].copy_button.addEventListener('click', e => {
356+
e.preventDefault()
357+
e.stopPropagation()
358+
359+
const j = e.currentTarget.getAttribute('data-i') * 1
360+
const value = this.getValue()
361+
362+
value.splice(j + 1, 0, value[j])
363+
364+
this.setValue(value)
365+
this.onChange(true)
366+
this.jsoneditor.trigger('copyRow', this.rows[j + 1])
367+
})
368+
controlsHolder.appendChild(this.rows[i].copy_button)
369+
}
370+
371+
if (!this.hide_move_buttons) {
342372
this.rows[i].moveup_button = this.getButton('', 'moveup', this.translate('button_move_up_title'))
343373
this.rows[i].moveup_button.classList.add('moveup', 'json-editor-btntype-move')
344374
this.rows[i].moveup_button.setAttribute('data-i', i)
345375
this.rows[i].moveup_button.addEventListener('click', e => {
346376
e.preventDefault()
347377
e.stopPropagation()
348-
const i = e.currentTarget.getAttribute('data-i') * 1
349378

350-
if (i <= 0) return
351-
const rows = this.getValue()
352-
const tmp = rows[i - 1]
353-
rows[i - 1] = rows[i]
354-
rows[i] = tmp
379+
const j = e.currentTarget.getAttribute('data-i') * 1
380+
const value = this.getValue()
355381

356-
this.setValue(rows)
382+
value.splice(j - 1, 0, value.splice(j, 1)[0])
383+
384+
this.setValue(value)
357385
this.onChange(true)
358-
this.jsoneditor.trigger('moveRow', this.rows[i - 1])
386+
this.jsoneditor.trigger('moveRow', this.rows[j - 1])
359387
})
360388
controlsHolder.appendChild(this.rows[i].moveup_button)
361389
}
@@ -367,16 +395,15 @@ export class TableEditor extends ArrayEditor {
367395
this.rows[i].movedown_button.addEventListener('click', e => {
368396
e.preventDefault()
369397
e.stopPropagation()
370-
const i = e.currentTarget.getAttribute('data-i') * 1
371-
const rows = this.getValue()
372-
if (i >= rows.length - 1) return
373-
const tmp = rows[i + 1]
374-
rows[i + 1] = rows[i]
375-
rows[i] = tmp
376-
377-
this.setValue(rows)
398+
399+
const j = e.currentTarget.getAttribute('data-i') * 1
400+
const value = this.getValue()
401+
402+
value.splice(j + 1, 0, value.splice(j, 1)[0])
403+
404+
this.setValue(value)
378405
this.onChange(true)
379-
this.jsoneditor.trigger('moveRow', this.rows[i + 1])
406+
this.jsoneditor.trigger('moveRow', this.rows[j + 1])
380407
})
381408
controlsHolder.appendChild(this.rows[i].movedown_button)
382409
}

tests/codeceptjs/editors/array_test.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,44 @@ Scenario('should trigger array (table) editing triggers', async (I) => {
1212
I.amOnPage('table-move-events.html');
1313
I.seeElement('[data-schemapath="root.0"]');
1414
I.seeElement('[data-schemapath="root.1"]');
15+
I.seeElement('[data-schemapath="root.2"]');
16+
I.seeElement('[data-schemapath="root.3"]');
17+
I.seeElement('[data-schemapath="root.4"]');
1518
I.click('.get-value');
1619
value = await I.grabValueFrom('.debug');
17-
assert.equal(value, '["A","B"]');
20+
assert.equal(value, '["A","B","C","D","E"]');
1821

1922
I.amAcceptingPopups();
20-
I.click('.json-editor-btn-moveup');
23+
I.click('//button[contains(@class, "json-editor-btn-moveup") and @data-i="1"]');
2124
I.seeInPopup('moveRow');
2225
I.acceptPopup();
2326
I.click('.get-value');
2427
value = await I.grabValueFrom('.debug');
25-
assert.equal(value, '["B","A"]');
28+
assert.equal(value, '["B","A","C","D","E"]');
2629

2730
I.amAcceptingPopups();
28-
I.click('.json-editor-btn-movedown');
31+
I.click('//button[contains(@class, "json-editor-btn-movedown") and @data-i="1"]');
2932
I.seeInPopup('moveRow');
3033
I.acceptPopup();
3134
I.click('.get-value');
3235
value = await I.grabValueFrom('.debug');
33-
assert.equal(value, '["A","B"]');
36+
assert.equal(value, '["B","C","A","D","E"]');
37+
38+
I.amAcceptingPopups();
39+
I.click('//button[contains(@class, "json-editor-btn-copy") and @data-i="2"]');
40+
I.seeInPopup('copyRow');
41+
I.acceptPopup();
42+
I.click('.get-value');
43+
value = await I.grabValueFrom('.debug');
44+
assert.equal(value, '["B","C","A","A","D","E"]');
3445

3546
I.amAcceptingPopups();
3647
I.click('.json-editor-btntype-add');
3748
I.seeInPopup('addRow');
3849
I.acceptPopup();
3950
I.click('.get-value');
4051
value = await I.grabValueFrom('.debug');
41-
assert.equal(value, '["A","B",""]');
52+
assert.equal(value, '["B","C","A","A","D","E",""]');
4253

4354
// This test will fail when using Puppeteer due to the way Puppeteer handles popups.
4455
// Puppeteer apparently only sees the text in the last popup, so it doesn't see the
@@ -54,7 +65,7 @@ Scenario('should trigger array (table) editing triggers', async (I) => {
5465
I.acceptPopup();
5566
I.click('.get-value');
5667
value = await I.grabValueFrom('.debug');
57-
assert.equal(value, '["A","B"]');
68+
assert.equal(value, '["B","C","A","A","D","E"]');
5869

5970
// This test will fail when using Puppeteer due to the way Puppeteer handles popups.
6071
I.amAcceptingPopups();

tests/pages/table-move-events.html

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,32 @@
2323
}
2424
};
2525
var editor = new JSONEditor(container, {
26-
schema: schema
26+
schema: schema,
27+
enable_array_copy: true
2728
});
2829
document.querySelector('.get-value').addEventListener('click', function () {
2930
debug.value = JSON.stringify(editor.getValue());
3031
});
31-
editor.setValue(["A","B"]);
32+
editor.setValue(["A","B","C","D","E"]);
33+
editor.on('copyRow', function () {
34+
alert('copyRow');
35+
console.log('copyRow');
36+
});
3237
editor.on('moveRow', function () {
33-
alert('moveRow')
34-
console.log('moveRow')
38+
alert('moveRow');
39+
console.log('moveRow');
3540
});
3641
editor.on('addRow', function () {
37-
alert('addRow')
38-
console.log('addRow')
42+
alert('addRow');
43+
console.log('addRow');
3944
});
4045
editor.on('deleteRow', function () {
41-
alert('deleteRow')
42-
console.log('deleteRow')
46+
alert('deleteRow');
47+
console.log('deleteRow');
4348
});
4449
editor.on('deleteAllRows', function () {
45-
alert('deleteAllRows')
46-
console.log('deleteAllRows')
50+
alert('deleteAllRows');
51+
console.log('deleteAllRows');
4752
});
4853
</script>
4954

0 commit comments

Comments
 (0)