@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.
- package/dist/AIMarkdownEditor.d.ts +35 -0
- package/dist/AIMarkdownEditor.d.ts.map +1 -0
- package/dist/AIMarkdownEditor.js +412 -0
- package/dist/AIMarkdownEditor.js.map +1 -0
- package/dist/AITextEditor.d.ts +183 -0
- package/dist/AITextEditor.d.ts.map +1 -0
- package/dist/AITextEditor.js +63 -27
- package/dist/AITextEditor.js.map +1 -1
- package/dist/ButtonToolbar.d.ts +35 -0
- package/dist/ButtonToolbar.d.ts.map +1 -0
- package/dist/ButtonToolbar.js +220 -0
- package/dist/ButtonToolbar.js.map +1 -0
- package/dist/CheckList.d.ts +31 -0
- package/dist/CheckList.d.ts.map +1 -0
- package/dist/CheckList.js +336 -0
- package/dist/CheckList.js.map +1 -0
- package/dist/ChunkUploader.d.ts +125 -0
- package/dist/ChunkUploader.d.ts.map +1 -0
- package/dist/ChunkUploader.js +756 -0
- package/dist/ChunkUploader.js.map +1 -0
- package/dist/ComicBalloon.d.ts +82 -0
- package/dist/ComicBalloon.d.ts.map +1 -0
- package/dist/ComicBalloon.js +346 -0
- package/dist/ComicBalloon.js.map +1 -0
- package/dist/ContainerBox.d.ts +112 -0
- package/dist/ContainerBox.d.ts.map +1 -0
- package/dist/ContainerBox.js +359 -0
- package/dist/ContainerBox.js.map +1 -0
- package/dist/DateSelector.d.ts +103 -0
- package/dist/DateSelector.d.ts.map +1 -0
- package/dist/Dialog.d.ts +102 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +299 -0
- package/dist/Dialog.js.map +1 -0
- package/dist/Drawer.d.ts +63 -0
- package/dist/Drawer.d.ts.map +1 -0
- package/dist/Drawer.js +340 -0
- package/dist/Drawer.js.map +1 -0
- package/dist/ImageView.d.ts +42 -0
- package/dist/ImageView.d.ts.map +1 -0
- package/dist/ImageView.js +209 -0
- package/dist/ImageView.js.map +1 -0
- package/dist/MarkdownPreview.d.ts +25 -0
- package/dist/MarkdownPreview.d.ts.map +1 -0
- package/dist/MarkdownPreview.js +147 -0
- package/dist/MarkdownPreview.js.map +1 -0
- package/dist/PopoverMenu.d.ts +103 -0
- package/dist/PopoverMenu.d.ts.map +1 -0
- package/dist/ResizableCropper.d.ts +158 -0
- package/dist/ResizableCropper.d.ts.map +1 -0
- package/dist/ResizableCropper.js +562 -0
- package/dist/ResizableCropper.js.map +1 -0
- package/dist/SmartSelect.d.ts +100 -0
- package/dist/SmartSelect.d.ts.map +1 -0
- package/dist/SmartSelect.js +45 -2
- package/dist/SmartSelect.js.map +1 -1
- package/dist/Toast.d.ts +127 -0
- package/dist/Toast.d.ts.map +1 -0
- package/dist/Toast.js +79 -49
- package/dist/Toast.js.map +1 -1
- package/dist/TreeView.d.ts +84 -0
- package/dist/TreeView.d.ts.map +1 -0
- package/dist/TreeView.js +478 -0
- package/dist/TreeView.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -14
- package/dist/index.js.map +1 -1
- package/package.json +60 -5
- package/src/AIMarkdownEditor.ts +568 -0
- package/src/AITextEditor.ts +97 -2
- package/src/ButtonToolbar.ts +302 -0
- package/src/CheckList.ts +438 -0
- package/src/ChunkUploader.ts +1135 -0
- package/src/ComicBalloon.ts +709 -0
- package/src/ContainerBox.ts +570 -0
- package/src/Dialog.ts +510 -0
- package/src/Drawer.ts +435 -0
- package/src/ImageView.ts +265 -0
- package/src/MarkdownPreview.ts +213 -0
- package/src/ResizableCropper.ts +1099 -0
- package/src/SmartSelect.ts +48 -2
- package/src/Toast.ts +96 -32
- package/src/TreeView.ts +673 -0
- 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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"}
|