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',