diff --git a/packages/web/src/elements/ObjectListControl.svelte b/packages/web/src/elements/ObjectListControl.svelte
index c4c1cd36a..eaf9fc477 100644
--- a/packages/web/src/elements/ObjectListControl.svelte
+++ b/packages/web/src/elements/ObjectListControl.svelte
@@ -14,10 +14,16 @@
export let clickable = false;
export let onAddNew = null;
export let displayNameFieldName = null;
+ export let multipleItemsActions = null;
export let filters = writable({});
let collapsed = false;
+ let activeMultipleSelection;
+
+ function handleChangeMultipleSelection(list) {
+ activeMultipleSelection = list;
+ }
{#if collection?.length > 0 || showIfEmpty || emptyMessage}
@@ -35,6 +41,16 @@
{#if onAddNew}
Add new
{/if}
+ {#if multipleItemsActions && activeMultipleSelection && activeMultipleSelection?.length > 0}
+ {#each multipleItemsActions as item}
+
+ item.onClick(activeMultipleSelection)}>
+
+ {item.text} ({activeMultipleSelection.length})
+
+
+ {/each}
+ {/if}
{#if (collection?.length || 0) == 0 && emptyMessage}
@@ -45,6 +61,7 @@
diff --git a/packages/web/src/elements/TableControl.svelte b/packages/web/src/elements/TableControl.svelte
index b88f47465..6da2e8914 100644
--- a/packages/web/src/elements/TableControl.svelte
+++ b/packages/web/src/elements/TableControl.svelte
@@ -39,6 +39,9 @@
export let disableFocusOutline = false;
export let emptyMessage = null;
export let noCellPadding = false;
+ export let allowMultiSelect = false;
+ export let selectedIndexes = [];
+ export let onChangeMultipleSelection = null;
export let domTable = undefined;
export let stickyHeader = false;
@@ -53,6 +56,9 @@
const dispatch = createEventDispatcher();
+ let dragStartIndex = null;
+ let dragCurrentIndex = null;
+
$: columnList = _.compact(_.flatten(columns));
onMount(() => {
@@ -174,6 +180,15 @@
$: checkableFlatRowsShown = flatRowsShown.filter(x => itemSupportsCheckbox(x));
// $: groupedRows = computeGroupedRows(sortedRows);
+
+ $: if (onChangeMultipleSelection && flatRowsShown) {
+ onChangeMultipleSelection(selectedIndexes.map(index => flatRowsShown[index]));
+ }
+
+ $: if (flatRowsShown) {
+ // reset selection on items changed
+ selectedIndexes = [];
+ }
{
@@ -287,6 +303,30 @@
dispatch('clickrow', row);
}
}}
+ on:mousedown={event => {
+ if (allowMultiSelect && !event.ctrlKey && !event.metaKey) {
+ selectedIndexes = [];
+ dragStartIndex = index;
+ }
+ }}
+ on:mousemove={() => {
+ if (dragStartIndex != null && allowMultiSelect) {
+ dragCurrentIndex = index;
+ if (dragCurrentIndex != dragStartIndex || selectedIndexes.length > 0) {
+ if (dragCurrentIndex < dragStartIndex) {
+ selectedIndexes = _.range(dragCurrentIndex, dragStartIndex + 1);
+ } else {
+ selectedIndexes = _.range(dragStartIndex, dragCurrentIndex + 1);
+ }
+ } else if (selectedIndexes.length > 0) {
+ selectedIndexes = [dragCurrentIndex];
+ }
+ }
+ }}
+ on:mouseup={event => {
+ dragCurrentIndex = null;
+ dragStartIndex = null;
+ }}
data-testid={`TableControl_row_${index}`}
>
{#if checkedKeys}
diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte
index 83a51c23e..e1689abf3 100644
--- a/packages/web/src/icons/FontIcon.svelte
+++ b/packages/web/src/icons/FontIcon.svelte
@@ -159,6 +159,7 @@
'icon ai': 'mdi mdi-head-lightbulb',
'icon wait': 'mdi mdi-timer-sand',
'icon more': 'mdi mdi-more',
+ 'icon copy': 'mdi mdi-content-copy',
'icon run': 'mdi mdi-play',
'icon chevron-down': 'mdi mdi-chevron-down',
diff --git a/packages/web/src/tableeditor/TableEditor.svelte b/packages/web/src/tableeditor/TableEditor.svelte
index 05234b405..8b7d54e4b 100644
--- a/packages/web/src/tableeditor/TableEditor.svelte
+++ b/packages/web/src/tableeditor/TableEditor.svelte
@@ -193,6 +193,36 @@
on:clickrow={e => showModal(ColumnEditorModal, { columnInfo: e.detail, tableInfo, setTableInfo, driver })}
onAddNew={isWritable ? addColumn : null}
displayNameFieldName="columnName"
+ multipleItemsActions={[
+ {
+ text: 'Remove',
+ icon: 'icon delete',
+ onClick: selected => {
+ setTableInfo(tbl => {
+ const newColumns = tbl.columns.filter(x => !selected.find(y => y.columnName === x.columnName));
+ return { ...tbl, columns: newColumns };
+ });
+ },
+ },
+ {
+ text: 'Copy names',
+ icon: 'icon copy',
+ onClick: selected => {
+ const names = selected.map(x => x.columnName).join('\n');
+ navigator.clipboard.writeText(names);
+ },
+ },
+ {
+ text: 'Copy definitions',
+ icon: 'icon copy',
+ onClick: selected => {
+ const names = selected
+ .map(x => `${x.columnName} ${x.dataType}${x.notNull ? ' NOT NULL' : ''}`)
+ .join(',\n');
+ navigator.clipboard.writeText(names);
+ },
+ },
+ ]}
columns={[
!driver?.dialect?.specificNullabilityImplementation && {
fieldName: 'notNull',