Skip to content

Commit 6304f46

Browse files
author
Aman Kumar
committed
feat(Delete Confirmation): implement delete confirmation popup and enhance delete button functionality
- Added a global delete key handler to manage delete/backspace key presses, triggering a confirmation popup for deleting selected annotations. - Introduced a delete confirmation popup with options to cancel or confirm deletion, improving user experience and preventing accidental deletions. - Updated the annotation toolbar to include a delete button that shows/hides based on annotation selection state. - Emitted an 'ANNOTATION_DESELECT' event to handle deselection logic across annotations. - Enhanced styling for the delete confirmation popup and button interactions for better UI consistency.
1 parent 42b0b36 commit 6304f46

File tree

9 files changed

+448
-49
lines changed

9 files changed

+448
-49
lines changed

src/core/event-emitter.core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export type InstanceEventType =
3636
| 'ANNOTATION_SELECTED'
3737
| 'ANNOTATION_CREATED'
3838
| 'ANNOTATION_DELETED'
39+
| 'ANNOTATION_DESELECT'
3940
| 'ANNOTATION_UPDATED'
4041
| 'DRAWING_STARTED'
4142
| 'DRAWING_FINISHED'

src/style/global.css

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,128 @@ span.a-active-highlight {
451451
.pdf-error-message {
452452
color: var(--text-on-background-light);
453453
}
454+
}
455+
456+
/* ========================== */
457+
/* Delete Confirmation Popup */
458+
/* ========================== */
459+
460+
.a-delete-confirmation-popup {
461+
position: fixed;
462+
top: 0;
463+
left: 0;
464+
width: 100%;
465+
height: 100%;
466+
background: rgba(0, 0, 0, 0.5);
467+
display: flex;
468+
align-items: center;
469+
justify-content: center;
470+
z-index: 10000;
471+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
472+
animation: fadeIn 0.2s ease-out;
473+
}
474+
475+
.a-delete-confirmation-popup .a-popup-content {
476+
background: white;
477+
border-radius: 8px;
478+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
479+
max-width: 400px;
480+
width: 90%;
481+
overflow: hidden;
482+
animation: slideIn 0.2s ease-out;
483+
}
484+
485+
.a-delete-confirmation-popup .a-popup-header {
486+
padding: 20px 20px 0 20px;
487+
border-bottom: 1px solid #e5e5e5;
488+
}
489+
490+
.a-delete-confirmation-popup .a-popup-header h3 {
491+
margin: 0 0 20px 0;
492+
font-size: 18px;
493+
font-weight: 600;
494+
color: #333;
495+
}
496+
497+
.a-delete-confirmation-popup .a-popup-body {
498+
padding: 20px;
499+
}
500+
501+
.a-delete-confirmation-popup .a-popup-body p {
502+
margin: 0 0 10px 0;
503+
color: #666;
504+
line-height: 1.5;
505+
}
506+
507+
.a-delete-confirmation-popup .a-popup-body .a-annotation-type {
508+
margin: 0;
509+
color: #999;
510+
font-size: 14px;
511+
font-style: italic;
512+
}
513+
514+
.a-delete-confirmation-popup .a-popup-actions {
515+
padding: 0 20px 20px 20px;
516+
display: flex;
517+
gap: 10px;
518+
justify-content: flex-end;
519+
}
520+
521+
.a-delete-confirmation-popup .a-btn {
522+
padding: 8px 16px;
523+
border-radius: 4px;
524+
cursor: pointer;
525+
font-size: 14px;
526+
transition: all 0.2s;
527+
border: none;
528+
outline: none;
529+
}
530+
531+
.a-delete-confirmation-popup .a-btn-cancel {
532+
border: 1px solid #ddd;
533+
background: white;
534+
color: #666;
535+
}
536+
537+
.a-delete-confirmation-popup .a-btn-cancel:hover {
538+
background: #f8f9fa;
539+
border-color: #adb5bd;
540+
}
541+
542+
.a-delete-confirmation-popup .a-btn-delete {
543+
border: 1px solid #dc3545;
544+
background: #dc3545;
545+
color: white;
546+
}
547+
548+
.a-delete-confirmation-popup .a-btn-delete:hover {
549+
background: #c82333;
550+
border-color: #c82333;
551+
}
552+
553+
.a-delete-confirmation-popup .a-btn:focus {
554+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
555+
}
556+
557+
/* Animations */
558+
@keyframes fadeIn {
559+
from {
560+
opacity: 0;
561+
}
562+
563+
to {
564+
opacity: 1;
565+
}
566+
}
567+
568+
@keyframes slideIn {
569+
from {
570+
transform: translateY(-20px);
571+
opacity: 0;
572+
}
573+
574+
to {
575+
transform: translateY(0);
576+
opacity: 1;
577+
}
454578
}

src/style/toolbar.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,35 @@
476476
width: 14px;
477477
}
478478

479+
.a-toolbar-button .a-annotation-toolbar-delete-icon::before {
480+
content: '\e872';
481+
font-size: 1.34rem !important;
482+
}
483+
484+
/* Smooth transitions for delete button visibility */
485+
.a-toolbar-item[data-delete-button] {
486+
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
487+
}
488+
489+
.a-toolbar-item[data-delete-button].hidden {
490+
opacity: 0;
491+
transform: scale(0.8);
492+
pointer-events: none;
493+
}
494+
495+
/* Right container styling for better alignment */
496+
.a-annotation-toolbar-right-container {
497+
display: flex;
498+
align-items: center;
499+
gap: 8px;
500+
margin-left: auto;
501+
}
502+
503+
/* Ensure delete button has consistent spacing with shape buttons */
504+
.a-annotation-toolbar-right-container .a-toolbar-item[data-delete-button] {
505+
margin-left: 8px;
506+
}
507+
479508
.a-annotation-shape-button {
480509
display: inline-flex;
481510
align-items: center;

src/types/events.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type Events =
2222
| 'ANNOTATION_SELECTED'
2323
| 'ANNOTATION_CREATED'
2424
| 'ANNOTATION_DELETED'
25+
| 'ANNOTATION_DESELECT'
2526
| 'ANNOTATION_UPDATED'
2627
| 'DRAWING_STARTED'
2728
| 'DRAWING_FINISHED'

src/viewer/annotations/ellipse-annotation.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export class EllipseAnnotation extends Annotation {
5454
instanceId: string;
5555
containerId: string;
5656
};
57-
private _onDeleteKeyBound = this._onDeleteKey.bind(this);
5857
private _bindOnScaleChange = this._onScaleChange.bind(this);
5958

6059
/**
@@ -220,22 +219,23 @@ export class EllipseAnnotation extends Annotation {
220219
this._onShapeUpdate();
221220
}
222221

223-
/** Adds resizers and listens for Delete/Backspace. */
222+
/** Adds resizers. */
224223
public select(): void {
225224
if (!this._resizer) {
226225
this._resizer = new Resizer(this.__svg, this.__element as any, this._onShapeUpdate.bind(this), this._constraints);
227226
this.__svg.focus();
228-
this.__svg.addEventListener('keydown', this._onDeleteKeyBound);
229227
}
230228
}
231229

232230
/** Removes resizers and key listener. */
233231
public deselect(): void {
234232
if (this._resizer) {
235-
this.__svg.removeEventListener('keydown', this._onDeleteKeyBound);
236233
this._resizer.removeResizers();
237234
this._resizer = null;
238235
}
236+
237+
// Emit deselection event
238+
this.events.emit('ANNOTATION_DESELECT');
239239
}
240240

241241
/**
@@ -313,14 +313,6 @@ export class EllipseAnnotation extends Annotation {
313313
this.__svg.appendChild(this.__hitElementRect);
314314
}
315315

316-
/**
317-
* Handles Delete/Backspace key to remove the annotation.
318-
* @param e Keyboard event
319-
*/
320-
private _onDeleteKey(e: KeyboardEvent): void {
321-
if (e.key === 'Delete' || e.key === 'Backspace') this.deleteAnnotation();
322-
}
323-
324316
/**
325317
* Stores logical geometry for zoom recalculation.
326318
* @param scale Current scale factor

src/viewer/annotations/line-annotation.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export class LineAnnotation extends Annotation {
5454
instanceId: string;
5555
containerId: string;
5656
};
57-
private _onDeleteKeyBound = this._onDeleteKey.bind(this);
5857
private _bindOnScaleChange = this._onScaleChange.bind(this);
5958

6059
/**
@@ -227,25 +226,26 @@ export class LineAnnotation extends Annotation {
227226
}
228227

229228
/**
230-
* Select this annotation, adding resize handles and delete‐key listener.
229+
* Select this annotation, adding resize handles.
231230
*/
232231
public select(): void {
233232
if (!this._resizer) {
234233
this._resizer = new Resizer(this.__svg, this.__element as SVGGraphicsElement, this._onShapeUpdate.bind(this), this.__annotationDrawerContainer.getBoundingClientRect());
235234
this.__svg.focus();
236-
this.__svg.addEventListener('keydown', this._onDeleteKeyBound);
237235
}
238236
}
239237

240238
/**
241-
* Deselect this annotation, removing handles and delete‐key listener.
239+
* Deselect this annotation, removing handles.
242240
*/
243241
public deselect(): void {
244242
if (this._resizer) {
245-
this.__svg.removeEventListener('keydown', this._onDeleteKeyBound);
246243
this._resizer.removeResizers();
247244
this._resizer = null;
248245
}
246+
247+
// Emit deselection event
248+
this.events.emit('ANNOTATION_DESELECT');
249249
}
250250

251251
/**
@@ -338,13 +338,6 @@ export class LineAnnotation extends Annotation {
338338
this.__svg.appendChild(hit);
339339
}
340340

341-
/** Handle Delete/Backspace key to remove annotation. */
342-
private _onDeleteKey(e: KeyboardEvent): void {
343-
if (e.key === 'Delete' || e.key === 'Backspace') {
344-
this.deleteAnnotation();
345-
}
346-
}
347-
348341
/** Capture logical (unscaled) endpoints for later zoom updates. */
349342
private _captureOriginal(): void {
350343
const { x1, y1, x2, y2 } = this._logicalCoords();

src/viewer/annotations/rectangle-annotation.ts

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export class RectangleAnnotation extends Annotation {
4646
instanceId: string;
4747
containerId: string;
4848
};
49-
private _onDeleteKeyBound = this._onDeleteKey.bind(this);
5049
private _bindOnScaleChange = this._onScaleChange.bind(this);
5150

5251
/** Unique identifier of this annotation. */
@@ -222,25 +221,26 @@ export class RectangleAnnotation extends Annotation {
222221
}
223222

224223
/**
225-
* Selects this annotation, adding resizers and Delete/Backspace handler.
224+
* Selects this annotation, adding resizers.
226225
*/
227226
public select(): void {
228227
if (!this._resizer) {
229228
this._resizer = new Resizer(this.__svg, this.__element as SVGGraphicsElement, this._onShapeUpdate.bind(this), this._constraints);
230229
this.__svg.focus();
231-
this._addDeleteEvent();
232230
}
233231
}
234232

235233
/**
236-
* Deselects this annotation, removing resizers and keyboard handler.
234+
* Deselects this annotation, removing resizers.
237235
*/
238236
public deselect(): void {
239237
if (this._resizer) {
240-
this._removeDeleteEvent();
241238
this._resizer.removeResizers();
242239
this._resizer = null;
243240
}
241+
242+
// Emit deselection event
243+
this.events.emit('ANNOTATION_DESELECT');
244244
}
245245

246246
/**
@@ -250,7 +250,6 @@ export class RectangleAnnotation extends Annotation {
250250
public deleteAnnotation(suppressEvent = false): void {
251251
if (this.__svg) {
252252
if (this._resizer) {
253-
this._removeDeleteEvent();
254253
this._resizer.removeResizers();
255254
this._resizer = null;
256255
}
@@ -384,25 +383,6 @@ export class RectangleAnnotation extends Annotation {
384383
}
385384
}
386385

387-
/** Adds keyboard listener for Delete/Backspace. */
388-
private _addDeleteEvent(): void {
389-
this.__svg.addEventListener('keydown', this._onDeleteKeyBound);
390-
}
391-
392-
/** Removes keyboard listener for Delete/Backspace. */
393-
private _removeDeleteEvent(): void {
394-
this.__svg.removeEventListener('keydown', this._onDeleteKeyBound);
395-
}
396-
397-
/**
398-
* Handles Delete or Backspace key to delete the annotation.
399-
*/
400-
private _onDeleteKey(event: KeyboardEvent): void {
401-
if (event.key === 'Delete' || event.key === 'Backspace') {
402-
this.deleteAnnotation();
403-
}
404-
}
405-
406386
/** Captures current un-scaled position and size for zoom adjustments. */
407387
private _maintainOriginalBounding(zoomLevel = 1): void {
408388
// Always normalize to scale=1 for consistency, regardless of current zoom

0 commit comments

Comments
 (0)