@@ -2211,6 +2211,7 @@ export class ToolStarterApp {
22112211 const pointRounding = this . shapePointRoundingValues ( shape ) ;
22122212 shape . geometry . points . forEach ( ( point , index ) => {
22132213 grid . append ( this . createPolygonPointRow ( point , index , {
2214+ deletable : editablePolygon ,
22142215 rounded : pointRounding [ index ] === true ,
22152216 selectable : editablePolygon
22162217 } ) ) ;
@@ -2289,7 +2290,7 @@ export class ToolStarterApp {
22892290 } ) ;
22902291 }
22912292
2292- createPolygonPointRow ( point , index , { rounded = false , selectable = true } = { } ) {
2293+ createPolygonPointRow ( point , index , { deletable = true , rounded = false , selectable = true } = { } ) {
22932294 const row = document . createElement ( "div" ) ;
22942295 row . className = "object-vector-studio-v2__polygon-point-field" ;
22952296 row . dataset . polygonPointIndex = String ( index ) ;
@@ -2298,7 +2299,7 @@ export class ToolStarterApp {
22982299 row . setAttribute ( "role" , "button" ) ;
22992300 row . setAttribute ( "tabindex" , "0" ) ;
23002301 row . setAttribute ( "aria-pressed" , "false" ) ;
2301- row . title = "Select this point row for Add Point or Delete Point(s) ." ;
2302+ row . title = "Select this point row for Add Point." ;
23022303 row . addEventListener ( "click" , ( event ) => this . handlePolygonPointRowSelection ( event , row ) ) ;
23032304 row . addEventListener ( "keydown" , ( event ) => {
23042305 if ( event . key === "Enter" || event . key === " " ) {
@@ -2344,24 +2345,36 @@ export class ToolStarterApp {
23442345 roundCheckbox . addEventListener ( "change" , ( ) => this . updateSelectedShapePointRounding ( index , roundCheckbox . checked ) ) ;
23452346 roundLabel . append ( roundCaption , roundCheckbox ) ;
23462347 row . append ( roundLabel ) ;
2348+ if ( deletable ) {
2349+ const deleteButton = document . createElement ( "button" ) ;
2350+ deleteButton . type = "button" ;
2351+ deleteButton . className = "object-vector-studio-v2__polygon-point-delete" ;
2352+ deleteButton . dataset . polygonPointDelete = "true" ;
2353+ deleteButton . dataset . polygonPointIndex = String ( index ) ;
2354+ deleteButton . setAttribute ( "aria-label" , `Delete point ${ index + 1 } ` ) ;
2355+ deleteButton . title = `Delete point ${ index + 1 } ` ;
2356+ this . applyIconGlyph ( deleteButton , "delete" ) ;
2357+ deleteButton . addEventListener ( "pointerdown" , ( event ) => event . stopPropagation ( ) ) ;
2358+ deleteButton . addEventListener ( "click" , ( event ) => {
2359+ event . preventDefault ( ) ;
2360+ event . stopPropagation ( ) ;
2361+ this . deletePolygonPointRow ( index , deleteButton ) ;
2362+ } ) ;
2363+ row . append ( deleteButton ) ;
2364+ }
23472365 return row ;
23482366 }
23492367
23502368 createPolygonSideActions ( ) {
23512369 const actions = document . createElement ( "div" ) ;
23522370 actions . className = "object-vector-studio-v2__polygon-side-actions" ;
2353- [
2354- [ "add" , "Add Point" , "add" , ( ) => this . addPolygonSideRow ( ) ] ,
2355- [ "delete" , "Delete Point(s)" , "delete" , ( ) => this . deletePolygonPointRows ( ) ]
2356- ] . forEach ( ( [ action , label , iconKey , handler ] ) => {
2357- const button = document . createElement ( "button" ) ;
2358- button . type = "button" ;
2359- button . dataset . polygonSideAction = action ;
2360- button . textContent = label ;
2361- this . applyIconGlyph ( button , iconKey ) ;
2362- button . addEventListener ( "click" , handler ) ;
2363- actions . append ( button ) ;
2364- } ) ;
2371+ const button = document . createElement ( "button" ) ;
2372+ button . type = "button" ;
2373+ button . dataset . polygonSideAction = "add" ;
2374+ button . textContent = "Add Point" ;
2375+ this . applyIconGlyph ( button , "add" ) ;
2376+ button . addEventListener ( "click" , ( ) => this . addPolygonSideRow ( ) ) ;
2377+ actions . append ( button ) ;
23652378 return actions ;
23662379 }
23672380
@@ -6159,7 +6172,7 @@ export class ToolStarterApp {
61596172 } ) ;
61606173 }
61616174
6162- deletePolygonPointRows ( ) {
6175+ deletePolygonPointRow ( pointIndex , sourceButton = null ) {
61636176 const selected = this . selectedShape ( ) ;
61646177 if ( ! selected || ! [ "polygon" , "polyline" ] . includes ( shapeGeometryTool ( selected ) ) ) {
61656178 this . statusLog . write ( "WARN Delete point skipped: no editable point-list shape is selected." ) ;
@@ -6171,33 +6184,38 @@ export class ToolStarterApp {
61716184 }
61726185 const geometry = this . readCurrentPolygonGeometry ( selected ) ;
61736186 if ( ! geometry . ok ) {
6187+ this . markPolygonPointDeleteInvalid ( sourceButton , geometry . error ) ;
61746188 this . statusLog . write ( `FAIL Delete point rejected for shape row ${ this . selectedShapeIndex } : ${ geometry . error } ` ) ;
61756189 return ;
61766190 }
6177- const checkedIndexes = this . checkedPolygonPointIndexes ( ) ;
6178- if ( ! checkedIndexes . length ) {
6179- const message = "select at least one polygon point to delete." ;
6180- this . markPolygonSideActionInvalid ( "delete" , message ) ;
6181- this . clearPolygonPointSelections ( ) ;
6191+ const normalizedIndex = Number ( pointIndex ) ;
6192+ if ( ! Number . isInteger ( normalizedIndex ) || normalizedIndex < 0 || normalizedIndex >= geometry . value . points . length ) {
6193+ const message = `point ${ normalizedIndex + 1 } is not available.` ;
6194+ this . markPolygonPointDeleteInvalid ( sourceButton , message ) ;
61826195 this . statusLog . write ( `FAIL Delete point rejected for shape row ${ this . selectedShapeIndex } : ${ message } ` ) ;
61836196 return ;
61846197 }
6185- const checkedSet = new Set ( checkedIndexes ) ;
6186- const nextPoints = geometry . value . points . filter ( ( _ , index ) => ! checkedSet . has ( index ) ) ;
6187- const nextPointRounding = this . currentPointRoundingRows ( geometry . value . points . length ) . filter ( ( _ , index ) => ! checkedSet . has ( index ) ) ;
61886198 const minPointCount = shapeGeometryTool ( selected ) === "polyline" ? 2 : 4 ;
6199+ if ( geometry . value . points . length - 1 < minPointCount ) {
6200+ const message = `${ shapeGeometryTool ( selected ) } must keep at least ${ minPointCount } points.` ;
6201+ this . markPolygonPointDeleteInvalid ( sourceButton , message ) ;
6202+ this . statusLog . write ( `FAIL Delete point rejected for shape row ${ this . selectedShapeIndex } : ${ message } ` ) ;
6203+ return ;
6204+ }
6205+ const nextPoints = geometry . value . points . filter ( ( _ , index ) => index !== normalizedIndex ) ;
6206+ const nextPointRounding = this . currentPointRoundingRows ( geometry . value . points . length ) . filter ( ( _ , index ) => index !== normalizedIndex ) ;
61896207 if ( nextPoints . length < minPointCount ) {
61906208 const message = `${ shapeGeometryTool ( selected ) } must keep at least ${ minPointCount } points.` ;
6191- this . markPolygonSideActionInvalid ( "delete" , message ) ;
6192- this . clearPolygonPointSelections ( ) ;
6209+ this . markPolygonPointDeleteInvalid ( sourceButton , message ) ;
61936210 this . statusLog . write ( `FAIL Delete point rejected for shape row ${ this . selectedShapeIndex } : ${ message } ` ) ;
61946211 return ;
61956212 }
61966213 this . rebuildPolygonPointList ( nextPoints , nextPointRounding ) ;
61976214 this . clearPolygonPointSelections ( ) ;
61986215 this . clearPolygonSideActionValidity ( ) ;
6216+ this . clearPolygonPointDeleteValidity ( ) ;
61996217 this . applyShapeGeometryEdits ( {
6200- okMessage : `OK Deleted ${ checkedIndexes . length } point${ checkedIndexes . length === 1 ? "" : "s" } from shape row ${ this . selectedShapeIndex } .`
6218+ okMessage : `OK Deleted point ${ normalizedIndex + 1 } from shape row ${ this . selectedShapeIndex } .`
62016219 } ) ;
62026220 }
62036221
@@ -6220,6 +6238,23 @@ export class ToolStarterApp {
62206238 } ) ;
62216239 }
62226240
6241+ markPolygonPointDeleteInvalid ( sourceButton , message ) {
6242+ if ( ! sourceButton ) {
6243+ return ;
6244+ }
6245+ sourceButton . dataset . validationState = "invalid" ;
6246+ sourceButton . setAttribute ( "aria-invalid" , "true" ) ;
6247+ sourceButton . title = message ;
6248+ }
6249+
6250+ clearPolygonPointDeleteValidity ( ) {
6251+ this . elements . shapeGeometryDetails . querySelectorAll ( "[data-polygon-point-delete='true']" ) . forEach ( ( button ) => {
6252+ delete button . dataset . validationState ;
6253+ button . removeAttribute ( "aria-invalid" ) ;
6254+ button . title = `Delete point ${ Number ( button . dataset . polygonPointIndex ) + 1 } ` ;
6255+ } ) ;
6256+ }
6257+
62236258 currentPointRoundingRows ( expectedCount = null ) {
62246259 const checkboxes = Array . from ( this . elements . shapeGeometryDetails . querySelectorAll ( "[data-polygon-point-round='true']" ) ) ;
62256260 const pointRounding = checkboxes
@@ -6297,6 +6332,11 @@ export class ToolStarterApp {
62976332 if ( roundCheckbox ) {
62986333 roundCheckbox . setAttribute ( "aria-label" , `Round point ${ index + 1 } ` ) ;
62996334 }
6335+ const deleteButton = row . querySelector ( "[data-polygon-point-delete='true']" ) ;
6336+ if ( deleteButton ) {
6337+ deleteButton . setAttribute ( "aria-label" , `Delete point ${ index + 1 } ` ) ;
6338+ deleteButton . title = `Delete point ${ index + 1 } ` ;
6339+ }
63006340 } ) ;
63016341 }
63026342
0 commit comments