Skip to content

Commit 4f0aaff

Browse files
Delete Item from Context Menu (#5428)
* Implement delete item from context menu Co-authored-by: Lokesh Dogga <[email protected]>
1 parent cfe7f4c commit 4f0aaff

File tree

7 files changed

+274
-22
lines changed

7 files changed

+274
-22
lines changed

packages/core/src/dynamoDb/commands/deleteDynamoDbTable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export async function deleteDynamoDbTable(
2727
})
2828

2929
if (!isConfirmed) {
30-
getLogger().debug(`Delete action canceled on DynamoDB table: ${node.dynamoDbtable}`)
30+
getLogger().debug(`Delete action cancelled on DynamoDB table: ${node.dynamoDbtable}`)
3131
return
3232
}
3333

packages/core/src/dynamoDb/utils/dynamodb.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,29 @@ function getAttributeValue(attribute: AttributeValue): { key: string; value: any
216216
}
217217
return undefined
218218
}
219+
220+
export async function deleteItem(
221+
tableName: string,
222+
selectedRow: RowData,
223+
tableSchema: TableSchema,
224+
regionCode: string,
225+
client = new DynamoDbClient(regionCode)
226+
) {
227+
const partitionKeyName = tableSchema.partitionKey.name
228+
const partitionKeyValue = selectedRow[partitionKeyName]
229+
230+
const deleteRequest: DynamoDB.DocumentClient.DeleteItemInput = {
231+
TableName: tableName,
232+
Key: {
233+
[partitionKeyName]: {
234+
S: partitionKeyValue,
235+
} as any,
236+
},
237+
}
238+
if (tableSchema.sortKey) {
239+
const sortKeyName = tableSchema.sortKey.name
240+
const sortKeyValue = selectedRow[sortKeyName]
241+
deleteRequest.Key[sortKeyName] = { S: sortKeyValue } as any
242+
}
243+
return await client.deleteItem(deleteRequest)
244+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<template>
2+
<div class="context-menu" v-if="visible" :style="style">
3+
<button @click="onCopyCell">
4+
<p>
5+
<span class="icon icon-sm icon-vscode-copy"></span>
6+
Copy
7+
</p>
8+
</button>
9+
<button @click="onCopyRow">
10+
<p>
11+
<span class="icon icon-sm icon-vscode-copy"></span>
12+
Copy Row
13+
</p>
14+
</button>
15+
<vscode-divider style="margin-top: 2px"></vscode-divider>
16+
<button @click="onDelete">
17+
<p>
18+
<span class="icon icon-sm icon-vscode-discard"></span>
19+
Delete Item
20+
</p>
21+
</button>
22+
<button @click="onEdit">
23+
<p>
24+
<span class="icon icon-sm icon-vscode-edit"></span>
25+
Edit Item
26+
</p>
27+
</button>
28+
</div>
29+
</template>
30+
31+
<script lang="ts">
32+
import { defineComponent, computed, PropType } from 'vue'
33+
34+
export default defineComponent({
35+
props: {
36+
position: {
37+
type: Object as PropType<{ top: number; left: number }>,
38+
required: true,
39+
},
40+
visible: {
41+
type: Boolean,
42+
required: true,
43+
},
44+
},
45+
setup(props, { emit }) {
46+
const style = computed(() => ({
47+
top: `${props.position.top}px`,
48+
left: `${props.position.left}px`,
49+
}))
50+
51+
const onCopyCell = () => {
52+
emit('copyCell')
53+
emit('close')
54+
}
55+
56+
const onCopyRow = () => {
57+
emit('copyRow')
58+
emit('close')
59+
}
60+
61+
const onDelete = () => {
62+
emit('delete')
63+
emit('close')
64+
}
65+
66+
const onEdit = () => {
67+
emit('edit')
68+
emit('close')
69+
}
70+
71+
return {
72+
style,
73+
onCopyCell,
74+
onCopyRow,
75+
onDelete,
76+
onEdit,
77+
}
78+
},
79+
})
80+
</script>
81+
82+
<style scoped>
83+
.context-menu {
84+
display: flex;
85+
flex-direction: column;
86+
align-items: center; /* Centers items vertically */
87+
position: absolute;
88+
background-color: #fff;
89+
border: 1px solid #ccc;
90+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
91+
padding: 10px;
92+
width: 150px; /* Adjust width as needed */
93+
border-radius: 5px;
94+
z-index: 1000; /* Ensure menu appears above other elements */
95+
}
96+
97+
.context-menu button {
98+
width: 100%;
99+
padding: 2px;
100+
border: none;
101+
cursor: pointer;
102+
display: flex;
103+
flex-direction: row;
104+
justify-content: start;
105+
background: inherit;
106+
}
107+
108+
.context-menu button:hover {
109+
background-color: #beb1b1;
110+
}
111+
112+
.context-menu p {
113+
color: black;
114+
}
115+
</style>

packages/core/src/dynamoDb/vue/tableView.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ vscode-link {
4848
font-size: 1.5em;
4949
}
5050

51-
/* vscode-data-grid-row.sticky-header {
52-
border-bottom: 1px solid;
53-
} */
54-
5551
vscode-data-grid-row {
5652
width: max-content;
5753
}

packages/core/src/dynamoDb/vue/tableView.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ import { ExtContext } from '../../shared'
88
import { VueWebview } from '../../webviews/main'
99
import { getLogger, Logger } from '../../shared/logger'
1010
import { Key, ScanInput } from 'aws-sdk/clients/dynamodb'
11+
import * as localizedText from '../../shared/localizedText'
12+
import { copyToClipboard, showConfirmationMessage } from '../../shared/utilities/messages'
1113
import { DynamoDbTarget, telemetry } from '../../shared/telemetry/telemetry'
12-
import { getTableContent, queryTableContent, RowData, TableData, getTableKeySchema } from '../utils/dynamodb'
14+
import {
15+
getTableContent,
16+
queryTableContent,
17+
RowData,
18+
TableData,
19+
getTableKeySchema,
20+
deleteItem,
21+
TableSchema,
22+
} from '../utils/dynamodb'
1323

1424
const localize = nls.loadMessageBundle()
1525

@@ -71,6 +81,46 @@ export class DynamoDbTableWebview extends VueWebview {
7181
public async getTableSchema() {
7282
return await getTableKeySchema(this.data.tableName, this.data.region)
7383
}
84+
85+
public async copyCell(selectedCell: string) {
86+
if (selectedCell !== '') {
87+
await copyToClipboard(JSON.stringify(selectedCell), 'TableCell')
88+
}
89+
}
90+
91+
public async copyRow(selectedRow: RowData) {
92+
if (selectedRow !== undefined) {
93+
await copyToClipboard(JSON.stringify(selectedRow), 'TableItem')
94+
}
95+
}
96+
97+
public async deleteItem(selectedRow: RowData, tableSchema: TableSchema) {
98+
const isConfirmed = await showConfirmationMessage({
99+
prompt: localize(
100+
'AWS.dynamoDb.deleteItem.prompt',
101+
'Are you sure you want to delete the item with partition key: {0}?',
102+
selectedRow[tableSchema.partitionKey.name]
103+
),
104+
confirm: localizedText.localizedDelete,
105+
cancel: localizedText.cancel,
106+
})
107+
108+
if (!isConfirmed) {
109+
getLogger().debug(`Delete action cancelled on DynamoDB Item`)
110+
return
111+
}
112+
113+
if (selectedRow === undefined || tableSchema === undefined) {
114+
throw new Error('Invalid row, failed to delete the item.')
115+
}
116+
try {
117+
await deleteItem(this.data.tableName, selectedRow, tableSchema, this.data.region)
118+
return this.fetchPageData(this.data.lastEvaluatedKey, this.data.currentPage)
119+
} catch (err) {
120+
getLogger().error(`Delete action failed on DynamoDB Item`)
121+
return undefined
122+
}
123+
}
74124
}
75125

76126
const Panel = VueWebview.compilePanel(DynamoDbTableWebview)

packages/core/src/dynamoDb/vue/tableView.vue

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,64 @@
6262
v-for="row in dynamoDbTableData.tableContent"
6363
@contextmenu.prevent="showContextMenu($event, row)"
6464
>
65-
<vscode-data-grid-cell v-for="(key, index) in Object.keys(row)" :grid-column="index + 1">{{
66-
row[key]
67-
}}</vscode-data-grid-cell>
65+
<vscode-data-grid-cell v-for="(key, index) in Object.keys(row)" :grid-column="index + 1">
66+
{{ row[key] }}
67+
</vscode-data-grid-cell>
6868
</vscode-data-grid-row>
69-
<!-- {{ updateTableSection(dynamoDbTableData) }} -->
7069
</vscode-data-grid>
70+
71+
<!-- Context Menu -->
72+
<ContextMenu
73+
v-if="contextMenuVisible"
74+
:position="contextMenuPosition"
75+
:visible="contextMenuVisible"
76+
@copyCell="handleCopyCell"
77+
@copyRow="handleCopyRow"
78+
@delete="handleDelete"
79+
@edit="handleEdit"
80+
@close="contextMenuVisible = false"
81+
/>
7182
</div>
7283
</div>
7384
</template>
7485

7586
<script setup lang="ts">
7687
import { allComponents, provideVSCodeDesignSystem } from '@vscode/webview-ui-toolkit'
88+
import { ref, onMounted, onUnmounted } from 'vue'
89+
import ContextMenu from './contextMenu.vue'
7790
7891
provideVSCodeDesignSystem().register(allComponents)
92+
93+
onMounted(() => {
94+
document.addEventListener('click', handleClickOutside)
95+
})
96+
97+
onUnmounted(() => {
98+
document.removeEventListener('click', handleClickOutside)
99+
})
100+
101+
function handleClickOutside(event: MouseEvent) {
102+
const contextMenuElement = document.querySelector('.context-menu')
103+
if (contextMenuElement && !contextMenuElement.contains(event.target as Node)) {
104+
contextMenuVisible.value = false
105+
}
106+
}
79107
</script>
80108

81109
<script lang="ts">
82110
import { defineComponent } from 'vue'
83-
import { RowData } from '../utils/dynamodb'
111+
import { RowData, TableSchema } from '../utils/dynamodb'
84112
import { DynamoDbTableWebview, DynamoDbTableData } from './tableView'
85113
import { WebviewClientFactory } from '../../webviews/client'
86114
import { Key } from 'aws-sdk/clients/dynamodb'
87115
88116
const client = WebviewClientFactory.create<DynamoDbTableWebview>()
117+
const contextMenuVisible = ref(false)
118+
const contextMenuPosition = ref({ top: 0, left: 0 })
119+
let selectedRow = ref<RowData>()
120+
let tableSchema: TableSchema
121+
let selectedCell = ''
122+
89123
export default defineComponent({
90124
data() {
91125
return {
@@ -107,7 +141,7 @@ export default defineComponent({
107141
async created() {
108142
this.isLoading = true
109143
this.dynamoDbTableData = await client.init()
110-
const tableSchema = await client.getTableSchema()
144+
tableSchema = await client.getTableSchema()
111145
this.partitionKey = tableSchema.partitionKey.name
112146
this.sortKey = tableSchema.sortKey?.name ?? ''
113147
this.pageKeys = [undefined, this.dynamoDbTableData.lastEvaluatedKey]
@@ -128,13 +162,6 @@ export default defineComponent({
128162
},
129163
},
130164
methods: {
131-
updateTableSection(dynamoDbTableData: Pick<DynamoDbTableData, 'tableContent'>) {
132-
const basicGrid = document.getElementById('datagrid')
133-
if (basicGrid) {
134-
;(basicGrid as any).rowsData = dynamoDbTableData.tableContent
135-
}
136-
},
137-
138165
async refreshTable() {
139166
this.updatePageNumber()
140167
this.isLoading = true
@@ -198,9 +225,39 @@ export default defineComponent({
198225
this.dynamoDbTableData = await client.queryData(queryRequest)
199226
},
200227
201-
showContextMenu(event: any, row: any) {
202-
console.log(event)
203-
console.log(row)
228+
showContextMenu(event: MouseEvent, row: any) {
229+
event.preventDefault()
230+
contextMenuPosition.value = { top: event.clientY, left: event.clientX }
231+
contextMenuVisible.value = true
232+
selectedRow.value = row
233+
selectedCell = (event.target as any).innerHTML
234+
},
235+
236+
handleCopyCell() {
237+
client.copyCell(selectedCell)
238+
},
239+
240+
handleCopyRow() {
241+
if (selectedRow.value === undefined) {
242+
return
243+
}
244+
client.copyRow(selectedRow.value)
245+
},
246+
247+
async handleDelete() {
248+
if (selectedRow.value === undefined) {
249+
return
250+
}
251+
const response = await client.deleteItem(selectedRow.value, tableSchema)
252+
if (response) {
253+
this.dynamoDbTableData = response
254+
this.updatePageNumber()
255+
}
256+
},
257+
258+
handleEdit() {
259+
// Handle edit logic
260+
console.log('Edit clicked')
204261
},
205262
},
206263
})

packages/core/src/shared/clients/dynamoDbClient.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,12 @@ export class DynamoDbClient {
7070
const sdkClient = await this.createSdkClient()
7171
return sdkClient.query(request).promise()
7272
}
73+
74+
/**
75+
* Delete an Item from a table in DynamoDB.
76+
*/
77+
public async deleteItem(request: DynamoDB.Types.DeleteItemInput) {
78+
const sdkClient = await this.createSdkClient()
79+
return sdkClient.deleteItem(request).promise()
80+
}
7381
}

0 commit comments

Comments
 (0)