@@ -56,6 +56,11 @@ interface Nodes {
5656 * Caption element for the image.
5757 */
5858 caption : HTMLElement ;
59+
60+ /**
61+ * Resize handle element.
62+ */
63+ resizeHandle ?: HTMLElement ;
5964}
6065
6166/**
@@ -112,6 +117,11 @@ export default class Ui {
112117 */
113118 private readOnly : boolean ;
114119
120+ /**
121+ * Tracked width in pixels.
122+ */
123+ private currentWidth ?: number ;
124+
115125 /**
116126 * @param ui - image tool Ui module
117127 * @param ui.api - Editor.js API
@@ -244,11 +254,73 @@ export default class Ui {
244254 if ( this . nodes . imagePreloader !== undefined ) {
245255 this . nodes . imagePreloader . style . backgroundImage = '' ;
246256 }
257+ this . applyWidth ( this . currentWidth ) ;
258+ this . ensureResizeHandle ( ) ;
247259 } ) ;
248260
249261 this . nodes . imageContainer . appendChild ( this . nodes . imageEl ) ;
250262 }
251263
264+ /**
265+ * Apply a specific width (px) to the image container.
266+ */
267+ public applyWidth ( width ?: number ) : void {
268+ const MIN = 40 ;
269+ const parentWidth = this . nodes . wrapper . parentElement ?. getBoundingClientRect ( ) ?. width ;
270+ const max = parentWidth && Number . isFinite ( parentWidth ) ? Math . max ( MIN , parentWidth ) : undefined ;
271+ let next = width ;
272+ if ( typeof next === 'number' && next > 0 ) {
273+ if ( max ) next = Math . min ( next , max ) ;
274+ next = Math . max ( MIN , next ) ;
275+ this . currentWidth = next ;
276+ this . nodes . imageContainer . style . width = `${ next } px` ;
277+ } else {
278+ this . currentWidth = undefined ;
279+ this . nodes . imageContainer . style . width = '' ;
280+ }
281+ }
282+
283+ /**
284+ * Returns current width if set.
285+ */
286+ public getWidth ( ) : number | undefined {
287+ if ( typeof this . currentWidth === 'number' ) return this . currentWidth ;
288+ const inline = parseFloat ( this . nodes . imageContainer . style . width ) ;
289+ return Number . isFinite ( inline ) ? inline : undefined ;
290+ }
291+
292+ /**
293+ * Ensure resize handle exists and is wired.
294+ */
295+ private ensureResizeHandle ( ) : void {
296+ if ( this . readOnly || this . nodes . resizeHandle ) return ;
297+ const handle = make ( 'div' , this . CSS . resizeHandle ) ;
298+ let startX = 0 ;
299+ let startWidth = 0 ;
300+
301+ const onMove = ( event : MouseEvent ) => {
302+ const delta = event . clientX - startX ;
303+ const next = startWidth + delta ;
304+ this . applyWidth ( next ) ;
305+ } ;
306+
307+ const onUp = ( ) => {
308+ document . removeEventListener ( 'mousemove' , onMove ) ;
309+ document . removeEventListener ( 'mouseup' , onUp ) ;
310+ } ;
311+
312+ handle . addEventListener ( 'mousedown' , ( event : MouseEvent ) => {
313+ event . preventDefault ( ) ;
314+ startX = event . clientX ;
315+ startWidth = this . nodes . imageContainer . getBoundingClientRect ( ) . width ;
316+ document . addEventListener ( 'mousemove' , onMove ) ;
317+ document . addEventListener ( 'mouseup' , onUp ) ;
318+ } ) ;
319+
320+ this . nodes . imageContainer . appendChild ( handle ) ;
321+ this . nodes . resizeHandle = handle ;
322+ }
323+
252324 /**
253325 * Shows caption input
254326 * @param text - caption content text
@@ -291,6 +363,7 @@ export default class Ui {
291363 imagePreloader : 'image-tool__image-preloader' ,
292364 imageEl : 'image-tool__image-picture' ,
293365 caption : 'image-tool__caption' ,
366+ resizeHandle : 'image-tool__image-resize-handle' ,
294367 } ;
295368 } ;
296369
0 commit comments