@liwe3/webcomponents 1.0.14 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/AIMarkdownEditor.d.ts +35 -0
  2. package/dist/AIMarkdownEditor.d.ts.map +1 -0
  3. package/dist/AIMarkdownEditor.js +412 -0
  4. package/dist/AIMarkdownEditor.js.map +1 -0
  5. package/dist/AITextEditor.d.ts +183 -0
  6. package/dist/AITextEditor.d.ts.map +1 -0
  7. package/dist/AITextEditor.js +63 -27
  8. package/dist/AITextEditor.js.map +1 -1
  9. package/dist/ButtonToolbar.d.ts +35 -0
  10. package/dist/ButtonToolbar.d.ts.map +1 -0
  11. package/dist/ButtonToolbar.js +220 -0
  12. package/dist/ButtonToolbar.js.map +1 -0
  13. package/dist/CheckList.d.ts +31 -0
  14. package/dist/CheckList.d.ts.map +1 -0
  15. package/dist/CheckList.js +336 -0
  16. package/dist/CheckList.js.map +1 -0
  17. package/dist/ChunkUploader.d.ts +125 -0
  18. package/dist/ChunkUploader.d.ts.map +1 -0
  19. package/dist/ChunkUploader.js +756 -0
  20. package/dist/ChunkUploader.js.map +1 -0
  21. package/dist/ComicBalloon.d.ts +82 -0
  22. package/dist/ComicBalloon.d.ts.map +1 -0
  23. package/dist/ComicBalloon.js +346 -0
  24. package/dist/ComicBalloon.js.map +1 -0
  25. package/dist/ContainerBox.d.ts +112 -0
  26. package/dist/ContainerBox.d.ts.map +1 -0
  27. package/dist/ContainerBox.js +359 -0
  28. package/dist/ContainerBox.js.map +1 -0
  29. package/dist/DateSelector.d.ts +103 -0
  30. package/dist/DateSelector.d.ts.map +1 -0
  31. package/dist/Dialog.d.ts +102 -0
  32. package/dist/Dialog.d.ts.map +1 -0
  33. package/dist/Dialog.js +299 -0
  34. package/dist/Dialog.js.map +1 -0
  35. package/dist/Drawer.d.ts +63 -0
  36. package/dist/Drawer.d.ts.map +1 -0
  37. package/dist/Drawer.js +340 -0
  38. package/dist/Drawer.js.map +1 -0
  39. package/dist/ImageView.d.ts +42 -0
  40. package/dist/ImageView.d.ts.map +1 -0
  41. package/dist/ImageView.js +209 -0
  42. package/dist/ImageView.js.map +1 -0
  43. package/dist/MarkdownPreview.d.ts +25 -0
  44. package/dist/MarkdownPreview.d.ts.map +1 -0
  45. package/dist/MarkdownPreview.js +147 -0
  46. package/dist/MarkdownPreview.js.map +1 -0
  47. package/dist/PopoverMenu.d.ts +103 -0
  48. package/dist/PopoverMenu.d.ts.map +1 -0
  49. package/dist/ResizableCropper.d.ts +158 -0
  50. package/dist/ResizableCropper.d.ts.map +1 -0
  51. package/dist/ResizableCropper.js +562 -0
  52. package/dist/ResizableCropper.js.map +1 -0
  53. package/dist/SmartSelect.d.ts +100 -0
  54. package/dist/SmartSelect.d.ts.map +1 -0
  55. package/dist/SmartSelect.js +45 -2
  56. package/dist/SmartSelect.js.map +1 -1
  57. package/dist/Toast.d.ts +127 -0
  58. package/dist/Toast.d.ts.map +1 -0
  59. package/dist/Toast.js +79 -49
  60. package/dist/Toast.js.map +1 -1
  61. package/dist/TreeView.d.ts +84 -0
  62. package/dist/TreeView.d.ts.map +1 -0
  63. package/dist/TreeView.js +478 -0
  64. package/dist/TreeView.js.map +1 -0
  65. package/dist/index.d.ts +23 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +51 -14
  68. package/dist/index.js.map +1 -1
  69. package/package.json +60 -5
  70. package/src/AIMarkdownEditor.ts +568 -0
  71. package/src/AITextEditor.ts +97 -2
  72. package/src/ButtonToolbar.ts +302 -0
  73. package/src/CheckList.ts +438 -0
  74. package/src/ChunkUploader.ts +1135 -0
  75. package/src/ComicBalloon.ts +709 -0
  76. package/src/ContainerBox.ts +570 -0
  77. package/src/Dialog.ts +510 -0
  78. package/src/Drawer.ts +435 -0
  79. package/src/ImageView.ts +265 -0
  80. package/src/MarkdownPreview.ts +213 -0
  81. package/src/ResizableCropper.ts +1099 -0
  82. package/src/SmartSelect.ts +48 -2
  83. package/src/Toast.ts +96 -32
  84. package/src/TreeView.ts +673 -0
  85. package/src/index.ts +129 -27
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChunkUploader.js","sources":["../src/ChunkUploader.ts"],"sourcesContent":["/**\n * ChunkUploader Web Component\n * A file uploader with drag & drop, file previews, and chunked upload support\n */\n\nexport interface UploadedFile {\n\tid : string;\n\tfile : File;\n\tstatus : 'pending' | 'uploading' | 'completed' | 'error' | 'aborted';\n\tprogress : number;\n\tuploadedBytes : number;\n\tpreview? : string;\n\terror? : string;\n\tuploadId? : string; // R2 upload ID for aborting\n\tkey? : string; // R2 key for aborting\n}\n\nexport interface ChunkUploaderConfig {\n\tserverURL : string;\n\tchunkSize : number; // in MB\n\tauthToken? : string;\n\tvalidFiletypes? : string[]; // Array of extensions like ['jpg', 'png', 'pdf']\n\tmaxFileSize? : number; // in MB\n\tlabelDropFiles? : string; // Custom text for drop zone (default: \"Drop files here\")\n\tlabelBrowse? : string; // Custom label for browse button (default: \"Browse Files\")\n\tfolder? : string; // Destination folder name for uploads\n\tcompact? : boolean; // Compact mode (single file, no preview)\n\tonfilecomplete? : ( file : UploadedFile ) => void;\n\tonuploadcomplete? : ( files : UploadedFile[] ) => void;\n\tparseResponse? : ( response : any, endpoint : 'initiate' | 'part' | 'complete' ) => any; // Transform endpoint responses\n}\n\nconst DEFAULT_CHUNK_SIZE = 5; // 5MB - R2/S3 minimum part size (except last part)\nconst DEFAULT_MAX_FILE_SIZE = 5120; // 5GB in MB\n\nexport class ChunkUploaderElement extends HTMLElement {\n\tdeclare shadowRoot : ShadowRoot;\n\tprivate files : Map<string, UploadedFile> = new Map();\n\tprivate config : ChunkUploaderConfig = {\n\t\tserverURL: '',\n\t\tchunkSize: DEFAULT_CHUNK_SIZE,\n\t\tmaxFileSize: DEFAULT_MAX_FILE_SIZE,\n\t};\n\tprivate isUploading = false;\n\tprivate abortController : AbortController | null = null;\n\n\tconstructor () {\n\t\tsuper();\n\t\tthis.attachShadow( { mode: 'open' } );\n\t\tthis.render();\n\t\tthis.bindEvents();\n\t}\n\n\tstatic get observedAttributes () : string[] {\n\t\treturn [ 'server-url', 'chunk-size', 'auth-token', 'valid-filetypes', 'max-file-size', 'label-drop-files', 'label-browse', 'folder', 'compact' ];\n\t}\n\n\tattributeChangedCallback ( name : string, oldValue : string | null, newValue : string | null ) : void {\n\t\tif ( oldValue !== newValue ) {\n\t\t\tswitch ( name ) {\n\t\t\t\tcase 'server-url':\n\t\t\t\t\tthis.config.serverURL = newValue || '';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chunk-size':\n\t\t\t\t\tthis.config.chunkSize = parseFloat( newValue || String( DEFAULT_CHUNK_SIZE ) );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'auth-token':\n\t\t\t\t\tthis.config.authToken = newValue || undefined;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'valid-filetypes':\n\t\t\t\t\tthis.config.validFiletypes = newValue ? newValue.split( ',' ).map( ( ext ) => ext.trim() ) : undefined;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'max-file-size':\n\t\t\t\t\tthis.config.maxFileSize = parseFloat( newValue || String( DEFAULT_MAX_FILE_SIZE ) );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'label-drop-files':\n\t\t\t\t\tthis.config.labelDropFiles = newValue || undefined;\n\t\t\t\t\tthis.updateLabels();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'label-browse':\n\t\t\t\t\tthis.config.labelBrowse = newValue || undefined;\n\t\t\t\t\tthis.updateLabels();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'folder':\n\t\t\t\t\tthis.config.folder = newValue || undefined;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'compact':\n\t\t\t\t\tthis.config.compact = newValue !== null;\n\t\t\t\t\tthis.updateCompactMode();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Property getters and setters\n\tget serverURL () : string {\n\t\treturn this.config.serverURL;\n\t}\n\n\tset serverURL ( value : string ) {\n\t\tthis.config.serverURL = value;\n\t\tthis.setAttribute( 'server-url', value );\n\t}\n\n\tget chunkSize () : number {\n\t\treturn this.config.chunkSize;\n\t}\n\n\tset chunkSize ( value : number ) {\n\t\tthis.config.chunkSize = value;\n\t\tthis.setAttribute( 'chunk-size', value.toString() );\n\t}\n\n\tget authToken () : string | undefined {\n\t\treturn this.config.authToken;\n\t}\n\n\tset authToken ( value : string | undefined ) {\n\t\tif ( value ) {\n\t\t\tthis.config.authToken = value;\n\t\t\tthis.setAttribute( 'auth-token', value );\n\t\t} else {\n\t\t\tthis.config.authToken = undefined;\n\t\t\tthis.removeAttribute( 'auth-token' );\n\t\t}\n\t}\n\n\tget validFiletypes () : string[] | undefined {\n\t\treturn this.config.validFiletypes;\n\t}\n\n\tset validFiletypes ( value : string[] | undefined ) {\n\t\tthis.config.validFiletypes = value;\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'valid-filetypes', value.join( ',' ) );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'valid-filetypes' );\n\t\t}\n\t}\n\n\tget maxFileSize () : number {\n\t\treturn this.config.maxFileSize || DEFAULT_MAX_FILE_SIZE;\n\t}\n\n\tset maxFileSize ( value : number ) {\n\t\tthis.config.maxFileSize = value;\n\t\tthis.setAttribute( 'max-file-size', value.toString() );\n\t}\n\n\tget labelDropFiles () : string | undefined {\n\t\treturn this.config.labelDropFiles;\n\t}\n\n\tset labelDropFiles ( value : string | undefined ) {\n\t\tthis.config.labelDropFiles = value;\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'label-drop-files', value );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'label-drop-files' );\n\t\t}\n\t\tthis.updateLabels();\n\t}\n\n\tget labelBrowse () : string | undefined {\n\t\treturn this.config.labelBrowse;\n\t}\n\n\tset labelBrowse ( value : string | undefined ) {\n\t\tthis.config.labelBrowse = value;\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'label-browse', value );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'label-browse' );\n\t\t}\n\t\tthis.updateLabels();\n\t}\n\n\tget folder () : string | undefined {\n\t\treturn this.config.folder;\n\t}\n\n\tset folder ( value : string | undefined ) {\n\t\tthis.config.folder = value;\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'folder', value );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'folder' );\n\t\t}\n\t}\n\n\tget compact () : boolean {\n\t\treturn !!this.config.compact;\n\t}\n\n\tset compact ( value : boolean ) {\n\t\tthis.config.compact = value;\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'compact', '' );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'compact' );\n\t\t}\n\t\tthis.updateCompactMode();\n\t}\n\n\tset onfilecomplete ( callback : (( file : UploadedFile ) => void) | undefined ) {\n\t\tthis.config.onfilecomplete = callback;\n\t}\n\n\tset onuploadcomplete ( callback : (( files : UploadedFile[] ) => void) | undefined ) {\n\t\tthis.config.onuploadcomplete = callback;\n\t}\n\n\tset parseResponse ( callback : (( response : any, endpoint : 'initiate' | 'part' | 'complete' ) => any) | undefined ) {\n\t\tthis.config.parseResponse = callback;\n\t}\n\n\t/**\n\t * Updates labels in the DOM when properties change\n\t */\n\tprivate updateLabels () : void {\n\t\tconst dropText = this.shadowRoot.querySelector( '.upload-text' );\n\t\tconst browseBtn = this.shadowRoot.querySelector( '#browseBtn' );\n\n\t\tif ( dropText ) {\n\t\t\tdropText.textContent = this.config.labelDropFiles || 'Drop files here';\n\t\t}\n\t\tif ( browseBtn ) {\n\t\t\tbrowseBtn.textContent = this.config.labelBrowse || 'Browse Files';\n\t\t}\n\t}\n\n\t/**\n\t * Updates compact mode styles and attributes\n\t */\n\tprivate updateCompactMode () : void {\n\t\tconst container = this.shadowRoot.querySelector( '.container' );\n\t\tconst fileInput = this.shadowRoot.querySelector( '#fileInput' ) as HTMLInputElement;\n\n\t\tif ( this.config.compact ) {\n\t\t\tcontainer?.classList.add( 'compact' );\n\t\t\tfileInput?.removeAttribute( 'multiple' );\n\t\t} else {\n\t\t\tcontainer?.classList.remove( 'compact' );\n\t\t\tfileInput?.setAttribute( 'multiple', '' );\n\t\t}\n\t}\n\n\t/**\n\t * Formats bytes to human readable string\n\t */\n\tprivate formatBytes ( bytes : number ) : string {\n\t\tif ( bytes === 0 ) return '0 Bytes';\n\t\tconst k = 1024;\n\t\tconst sizes = [ 'Bytes', 'KB', 'MB', 'GB' ];\n\t\tconst i = Math.floor( Math.log( bytes ) / Math.log( k ) );\n\t\treturn Math.round( ( bytes / Math.pow( k, i ) ) * 100 ) / 100 + ' ' + sizes[i];\n\t}\n\n\t/**\n\t * Generates a unique ID for a file\n\t */\n\tprivate generateFileId () : string {\n\t\treturn `file-${Date.now()}-${Math.random().toString( 36 ).substr( 2, 9 )}`;\n\t}\n\n\t/**\n\t * Validates a file based on config\n\t */\n\tprivate validateFile ( file : File ) : { valid : boolean; error? : string } {\n\t\t// Check file size\n\t\tconst maxSizeBytes = this.maxFileSize * 1024 * 1024;\n\t\tif ( file.size > maxSizeBytes ) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: `File size exceeds maximum of ${this.formatBytes( maxSizeBytes )}`,\n\t\t\t};\n\t\t}\n\n\t\t// Check file type\n\t\tif ( this.config.validFiletypes && this.config.validFiletypes.length > 0 ) {\n\t\t\tconst extension = file.name.split( '.' ).pop()?.toLowerCase();\n\t\t\tif ( !extension || !this.config.validFiletypes.includes( extension ) ) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: `File type .${extension} is not allowed. Valid types: ${this.config.validFiletypes.join( ', ' )}`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { valid: true };\n\t}\n\n\t/**\n\t * Generates a preview for image files\n\t */\n\tprivate async generatePreview ( file : File ) : Promise<string | undefined> {\n\t\tif ( !file.type.startsWith( 'image/' ) ) return undefined;\n\n\t\treturn new Promise( ( resolve ) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onload = ( e ) => resolve( e.target?.result as string );\n\t\t\treader.onerror = () => resolve( undefined );\n\t\t\treader.readAsDataURL( file );\n\t\t} );\n\t}\n\n\t/**\n\t * Adds files to the upload queue\n\t */\n\tprivate async addFiles ( fileList : FileList ) : Promise<void> {\n\t\tif ( this.config.compact ) {\n\t\t\tthis.files.clear();\n\t\t}\n\n\t\tconst files = Array.from( fileList );\n\t\tconst filesToProcess = this.config.compact ? [ files[0] ] : files;\n\n\t\tfor ( const file of filesToProcess ) {\n\t\t\tif ( !file ) continue;\n\t\t\tconst validation = this.validateFile( file );\n\n\t\t\tconst id = this.generateFileId();\n\t\t\tconst uploadedFile : UploadedFile = {\n\t\t\t\tid,\n\t\t\t\tfile,\n\t\t\t\tstatus: validation.valid ? 'pending' : 'error',\n\t\t\t\tprogress: 0,\n\t\t\t\tuploadedBytes: 0,\n\t\t\t\terror: validation.error,\n\t\t\t};\n\n\t\t\t// Generate preview for images\n\t\t\tif ( validation.valid && file.type.startsWith( 'image/' ) ) {\n\t\t\t\tuploadedFile.preview = await this.generatePreview( file );\n\t\t\t}\n\n\t\t\tthis.files.set( id, uploadedFile );\n\t\t}\n\n\t\tthis.renderFileCards();\n\t}\n\n\t/**\n\t * Removes a file from the queue\n\t */\n\tprivate removeFile ( id : string ) : void {\n\t\tthis.files.delete( id );\n\t\tthis.renderFileCards();\n\t}\n\n\t/**\n\t * Uploads a single file with chunking\n\t */\n\tprivate async uploadFile ( uploadedFile : UploadedFile ) : Promise<void> {\n\t\tif ( !this.config.serverURL ) {\n\t\t\tthrow new Error( 'Server URL is not configured' );\n\t\t}\n\n\t\t// Remove trailing slash from serverURL if present\n\t\tconst serverUrl = this.config.serverURL.replace( /\\/$/, '' );\n\n\t\tconst { file } = uploadedFile;\n\t\tuploadedFile.status = 'uploading';\n\t\tuploadedFile.progress = 0;\n\t\tthis.updateFileCard( uploadedFile.id );\n\n\t\ttry {\n\t\t\tconst headers : HeadersInit = {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t};\n\n\t\t\tif ( this.config.authToken ) {\n\t\t\t\theaders['Authorization'] = `Bearer ${this.config.authToken}`;\n\t\t\t}\n\n\t\t\t// Step 1: Initiate multipart upload\n\t\t\tconst initResponse = await fetch( `${serverUrl}/api/upload/initiate`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tmode: 'cors',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify( {\n\t\t\t\t\tfileName: file.name,\n\t\t\t\t\tfileType: file.type,\n\t\t\t\t\tfolder: this.config.folder,\n\t\t\t\t} ),\n\t\t\t\tsignal: this.abortController?.signal,\n\t\t\t} );\n\n\t\t\tif ( !initResponse.ok ) {\n\t\t\t\tthrow new Error( `Failed to initiate upload: ${await initResponse.text()}` );\n\t\t\t}\n\n\t\t\tlet initData = await initResponse.json();\n\t\t\tif ( this.config.parseResponse ) {\n\t\t\t\tinitData = this.config.parseResponse( initData, 'initiate' );\n\t\t\t}\n\t\t\tconst { uploadId, key } = initData;\n\n\t\t\t// Store upload metadata for potential abort\n\t\t\tuploadedFile.uploadId = uploadId;\n\t\t\tuploadedFile.key = key;\n\n\t\t\t// Step 2: Upload chunks\n\t\t\tconst chunkSizeBytes = this.config.chunkSize * 1024 * 1024;\n\t\t\tconst totalParts = Math.ceil( file.size / chunkSizeBytes );\n\t\t\tconst parts : Array<{ partNumber : number; etag : string }> = [];\n\n\t\t\tfor ( let partNumber = 1; partNumber <= totalParts; partNumber++ ) {\n\t\t\t\t// Check if upload was aborted\n\t\t\t\tif ( this.abortController?.signal.aborted ) {\n\t\t\t\t\tthrow new Error( 'Upload aborted by user' );\n\t\t\t\t}\n\n\t\t\t\tconst start = ( partNumber - 1 ) * chunkSizeBytes;\n\t\t\t\tconst end = Math.min( start + chunkSizeBytes, file.size );\n\t\t\t\tconst chunk = file.slice( start, end );\n\n\t\t\t\t// Create headers for this part upload\n\t\t\t\tconst partHeaders : HeadersInit = {\n\t\t\t\t\t'Content-Type': 'application/octet-stream',\n\t\t\t\t\t'X-Upload-Id': uploadId,\n\t\t\t\t\t'X-Key': key,\n\t\t\t\t\t'X-Part-Number': partNumber.toString(),\n\t\t\t\t};\n\n\t\t\t\tif ( this.config.authToken ) {\n\t\t\t\t\tpartHeaders['Authorization'] = `Bearer ${this.config.authToken}`;\n\t\t\t\t}\n\n\t\t\t\tconst partResponse = await fetch( `${serverUrl}/api/upload/part`, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tmode: 'cors',\n\t\t\t\t\theaders: partHeaders,\n\t\t\t\t\tbody: chunk,\n\t\t\t\t\tsignal: this.abortController?.signal,\n\t\t\t\t} );\n\n\t\t\t\tif ( !partResponse.ok ) {\n\t\t\t\t\tthrow new Error( `Failed to upload part ${partNumber}` );\n\t\t\t\t}\n\n\t\t\t\tlet partData = await partResponse.json();\n\t\t\t\tif ( this.config.parseResponse ) {\n\t\t\t\t\tpartData = this.config.parseResponse( partData, 'part' );\n\t\t\t\t}\n\t\t\t\tconst { etag } = partData;\n\t\t\t\tparts.push( { partNumber, etag } );\n\n\t\t\t\t// Update progress\n\t\t\t\tuploadedFile.uploadedBytes = end;\n\t\t\t\tuploadedFile.progress = ( uploadedFile.uploadedBytes / file.size ) * 100;\n\t\t\t\tthis.updateFileCard( uploadedFile.id );\n\t\t\t}\n\n\t\t\t// Step 3: Complete multipart upload\n\t\t\tconst completeResponse = await fetch( `${serverUrl}/api/upload/complete`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tmode: 'cors',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify( {\n\t\t\t\t\tuploadId,\n\t\t\t\t\tkey,\n\t\t\t\t\tparts,\n\t\t\t\t} ),\n\t\t\t\tsignal: this.abortController?.signal,\n\t\t\t} );\n\n\t\t\tif ( !completeResponse.ok ) {\n\t\t\t\tthrow new Error( 'Failed to complete upload' );\n\t\t\t}\n\n\t\t\tlet completeData = await completeResponse.json();\n\t\t\tif ( this.config.parseResponse ) {\n\t\t\t\tcompleteData = this.config.parseResponse( completeData, 'complete' );\n\t\t\t}\n\n\t\t\tuploadedFile.status = 'completed';\n\t\t\tuploadedFile.progress = 100;\n\t\t\tthis.updateFileCard( uploadedFile.id );\n\n\t\t\t// Dispatch file complete event\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent( 'filecomplete', {\n\t\t\t\t\tdetail: uploadedFile,\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t} ),\n\t\t\t);\n\n\t\t\t// Call callback if provided\n\t\t\tif ( this.config.onfilecomplete ) {\n\t\t\t\tthis.config.onfilecomplete( uploadedFile );\n\t\t\t}\n\t\t} catch ( error ) {\n\t\t\t// Check if this is an abort error (from AbortController)\n\t\t\tconst isAbortError = error instanceof Error\n\t\t\t\t&& ( error.name === 'AbortError' || error.message === 'Upload aborted by user' );\n\n\t\t\t// Only set error status if not aborted\n\t\t\tif ( !isAbortError ) {\n\t\t\t\tuploadedFile.status = 'error';\n\t\t\t\tuploadedFile.error = error instanceof Error ? error.message : 'Unknown error';\n\t\t\t\tthis.updateFileCard( uploadedFile.id );\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Starts uploading all pending files\n\t */\n\tprivate async startUpload () : Promise<void> {\n\t\tif ( this.isUploading ) return;\n\n\t\tconst pendingFiles = Array.from( this.files.values() ).filter( ( f ) => f.status === 'pending' );\n\t\tif ( pendingFiles.length === 0 ) return;\n\n\t\tthis.isUploading = true;\n\t\tthis.abortController = new AbortController();\n\n\t\tconst uploadBtn = this.shadowRoot.querySelector( '#uploadBtn' ) as HTMLButtonElement;\n\t\tconst abortBtn = this.shadowRoot.querySelector( '#abortBtn' ) as HTMLButtonElement;\n\n\t\tif ( uploadBtn ) {\n\t\t\tuploadBtn.disabled = true;\n\t\t\tuploadBtn.textContent = 'Uploading...';\n\t\t}\n\n\t\tif ( abortBtn ) {\n\t\t\tabortBtn.style.display = 'inline-block';\n\t\t}\n\n\t\ttry {\n\t\t\tfor ( const file of pendingFiles ) {\n\t\t\t\t// Check if upload was aborted\n\t\t\t\tif ( this.abortController.signal.aborted ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait this.uploadFile( file );\n\t\t\t}\n\n\t\t\t// All uploads complete\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent( 'uploadcomplete', {\n\t\t\t\t\tdetail: Array.from( this.files.values() ),\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t} ),\n\t\t\t);\n\n\t\t\t// Call callback if provided\n\t\t\tif ( this.config.onuploadcomplete ) {\n\t\t\t\tthis.config.onuploadcomplete( Array.from( this.files.values() ) );\n\t\t\t}\n\t\t} catch ( error ) {\n\t\t\t// Only log non-abort errors\n\t\t\tif ( error instanceof Error && error.message !== 'Upload aborted by user' ) {\n\t\t\t\tconsole.error( 'Upload error:', error );\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isUploading = false;\n\t\t\tif ( uploadBtn ) {\n\t\t\t\tuploadBtn.disabled = false;\n\t\t\t\tuploadBtn.textContent = 'Upload Files';\n\t\t\t}\n\t\t\tif ( abortBtn ) {\n\t\t\t\tabortBtn.style.display = 'none';\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Aborts all pending and uploading files\n\t */\n\tprivate async abortAllUploads () : Promise<void> {\n\t\t// Trigger abort signal to stop ongoing fetch requests\n\t\tif ( this.abortController ) {\n\t\t\tthis.abortController.abort();\n\t\t}\n\n\t\tconst filesToAbort = Array.from( this.files.values() ).filter(\n\t\t\t( f ) => f.status === 'pending' || f.status === 'uploading',\n\t\t);\n\n\t\tif ( filesToAbort.length === 0 ) return;\n\n\t\tconst headers : HeadersInit = {\n\t\t\t'Content-Type': 'application/json',\n\t\t};\n\n\t\tif ( this.config.authToken ) {\n\t\t\theaders['Authorization'] = `Bearer ${this.config.authToken}`;\n\t\t}\n\n\t\tfor ( const file of filesToAbort ) {\n\t\t\t// If upload was initiated, abort it on the server\n\t\t\tif ( file.uploadId && file.key ) {\n\t\t\t\ttry {\n\t\t\t\t\t// Remove trailing slash from serverURL if present\n\t\t\t\t\tconst serverUrl = this.config.serverURL.replace( /\\/$/, '' );\n\t\t\t\t\tawait fetch( `${serverUrl}/api/upload/abort`, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\tmode: 'cors',\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tbody: JSON.stringify( {\n\t\t\t\t\t\t\tuploadId: file.uploadId,\n\t\t\t\t\t\t\tkey: file.key,\n\t\t\t\t\t\t} ),\n\t\t\t\t\t} );\n\t\t\t\t} catch ( error ) {\n\t\t\t\t\tconsole.error( `Failed to abort upload for ${file.file.name}:`, error );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update file status\n\t\t\tfile.status = 'aborted';\n\t\t\tfile.error = 'Upload aborted by user';\n\t\t\tthis.updateFileCard( file.id );\n\t\t}\n\n\t\t// Dispatch abort event\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent( 'uploadaborted', {\n\t\t\t\tdetail: filesToAbort,\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t} ),\n\t\t);\n\n\t\t// Reset upload state\n\t\tthis.isUploading = false;\n\t\tconst uploadBtn = this.shadowRoot.querySelector( '#uploadBtn' ) as HTMLButtonElement;\n\t\tconst abortBtn = this.shadowRoot.querySelector( '#abortBtn' ) as HTMLButtonElement;\n\n\t\tif ( uploadBtn ) {\n\t\t\tuploadBtn.disabled = false;\n\t\t\tuploadBtn.textContent = 'Upload Files';\n\t\t}\n\n\t\tif ( abortBtn ) {\n\t\t\tabortBtn.style.display = 'none';\n\t\t}\n\t}\n\n\t/**\n\t * Updates a single file card in the DOM\n\t */\n\tprivate updateFileCard ( fileId : string ) : void {\n\t\tconst file = this.files.get( fileId );\n\t\tif ( !file ) return;\n\n\t\tconst card = this.shadowRoot.querySelector( `[data-file-id=\"${fileId}\"]` ) as HTMLElement;\n\t\tif ( !card ) return;\n\n\t\tconst progressBar = card.querySelector( '.progress-bar' ) as HTMLElement;\n\t\tconst progressText = card.querySelector( '.progress-text' ) as HTMLElement;\n\t\tconst statusDiv = card.querySelector( '.status' ) as HTMLElement;\n\n\t\tif ( progressBar ) {\n\t\t\tprogressBar.style.width = `${file.progress}%`;\n\n\t\t\t// Color based on status\n\t\t\tif ( file.status === 'completed' ) {\n\t\t\t\tprogressBar.style.backgroundColor = '#22c55e'; // green\n\t\t\t} else if ( file.status === 'error' ) {\n\t\t\t\tprogressBar.style.backgroundColor = '#ef4444'; // red\n\t\t\t} else if ( file.status === 'aborted' ) {\n\t\t\t\tprogressBar.style.backgroundColor = '#f59e0b'; // orange\n\t\t\t} else {\n\t\t\t\tprogressBar.style.backgroundColor = 'var(--color-primary)';\n\t\t\t}\n\t\t}\n\n\t\tif ( progressText ) {\n\t\t\tprogressText.textContent = `${Math.round( file.progress )}%`;\n\t\t}\n\n\t\tif ( statusDiv && file.error ) {\n\t\t\tstatusDiv.textContent = file.error;\n\t\t\tstatusDiv.style.display = 'block';\n\t\t}\n\t}\n\n\t/**\n\t * Renders all file cards\n\t */\n\tprivate renderFileCards () : void {\n\t\tconst container = this.shadowRoot.querySelector( '#fileCardsContainer' );\n\t\tif ( !container ) return;\n\n\t\tif ( this.files.size === 0 ) {\n\t\t\tcontainer.innerHTML = '';\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.innerHTML = Array.from( this.files.values() ).map( ( file ) => `\n <div class=\"file-card\" data-file-id=\"${file.id}\">\n <button class=\"remove-btn\" data-file-id=\"${file.id}\">×</button>\n ${file.preview ? `<div class=\"preview\"><img src=\"${file.preview}\" alt=\"Preview\"></div>` : '<div class=\"preview no-preview\">📄</div>'}\n <div class=\"file-info\">\n <div class=\"file-name\" title=\"${file.file.name}\">${file.file.name}</div>\n <div class=\"file-size\">${this.formatBytes( file.file.size )}</div>\n </div>\n <div class=\"progress-container\">\n <div class=\"progress-bar-bg\">\n <div class=\"progress-bar\" style=\"width: ${file.progress}%; background-color: ${\n\t\t\tfile.status === 'completed' ? '#22c55e' : file.status === 'error' ? '#ef4444' : 'var(--color-primary)'\n\t\t}\"></div>\n </div>\n <div class=\"progress-text\">${Math.round( file.progress )}%</div>\n </div>\n ${file.error ? `<div class=\"status error\">${file.error}</div>` : ''}\n </div>\n ` ).join( '' );\n\n\t\t// Bind remove button events\n\t\tconst removeButtons = container.querySelectorAll( '.remove-btn' );\n\t\tremoveButtons.forEach( ( btn ) => {\n\t\t\tbtn.addEventListener( 'click', ( e ) => {\n\t\t\t\te.stopPropagation();\n\t\t\t\tconst fileId = ( btn as HTMLElement ).dataset.fileId;\n\t\t\t\tif ( fileId ) this.removeFile( fileId );\n\t\t\t} );\n\t\t} );\n\n\t\t// Show/hide upload button\n\t\tconst uploadBtn = this.shadowRoot.querySelector( '#uploadBtn' ) as HTMLButtonElement;\n\t\tif ( uploadBtn ) {\n\t\t\tconst hasPendingFiles = Array.from( this.files.values() ).some( ( f ) => f.status === 'pending' );\n\t\t\tuploadBtn.style.display = hasPendingFiles ? 'block' : 'none';\n\t\t}\n\t}\n\n\t/**\n\t * Binds event listeners\n\t */\n\tprivate bindEvents () : void {\n\t\tconst uploadZone = this.shadowRoot.querySelector( '#uploadZone' ) as HTMLElement;\n\t\tconst fileInput = this.shadowRoot.querySelector( '#fileInput' ) as HTMLInputElement;\n\t\tconst browseBtn = this.shadowRoot.querySelector( '#browseBtn' ) as HTMLButtonElement;\n\t\tconst uploadBtn = this.shadowRoot.querySelector( '#uploadBtn' ) as HTMLButtonElement;\n\t\tconst abortBtn = this.shadowRoot.querySelector( '#abortBtn' ) as HTMLButtonElement;\n\n\t\t// Click to browse\n\t\tbrowseBtn?.addEventListener( 'click', () => fileInput?.click() );\n\n\t\t// Drag and drop\n\t\tuploadZone?.addEventListener( 'dragover', ( e ) => {\n\t\t\te.preventDefault();\n\t\t\tuploadZone.classList.add( 'drag-over' );\n\t\t} );\n\n\t\tuploadZone?.addEventListener( 'dragleave', () => {\n\t\t\tuploadZone.classList.remove( 'drag-over' );\n\t\t} );\n\n\t\tuploadZone?.addEventListener( 'drop', ( e ) => {\n\t\t\te.preventDefault();\n\t\t\tuploadZone.classList.remove( 'drag-over' );\n\t\t\tif ( e.dataTransfer?.files ) {\n\t\t\t\tthis.addFiles( e.dataTransfer.files );\n\t\t\t}\n\t\t} );\n\n\t\t// File input change\n\t\tfileInput?.addEventListener( 'change', ( e ) => {\n\t\t\tconst files = ( e.target as HTMLInputElement ).files;\n\t\t\tif ( files ) {\n\t\t\t\tthis.addFiles( files );\n\t\t\t\t// Reset input so same file can be added again\n\t\t\t\tfileInput.value = '';\n\t\t\t}\n\t\t} );\n\n\t\t// Upload button\n\t\tuploadBtn?.addEventListener( 'click', () => this.startUpload() );\n\n\t\t// Abort button\n\t\tabortBtn?.addEventListener( 'click', () => this.abortAllUploads() );\n\t}\n\n\t/**\n\t * Renders the component\n\t */\n\tprivate render () : void {\n\t\tthis.shadowRoot.innerHTML = `\n <style>\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .container {\n width: 100%;\n }\n\n .upload-zone {\n border: 2px dashed #ccc;\n border-radius: 8px;\n padding: 10px;\n text-align: center;\n cursor: pointer;\n transition: all 0.3s ease;\n background: #fafafa;\n }\n\n .upload-zone:hover {\n border-color: #4CAF50;\n background: #f0f9f0;\n }\n\n .upload-zone.drag-over {\n border-color: #4CAF50;\n background: #e8f5e9;\n }\n\n .upload-zone-content {\n pointer-events: none;\n }\n\n .upload-icon {\n font-size: 48px;\n margin-bottom: 16px;\n }\n\n .upload-text {\n font-size: 18px;\n margin-bottom: 8px;\n color: #333;\n }\n\n .upload-hint {\n font-size: 14px;\n color: #666;\n }\n\n .browse-btn {\n background: #4CAF50;\n color: white;\n border: none;\n padding: 12px 24px;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n margin-top: 16px;\n transition: background 0.3s ease;\n pointer-events: auto;\n }\n\n .browse-btn:hover {\n background: #45a049;\n }\n\n input[type=\"file\"] {\n display: none;\n }\n\n .file-cards-container {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n margin-top: 24px;\n }\n\n .file-card {\n position: relative;\n width: 200px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 12px;\n background: white;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n transition: box-shadow 0.3s ease;\n }\n\n .file-card:hover {\n box-shadow: 0 4px 8px rgba(0,0,0,0.15);\n }\n\n .remove-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background: rgba(239, 68, 68, 0.9);\n color: white;\n font-size: 18px;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n transition: background 0.3s ease;\n z-index: 1;\n }\n\n .remove-btn:hover {\n background: rgba(239, 68, 68, 1);\n }\n\n .preview {\n width: 100%;\n height: 120px;\n border-radius: 4px;\n overflow: hidden;\n background: #f5f5f5;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 8px;\n }\n\n .preview img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .preview.no-preview {\n font-size: 48px;\n }\n\n .file-info {\n margin-bottom: 8px;\n }\n\n .file-name {\n font-size: 14px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-bottom: 4px;\n }\n\n .file-size {\n font-size: 12px;\n color: #666;\n }\n\n .progress-container {\n margin-top: 8px;\n }\n\n .progress-bar-bg {\n width: 100%;\n height: 8px;\n background: #e0e0e0;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 4px;\n }\n\n .progress-bar {\n height: 100%;\n background: var(--color-primary, #4CAF50);\n transition: width 0.3s ease, background-color 0.3s ease;\n }\n\n .progress-text {\n font-size: 12px;\n color: #666;\n text-align: center;\n }\n\n .status {\n font-size: 12px;\n color: #ef4444;\n margin-top: 4px;\n display: none;\n }\n\n .status.error {\n display: block;\n }\n\n .upload-btn {\n background: #4CAF50;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n margin-top: 24px;\n transition: background 0.3s ease;\n display: none;\n }\n\n .upload-btn:hover:not(:disabled) {\n background: #45a049;\n }\n\n .upload-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .abort-btn {\n background: #ef4444;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n margin-top: 24px;\n margin-left: 12px;\n transition: background 0.3s ease;\n display: none;\n }\n\n .abort-btn:hover {\n background: #dc2626;\n }\n\n .buttons-container {\n display: flex;\n align-items: center;\n }\n\n /* Compact Mode Styles */\n .container.compact .upload-zone {\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n }\n\n .container.compact .upload-icon,\n .container.compact .upload-hint {\n display: none;\n }\n\n .container.compact .upload-text {\n font-size: 14px;\n margin-bottom: 0;\n }\n\n .container.compact .browse-btn {\n margin-top: 0;\n padding: 6px 12px;\n font-size: 14px;\n }\n\n .container.compact .file-cards-container {\n margin-top: 12px;\n }\n\n .container.compact .file-card {\n width: 100%;\n display: flex;\n align-items: center;\n padding: 8px;\n height: auto;\n }\n\n .container.compact .preview {\n display: none !important;\n }\n\n .container.compact .file-info {\n margin-bottom: 0;\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 0 0 auto;\n }\n\n .container.compact .file-name {\n margin-bottom: 0;\n max-width: 150px;\n }\n\n .container.compact .progress-container {\n margin-top: 0;\n flex: 1;\n margin-left: 12px;\n margin-right: 32px;\n }\n\n .container.compact .remove-btn {\n top: 50%;\n transform: translateY(-50%);\n }\n </style>\n\n <div class=\"container\">\n <div class=\"upload-zone\" id=\"uploadZone\">\n <input type=\"file\" id=\"fileInput\" multiple>\n <div class=\"upload-zone-content\">\n <div class=\"upload-icon\">📁</div>\n <div class=\"upload-text\">${this.config.labelDropFiles || 'Drop files here'}</div>\n <div class=\"upload-hint\">or</div>\n <button class=\"browse-btn\" id=\"browseBtn\">${this.config.labelBrowse || 'Browse Files'}</button>\n </div>\n </div>\n\n <div class=\"file-cards-container\" id=\"fileCardsContainer\"></div>\n\n <div class=\"buttons-container\">\n <button class=\"upload-btn\" id=\"uploadBtn\">Upload Files</button>\n <button class=\"abort-btn\" id=\"abortBtn\">Abort Upload</button>\n </div>\n </div>\n `;\n\t}\n}\n\n/**\n * Defines the custom element if not already defined\n */\nexport const defineChunkUploader = ( tagName = 'liwe3-chunk-uploader' ) : void => {\n\tif ( typeof window !== 'undefined' && !customElements.get( tagName ) ) {\n\t\tcustomElements.define( tagName, ChunkUploaderElement );\n\t}\n};\n\n// Auto-register with default tag name\nif ( typeof window !== 'undefined' ) {\n\tdefineChunkUploader();\n}\n"],"names":["ChunkUploaderElement","name","oldValue","newValue","ext","value","callback","dropText","browseBtn","container","fileInput","bytes","k","sizes","i","file","maxSizeBytes","extension","resolve","reader","e","fileList","files","filesToProcess","validation","id","uploadedFile","serverUrl","headers","initResponse","initData","uploadId","key","chunkSizeBytes","totalParts","parts","partNumber","start","end","chunk","partHeaders","partResponse","partData","etag","completeResponse","completeData","error","pendingFiles","f","uploadBtn","abortBtn","filesToAbort","fileId","card","progressBar","progressText","statusDiv","btn","hasPendingFiles","uploadZone","defineChunkUploader","tagName"],"mappings":"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AAA;AAAA,EAKQ,SAAiB;AACxB,SAAK,WAAW,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAgUS,KAAK,OAAO,kBAAkB,iBAAiB;AAAA;AAAA,wDAE9B,KAAK,OAAO,eAAe,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYhG;AACD;AAKO,MAAMU,IAAsB,CAAEC,IAAU,2BAAmC;AACjF,EAAK,OAAO,SAAW,OAAe,CAAC,eAAe,IAAKA,CAAQ,KAClE,eAAe,OAAQA,GAAS7D,CAAqB;AAEvD;AAGK,OAAO,SAAW,OACtB4D,EAAA;"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * ComicBalloon Web Component
3
+ * A customizable comic balloon with different styles and draggable handler
4
+ */
5
+ export declare enum BalloonType {
6
+ TALK = "talk",
7
+ CLOUD = "cloud",
8
+ WHISPER = "whisper",
9
+ RECTANGLE = "rectangle"
10
+ }
11
+ export type HandlerPosition = {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ export interface IComicBalloon extends HTMLElement {
16
+ type: BalloonType;
17
+ textContent: string;
18
+ handlerPosition: HandlerPosition;
19
+ updateHandlerPosition(position: HandlerPosition): void;
20
+ getHTML(): string;
21
+ }
22
+ export type ContentChangeEvent = CustomEvent<{
23
+ newContent: string;
24
+ balloonType: BalloonType;
25
+ }>;
26
+ export type HandlerMoveEvent = CustomEvent<{
27
+ finalPosition: HandlerPosition;
28
+ balloonType: BalloonType;
29
+ }>;
30
+ export type ResizeEvent = CustomEvent<{
31
+ width: number;
32
+ height: number;
33
+ balloonType: BalloonType;
34
+ }>;
35
+ export declare class ComicBalloonElement extends HTMLElement implements IComicBalloon {
36
+ shadowRoot: ShadowRoot;
37
+ private _type;
38
+ private _handlerPosition;
39
+ private isDragging;
40
+ private isResizing;
41
+ private dragStartOffset;
42
+ private resizeStartPos;
43
+ private contentEditableElement?;
44
+ private handler?;
45
+ private resizeHandle?;
46
+ private balloon?;
47
+ static get observedAttributes(): string[];
48
+ constructor();
49
+ connectedCallback(): void;
50
+ disconnectedCallback(): void;
51
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
52
+ get type(): BalloonType;
53
+ set type(value: BalloonType);
54
+ get handlerPosition(): HandlerPosition;
55
+ set handlerPosition(value: HandlerPosition);
56
+ updateHandlerPosition(position: HandlerPosition): void;
57
+ getHTML(): string;
58
+ private render;
59
+ private getStyles;
60
+ private updateBalloonStyle;
61
+ private updateCloudShape;
62
+ private updateHandlerVisual;
63
+ private updateSVGPointer;
64
+ private drawTalkPointer;
65
+ private drawCloudPointer;
66
+ private drawWhisperPointer;
67
+ private setupEventListeners;
68
+ private removeEventListeners;
69
+ private handleMouseDown;
70
+ private handleTouchStart;
71
+ private startDrag;
72
+ private handleDrag;
73
+ private stopDrag;
74
+ private handleContentBlur;
75
+ private handleResizeMouseDown;
76
+ private handleResizeTouchStart;
77
+ private startResize;
78
+ private handleResize;
79
+ private stopResize;
80
+ }
81
+ export declare const defineComicBalloon: () => void;
82
+ //# sourceMappingURL=ComicBalloon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComicBalloon.d.ts","sourceRoot":"","sources":["../src/ComicBalloon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oBAAY,WAAW;IACtB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,SAAS,cAAc;CACvB;AAED,MAAM,MAAM,eAAe,GAAG;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV,CAAC;AAEF,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,eAAe,CAAC;IACjC,qBAAqB,CAAE,QAAQ,EAAE,eAAe,GAAI,IAAI,CAAC;IACzD,OAAO,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC;IAC1C,aAAa,EAAE,eAAe,CAAC;IAC/B,WAAW,EAAE,WAAW,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;CACzB,CAAC,CAAC;AAEH,qBAAa,mBAAoB,SAAQ,WAAY,YAAW,aAAa;IACpE,UAAU,EAAE,UAAU,CAAC;IAC/B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,sBAAsB,CAAC,CAAiB;IAChD,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,OAAO,CAAC,CAAc;IAE9B,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;;IAOD,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAI5B,wBAAwB,CAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAI,IAAI;IAalF,IAAI,IAAI,IAAI,WAAW,CAEtB;IAED,IAAI,IAAI,CAAE,KAAK,EAAE,WAAW,EAG3B;IAED,IAAI,eAAe,IAAI,eAAe,CAErC;IAED,IAAI,eAAe,CAAE,KAAK,EAAE,eAAe,EAG1C;IAED,qBAAqB,CAAE,QAAQ,EAAE,eAAe,GAAI,IAAI;IAIxD,OAAO,IAAI,MAAM;IAIjB,OAAO,CAAC,MAAM;IA4Bd,OAAO,CAAC,SAAS;IAgIjB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,gBAAgB;IAyCxB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,UAAU;CAkBlB;AAED,eAAO,MAAM,kBAAkB,QAAO,IAIrC,CAAC"}
@@ -0,0 +1,346 @@
1
+ var D = /* @__PURE__ */ ((g) => (g.TALK = "talk", g.CLOUD = "cloud", g.WHISPER = "whisper", g.RECTANGLE = "rectangle", g))(D || {});
2
+ class T extends HTMLElement {
3
+ constructor() {
4
+ super(), this._type = "talk", this._handlerPosition = { x: 50, y: 100 }, this.isDragging = !1, this.isResizing = !1, this.dragStartOffset = { x: 0, y: 0 }, this.resizeStartPos = { x: 0, y: 0 }, this.attachShadow({ mode: "open" });
5
+ }
6
+ static get observedAttributes() {
7
+ return ["type", "text"];
8
+ }
9
+ connectedCallback() {
10
+ this.render(), this.setupEventListeners();
11
+ }
12
+ disconnectedCallback() {
13
+ this.removeEventListeners();
14
+ }
15
+ attributeChangedCallback(t, e, n) {
16
+ e !== n && (t === "type" ? (this._type = n || "talk", this.updateBalloonStyle()) : t === "text" && this.contentEditableElement && (this.contentEditableElement.textContent = n));
17
+ }
18
+ get type() {
19
+ return this._type;
20
+ }
21
+ set type(t) {
22
+ this._type = t, this.setAttribute("type", t);
23
+ }
24
+ get handlerPosition() {
25
+ return { ...this._handlerPosition };
26
+ }
27
+ set handlerPosition(t) {
28
+ this._handlerPosition = { ...t }, this.updateHandlerVisual();
29
+ }
30
+ updateHandlerPosition(t) {
31
+ this.handlerPosition = t;
32
+ }
33
+ getHTML() {
34
+ return this.outerHTML;
35
+ }
36
+ render() {
37
+ this.shadowRoot.innerHTML = `
38
+ <style>${this.getStyles()}</style>
39
+ <div class="comic-balloon-container">
40
+ <svg class="balloon-svg" xmlns="http://www.w3.org/2000/svg">
41
+ <g class="cloud-shape"></g>
42
+ </svg>
43
+ <div class="balloon ${this._type}">
44
+ <div class="content" contenteditable="true" role="textbox" aria-label="Balloon text">
45
+ ${this.getAttribute("text") || "Type your text here..."}
46
+ </div>
47
+ <div class="resize-handle" role="button" aria-label="Resize balloon"></div>
48
+ </div>
49
+ <div class="handler ${this._type}" role="button" aria-label="Drag to reposition pointer"></div>
50
+ </div>
51
+ `, this.contentEditableElement = this.shadowRoot.querySelector(".content"), this.handler = this.shadowRoot.querySelector(".handler"), this.resizeHandle = this.shadowRoot.querySelector(".resize-handle"), this.balloon = this.shadowRoot.querySelector(".balloon"), this._type === "cloud" && this.updateCloudShape(), this.updateHandlerVisual();
52
+ }
53
+ getStyles() {
54
+ return `
55
+ :host {
56
+ display: inline-block;
57
+ position: relative;
58
+ width: 300px;
59
+ min-height: 150px;
60
+ overflow: visible;
61
+ }
62
+
63
+ .comic-balloon-container {
64
+ position: relative;
65
+ width: 100%;
66
+ height: 100%;
67
+ padding: 50px;
68
+ margin: -50px;
69
+ }
70
+
71
+ .balloon-svg {
72
+ position: absolute;
73
+ top: 0;
74
+ left: 0;
75
+ width: 100%;
76
+ height: 100%;
77
+ pointer-events: none;
78
+ z-index: 1;
79
+ overflow: visible;
80
+ }
81
+
82
+ .balloon {
83
+ position: relative;
84
+ padding: 20px;
85
+ background: white;
86
+ border: 3px solid #000;
87
+ min-height: 100px;
88
+ z-index: 2;
89
+ margin: 50px;
90
+ width: calc(100% - 100px);
91
+ height: calc(100% - 100px);
92
+ box-sizing: border-box;
93
+ }
94
+
95
+ .balloon.talk {
96
+ border-radius: 25px;
97
+ }
98
+
99
+ .balloon.cloud {
100
+ border: none;
101
+ background: transparent;
102
+ position: relative;
103
+ }
104
+
105
+ .balloon.cloud .content {
106
+ position: relative;
107
+ z-index: 10;
108
+ }
109
+
110
+ .balloon.whisper {
111
+ border-radius: 25px;
112
+ border: 3px dashed #000;
113
+ }
114
+
115
+ .balloon.rectangle {
116
+ border-radius: 5px;
117
+ }
118
+
119
+ .content {
120
+ outline: none;
121
+ min-height: 60px;
122
+ font-family: 'Comic Sans MS', cursive, sans-serif;
123
+ font-size: 16px;
124
+ line-height: 1.4;
125
+ color: #000;
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ text-align: center;
130
+ }
131
+
132
+ .content:empty:before {
133
+ content: attr(aria-label);
134
+ color: #999;
135
+ }
136
+
137
+ .handler {
138
+ position: absolute;
139
+ width: 20px;
140
+ height: 20px;
141
+ cursor: move;
142
+ z-index: 3;
143
+ user-select: none;
144
+ background: rgba(0, 0, 0, 0.1);
145
+ border-radius: 50%;
146
+ border: 2px solid rgba(0, 0, 0, 0.3);
147
+ }
148
+
149
+ .handler:hover {
150
+ background: rgba(0, 0, 0, 0.2);
151
+ border-color: rgba(0, 0, 0, 0.5);
152
+ }
153
+
154
+ .handler.dragging {
155
+ cursor: grabbing;
156
+ background: rgba(0, 0, 0, 0.3);
157
+ }
158
+
159
+ .resize-handle {
160
+ position: absolute;
161
+ bottom: 0;
162
+ right: 0;
163
+ width: 20px;
164
+ height: 20px;
165
+ cursor: nwse-resize;
166
+ z-index: 4;
167
+ background: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 0.5) 50%);
168
+ border-bottom-right-radius: inherit;
169
+ }
170
+
171
+ .resize-handle:hover {
172
+ background: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 0.8) 50%);
173
+ }
174
+
175
+ .resize-handle.resizing {
176
+ background: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 1) 50%);
177
+ }
178
+ `;
179
+ }
180
+ updateBalloonStyle() {
181
+ !this.balloon || !this.handler || (this.balloon.className = `balloon ${this._type}`, this.handler.className = `handler ${this._type}`, this._type === "cloud" && this.updateCloudShape(), this.updateHandlerVisual());
182
+ }
183
+ updateCloudShape() {
184
+ if (!this.balloon) return;
185
+ const t = this.shadowRoot.querySelector(".cloud-shape");
186
+ if (!t) return;
187
+ const e = this.balloon.getBoundingClientRect(), n = this.shadowRoot.querySelector(".comic-balloon-container")?.getBoundingClientRect();
188
+ if (!n) return;
189
+ const s = e.left - n.left, o = e.top - n.top, d = e.width, r = e.height;
190
+ t.innerHTML = "";
191
+ const l = s + d / 2, h = o + r / 2, c = 14, p = d / 2.5, b = r / 2.5;
192
+ let u = "";
193
+ for (let i = 0; i <= c; i++) {
194
+ const m = i / c * Math.PI * 2 - Math.PI / 2, M = (i + 1) / c * Math.PI * 2 - Math.PI / 2, v = l + Math.cos(m) * p, w = h + Math.sin(m) * b, f = l + Math.cos(M) * p, y = h + Math.sin(M) * b, z = (v + f) / 2, L = (w + y) / 2, R = f - v, S = y - w, k = Math.sqrt(R * R + S * S), x = z - l, E = L - h, C = Math.sqrt(x * x + E * E), A = x / C, _ = E / C, P = k * 0.8, $ = z + A * P, H = L + _ * P;
195
+ i === 0 && (u += `M ${v} ${w} `), u += `Q ${$} ${H} ${f} ${y} `;
196
+ }
197
+ u += "Z";
198
+ const a = document.createElementNS("http://www.w3.org/2000/svg", "path");
199
+ a.setAttribute("d", u), a.setAttribute("fill", "white"), a.setAttribute("stroke", "#000"), a.setAttribute("stroke-width", "3"), a.setAttribute("stroke-linejoin", "round"), t.appendChild(a);
200
+ }
201
+ updateHandlerVisual() {
202
+ if (!this.handler || !this.balloon) return;
203
+ const t = this._handlerPosition.x, e = this._handlerPosition.y;
204
+ this.handler.style.left = `${t - 10}px`, this.handler.style.top = `${e - 10}px`, this._type === "cloud" && this.updateCloudShape(), this.updateSVGPointer();
205
+ }
206
+ updateSVGPointer() {
207
+ const t = this.shadowRoot.querySelector(".balloon-svg");
208
+ if (!t || !this.balloon) return;
209
+ const e = this.balloon.getBoundingClientRect(), n = t.getBoundingClientRect();
210
+ let s = t.querySelector(".pointer-group");
211
+ s || (s = document.createElementNS("http://www.w3.org/2000/svg", "g"), s.setAttribute("class", "pointer-group"), t.appendChild(s)), s.innerHTML = "";
212
+ const o = e.left - n.left, d = e.top - n.top, r = o + e.width / 2, l = d + e.height / 2, h = this._handlerPosition.x, c = this._handlerPosition.y, p = Math.atan2(c - l, h - r), b = e.width / 2, u = e.height / 2, a = r + Math.cos(p) * b * 0.85, i = l + Math.sin(p) * u * 0.85;
213
+ this._type === "talk" || this._type === "rectangle" ? this.drawTalkPointer(s, a, i, h, c) : this._type === "cloud" ? this.drawCloudPointer(s, a, i, h, c) : this._type === "whisper" && this.drawWhisperPointer(s, a, i, h, c);
214
+ }
215
+ drawTalkPointer(t, e, n, s, o) {
216
+ const r = Math.atan2(o - n, s - e) + Math.PI / 2, l = 20, h = e + Math.cos(r) * l / 2, c = n + Math.sin(r) * l / 2, p = e - Math.cos(r) * l / 2, b = n - Math.sin(r) * l / 2, u = s, a = o, i = document.createElementNS("http://www.w3.org/2000/svg", "path"), m = `M ${h} ${c} L ${u} ${a} L ${p} ${b} Z`;
217
+ i.setAttribute("d", m), i.setAttribute("fill", "white"), i.setAttribute("stroke", "#000"), i.setAttribute("stroke-width", "3"), i.setAttribute("stroke-linejoin", "round"), t.appendChild(i);
218
+ }
219
+ drawCloudPointer(t, e, n, s, o) {
220
+ const d = s - e, r = o - n, l = 3;
221
+ for (let h = 0; h < l; h++) {
222
+ const c = (h + 1) / (l + 1), p = e + d * c, b = n + r * c, u = 8 - h * 2, a = document.createElementNS("http://www.w3.org/2000/svg", "circle");
223
+ a.setAttribute("cx", p.toString()), a.setAttribute("cy", b.toString()), a.setAttribute("r", u.toString()), a.setAttribute("fill", "white"), a.setAttribute("stroke", "#000"), a.setAttribute("stroke-width", "2"), t.appendChild(a);
224
+ }
225
+ }
226
+ drawWhisperPointer(t, e, n, s, o) {
227
+ const r = Math.atan2(o - n, s - e) + Math.PI / 2, l = 20, h = e + Math.cos(r) * l / 2, c = n + Math.sin(r) * l / 2, p = e - Math.cos(r) * l / 2, b = n - Math.sin(r) * l / 2, u = s, a = o, i = document.createElementNS("http://www.w3.org/2000/svg", "path"), m = `M ${h} ${c} L ${u} ${a} L ${p} ${b}`;
228
+ i.setAttribute("d", m), i.setAttribute("fill", "none"), i.setAttribute("stroke", "#000"), i.setAttribute("stroke-width", "3"), i.setAttribute("stroke-dasharray", "10 5"), i.setAttribute("stroke-linejoin", "round"), i.setAttribute("stroke-linecap", "round"), t.appendChild(i);
229
+ }
230
+ setupEventListeners() {
231
+ this.handler && (this.handler.addEventListener("mousedown", this.handleMouseDown.bind(this)), this.handler.addEventListener("touchstart", this.handleTouchStart.bind(this), { passive: !1 }), this.resizeHandle && (this.resizeHandle.addEventListener("mousedown", this.handleResizeMouseDown.bind(this)), this.resizeHandle.addEventListener("touchstart", this.handleResizeTouchStart.bind(this), { passive: !1 })), this.contentEditableElement && this.contentEditableElement.addEventListener("blur", this.handleContentBlur.bind(this)));
232
+ }
233
+ removeEventListeners() {
234
+ this.handler && (this.handler.removeEventListener("mousedown", this.handleMouseDown.bind(this)), this.handler.removeEventListener("touchstart", this.handleTouchStart.bind(this)), this.resizeHandle && (this.resizeHandle.removeEventListener("mousedown", this.handleResizeMouseDown.bind(this)), this.resizeHandle.removeEventListener("touchstart", this.handleResizeTouchStart.bind(this))), this.contentEditableElement && this.contentEditableElement.removeEventListener("blur", this.handleContentBlur.bind(this)));
235
+ }
236
+ handleMouseDown(t) {
237
+ t.preventDefault(), this.startDrag(t.clientX, t.clientY);
238
+ const e = (s) => this.handleDrag(s.clientX, s.clientY), n = () => {
239
+ this.stopDrag(), document.removeEventListener("mousemove", e), document.removeEventListener("mouseup", n);
240
+ };
241
+ document.addEventListener("mousemove", e), document.addEventListener("mouseup", n);
242
+ }
243
+ handleTouchStart(t) {
244
+ t.preventDefault();
245
+ const e = t.touches[0];
246
+ this.startDrag(e.clientX, e.clientY);
247
+ const n = (o) => {
248
+ const d = o.touches[0];
249
+ this.handleDrag(d.clientX, d.clientY);
250
+ }, s = () => {
251
+ this.stopDrag(), document.removeEventListener("touchmove", n), document.removeEventListener("touchend", s);
252
+ };
253
+ document.addEventListener("touchmove", n, { passive: !1 }), document.addEventListener("touchend", s);
254
+ }
255
+ startDrag(t, e) {
256
+ this.isDragging = !0, this.handler && this.handler.classList.add("dragging");
257
+ const n = this.shadowRoot.querySelector(".comic-balloon-container")?.getBoundingClientRect();
258
+ n && (this.dragStartOffset = {
259
+ x: t - n.left - this._handlerPosition.x,
260
+ y: e - n.top - this._handlerPosition.y
261
+ });
262
+ }
263
+ handleDrag(t, e) {
264
+ if (!this.isDragging) return;
265
+ const n = this.shadowRoot.querySelector(".comic-balloon-container")?.getBoundingClientRect();
266
+ if (!n) return;
267
+ let s = t - n.left - this.dragStartOffset.x, o = e - n.top - this.dragStartOffset.y;
268
+ s = Math.max(-50, Math.min(n.width + 50, s)), o = Math.max(-50, Math.min(n.height + 50, o)), this._handlerPosition = { x: s, y: o }, this.updateHandlerVisual();
269
+ }
270
+ stopDrag() {
271
+ this.isDragging = !1, this.handler && this.handler.classList.remove("dragging"), this.dispatchEvent(new CustomEvent("handler-move", {
272
+ detail: {
273
+ finalPosition: { ...this._handlerPosition },
274
+ balloonType: this._type
275
+ },
276
+ bubbles: !0,
277
+ composed: !0
278
+ }));
279
+ }
280
+ handleContentBlur() {
281
+ this.contentEditableElement && this.dispatchEvent(new CustomEvent("balloon-content-change", {
282
+ detail: {
283
+ newContent: this.contentEditableElement.textContent || "",
284
+ balloonType: this._type
285
+ },
286
+ bubbles: !0,
287
+ composed: !0
288
+ }));
289
+ }
290
+ handleResizeMouseDown(t) {
291
+ t.preventDefault(), t.stopPropagation(), this.startResize(t.clientX, t.clientY);
292
+ const e = (s) => this.handleResize(s.clientX, s.clientY), n = () => {
293
+ this.stopResize(), document.removeEventListener("mousemove", e), document.removeEventListener("mouseup", n);
294
+ };
295
+ document.addEventListener("mousemove", e), document.addEventListener("mouseup", n);
296
+ }
297
+ handleResizeTouchStart(t) {
298
+ t.preventDefault(), t.stopPropagation();
299
+ const e = t.touches[0];
300
+ this.startResize(e.clientX, e.clientY);
301
+ const n = (o) => {
302
+ const d = o.touches[0];
303
+ this.handleResize(d.clientX, d.clientY);
304
+ }, s = () => {
305
+ this.stopResize(), document.removeEventListener("touchmove", n), document.removeEventListener("touchend", s);
306
+ };
307
+ document.addEventListener("touchmove", n, { passive: !1 }), document.addEventListener("touchend", s);
308
+ }
309
+ startResize(t, e) {
310
+ !this.balloon || !this.resizeHandle || (this.isResizing = !0, this.resizeHandle.classList.add("resizing"), this.resizeStartPos = {
311
+ x: t,
312
+ y: e
313
+ });
314
+ }
315
+ handleResize(t, e) {
316
+ if (!this.isResizing) return;
317
+ const n = t - this.resizeStartPos.x, s = e - this.resizeStartPos.y, o = this.getBoundingClientRect(), d = Math.max(150, o.width + n), r = Math.max(100, o.height + s);
318
+ this.style.width = `${d}px`, this.style.height = `${r}px`, this.resizeStartPos = {
319
+ x: t,
320
+ y: e
321
+ }, this.updateHandlerVisual();
322
+ }
323
+ stopResize() {
324
+ if (!this.isResizing || !this.balloon || !this.resizeHandle) return;
325
+ this.isResizing = !1, this.resizeHandle.classList.remove("resizing");
326
+ const t = this.balloon.getBoundingClientRect();
327
+ this.dispatchEvent(new CustomEvent("balloon-resize", {
328
+ detail: {
329
+ width: t.width,
330
+ height: t.height,
331
+ balloonType: this._type
332
+ },
333
+ bubbles: !0,
334
+ composed: !0
335
+ }));
336
+ }
337
+ }
338
+ const X = () => {
339
+ typeof window < "u" && !customElements.get("comic-balloon") && customElements.define("comic-balloon", T);
340
+ };
341
+ export {
342
+ D as BalloonType,
343
+ T as ComicBalloonElement,
344
+ X as defineComicBalloon
345
+ };
346
+ //# sourceMappingURL=ComicBalloon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComicBalloon.js","sources":["../src/ComicBalloon.ts"],"sourcesContent":["/**\n * ComicBalloon Web Component\n * A customizable comic balloon with different styles and draggable handler\n */\n\nexport enum BalloonType {\n\tTALK = 'talk',\n\tCLOUD = 'cloud',\n\tWHISPER = 'whisper',\n\tRECTANGLE = 'rectangle'\n}\n\nexport type HandlerPosition = {\n\tx: number;\n\ty: number;\n};\n\nexport interface IComicBalloon extends HTMLElement {\n\ttype: BalloonType;\n\ttextContent: string;\n\thandlerPosition: HandlerPosition;\n\tupdateHandlerPosition( position: HandlerPosition ): void;\n\tgetHTML(): string;\n}\n\nexport type ContentChangeEvent = CustomEvent<{\n\tnewContent: string;\n\tballoonType: BalloonType;\n}>;\n\nexport type HandlerMoveEvent = CustomEvent<{\n\tfinalPosition: HandlerPosition;\n\tballoonType: BalloonType;\n}>;\n\nexport type ResizeEvent = CustomEvent<{\n\twidth: number;\n\theight: number;\n\tballoonType: BalloonType;\n}>;\n\nexport class ComicBalloonElement extends HTMLElement implements IComicBalloon {\n\tdeclare shadowRoot: ShadowRoot;\n\tprivate _type: BalloonType = BalloonType.TALK;\n\tprivate _handlerPosition: HandlerPosition = { x: 50, y: 100 };\n\tprivate isDragging: boolean = false;\n\tprivate isResizing: boolean = false;\n\tprivate dragStartOffset: { x: number; y: number } = { x: 0, y: 0 };\n\tprivate resizeStartPos: { x: number; y: number } = { x: 0, y: 0 };\n\tprivate contentEditableElement?: HTMLDivElement;\n\tprivate handler?: HTMLElement;\n\tprivate resizeHandle?: HTMLElement;\n\tprivate balloon?: HTMLElement;\n\n\tstatic get observedAttributes(): string[] {\n\t\treturn [ 'type', 'text' ];\n\t}\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.attachShadow( { mode: 'open' } );\n\t}\n\n\tconnectedCallback(): void {\n\t\tthis.render();\n\t\tthis.setupEventListeners();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tthis.removeEventListeners();\n\t}\n\n\tattributeChangedCallback( name: string, oldValue: string, newValue: string ): void {\n\t\tif ( oldValue === newValue ) return;\n\n\t\tif ( name === 'type' ) {\n\t\t\tthis._type = ( newValue as BalloonType ) || BalloonType.TALK;\n\t\t\tthis.updateBalloonStyle();\n\t\t} else if ( name === 'text' ) {\n\t\t\tif ( this.contentEditableElement ) {\n\t\t\t\tthis.contentEditableElement.textContent = newValue;\n\t\t\t}\n\t\t}\n\t}\n\n\tget type(): BalloonType {\n\t\treturn this._type;\n\t}\n\n\tset type( value: BalloonType ) {\n\t\tthis._type = value;\n\t\tthis.setAttribute( 'type', value );\n\t}\n\n\tget handlerPosition(): HandlerPosition {\n\t\treturn { ...this._handlerPosition };\n\t}\n\n\tset handlerPosition( value: HandlerPosition ) {\n\t\tthis._handlerPosition = { ...value };\n\t\tthis.updateHandlerVisual();\n\t}\n\n\tupdateHandlerPosition( position: HandlerPosition ): void {\n\t\tthis.handlerPosition = position;\n\t}\n\n\tgetHTML(): string {\n\t\treturn this.outerHTML;\n\t}\n\n\tprivate render(): void {\n\t\tthis.shadowRoot.innerHTML = `\n\t\t\t<style>${this.getStyles()}</style>\n\t\t\t<div class=\"comic-balloon-container\">\n\t\t\t\t<svg class=\"balloon-svg\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t<g class=\"cloud-shape\"></g>\n\t\t\t\t</svg>\n\t\t\t\t<div class=\"balloon ${this._type}\">\n\t\t\t\t\t<div class=\"content\" contenteditable=\"true\" role=\"textbox\" aria-label=\"Balloon text\">\n\t\t\t\t\t\t${this.getAttribute( 'text' ) || 'Type your text here...'}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"resize-handle\" role=\"button\" aria-label=\"Resize balloon\"></div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"handler ${this._type}\" role=\"button\" aria-label=\"Drag to reposition pointer\"></div>\n\t\t\t</div>\n\t\t`;\n\n\t\tthis.contentEditableElement = this.shadowRoot.querySelector( '.content' ) as HTMLDivElement;\n\t\tthis.handler = this.shadowRoot.querySelector( '.handler' ) as HTMLElement;\n\t\tthis.resizeHandle = this.shadowRoot.querySelector( '.resize-handle' ) as HTMLElement;\n\t\tthis.balloon = this.shadowRoot.querySelector( '.balloon' ) as HTMLElement;\n\n\t\tif ( this._type === BalloonType.CLOUD ) {\n\t\t\tthis.updateCloudShape();\n\t\t}\n\t\tthis.updateHandlerVisual();\n\t}\n\n\tprivate getStyles(): string {\n\t\treturn `\n\t\t\t:host {\n\t\t\t\tdisplay: inline-block;\n\t\t\t\tposition: relative;\n\t\t\t\twidth: 300px;\n\t\t\t\tmin-height: 150px;\n\t\t\t\toverflow: visible;\n\t\t\t}\n\n\t\t\t.comic-balloon-container {\n\t\t\t\tposition: relative;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tpadding: 50px;\n\t\t\t\tmargin: -50px;\n\t\t\t}\n\n\t\t\t.balloon-svg {\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tpointer-events: none;\n\t\t\t\tz-index: 1;\n\t\t\t\toverflow: visible;\n\t\t\t}\n\n\t\t\t.balloon {\n\t\t\t\tposition: relative;\n\t\t\t\tpadding: 20px;\n\t\t\t\tbackground: white;\n\t\t\t\tborder: 3px solid #000;\n\t\t\t\tmin-height: 100px;\n\t\t\t\tz-index: 2;\n\t\t\t\tmargin: 50px;\n\t\t\t\twidth: calc(100% - 100px);\n\t\t\t\theight: calc(100% - 100px);\n\t\t\t\tbox-sizing: border-box;\n\t\t\t}\n\n\t\t\t.balloon.talk {\n\t\t\t\tborder-radius: 25px;\n\t\t\t}\n\n\t\t\t.balloon.cloud {\n\t\t\t\tborder: none;\n\t\t\t\tbackground: transparent;\n\t\t\t\tposition: relative;\n\t\t\t}\n\t\t\t\n\t\t\t.balloon.cloud .content {\n\t\t\t\tposition: relative;\n\t\t\t\tz-index: 10;\n\t\t\t}\n\n\t\t\t.balloon.whisper {\n\t\t\t\tborder-radius: 25px;\n\t\t\t\tborder: 3px dashed #000;\n\t\t\t}\n\n\t\t\t.balloon.rectangle {\n\t\t\t\tborder-radius: 5px;\n\t\t\t}\n\n\t\t\t.content {\n\t\t\t\toutline: none;\n\t\t\t\tmin-height: 60px;\n\t\t\t\tfont-family: 'Comic Sans MS', cursive, sans-serif;\n\t\t\t\tfont-size: 16px;\n\t\t\t\tline-height: 1.4;\n\t\t\t\tcolor: #000;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\ttext-align: center;\n\t\t\t}\n\n\t\t\t.content:empty:before {\n\t\t\t\tcontent: attr(aria-label);\n\t\t\t\tcolor: #999;\n\t\t\t}\n\n\t\t\t.handler {\n\t\t\t\tposition: absolute;\n\t\t\t\twidth: 20px;\n\t\t\t\theight: 20px;\n\t\t\t\tcursor: move;\n\t\t\t\tz-index: 3;\n\t\t\t\tuser-select: none;\n\t\t\t\tbackground: rgba(0, 0, 0, 0.1);\n\t\t\t\tborder-radius: 50%;\n\t\t\t\tborder: 2px solid rgba(0, 0, 0, 0.3);\n\t\t\t}\n\n\t\t\t.handler:hover {\n\t\t\t\tbackground: rgba(0, 0, 0, 0.2);\n\t\t\t\tborder-color: rgba(0, 0, 0, 0.5);\n\t\t\t}\n\n\t\t\t.handler.dragging {\n\t\t\t\tcursor: grabbing;\n\t\t\t\tbackground: rgba(0, 0, 0, 0.3);\n\t\t\t}\n\n\t\t\t.resize-handle {\n\t\t\t\tposition: absolute;\n\t\t\t\tbottom: 0;\n\t\t\t\tright: 0;\n\t\t\t\twidth: 20px;\n\t\t\t\theight: 20px;\n\t\t\t\tcursor: nwse-resize;\n\t\t\t\tz-index: 4;\n\t\t\t\tbackground: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 0.5) 50%);\n\t\t\t\tborder-bottom-right-radius: inherit;\n\t\t\t}\n\n\t\t\t.resize-handle:hover {\n\t\t\t\tbackground: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 0.8) 50%);\n\t\t\t}\n\n\t\t\t.resize-handle.resizing {\n\t\t\t\tbackground: linear-gradient(135deg, transparent 50%, rgba(102, 126, 234, 1) 50%);\n\t\t\t}\n\t\t`;\n\t}\n\n\tprivate updateBalloonStyle(): void {\n\t\tif ( !this.balloon || !this.handler ) return;\n\n\t\tthis.balloon.className = `balloon ${this._type}`;\n\t\tthis.handler.className = `handler ${this._type}`;\n\t\t\n\t\tif ( this._type === BalloonType.CLOUD ) {\n\t\t\tthis.updateCloudShape();\n\t\t}\n\t\t\n\t\tthis.updateHandlerVisual();\n\t}\n\n\tprivate updateCloudShape(): void {\n\t\tif ( !this.balloon ) return;\n\t\t\n\t\tconst cloudGroup = this.shadowRoot.querySelector( '.cloud-shape' ) as SVGGElement;\n\t\tif ( !cloudGroup ) return;\n\t\t\n\t\tconst rect = this.balloon.getBoundingClientRect();\n\t\tconst containerRect = this.shadowRoot.querySelector( '.comic-balloon-container' )?.getBoundingClientRect();\n\t\tif ( !containerRect ) return;\n\t\t\n\t\tconst x = rect.left - containerRect.left;\n\t\tconst y = rect.top - containerRect.top;\n\t\tconst w = rect.width;\n\t\tconst h = rect.height;\n\t\t\n\t\tcloudGroup.innerHTML = '';\n\t\t\n\t\tconst centerX = x + w / 2;\n\t\tconst centerY = y + h / 2;\n\t\t\n\t\tconst numScallops = 14;\n\t\tconst radiusX = w / 2.5;\n\t\tconst radiusY = h / 2.5;\n\t\t\n\t\tlet pathData = '';\n\t\t\n\t\tfor ( let i = 0; i <= numScallops; i++ ) {\n\t\t\tconst angle = ( i / numScallops ) * Math.PI * 2 - Math.PI / 2;\n\t\t\tconst nextAngle = ( ( i + 1 ) / numScallops ) * Math.PI * 2 - Math.PI / 2;\n\t\t\t\n\t\t\tconst px = centerX + Math.cos( angle ) * radiusX;\n\t\t\tconst py = centerY + Math.sin( angle ) * radiusY;\n\t\t\t\n\t\t\tconst nx = centerX + Math.cos( nextAngle ) * radiusX;\n\t\t\tconst ny = centerY + Math.sin( nextAngle ) * radiusY;\n\t\t\t\n\t\t\t// Chord midpoint\n\t\t\tconst mx = ( px + nx ) / 2;\n\t\t\tconst my = ( py + ny ) / 2;\n\t\t\t\n\t\t\t// Chord length\n\t\t\tconst dx = nx - px;\n\t\t\tconst dy = ny - py;\n\t\t\tconst chordLen = Math.sqrt( dx * dx + dy * dy );\n\t\t\t\n\t\t\t// Vector from center to midpoint\n\t\t\tconst vx = mx - centerX;\n\t\t\tconst vy = my - centerY;\n\t\t\tconst vLen = Math.sqrt( vx * vx + vy * vy );\n\t\t\t\n\t\t\t// Normalized vector\n\t\t\tconst nx_vec = vx / vLen;\n\t\t\tconst ny_vec = vy / vLen;\n\t\t\t\n\t\t\t// Control point distance\n\t\t\tconst scallopHeight = chordLen * 0.8;\n\t\t\t\n\t\t\tconst cx = mx + nx_vec * scallopHeight;\n\t\t\tconst cy = my + ny_vec * scallopHeight;\n\t\t\t\n\t\t\tif ( i === 0 ) {\n\t\t\t\tpathData += `M ${px} ${py} `;\n\t\t\t}\n\t\t\t\n\t\t\tpathData += `Q ${cx} ${cy} ${nx} ${ny} `;\n\t\t}\n\t\t\n\t\tpathData += 'Z';\n\t\t\n\t\tconst path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );\n\t\tpath.setAttribute( 'd', pathData );\n\t\tpath.setAttribute( 'fill', 'white' );\n\t\tpath.setAttribute( 'stroke', '#000' );\n\t\tpath.setAttribute( 'stroke-width', '3' );\n\t\tpath.setAttribute( 'stroke-linejoin', 'round' );\n\t\t\n\t\tcloudGroup.appendChild( path );\n\t}\n\n\tprivate updateHandlerVisual(): void {\n\t\tif ( !this.handler || !this.balloon ) return;\n\n\t\tconst relativeX = this._handlerPosition.x;\n\t\tconst relativeY = this._handlerPosition.y;\n\n\t\tthis.handler.style.left = `${relativeX - 10}px`;\n\t\tthis.handler.style.top = `${relativeY - 10}px`;\n\t\t\n\t\tif ( this._type === BalloonType.CLOUD ) {\n\t\t\tthis.updateCloudShape();\n\t\t}\n\n\t\tthis.updateSVGPointer();\n\t}\n\n\tprivate updateSVGPointer(): void {\n\t\tconst svg = this.shadowRoot.querySelector( '.balloon-svg' ) as SVGElement;\n\t\tif ( !svg || !this.balloon ) return;\n\n\t\tconst balloonRect = this.balloon.getBoundingClientRect();\n\t\tconst containerRect = svg.getBoundingClientRect();\n\n\t\tlet pointerGroup = svg.querySelector( '.pointer-group' );\n\t\tif ( !pointerGroup ) {\n\t\t\tpointerGroup = document.createElementNS( 'http://www.w3.org/2000/svg', 'g' );\n\t\t\tpointerGroup.setAttribute( 'class', 'pointer-group' );\n\t\t\tsvg.appendChild( pointerGroup );\n\t\t}\n\n\t\tpointerGroup.innerHTML = '';\n\n\t\tconst balloonLeft = balloonRect.left - containerRect.left;\n\t\tconst balloonTop = balloonRect.top - containerRect.top;\n\t\tconst balloonCenterX = balloonLeft + balloonRect.width / 2;\n\t\tconst balloonCenterY = balloonTop + balloonRect.height / 2;\n\n\t\tconst handlerX = this._handlerPosition.x;\n\t\tconst handlerY = this._handlerPosition.y;\n\n\t\tconst angle = Math.atan2( handlerY - balloonCenterY, handlerX - balloonCenterX );\n\t\t\n\t\tconst balloonRadiusX = balloonRect.width / 2;\n\t\tconst balloonRadiusY = balloonRect.height / 2;\n\t\t\n\t\tconst edgeX = balloonCenterX + Math.cos( angle ) * balloonRadiusX * 0.85;\n\t\tconst edgeY = balloonCenterY + Math.sin( angle ) * balloonRadiusY * 0.85;\n\n\t\tif ( this._type === BalloonType.TALK || this._type === BalloonType.RECTANGLE ) {\n\t\t\tthis.drawTalkPointer( pointerGroup as SVGGElement, edgeX, edgeY, handlerX, handlerY );\n\t\t} else if ( this._type === BalloonType.CLOUD ) {\n\t\t\tthis.drawCloudPointer( pointerGroup as SVGGElement, edgeX, edgeY, handlerX, handlerY );\n\t\t} else if ( this._type === BalloonType.WHISPER ) {\n\t\t\tthis.drawWhisperPointer( pointerGroup as SVGGElement, edgeX, edgeY, handlerX, handlerY );\n\t\t}\n\t}\n\n\tprivate drawTalkPointer( group: SVGGElement, edgeX: number, edgeY: number, handlerX: number, handlerY: number ): void {\n\t\tconst angle = Math.atan2( handlerY - edgeY, handlerX - edgeX );\n\t\tconst perpAngle = angle + Math.PI / 2;\n\t\t\n\t\tconst baseWidth = 20;\n\t\t\n\t\tconst base1X = edgeX + Math.cos( perpAngle ) * baseWidth / 2;\n\t\tconst base1Y = edgeY + Math.sin( perpAngle ) * baseWidth / 2;\n\t\tconst base2X = edgeX - Math.cos( perpAngle ) * baseWidth / 2;\n\t\tconst base2Y = edgeY - Math.sin( perpAngle ) * baseWidth / 2;\n\t\tconst tipX = handlerX;\n\t\tconst tipY = handlerY;\n\n\t\tconst path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );\n\t\tconst d = `M ${base1X} ${base1Y} L ${tipX} ${tipY} L ${base2X} ${base2Y} Z`;\n\t\tpath.setAttribute( 'd', d );\n\t\tpath.setAttribute( 'fill', 'white' );\n\t\tpath.setAttribute( 'stroke', '#000' );\n\t\tpath.setAttribute( 'stroke-width', '3' );\n\t\tpath.setAttribute( 'stroke-linejoin', 'round' );\n\t\t\n\t\tgroup.appendChild( path );\n\t}\n\n\tprivate drawCloudPointer( group: SVGGElement, edgeX: number, edgeY: number, handlerX: number, handlerY: number ): void {\n\t\tconst dx = handlerX - edgeX;\n\t\tconst dy = handlerY - edgeY;\n\t\t\n\t\tconst numBubbles = 3;\n\t\tfor ( let i = 0; i < numBubbles; i++ ) {\n\t\t\tconst t = ( i + 1 ) / ( numBubbles + 1 );\n\t\t\tconst x = edgeX + dx * t;\n\t\t\tconst y = edgeY + dy * t;\n\t\t\tconst radius = 8 - i * 2;\n\t\t\t\n\t\t\tconst circle = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' );\n\t\t\tcircle.setAttribute( 'cx', x.toString() );\n\t\t\tcircle.setAttribute( 'cy', y.toString() );\n\t\t\tcircle.setAttribute( 'r', radius.toString() );\n\t\t\tcircle.setAttribute( 'fill', 'white' );\n\t\t\tcircle.setAttribute( 'stroke', '#000' );\n\t\t\tcircle.setAttribute( 'stroke-width', '2' );\n\t\t\t\n\t\t\tgroup.appendChild( circle );\n\t\t}\n\t}\n\n\tprivate drawWhisperPointer( group: SVGGElement, edgeX: number, edgeY: number, handlerX: number, handlerY: number ): void {\n\t\tconst angle = Math.atan2( handlerY - edgeY, handlerX - edgeX );\n\t\tconst perpAngle = angle + Math.PI / 2;\n\t\t\n\t\tconst baseWidth = 20;\n\t\t\n\t\tconst base1X = edgeX + Math.cos( perpAngle ) * baseWidth / 2;\n\t\tconst base1Y = edgeY + Math.sin( perpAngle ) * baseWidth / 2;\n\t\tconst base2X = edgeX - Math.cos( perpAngle ) * baseWidth / 2;\n\t\tconst base2Y = edgeY - Math.sin( perpAngle ) * baseWidth / 2;\n\t\tconst tipX = handlerX;\n\t\tconst tipY = handlerY;\n\n\t\tconst path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );\n\t\tconst d = `M ${base1X} ${base1Y} L ${tipX} ${tipY} L ${base2X} ${base2Y}`;\n\t\tpath.setAttribute( 'd', d );\n\t\tpath.setAttribute( 'fill', 'none' );\n\t\tpath.setAttribute( 'stroke', '#000' );\n\t\tpath.setAttribute( 'stroke-width', '3' );\n\t\tpath.setAttribute( 'stroke-dasharray', '10 5' );\n\t\tpath.setAttribute( 'stroke-linejoin', 'round' );\n\t\tpath.setAttribute( 'stroke-linecap', 'round' );\n\t\t\n\t\tgroup.appendChild( path );\n\t}\n\n\tprivate setupEventListeners(): void {\n\t\tif ( !this.handler ) return;\n\n\t\tthis.handler.addEventListener( 'mousedown', this.handleMouseDown.bind( this ) );\n\t\tthis.handler.addEventListener( 'touchstart', this.handleTouchStart.bind( this ), { passive: false } );\n\n\t\tif ( this.resizeHandle ) {\n\t\t\tthis.resizeHandle.addEventListener( 'mousedown', this.handleResizeMouseDown.bind( this ) );\n\t\t\tthis.resizeHandle.addEventListener( 'touchstart', this.handleResizeTouchStart.bind( this ), { passive: false } );\n\t\t}\n\n\t\tif ( this.contentEditableElement ) {\n\t\t\tthis.contentEditableElement.addEventListener( 'blur', this.handleContentBlur.bind( this ) );\n\t\t}\n\t}\n\n\tprivate removeEventListeners(): void {\n\t\tif ( !this.handler ) return;\n\n\t\tthis.handler.removeEventListener( 'mousedown', this.handleMouseDown.bind( this ) );\n\t\tthis.handler.removeEventListener( 'touchstart', this.handleTouchStart.bind( this ) );\n\n\t\tif ( this.resizeHandle ) {\n\t\t\tthis.resizeHandle.removeEventListener( 'mousedown', this.handleResizeMouseDown.bind( this ) );\n\t\t\tthis.resizeHandle.removeEventListener( 'touchstart', this.handleResizeTouchStart.bind( this ) );\n\t\t}\n\n\t\tif ( this.contentEditableElement ) {\n\t\t\tthis.contentEditableElement.removeEventListener( 'blur', this.handleContentBlur.bind( this ) );\n\t\t}\n\t}\n\n\tprivate handleMouseDown( e: MouseEvent ): void {\n\t\te.preventDefault();\n\t\tthis.startDrag( e.clientX, e.clientY );\n\n\t\tconst handleMouseMove = ( e: MouseEvent ) => this.handleDrag( e.clientX, e.clientY );\n\t\tconst handleMouseUp = () => {\n\t\t\tthis.stopDrag();\n\t\t\tdocument.removeEventListener( 'mousemove', handleMouseMove );\n\t\t\tdocument.removeEventListener( 'mouseup', handleMouseUp );\n\t\t};\n\n\t\tdocument.addEventListener( 'mousemove', handleMouseMove );\n\t\tdocument.addEventListener( 'mouseup', handleMouseUp );\n\t}\n\n\tprivate handleTouchStart( e: TouchEvent ): void {\n\t\te.preventDefault();\n\t\tconst touch = e.touches[0];\n\t\tthis.startDrag( touch.clientX, touch.clientY );\n\n\t\tconst handleTouchMove = ( e: TouchEvent ) => {\n\t\t\tconst touch = e.touches[0];\n\t\t\tthis.handleDrag( touch.clientX, touch.clientY );\n\t\t};\n\t\tconst handleTouchEnd = () => {\n\t\t\tthis.stopDrag();\n\t\t\tdocument.removeEventListener( 'touchmove', handleTouchMove );\n\t\t\tdocument.removeEventListener( 'touchend', handleTouchEnd );\n\t\t};\n\n\t\tdocument.addEventListener( 'touchmove', handleTouchMove, { passive: false } );\n\t\tdocument.addEventListener( 'touchend', handleTouchEnd );\n\t}\n\n\tprivate startDrag( clientX: number, clientY: number ): void {\n\t\tthis.isDragging = true;\n\t\tif ( this.handler ) {\n\t\t\tthis.handler.classList.add( 'dragging' );\n\t\t}\n\n\t\tconst containerRect = this.shadowRoot.querySelector( '.comic-balloon-container' )?.getBoundingClientRect();\n\t\tif ( containerRect ) {\n\t\t\tthis.dragStartOffset = {\n\t\t\t\tx: clientX - containerRect.left - this._handlerPosition.x,\n\t\t\t\ty: clientY - containerRect.top - this._handlerPosition.y\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate handleDrag( clientX: number, clientY: number ): void {\n\t\tif ( !this.isDragging ) return;\n\n\t\tconst containerRect = this.shadowRoot.querySelector( '.comic-balloon-container' )?.getBoundingClientRect();\n\t\tif ( !containerRect ) return;\n\n\t\tlet newX = clientX - containerRect.left - this.dragStartOffset.x;\n\t\tlet newY = clientY - containerRect.top - this.dragStartOffset.y;\n\n\t\tnewX = Math.max( -50, Math.min( containerRect.width + 50, newX ) );\n\t\tnewY = Math.max( -50, Math.min( containerRect.height + 50, newY ) );\n\n\t\tthis._handlerPosition = { x: newX, y: newY };\n\t\tthis.updateHandlerVisual();\n\t}\n\n\tprivate stopDrag(): void {\n\t\tthis.isDragging = false;\n\t\tif ( this.handler ) {\n\t\t\tthis.handler.classList.remove( 'dragging' );\n\t\t}\n\n\t\tthis.dispatchEvent( new CustomEvent<HandlerMoveEvent['detail']>( 'handler-move', {\n\t\t\tdetail: {\n\t\t\t\tfinalPosition: { ...this._handlerPosition },\n\t\t\t\tballoonType: this._type\n\t\t\t},\n\t\t\tbubbles: true,\n\t\t\tcomposed: true\n\t\t} ) );\n\t}\n\n\tprivate handleContentBlur(): void {\n\t\tif ( !this.contentEditableElement ) return;\n\n\t\tthis.dispatchEvent( new CustomEvent<ContentChangeEvent['detail']>( 'balloon-content-change', {\n\t\t\tdetail: {\n\t\t\t\tnewContent: this.contentEditableElement.textContent || '',\n\t\t\t\tballoonType: this._type\n\t\t\t},\n\t\t\tbubbles: true,\n\t\t\tcomposed: true\n\t\t} ) );\n\t}\n\n\tprivate handleResizeMouseDown( e: MouseEvent ): void {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tthis.startResize( e.clientX, e.clientY );\n\n\t\tconst handleMouseMove = ( e: MouseEvent ) => this.handleResize( e.clientX, e.clientY );\n\t\tconst handleMouseUp = () => {\n\t\t\tthis.stopResize();\n\t\t\tdocument.removeEventListener( 'mousemove', handleMouseMove );\n\t\t\tdocument.removeEventListener( 'mouseup', handleMouseUp );\n\t\t};\n\n\t\tdocument.addEventListener( 'mousemove', handleMouseMove );\n\t\tdocument.addEventListener( 'mouseup', handleMouseUp );\n\t}\n\n\tprivate handleResizeTouchStart( e: TouchEvent ): void {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tconst touch = e.touches[0];\n\t\tthis.startResize( touch.clientX, touch.clientY );\n\n\t\tconst handleTouchMove = ( e: TouchEvent ) => {\n\t\t\tconst touch = e.touches[0];\n\t\t\tthis.handleResize( touch.clientX, touch.clientY );\n\t\t};\n\t\tconst handleTouchEnd = () => {\n\t\t\tthis.stopResize();\n\t\t\tdocument.removeEventListener( 'touchmove', handleTouchMove );\n\t\t\tdocument.removeEventListener( 'touchend', handleTouchEnd );\n\t\t};\n\n\t\tdocument.addEventListener( 'touchmove', handleTouchMove, { passive: false } );\n\t\tdocument.addEventListener( 'touchend', handleTouchEnd );\n\t}\n\n\tprivate startResize( clientX: number, clientY: number ): void {\n\t\tif ( !this.balloon || !this.resizeHandle ) return;\n\n\t\tthis.isResizing = true;\n\t\tthis.resizeHandle.classList.add( 'resizing' );\n\n\t\tthis.resizeStartPos = {\n\t\t\tx: clientX,\n\t\t\ty: clientY\n\t\t};\n\t}\n\n\tprivate handleResize( clientX: number, clientY: number ): void {\n\t\tif ( !this.isResizing ) return;\n\n\t\tconst deltaX = clientX - this.resizeStartPos.x;\n\t\tconst deltaY = clientY - this.resizeStartPos.y;\n\n\t\tconst hostRect = this.getBoundingClientRect();\n\t\tconst newWidth = Math.max( 150, hostRect.width + deltaX );\n\t\tconst newHeight = Math.max( 100, hostRect.height + deltaY );\n\n\t\tthis.style.width = `${newWidth}px`;\n\t\tthis.style.height = `${newHeight}px`;\n\n\t\tthis.resizeStartPos = {\n\t\t\tx: clientX,\n\t\t\ty: clientY\n\t\t};\n\n\t\tthis.updateHandlerVisual();\n\t}\n\n\tprivate stopResize(): void {\n\t\tif ( !this.isResizing || !this.balloon || !this.resizeHandle ) return;\n\n\t\tthis.isResizing = false;\n\t\tthis.resizeHandle.classList.remove( 'resizing' );\n\n\t\tconst rect = this.balloon.getBoundingClientRect();\n\n\t\tthis.dispatchEvent( new CustomEvent<ResizeEvent['detail']>( 'balloon-resize', {\n\t\t\tdetail: {\n\t\t\t\twidth: rect.width,\n\t\t\t\theight: rect.height,\n\t\t\t\tballoonType: this._type\n\t\t\t},\n\t\t\tbubbles: true,\n\t\t\tcomposed: true\n\t\t} ) );\n\t}\n}\n\nexport const defineComicBalloon = (): void => {\n\tif ( typeof window !== 'undefined' && !customElements.get( 'comic-balloon' ) ) {\n\t\tcustomElements.define( 'comic-balloon', ComicBalloonElement );\n\t}\n};\n"],"names":["BalloonType","ComicBalloonElement","name","oldValue","newValue","value","position","cloudGroup","rect","containerRect","x","y","w","h","centerX","centerY","numScallops","radiusX","radiusY","pathData","angle","nextAngle","px","py","nx","ny","mx","my","dx","dy","chordLen","vx","vy","vLen","nx_vec","ny_vec","scallopHeight","cx","cy","path","relativeX","relativeY","svg","balloonRect","pointerGroup","balloonLeft","balloonTop","balloonCenterX","balloonCenterY","handlerX","handlerY","balloonRadiusX","balloonRadiusY","edgeX","edgeY","group","perpAngle","baseWidth","base1X","base1Y","base2X","base2Y","tipX","tipY","d","numBubbles","i","t","radius","circle","e","handleMouseMove","handleMouseUp","touch","handleTouchMove","handleTouchEnd","clientX","clientY","newX","newY","deltaX","deltaY","hostRect","newWidth","newHeight","defineComicBalloon"],"mappings":"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"}