@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":"ResizableCropper.js","sources":["../src/ResizableCropper.ts"],"sourcesContent":["/**\n * ResizableCropper Web Component\n * A container that wraps a single child element with drag-to-scale (resizing) and drag-to-crop (panning) capabilities\n */\n\nexport interface ResizableCropperState {\n\twidth : number;\n\theight : number;\n\tminWidth : number;\n\tminHeight : number;\n\taspectRatio : string | null;\n\tcontentElement : HTMLElement | null;\n\tcontentLeft : number;\n\tcontentTop : number;\n\trotation : number;\n\twrapperLeft : number;\n\twrapperTop : number;\n}\n\nexport interface ResizableCropperValues {\n\twrapperWidth : number;\n\twrapperHeight : number;\n\twrapperLeft : number;\n\twrapperTop : number;\n\tcontentWidth : number;\n\tcontentHeight : number;\n\tcontentLeft : number;\n\tcontentTop : number;\n\tzoom : number;\n\trotation : number;\n}\n\nexport interface ResizableCropperComponentState {\n\tmode : 'transform' | 'crop';\n\tdisabled : boolean;\n\tallowCrop : boolean;\n\tallowResize : boolean;\n\tallowRotate : boolean;\n\tallowDrag : boolean;\n\tminWidth : number;\n\tminHeight : number;\n\taspectRatio : string | null;\n\tvalues : ResizableCropperValues;\n}\n\nexport interface ResizableCropEventDetail {\n\twidth : number;\n\theight : number;\n\twrapperLeft : number;\n\twrapperTop : number;\n\tcontentLeft : number;\n\tcontentTop : number;\n\taction : 'scale' | 'crop' | 'pan' | 'rotate' | 'move';\n\trotation? : number;\n\thandle? : string;\n}\n\nexport class ResizableCropperElement extends HTMLElement {\n\tdeclare shadowRoot : ShadowRoot;\n\n\tprivate state : ResizableCropperState = {\n\t\twidth: 200,\n\t\theight: 150,\n\t\tminWidth: 50,\n\t\tminHeight: 50,\n\t\taspectRatio: null,\n\t\tcontentElement: null,\n\t\tcontentLeft: 0,\n\t\tcontentTop: 0,\n\t\trotation: 0,\n\t\twrapperLeft: 0,\n\t\twrapperTop: 0,\n\t};\n\n\tprivate wrapper! : HTMLElement;\n\tprivate contentSlot! : HTMLSlotElement;\n\tprivate handlesContainer! : HTMLElement;\n\n\tprivate isDragging = false;\n\tprivate dragAction : 'scale' | 'crop' | 'pan' | 'rotate' | 'move' | null = null;\n\tprivate dragHandle : string | null = null;\n\tprivate dragStartX = 0;\n\tprivate dragStartY = 0;\n\tprivate dragStartWidth = 0;\n\tprivate dragStartHeight = 0;\n\tprivate dragStartContentLeft = 0;\n\tprivate dragStartContentTop = 0;\n\tprivate dragStartContentWidth = 0;\n\tprivate dragStartContentHeight = 0;\n\tprivate initialContentWidth = 0;\n\tprivate initialContentHeight = 0;\n\tprivate _dragStartRotation = 0;\n\tprivate _dragStartPointerAngle = 0;\n\tprivate _dragRotateCenterX = 0;\n\tprivate _dragRotateCenterY = 0;\n\tprivate _interactionMode : 'transform' | 'crop' = 'transform';\n\tprivate _dragPointerOffsetX = 0;\n\tprivate _dragPointerOffsetY = 0;\n\tprivate _dragMoveContainer : HTMLElement | null = null;\n\n\tconstructor () {\n\t\tsuper();\n\t\tthis.attachShadow( { mode: 'open' } );\n\t\tthis.render();\n\t}\n\n\tstatic get observedAttributes () : string[] {\n\t\treturn [ 'width', 'height', 'min-width', 'min-height', 'aspect-ratio', 'disabled', 'allow-crop', 'allow-resize', 'allow-rotate', 'allow-drag' ];\n\t}\n\n\tattributeChangedCallback ( name : string, oldValue : string | null, newValue : string | null ) : void {\n\t\tif ( oldValue === newValue ) return;\n\n\t\tswitch ( name ) {\n\t\t\tcase 'width':\n\t\t\t\tthis.state.width = parseFloat( newValue || '200' );\n\t\t\t\tthis.updateWrapperDimensions();\n\t\t\t\tbreak;\n\t\t\tcase 'height':\n\t\t\t\tthis.state.height = parseFloat( newValue || '150' );\n\t\t\t\tthis.updateWrapperDimensions();\n\t\t\t\tbreak;\n\t\t\tcase 'min-width':\n\t\t\t\tthis.state.minWidth = parseFloat( newValue || '50' );\n\t\t\t\tbreak;\n\t\t\tcase 'min-height':\n\t\t\t\tthis.state.minHeight = parseFloat( newValue || '50' );\n\t\t\t\tbreak;\n\t\t\tcase 'aspect-ratio':\n\t\t\t\tthis.state.aspectRatio = newValue;\n\t\t\t\tbreak;\n\t\t\tcase 'allow-crop':\n\t\t\tcase 'allow-resize':\n\t\t\tcase 'allow-rotate':\n\t\t\tcase 'allow-drag':\n\t\t\t\tthis.updateHandlesVisibility();\n\t\t\t\tthis._applyInteractionModeUI();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconnectedCallback () : void {\n\t\tthis.wrapper = this.shadowRoot.querySelector( '#wrapper' )!;\n\t\tthis.shadowRoot.querySelector( '#clipper' )!;\n\t\tthis.contentSlot = this.shadowRoot.querySelector( 'slot' )!;\n\t\tthis.handlesContainer = this.shadowRoot.querySelector( '#handles-container' )!;\n\n\t\tthis.updateWrapperDimensions();\n\t\tthis.updateHandlesVisibility();\n\t\tthis._applyInteractionModeUI();\n\t\tthis._syncWrapperPositionFromLayout( this._getContainerForMove() );\n\t\tthis.bindEvents();\n\n\t\tthis.contentSlot.addEventListener( 'slotchange', () => {\n\t\t\tthis.updateContentElement();\n\t\t} );\n\n\t\tthis.updateContentElement();\n\t}\n\n\tdisconnectedCallback () : void {\n\t\tthis.unbindEvents();\n\t}\n\n\tprivate _applyInteractionModeUI () : void {\n\t\tif ( !this.wrapper ) return;\n\t\tthis.wrapper.style.cursor = this._interactionMode === 'transform' && this.allowDrag && !this.disabled ? 'move' : '';\n\t}\n\n\tprivate _setInteractionMode ( mode : 'transform' | 'crop' ) : void {\n\t\tif ( this._interactionMode === mode ) return;\n\t\tthis._interactionMode = mode;\n\t\tthis.updateHandlesVisibility();\n\t\tthis._applyInteractionModeUI();\n\t}\n\n\tprivate updateContentElement () : void {\n\t\tconst nodes = this.contentSlot.assignedElements();\n\t\tif ( nodes.length > 0 ) {\n\t\t\tthis.state.contentElement = nodes[0] as HTMLElement;\n\t\t\tthis.state.contentElement.style.position = 'absolute';\n\t\t\tthis.state.contentElement.style.left = `${this.state.contentLeft}px`;\n\t\t\tthis.state.contentElement.style.top = `${this.state.contentTop}px`;\n\n\t\t\t// Get initial content dimensions\n\t\t\tif ( this.state.contentElement instanceof HTMLImageElement ) {\n\t\t\t\tif ( this.state.contentElement.complete ) {\n\t\t\t\t\tthis.initializeContentSize();\n\t\t\t\t} else {\n\t\t\t\t\tthis.state.contentElement.addEventListener( 'load', () => {\n\t\t\t\t\t\tthis.initializeContentSize();\n\t\t\t\t\t}, { once: true } );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate initializeContentSize () : void {\n\t\tif ( !this.state.contentElement ) return;\n\t\tconst width = this.state.contentElement.offsetWidth;\n\t\tconst height = this.state.contentElement.offsetHeight;\n\t\tif ( width > 0 ) {\n\t\t\tthis.state.contentElement.style.width = `${width}px`;\n\t\t\tthis.state.contentElement.style.height = 'auto';\n\t\t\tthis.initialContentWidth = width;\n\t\t\tthis.initialContentHeight = height;\n\t\t}\n\t}\n\n\tprivate updateWrapperDimensions () : void {\n\t\tif ( !this.wrapper ) return;\n\t\tthis.wrapper.style.width = `${this.state.width}px`;\n\t\tthis.wrapper.style.height = `${this.state.height}px`;\n\t\tthis._applyWrapperTransform();\n\t}\n\n\tprivate _applyWrapperTransform () : void {\n\t\tif ( !this.wrapper ) return;\n\t\tthis.wrapper.style.transformOrigin = 'center center';\n\t\tthis.wrapper.style.transform = `rotate(${this.state.rotation}deg)`;\n\t}\n\n\tprivate updateHandlesVisibility () : void {\n\t\tif ( !this.handlesContainer ) return;\n\n\t\tconst scaleHandle = this.handlesContainer.querySelector( '[data-action=\"scale\"]' ) as HTMLElement;\n\t\tconst rotateHandle = this.handlesContainer.querySelector( '[data-action=\"rotate\"]' ) as HTMLElement;\n\t\tconst cropHandles = this.handlesContainer.querySelectorAll( '[data-action=\"crop\"]' );\n\n\t\tif ( scaleHandle ) {\n\t\t\tscaleHandle.style.display = this.allowResize ? '' : 'none';\n\t\t}\n\n\t\tif ( rotateHandle ) {\n\t\t\trotateHandle.style.display = this.allowRotate ? '' : 'none';\n\t\t}\n\n\t\tcropHandles.forEach( ( handle ) => {\n\t\t\t( handle as HTMLElement ).style.display = this.allowCrop && this._interactionMode === 'crop' ? '' : 'none';\n\t\t} );\n\t}\n\n\tprivate bindEvents () : void {\n\t\tthis.handlesContainer.addEventListener( 'pointerdown', this.handlePointerDown );\n\t\tthis.wrapper.addEventListener( 'pointerdown', this.handleWrapperPointerDown );\n\t\tthis.wrapper.addEventListener( 'dblclick', this.handleWrapperDoubleClick );\n\t\tdocument.addEventListener( 'pointerdown', this.handleDocumentPointerDown, true );\n\n\t\t// Add listener for dragging the content/image itself\n\t\tthis.contentSlot.addEventListener( 'slotchange', () => {\n\t\t\tconst elements = this.contentSlot.assignedElements();\n\t\t\tif ( elements.length > 0 ) {\n\t\t\t\tconst content = elements[0] as HTMLElement;\n\t\t\t\tcontent.addEventListener( 'pointerdown', this.handleContentPointerDown );\n\t\t\t}\n\t\t} );\n\t}\n\n\tprivate unbindEvents () : void {\n\t\tthis.handlesContainer.removeEventListener( 'pointerdown', this.handlePointerDown );\n\t\tthis.wrapper.removeEventListener( 'pointerdown', this.handleWrapperPointerDown );\n\t\tthis.wrapper.removeEventListener( 'dblclick', this.handleWrapperDoubleClick );\n\t\tdocument.removeEventListener( 'pointerdown', this.handleDocumentPointerDown, true );\n\n\t\tconst elements = this.contentSlot.assignedElements();\n\t\tif ( elements.length > 0 ) {\n\t\t\tconst content = elements[0] as HTMLElement;\n\t\t\tcontent.removeEventListener( 'pointerdown', this.handleContentPointerDown );\n\t\t}\n\t}\n\n\tprivate handleWrapperDoubleClick = ( event : MouseEvent ) : void => {\n\t\tif ( this.disabled ) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis._setInteractionMode( 'crop' );\n\t};\n\n\tprivate handleDocumentPointerDown = ( event : PointerEvent ) : void => {\n\t\tif ( this._interactionMode !== 'crop' ) return;\n\t\tconst path = event.composedPath();\n\t\tif ( path.includes( this ) ) return;\n\t\tthis._setInteractionMode( 'transform' );\n\t};\n\n\tprivate _getContainerForMove () : HTMLElement {\n\t\treturn this.parentElement || ( this.offsetParent as HTMLElement ) || document.body;\n\t}\n\n\tprivate _ensureContainerPositionedForMove ( container : HTMLElement ) : void {\n\t\tconst computed = window.getComputedStyle( container );\n\t\tif ( computed.position !== 'static' ) return;\n\t\tcontainer.style.position = 'relative';\n\t}\n\n\tprivate _syncWrapperPositionFromLayout ( container : HTMLElement ) : void {\n\t\tconst containerRect = container.getBoundingClientRect();\n\t\tconst hostRect = this.getBoundingClientRect();\n\t\tconst scrollLeft = container.scrollLeft;\n\t\tconst scrollTop = container.scrollTop;\n\t\tconst originLeft = containerRect.left + container.clientLeft;\n\t\tconst originTop = containerRect.top + container.clientTop;\n\t\tthis.state.wrapperLeft = hostRect.left - originLeft + scrollLeft;\n\t\tthis.state.wrapperTop = hostRect.top - originTop + scrollTop;\n\t}\n\n\tprivate _ensureAbsolutePositionForMove ( container : HTMLElement ) : void {\n\t\tthis._ensureContainerPositionedForMove( container );\n\t\tthis._syncWrapperPositionFromLayout( container );\n\t\tthis.style.position = 'absolute';\n\t\tthis.style.left = `${this.state.wrapperLeft}px`;\n\t\tthis.style.top = `${this.state.wrapperTop}px`;\n\t\tthis.style.touchAction = 'none';\n\t}\n\n\tprivate handleWrapperPointerDown = ( event : PointerEvent ) : void => {\n\t\tif ( this.disabled ) return;\n\t\tif ( !this.allowDrag ) return;\n\t\tif ( this._interactionMode !== 'transform' ) return;\n\n\t\tconst target = event.target as HTMLElement;\n\t\tif ( target.closest( '[data-action]' ) ) return;\n\n\t\t// In transform mode, dragging anywhere moves the whole component\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tconst container = this._getContainerForMove();\n\t\tthis._dragMoveContainer = container;\n\t\tthis._ensureAbsolutePositionForMove( container );\n\t\tthis._syncWrapperPositionFromLayout( container );\n\n\t\tconst hostRect = this.getBoundingClientRect();\n\t\tthis._dragPointerOffsetX = event.clientX - hostRect.left;\n\t\tthis._dragPointerOffsetY = event.clientY - hostRect.top;\n\n\t\tthis.isDragging = true;\n\t\tthis.dragAction = 'move';\n\t\tthis.dragHandle = null;\n\t\tthis.dragStartX = event.clientX;\n\t\tthis.dragStartY = event.clientY;\n\n\t\tdocument.addEventListener( 'pointermove', this.handlePointerMove );\n\t\tdocument.addEventListener( 'pointerup', this.handlePointerUp );\n\t\tthis.dispatchEvent( new CustomEvent( 'rcw:move-start', { detail: { action: 'move' } } ) );\n\t};\n\n\tprivate handlePointerDown = ( event : PointerEvent ) : void => {\n\t\tif ( this.disabled ) return;\n\n\t\tconst target = event.target as HTMLElement;\n\t\tconst handle = target.closest( '[data-action]' ) as HTMLElement;\n\t\tif ( !handle ) return;\n\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tthis.dragAction = handle.dataset.action as 'scale' | 'crop' | 'rotate';\n\t\tif ( this.dragAction === 'crop' && this._interactionMode !== 'crop' ) return;\n\t\tif ( this.dragAction === 'rotate' && !this.allowRotate ) return;\n\t\tthis.isDragging = true;\n\t\tthis.dragHandle = handle.dataset.corner || handle.dataset.side || null;\n\t\tthis.dragStartX = event.clientX;\n\t\tthis.dragStartY = event.clientY;\n\t\tthis.dragStartWidth = this.state.width;\n\t\tthis.dragStartHeight = this.state.height;\n\t\tthis.dragStartContentLeft = this.state.contentLeft;\n\t\tthis.dragStartContentTop = this.state.contentTop;\n\n\t\t// Get current content dimensions (ignore transforms)\n\t\tif ( this.state.contentElement ) {\n\t\t\tthis.dragStartContentWidth = this.state.contentElement.offsetWidth;\n\t\t\tthis.dragStartContentHeight = this.state.contentElement.offsetHeight;\n\t\t}\n\n\t\tif ( this.dragAction === 'rotate' ) {\n\t\t\tconst wrapperRect = this.wrapper.getBoundingClientRect();\n\t\t\tthis._dragRotateCenterX = wrapperRect.left + wrapperRect.width / 2;\n\t\t\tthis._dragRotateCenterY = wrapperRect.top + wrapperRect.height / 2;\n\t\t\tthis._dragStartPointerAngle = Math.atan2( event.clientY - this._dragRotateCenterY, event.clientX - this._dragRotateCenterX );\n\t\t\tthis._dragStartRotation = this.state.rotation;\n\t\t}\n\n\t\tdocument.addEventListener( 'pointermove', this.handlePointerMove );\n\t\tdocument.addEventListener( 'pointerup', this.handlePointerUp );\n\n\t\tconst eventName = this.dragAction === 'scale'\n\t\t\t? 'rcw:scale-start'\n\t\t\t: this.dragAction === 'crop'\n\t\t\t? 'rcw:crop-start'\n\t\t\t: 'rcw:rotate-start';\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent( eventName, {\n\t\t\t\tdetail: { action: this.dragAction, handle: this.dragHandle },\n\t\t\t} ),\n\t\t);\n\t};\n\n\tprivate handleContentPointerDown = ( event : PointerEvent ) : void => {\n\t\tif ( this.disabled ) return;\n\t\tif ( this._interactionMode !== 'crop' ) return;\n\n\t\t// Only allow panning if content is larger than wrapper\n\t\tif ( !this.state.contentElement ) return;\n\n\t\t// Check if content is larger than wrapper in either dimension (ignore transforms)\n\t\tconst canPan = this.state.contentElement.offsetWidth > this.state.width || this.state.contentElement.offsetHeight > this.state.height;\n\t\tif ( !canPan ) return;\n\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tthis.isDragging = true;\n\t\tthis.dragAction = 'pan';\n\t\tthis.dragHandle = null;\n\t\tthis.dragStartX = event.clientX;\n\t\tthis.dragStartY = event.clientY;\n\t\tthis.dragStartContentLeft = this.state.contentLeft;\n\t\tthis.dragStartContentTop = this.state.contentTop;\n\n\t\tdocument.addEventListener( 'pointermove', this.handlePointerMove );\n\t\tdocument.addEventListener( 'pointerup', this.handlePointerUp );\n\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent( 'rcw:pan-start', {\n\t\t\t\tdetail: { action: 'pan' },\n\t\t\t} ),\n\t\t);\n\t};\n\n\tprivate handlePointerMove = ( event : PointerEvent ) : void => {\n\t\tif ( !this.isDragging ) return;\n\t\tif ( this.dragAction === 'move' ) {\n\t\t\tconst container = this._dragMoveContainer || this._getContainerForMove();\n\t\t\tconst containerRect = container.getBoundingClientRect();\n\t\t\tconst originLeft = containerRect.left + container.clientLeft;\n\t\t\tconst originTop = containerRect.top + container.clientTop;\n\t\t\tthis.state.wrapperLeft = event.clientX - originLeft + container.scrollLeft - this._dragPointerOffsetX;\n\t\t\tthis.state.wrapperTop = event.clientY - originTop + container.scrollTop - this._dragPointerOffsetY;\n\t\t\tthis.style.left = `${this.state.wrapperLeft}px`;\n\t\t\tthis.style.top = `${this.state.wrapperTop}px`;\n\t\t\tthis.dispatchChange( 'move' );\n\t\t\treturn;\n\t\t}\n\t\tif ( this.dragAction === 'rotate' ) {\n\t\t\tthis.handleRotate( event.clientX, event.clientY );\n\t\t\tthis.dispatchChange( 'rotate' );\n\t\t\treturn;\n\t\t}\n\n\t\tconst deltaX = event.clientX - this.dragStartX;\n\t\tconst deltaY = event.clientY - this.dragStartY;\n\n\t\tif ( this.dragAction === 'scale' ) {\n\t\t\tthis.handleScale( deltaX, deltaY, this.dragHandle! );\n\t\t} else if ( this.dragAction === 'crop' ) {\n\t\t\tthis.handleCrop( deltaX, deltaY, this.dragHandle! );\n\t\t} else if ( this.dragAction === 'pan' ) {\n\t\t\tthis.handlePan( deltaX, deltaY );\n\t\t}\n\n\t\tthis.dispatchChange( this.dragAction! );\n\t};\n\tprivate handleRotate ( pointerX : number, pointerY : number ) : void {\n\t\tconst pointerAngle = Math.atan2( pointerY - this._dragRotateCenterY, pointerX - this._dragRotateCenterX );\n\t\tconst deltaAngle = pointerAngle - this._dragStartPointerAngle;\n\t\tconst deltaDegrees = deltaAngle * ( 180 / Math.PI );\n\t\tthis.state.rotation = this._dragStartRotation + deltaDegrees;\n\t\tthis._applyWrapperTransform();\n\t}\n\n\tprivate handlePointerUp = () : void => {\n\t\tif ( !this.isDragging ) return;\n\n\t\tthis.isDragging = false;\n\t\tdocument.removeEventListener( 'pointermove', this.handlePointerMove );\n\t\tdocument.removeEventListener( 'pointerup', this.handlePointerUp );\n\n\t\tif ( this.dragAction ) {\n\t\t\tthis.dispatchChange( this.dragAction );\n\t\t}\n\n\t\tthis.dragAction = null;\n\t\tthis.dragHandle = null;\n\t\tthis._dragMoveContainer = null;\n\t};\n\n\tprivate handleScale ( deltaX : number, deltaY : number, handleCorner : string ) : void {\n\t\tif ( !this.state.contentElement ) return;\n\n\t\tlet newWidth = this.dragStartWidth;\n\t\tlet newHeight = this.dragStartHeight;\n\n\t\t// Scale both the wrapper AND the content together\n\t\tswitch ( handleCorner ) {\n\t\t\tcase 'br': // Bottom-right\n\t\t\t\tnewWidth = this.dragStartWidth + deltaX;\n\t\t\t\tnewHeight = this.dragStartHeight + deltaY;\n\t\t\t\tbreak;\n\t\t\tcase 'bl': // Bottom-left\n\t\t\t\tnewWidth = this.dragStartWidth - deltaX;\n\t\t\t\tnewHeight = this.dragStartHeight + deltaY;\n\t\t\t\tbreak;\n\t\t\tcase 'tr': // Top-right\n\t\t\t\tnewWidth = this.dragStartWidth + deltaX;\n\t\t\t\tnewHeight = this.dragStartHeight - deltaY;\n\t\t\t\tbreak;\n\t\t\tcase 'tl': // Top-left\n\t\t\t\tnewWidth = this.dragStartWidth - deltaX;\n\t\t\t\tnewHeight = this.dragStartHeight - deltaY;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Apply minimum constraints\n\t\tnewWidth = Math.max( this.state.minWidth, newWidth );\n\t\tnewHeight = Math.max( this.state.minHeight, newHeight );\n\n\t\t// Apply aspect ratio if set\n\t\tif ( this.state.aspectRatio ) {\n\t\t\tconst ratio = this.parseAspectRatio( this.state.aspectRatio );\n\t\t\tif ( ratio ) {\n\t\t\t\tconst widthBasedHeight = newWidth / ratio;\n\t\t\t\tconst heightBasedWidth = newHeight * ratio;\n\n\t\t\t\tif ( Math.abs( newWidth - this.dragStartWidth ) > Math.abs( newHeight - this.dragStartHeight ) ) {\n\t\t\t\t\tnewHeight = widthBasedHeight;\n\t\t\t\t} else {\n\t\t\t\t\tnewWidth = heightBasedWidth;\n\t\t\t\t}\n\n\t\t\t\tnewWidth = Math.max( this.state.minWidth, newWidth );\n\t\t\t\tnewHeight = Math.max( this.state.minHeight, newHeight );\n\t\t\t}\n\t\t}\n\n\t\t// Calculate scale ratio to maintain proportional resizing\n\t\tconst scaleX = newWidth / this.dragStartWidth;\n\t\tconst scaleY = newHeight / this.dragStartHeight;\n\t\tconst scale = Math.min( scaleX, scaleY ); // Use uniform scale\n\n\t\t// Update wrapper size\n\t\tthis.state.width = newWidth;\n\t\tthis.state.height = newHeight;\n\t\tthis.updateWrapperDimensions();\n\n\t\t// Update content size proportionally\n\t\tconst newContentWidth = this.dragStartContentWidth * scale;\n\t\tconst newContentHeight = this.dragStartContentHeight * scale;\n\n\t\tthis.state.contentElement.style.width = `${newContentWidth}px`;\n\t\tthis.state.contentElement.style.height = `${newContentHeight}px`;\n\n\t\t// Adjust content position proportionally to keep it centered relative to wrapper\n\t\tthis.state.contentLeft = this.dragStartContentLeft * scale;\n\t\tthis.state.contentTop = this.dragStartContentTop * scale;\n\n\t\t// Clamp content position after scaling\n\t\tthis.clampContentPosition();\n\t}\n\n\tprivate handleCrop ( deltaX : number, deltaY : number, side : string ) : void {\n\t\t// Crop resizes the wrapper (visible area), not the content\n\t\tlet newWidth = this.dragStartWidth;\n\t\tlet newHeight = this.dragStartHeight;\n\n\t\tswitch ( side ) {\n\t\t\tcase 'l': // Left - resize wrapper from left\n\t\t\t\tnewWidth = this.dragStartWidth - deltaX;\n\t\t\t\tbreak;\n\t\t\tcase 'r': // Right - resize wrapper from right\n\t\t\t\tnewWidth = this.dragStartWidth + deltaX;\n\t\t\t\tbreak;\n\t\t\tcase 't': // Top - resize wrapper from top\n\t\t\t\tnewHeight = this.dragStartHeight - deltaY;\n\t\t\t\tbreak;\n\t\t\tcase 'b': // Bottom - resize wrapper from bottom\n\t\t\t\tnewHeight = this.dragStartHeight + deltaY;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Apply minimum constraints\n\t\tnewWidth = Math.max( this.state.minWidth, newWidth );\n\t\tnewHeight = Math.max( this.state.minHeight, newHeight );\n\n\t\t// Apply aspect ratio if set\n\t\tif ( this.state.aspectRatio ) {\n\t\t\tconst ratio = this.parseAspectRatio( this.state.aspectRatio );\n\t\t\tif ( ratio ) {\n\t\t\t\tconst widthBasedHeight = newWidth / ratio;\n\t\t\t\tconst heightBasedWidth = newHeight * ratio;\n\n\t\t\t\tif ( side === 'l' || side === 'r' ) {\n\t\t\t\t\tnewHeight = widthBasedHeight;\n\t\t\t\t} else {\n\t\t\t\t\tnewWidth = heightBasedWidth;\n\t\t\t\t}\n\n\t\t\t\tnewWidth = Math.max( this.state.minWidth, newWidth );\n\t\t\t\tnewHeight = Math.max( this.state.minHeight, newHeight );\n\t\t\t}\n\t\t}\n\n\t\tthis.state.width = newWidth;\n\t\tthis.state.height = newHeight;\n\t\tthis.updateWrapperDimensions();\n\n\t\t// When cropping from left or top, adjust content position\n\t\tif ( side === 'l' ) {\n\t\t\tconst widthDiff = newWidth - this.dragStartWidth;\n\t\t\tthis.state.contentLeft = this.dragStartContentLeft - widthDiff;\n\t\t} else if ( side === 't' ) {\n\t\t\tconst heightDiff = newHeight - this.dragStartHeight;\n\t\t\tthis.state.contentTop = this.dragStartContentTop - heightDiff;\n\t\t}\n\n\t\t// Clamp content position\n\t\tthis.clampContentPosition();\n\t}\n\n\tprivate clampContentPosition () : void {\n\t\tif ( !this.state.contentElement ) return;\n\n\t\tconst contentWidth = this.state.contentElement.offsetWidth;\n\t\tconst contentHeight = this.state.contentElement.offsetHeight;\n\t\tconst minLeft = Math.min( 0, this.state.width - contentWidth );\n\t\tconst minTop = Math.min( 0, this.state.height - contentHeight );\n\n\t\tthis.state.contentLeft = Math.max( minLeft, Math.min( 0, this.state.contentLeft ) );\n\t\tthis.state.contentTop = Math.max( minTop, Math.min( 0, this.state.contentTop ) );\n\n\t\tthis.state.contentElement.style.left = `${this.state.contentLeft}px`;\n\t\tthis.state.contentElement.style.top = `${this.state.contentTop}px`;\n\t}\n\n\tprivate handlePan ( deltaX : number, deltaY : number ) : void {\n\t\tif ( !this.state.contentElement ) return;\n\n\t\t// Pan the content by moving its position\n\t\tthis.state.contentLeft = this.dragStartContentLeft + deltaX;\n\t\tthis.state.contentTop = this.dragStartContentTop + deltaY;\n\n\t\t// Clamp to prevent blank areas\n\t\tthis.clampContentPosition();\n\t}\n\n\tprivate parseAspectRatio ( ratio : string ) : number | null {\n\t\tconst parts = ratio.split( '/' );\n\t\tif ( parts.length === 2 ) {\n\t\t\tconst width = parseFloat( parts[0] );\n\t\t\tconst height = parseFloat( parts[1] );\n\t\t\tif ( !isNaN( width ) && !isNaN( height ) && height !== 0 ) {\n\t\t\t\treturn width / height;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate dispatchChange ( action : 'scale' | 'crop' | 'pan' | 'rotate' | 'move' ) : void {\n\t\tconst detail : ResizableCropEventDetail = {\n\t\t\twidth: this.state.width,\n\t\t\theight: this.state.height,\n\t\t\twrapperLeft: this.state.wrapperLeft,\n\t\t\twrapperTop: this.state.wrapperTop,\n\t\t\tcontentLeft: this.state.contentLeft,\n\t\t\tcontentTop: this.state.contentTop,\n\t\t\taction,\n\t\t\trotation: this.state.rotation,\n\t\t\thandle: this.dragHandle || undefined,\n\t\t};\n\n\t\tthis.dispatchEvent( new CustomEvent( 'rcw:change', { detail } ) );\n\n\t\t// Dispatch the onChange event with full values\n\t\tthis.dispatchOnChange();\n\t}\n\n\tprivate dispatchOnChange () : void {\n\t\tconst values = this.getValues();\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent( 'change', {\n\t\t\t\tdetail: values,\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t} ),\n\t\t);\n\t}\n\n\tprivate render () : void {\n\t\tthis.shadowRoot.innerHTML = `\n\t\t\t<style>\n\t\t\t\t:host {\n\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\tposition: relative;\n\t\t\t\t}\n\n\t\t\t\t#wrapper {\n\t\t\t\t\tposition: relative;\n\t\t\t\t\tborder: 2px solid #007bff;\n\t\t\t\t\tbox-sizing: border-box;\n\t\t\t\t\tbackground: rgba(0, 123, 255, 0.05);\n\t\t\t\t}\n\n\t\t\t\t#clipper {\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tleft: 0;\n\t\t\t\t\tright: 0;\n\t\t\t\t\tbottom: 0;\n\t\t\t\t\toverflow: hidden;\n\t\t\t\t}\n\n\t\t\t\t::slotted(*) {\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\tmax-width: none;\n\t\t\t\t\tmax-height: none;\n\t\t\t\t\tcursor: grab;\n\t\t\t\t}\n\n\t\t\t\t::slotted(*:active) {\n\t\t\t\t\tcursor: grabbing;\n\t\t\t\t}\n\n\t\t\t\t#handles-container {\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tleft: 0;\n\t\t\t\t\tright: 0;\n\t\t\t\t\tbottom: 0;\n\t\t\t\t\tpointer-events: none;\n\t\t\t\t}\n\n\t\t\t\t.handle {\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\tbackground: white;\n\t\t\t\t\tborder: 2px solid #007bff;\n\t\t\t\t\tpointer-events: auto;\n\t\t\t\t\ttouch-action: none;\n\t\t\t\t\tz-index: 1000;\n\t\t\t\t}\n\n\t\t\t\t.handle.rotate {\n\t\t\t\t\twidth: 10px;\n\t\t\t\t\theight: 10px;\n\t\t\t\t\tborder-radius: 50%;\n\t\t\t\t\ttop: -18px;\n\t\t\t\t\tleft: 50%;\n\t\t\t\t\ttransform: translateX(-50%);\n\t\t\t\t\tcursor: grab;\n\t\t\t\t}\n\n\t\t\t\t.handle.rotate:active {\n\t\t\t\t\tcursor: grabbing;\n\t\t\t\t}\n\n\t\t\t\t.handle.scale {\n\t\t\t\t\twidth: 12px;\n\t\t\t\t\theight: 12px;\n\t\t\t\t\tborder-radius: 50%;\n\t\t\t\t\tcursor: nwse-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.scale.tl {\n\t\t\t\t\ttop: -6px;\n\t\t\t\t\tleft: -6px;\n\t\t\t\t\tcursor: nwse-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.scale.tr {\n\t\t\t\t\ttop: -6px;\n\t\t\t\t\tright: -6px;\n\t\t\t\t\tcursor: nesw-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.scale.bl {\n\t\t\t\t\tbottom: -6px;\n\t\t\t\t\tleft: -6px;\n\t\t\t\t\tcursor: nesw-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.scale.br {\n\t\t\t\t\tbottom: -6px;\n\t\t\t\t\tright: -6px;\n\t\t\t\t\tcursor: nwse-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.crop {\n\t\t\t\t\tbackground: #007bff;\n\t\t\t\t\tcursor: move;\n\t\t\t\t}\n\n\t\t\t\t.handle.crop.t {\n\t\t\t\t\ttop: -2px;\n\t\t\t\t\tleft: 50%;\n\t\t\t\t\ttransform: translateX(-50%);\n\t\t\t\t\twidth: 40px;\n\t\t\t\t\theight: 4px;\n\t\t\t\t\tcursor: ns-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.crop.b {\n\t\t\t\t\tbottom: -2px;\n\t\t\t\t\tleft: 50%;\n\t\t\t\t\ttransform: translateX(-50%);\n\t\t\t\t\twidth: 40px;\n\t\t\t\t\theight: 4px;\n\t\t\t\t\tcursor: ns-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.crop.l {\n\t\t\t\t\tleft: -2px;\n\t\t\t\t\ttop: 50%;\n\t\t\t\t\ttransform: translateY(-50%);\n\t\t\t\t\twidth: 4px;\n\t\t\t\t\theight: 40px;\n\t\t\t\t\tcursor: ew-resize;\n\t\t\t\t}\n\n\t\t\t\t.handle.crop.r {\n\t\t\t\t\tright: -2px;\n\t\t\t\t\ttop: 50%;\n\t\t\t\t\ttransform: translateY(-50%);\n\t\t\t\t\twidth: 4px;\n\t\t\t\t\theight: 40px;\n\t\t\t\t\tcursor: ew-resize;\n\t\t\t\t}\n\n\t\t\t\t:host([disabled]) #wrapper {\n\t\t\t\t\topacity: 0.6;\n\t\t\t\t\tcursor: not-allowed;\n\t\t\t\t}\n\n\t\t\t\t:host([disabled]) .handle {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\t\t\t</style>\n\n\t\t\t<div id=\"wrapper\">\n\t\t\t\t<div id=\"clipper\">\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</div>\n\n\t\t\t\t<div id=\"handles-container\">\n\t\t\t\t\t<!-- Rotate handle (top-center) -->\n\t\t\t\t\t<div class=\"handle rotate\" data-action=\"rotate\"></div>\n\n\t\t\t\t\t<!-- Scale handle (only bottom-right corner) -->\n\t\t\t\t\t<div class=\"handle scale br\" data-action=\"scale\" data-corner=\"br\"></div>\n\n\t\t\t\t\t<!-- Crop handles (only right and bottom) -->\n\t\t\t\t\t<div class=\"handle crop b\" data-action=\"crop\" data-side=\"b\"></div>\n\t\t\t\t\t<div class=\"handle crop r\" data-action=\"crop\" data-side=\"r\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`;\n\t}\n\n\t// Public API - Getters and Setters\n\tget width () : number {\n\t\treturn this.state.width;\n\t}\n\n\tset width ( value : number ) {\n\t\tthis.setAttribute( 'width', String( value ) );\n\t}\n\n\tget height () : number {\n\t\treturn this.state.height;\n\t}\n\n\tset height ( value : number ) {\n\t\tthis.setAttribute( 'height', String( value ) );\n\t}\n\n\tget minWidth () : number {\n\t\treturn this.state.minWidth;\n\t}\n\n\tset minWidth ( value : number ) {\n\t\tthis.setAttribute( 'min-width', String( value ) );\n\t}\n\n\tget minHeight () : number {\n\t\treturn this.state.minHeight;\n\t}\n\n\tset minHeight ( value : number ) {\n\t\tthis.setAttribute( 'min-height', String( value ) );\n\t}\n\n\tget aspectRatio () : string | null {\n\t\treturn this.state.aspectRatio;\n\t}\n\n\tset aspectRatio ( value : string | null ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'aspect-ratio', value );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'aspect-ratio' );\n\t\t}\n\t}\n\n\tget disabled () : boolean {\n\t\treturn this.hasAttribute( 'disabled' );\n\t}\n\n\tset disabled ( value : boolean ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'disabled', '' );\n\t\t} else {\n\t\t\tthis.removeAttribute( 'disabled' );\n\t\t}\n\t}\n\n\tget allowCrop () : boolean {\n\t\treturn this.hasAttribute( 'allow-crop' ) ? this.getAttribute( 'allow-crop' ) !== 'false' : true;\n\t}\n\n\tset allowCrop ( value : boolean ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'allow-crop', 'true' );\n\t\t} else {\n\t\t\tthis.setAttribute( 'allow-crop', 'false' );\n\t\t}\n\t}\n\n\tget allowResize () : boolean {\n\t\treturn this.hasAttribute( 'allow-resize' ) ? this.getAttribute( 'allow-resize' ) !== 'false' : true;\n\t}\n\n\tset allowResize ( value : boolean ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'allow-resize', 'true' );\n\t\t} else {\n\t\t\tthis.setAttribute( 'allow-resize', 'false' );\n\t\t}\n\t}\n\n\tget allowRotate () : boolean {\n\t\treturn this.hasAttribute( 'allow-rotate' ) ? this.getAttribute( 'allow-rotate' ) !== 'false' : true;\n\t}\n\n\tset allowRotate ( value : boolean ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'allow-rotate', 'true' );\n\t\t} else {\n\t\t\tthis.setAttribute( 'allow-rotate', 'false' );\n\t\t}\n\t}\n\n\tget allowDrag () : boolean {\n\t\treturn this.hasAttribute( 'allow-drag' ) ? this.getAttribute( 'allow-drag' ) !== 'false' : true;\n\t}\n\n\tset allowDrag ( value : boolean ) {\n\t\tif ( value ) {\n\t\t\tthis.setAttribute( 'allow-drag', 'true' );\n\t\t} else {\n\t\t\tthis.setAttribute( 'allow-drag', 'false' );\n\t\t}\n\t}\n\n\t/**\n\t * Gets the current values including wrapper size, content size, position, and zoom level\n\t */\n\tpublic getValues () : ResizableCropperValues {\n\t\t// Keep wrapperLeft/wrapperTop in sync even when not moved yet.\n\t\tthis._syncWrapperPositionFromLayout( this._getContainerForMove() );\n\n\t\tconst contentWidth = this.state.contentElement?.offsetWidth || 0;\n\t\tconst contentHeight = this.state.contentElement?.offsetHeight || 0;\n\n\t\t// Calculate zoom based on current content size vs initial size\n\t\tconst zoom = this.initialContentWidth > 0 ? contentWidth / this.initialContentWidth : 1;\n\n\t\treturn {\n\t\t\twrapperWidth: this.state.width,\n\t\t\twrapperHeight: this.state.height,\n\t\t\twrapperLeft: this.state.wrapperLeft,\n\t\t\twrapperTop: this.state.wrapperTop,\n\t\t\tcontentWidth,\n\t\t\tcontentHeight,\n\t\t\tcontentLeft: this.state.contentLeft,\n\t\t\tcontentTop: this.state.contentTop,\n\t\t\tzoom,\n\t\t\trotation: this.state.rotation,\n\t\t};\n\t}\n\n\t/**\n\t * Sets the values to reproduce size, zoom and pan\n\t * @param values - The values to set\n\t */\n\tpublic setValues ( values : Partial<ResizableCropperValues> ) : void {\n\t\t// Set wrapper size\n\t\tif ( values.wrapperWidth !== undefined ) {\n\t\t\tthis.state.width = values.wrapperWidth;\n\t\t}\n\t\tif ( values.wrapperHeight !== undefined ) {\n\t\t\tthis.state.height = values.wrapperHeight;\n\t\t}\n\t\tthis.updateWrapperDimensions();\n\n\t\tif ( values.wrapperLeft !== undefined || values.wrapperTop !== undefined ) {\n\t\t\tconst container = this._getContainerForMove();\n\t\t\tthis._ensureAbsolutePositionForMove( container );\n\t\t\tif ( values.wrapperLeft !== undefined ) this.state.wrapperLeft = values.wrapperLeft;\n\t\t\tif ( values.wrapperTop !== undefined ) this.state.wrapperTop = values.wrapperTop;\n\t\t\tthis.style.left = `${this.state.wrapperLeft}px`;\n\t\t\tthis.style.top = `${this.state.wrapperTop}px`;\n\t\t}\n\n\t\tif ( values.rotation !== undefined ) {\n\t\t\tthis.state.rotation = values.rotation;\n\t\t\tthis._applyWrapperTransform();\n\t\t}\n\n\t\tif ( !this.state.contentElement ) {\n\t\t\tthis.dispatchOnChange();\n\t\t\treturn;\n\t\t}\n\n\t\t// Set content size based on zoom or explicit dimensions\n\t\tif ( values.zoom !== undefined && this.initialContentWidth > 0 ) {\n\t\t\tconst newContentWidth = this.initialContentWidth * values.zoom;\n\t\t\tconst newContentHeight = this.initialContentHeight * values.zoom;\n\t\t\tthis.state.contentElement.style.width = `${newContentWidth}px`;\n\t\t\tthis.state.contentElement.style.height = `${newContentHeight}px`;\n\t\t} else if ( values.contentWidth !== undefined ) {\n\t\t\tthis.state.contentElement.style.width = `${values.contentWidth}px`;\n\t\t\tif ( values.contentHeight !== undefined ) {\n\t\t\t\tthis.state.contentElement.style.height = `${values.contentHeight}px`;\n\t\t\t} else {\n\t\t\t\tthis.state.contentElement.style.height = 'auto';\n\t\t\t}\n\t\t}\n\n\t\t// Set content position\n\t\tif ( values.contentLeft !== undefined ) {\n\t\t\tthis.state.contentLeft = values.contentLeft;\n\t\t}\n\t\tif ( values.contentTop !== undefined ) {\n\t\t\tthis.state.contentTop = values.contentTop;\n\t\t}\n\n\t\t// Apply position and clamp\n\t\tthis.clampContentPosition();\n\n\t\t// Dispatch change event\n\t\tthis.dispatchOnChange();\n\t}\n\n\t/**\n\t * Gets a serializable state snapshot of the component including flags, constraints, mode,\n\t * and the current transform/crop values.\n\t */\n\tpublic getState () : ResizableCropperComponentState {\n\t\treturn {\n\t\t\tmode: this._interactionMode,\n\t\t\tdisabled: this.disabled,\n\t\t\tallowCrop: this.allowCrop,\n\t\t\tallowResize: this.allowResize,\n\t\t\tallowRotate: this.allowRotate,\n\t\t\tallowDrag: this.allowDrag,\n\t\t\tminWidth: this.minWidth,\n\t\t\tminHeight: this.minHeight,\n\t\t\taspectRatio: this.aspectRatio,\n\t\t\tvalues: this.getValues(),\n\t\t};\n\t}\n\n\t/**\n\t * Restores a state snapshot produced by getState().\n\t * @param state - State to restore\n\t */\n\tpublic setState ( state : Partial<ResizableCropperComponentState> ) : void {\n\t\tif ( state.disabled !== undefined ) this.disabled = state.disabled;\n\t\tif ( state.allowCrop !== undefined ) this.allowCrop = state.allowCrop;\n\t\tif ( state.allowResize !== undefined ) this.allowResize = state.allowResize;\n\t\tif ( state.allowRotate !== undefined ) this.allowRotate = state.allowRotate;\n\t\tif ( state.allowDrag !== undefined ) this.allowDrag = state.allowDrag;\n\t\tif ( state.minWidth !== undefined ) this.minWidth = state.minWidth;\n\t\tif ( state.minHeight !== undefined ) this.minHeight = state.minHeight;\n\t\tif ( state.aspectRatio !== undefined ) this.aspectRatio = state.aspectRatio;\n\t\tif ( state.mode !== undefined ) this._setInteractionMode( state.mode );\n\t\tif ( state.values ) this.setValues( state.values );\n\t}\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nexport const defineResizableCropper = ( tagName : string = 'liwe3-resizable-cropper' ) : void => {\n\tif ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {\n\t\tcustomElements.define( tagName, ResizableCropperElement );\n\t}\n};\n\n// Auto-register with default tag name\ndefineResizableCropper();\n"],"names":["ResizableCropperElement","event","container","hostRect","handle","wrapperRect","eventName","containerRect","originLeft","originTop","deltaX","deltaY","name","oldValue","newValue","mode","nodes","width","height","scaleHandle","rotateHandle","cropHandles","elements","scrollLeft","scrollTop","pointerX","pointerY","deltaDegrees","handleCorner","newWidth","newHeight","ratio","widthBasedHeight","heightBasedWidth","scaleX","scaleY","scale","newContentWidth","newContentHeight","side","widthDiff","heightDiff","contentWidth","contentHeight","minLeft","minTop","parts","action","detail","values","value","zoom","state","defineResizableCropper","tagName"],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartSelect Web Component
|
|
3
|
+
* A customizable select dropdown with search, multi-select, and keyboard navigation
|
|
4
|
+
*/
|
|
5
|
+
export type SelectOption = {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class SmartSelectElement extends HTMLElement {
|
|
11
|
+
shadowRoot: ShadowRoot;
|
|
12
|
+
private isOpen;
|
|
13
|
+
private selectedOptions;
|
|
14
|
+
private filteredOptions;
|
|
15
|
+
private focusedIndex;
|
|
16
|
+
private searchValue;
|
|
17
|
+
private keyboardNavigating;
|
|
18
|
+
private keyboardTimer?;
|
|
19
|
+
constructor();
|
|
20
|
+
static get observedAttributes(): string[];
|
|
21
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
22
|
+
get multiple(): boolean;
|
|
23
|
+
set multiple(value: boolean);
|
|
24
|
+
get searchable(): boolean;
|
|
25
|
+
set searchable(value: boolean);
|
|
26
|
+
get placeholder(): string;
|
|
27
|
+
set placeholder(value: string);
|
|
28
|
+
get disabled(): boolean;
|
|
29
|
+
set disabled(value: boolean);
|
|
30
|
+
get value(): string | string[];
|
|
31
|
+
set value(val: string | string[]);
|
|
32
|
+
get options(): SelectOption[];
|
|
33
|
+
set options(opts: SelectOption[]);
|
|
34
|
+
/**
|
|
35
|
+
* Opens the dropdown
|
|
36
|
+
*/
|
|
37
|
+
open(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Closes the dropdown
|
|
40
|
+
*/
|
|
41
|
+
close(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Toggles the dropdown open/closed state
|
|
44
|
+
*/
|
|
45
|
+
toggle(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Selects an option by its value
|
|
48
|
+
*/
|
|
49
|
+
selectOption(value: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Deselects an option by its value
|
|
52
|
+
*/
|
|
53
|
+
deselectOption(value: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Returns an array of currently selected options
|
|
56
|
+
*/
|
|
57
|
+
getSelectedOptions(): SelectOption[];
|
|
58
|
+
/**
|
|
59
|
+
* Sets the options for the select component
|
|
60
|
+
*/
|
|
61
|
+
setOptions(options: SelectOption[]): void;
|
|
62
|
+
/**
|
|
63
|
+
* Handles search functionality
|
|
64
|
+
*/
|
|
65
|
+
private handleSearch;
|
|
66
|
+
/**
|
|
67
|
+
* Updates the visual focus state without full re-render
|
|
68
|
+
*/
|
|
69
|
+
private updateFocusedOption;
|
|
70
|
+
/**
|
|
71
|
+
* Scrolls the focused option into view
|
|
72
|
+
*/
|
|
73
|
+
private scrollToFocusedOption;
|
|
74
|
+
/**
|
|
75
|
+
* Calculates the optimal dropdown position based on viewport constraints
|
|
76
|
+
*/
|
|
77
|
+
private _calculateDropdownPosition;
|
|
78
|
+
/**
|
|
79
|
+
* Updates dropdown position using fixed positioning relative to viewport
|
|
80
|
+
*/
|
|
81
|
+
private _updateDropdownPosition;
|
|
82
|
+
/**
|
|
83
|
+
* Handles keyboard navigation
|
|
84
|
+
*/
|
|
85
|
+
private handleKeydown;
|
|
86
|
+
/**
|
|
87
|
+
* Binds all event listeners
|
|
88
|
+
*/
|
|
89
|
+
private bindEvents;
|
|
90
|
+
/**
|
|
91
|
+
* Renders the component
|
|
92
|
+
*/
|
|
93
|
+
private render;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Conditionally defines the custom element if in a browser environment.
|
|
97
|
+
*/
|
|
98
|
+
declare const defineSmartSelect: (tagName?: string) => void;
|
|
99
|
+
export { defineSmartSelect };
|
|
100
|
+
//# sourceMappingURL=SmartSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartSelect.d.ts","sourceRoot":"","sources":["../src/SmartSelect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,WAAW;IACzC,UAAU,EAAE,UAAU,CAAC;IAC/B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,aAAa,CAAC,CAAS;;IAe/B,MAAM,KAAK,kBAAkB,IAAK,MAAM,EAAE,CAEzC;IAED,wBAAwB,CAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAI,IAAI;IASjG,IAAI,QAAQ,IAAK,OAAO,CAEvB;IAED,IAAI,QAAQ,CAAG,KAAK,EAAE,OAAO,EAM5B;IAED,IAAI,UAAU,IAAK,OAAO,CAEzB;IAED,IAAI,UAAU,CAAG,KAAK,EAAE,OAAO,EAM9B;IAED,IAAI,WAAW,IAAK,MAAM,CAEzB;IAED,IAAI,WAAW,CAAG,KAAK,EAAE,MAAM,EAE9B;IAED,IAAI,QAAQ,IAAK,OAAO,CAEvB;IAED,IAAI,QAAQ,CAAG,KAAK,EAAE,OAAO,EAM5B;IAED,IAAI,KAAK,IAAK,MAAM,GAAG,MAAM,EAAE,CAK9B;IAED,IAAI,KAAK,CAAG,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAQjC;IAED,IAAI,OAAO,IAAK,YAAY,EAAE,CAW7B;IAED,IAAI,OAAO,CAAG,IAAI,EAAE,YAAY,EAAE,EAEjC;IAED;;OAEG;IACH,IAAI,IAAK,IAAI;IAyBb;;OAEG;IACH,KAAK,IAAK,IAAI;IAuBd;;OAEG;IACH,MAAM,IAAK,IAAI;IAQf;;OAEG;IACH,YAAY,CAAG,KAAK,EAAE,MAAM,GAAI,IAAI;IAiBpC;;OAEG;IACH,cAAc,CAAG,KAAK,EAAE,MAAM,GAAI,IAAI;IAMtC;;OAEG;IACH,kBAAkB,IAAK,YAAY,EAAE;IAIrC;;OAEG;IACH,UAAU,CAAG,OAAO,EAAE,YAAY,EAAE,GAAI,IAAI;IAO5C;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAuB7B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;OAEG;IACH,OAAO,CAAC,aAAa;IA0FrB;;OAEG;IACH,OAAO,CAAC,UAAU;IA4FlB;;OAEG;IACH,OAAO,CAAC,MAAM;CAqQf;AAED;;GAEG;AACH,QAAA,MAAM,iBAAiB,GAAK,UAAS,MAAuB,KAAI,IAI/D,CAAC;AAKF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
package/dist/SmartSelect.js
CHANGED
|
@@ -396,6 +396,42 @@ class g extends HTMLElement {
|
|
|
396
396
|
color: var(--option-selected-color, #1976d2);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
.option-content {
|
|
400
|
+
display: flex;
|
|
401
|
+
align-items: center;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.option-image {
|
|
405
|
+
width: 24px;
|
|
406
|
+
height: 24px;
|
|
407
|
+
border-radius: 50%;
|
|
408
|
+
object-fit: cover;
|
|
409
|
+
margin-right: 8px;
|
|
410
|
+
flex-shrink: 0;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.tag-image {
|
|
414
|
+
width: 16px;
|
|
415
|
+
height: 16px;
|
|
416
|
+
border-radius: 50%;
|
|
417
|
+
object-fit: cover;
|
|
418
|
+
margin-right: 4px;
|
|
419
|
+
flex-shrink: 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.single-value {
|
|
423
|
+
display: flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
gap: 8px;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.single-value-image {
|
|
429
|
+
width: 20px;
|
|
430
|
+
height: 20px;
|
|
431
|
+
border-radius: 50%;
|
|
432
|
+
object-fit: cover;
|
|
433
|
+
}
|
|
434
|
+
|
|
399
435
|
.no-options {
|
|
400
436
|
padding: 8px 12px;
|
|
401
437
|
color: var(--no-options-color, #6c757d);
|
|
@@ -408,10 +444,14 @@ class g extends HTMLElement {
|
|
|
408
444
|
<div class="selected-content">
|
|
409
445
|
${this.multiple && this.selectedOptions.length > 0 ? this.selectedOptions.map((s) => `
|
|
410
446
|
<span class="tag">
|
|
447
|
+
${s.image ? `<img src="${s.image}" class="tag-image" alt="">` : ""}
|
|
411
448
|
${s.label}
|
|
412
449
|
<span class="remove-tag" data-value="${s.value}">×</span>
|
|
413
450
|
</span>
|
|
414
|
-
`).join("") : `<
|
|
451
|
+
`).join("") : this.selectedOptions.length > 0 ? `<div class="single-value">
|
|
452
|
+
${this.selectedOptions[0].image ? `<img src="${this.selectedOptions[0].image}" class="single-value-image" alt="">` : ""}
|
|
453
|
+
<span>${this.selectedOptions[0].label}</span>
|
|
454
|
+
</div>` : `<span>${t}</span>`}
|
|
415
455
|
</div>
|
|
416
456
|
<div class="arrow ${this.isOpen ? "open" : ""}"></div>
|
|
417
457
|
</div>
|
|
@@ -432,7 +472,10 @@ class g extends HTMLElement {
|
|
|
432
472
|
class="option ${this.selectedOptions.find((i) => i.value === s.value) ? "selected" : ""} ${o === this.focusedIndex ? "focused" : ""}"
|
|
433
473
|
data-value="${s.value}"
|
|
434
474
|
>
|
|
435
|
-
|
|
475
|
+
<div class="option-content">
|
|
476
|
+
${s.image ? `<img src="${s.image}" class="option-image" alt="">` : ""}
|
|
477
|
+
<span>${s.label}</span>
|
|
478
|
+
</div>
|
|
436
479
|
</div>
|
|
437
480
|
`).join("") : '<div class="no-options">No options available</div>'}
|
|
438
481
|
</div>
|
package/dist/SmartSelect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartSelect.js","sources":["../src/SmartSelect.ts"],"sourcesContent":["/**\n * SmartSelect Web Component\n * A customizable select dropdown with search, multi-select, and keyboard navigation\n */\n\nexport type SelectOption = {\n value: string;\n label: string;\n};\n\nexport class SmartSelectElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private isOpen: boolean = false;\n private selectedOptions: SelectOption[] = [];\n private filteredOptions: SelectOption[] = [];\n private focusedIndex: number = -1;\n private searchValue: string = '';\n private keyboardNavigating: boolean = false;\n private keyboardTimer?: number;\n\n constructor () {\n super();\n this.attachShadow( { mode: 'open' } );\n\n // Make component focusable\n if ( !this.hasAttribute( 'tabindex' ) ) {\n this.setAttribute( 'tabindex', '0' );\n }\n\n this.render();\n this.bindEvents();\n }\n\n static get observedAttributes (): string[] {\n return [ 'multiple', 'searchable', 'placeholder', 'disabled', 'value', 'options' ];\n }\n\n attributeChangedCallback ( name: string, oldValue: string | null, newValue: string | null ): void {\n if ( oldValue !== newValue ) {\n if ( name === 'options' ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n }\n }\n\n get multiple (): boolean {\n return this.hasAttribute( 'multiple' );\n }\n\n set multiple ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'multiple', '' );\n } else {\n this.removeAttribute( 'multiple' );\n }\n }\n\n get searchable (): boolean {\n return this.hasAttribute( 'searchable' );\n }\n\n set searchable ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'searchable', '' );\n } else {\n this.removeAttribute( 'searchable' );\n }\n }\n\n get placeholder (): string {\n return this.getAttribute( 'placeholder' ) || 'Select an option';\n }\n\n set placeholder ( value: string ) {\n this.setAttribute( 'placeholder', value );\n }\n\n get disabled (): boolean {\n return this.hasAttribute( 'disabled' );\n }\n\n set disabled ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'disabled', '' );\n } else {\n this.removeAttribute( 'disabled' );\n }\n }\n\n get value (): string | string[] {\n if ( this.multiple ) {\n return this.selectedOptions.map( opt => opt.value );\n }\n return this.selectedOptions.length > 0 ? this.selectedOptions[ 0 ].value : '';\n }\n\n set value ( val: string | string[] ) {\n if ( this.multiple && Array.isArray( val ) ) {\n this.selectedOptions = this.options.filter( opt => val.includes( opt.value ) );\n } else {\n const option = this.options.find( opt => opt.value === val );\n this.selectedOptions = option ? [ option ] : [];\n }\n this.render();\n }\n\n get options (): SelectOption[] {\n const optionsAttr = this.getAttribute( 'options' );\n if ( optionsAttr ) {\n try {\n return JSON.parse( optionsAttr );\n } catch ( e ) {\n console.error( 'Invalid options format:', e );\n return [];\n }\n }\n return [];\n }\n\n set options ( opts: SelectOption[] ) {\n this.setAttribute( 'options', JSON.stringify( opts ) );\n }\n\n /**\n * Opens the dropdown\n */\n open (): void {\n if ( this.disabled ) return;\n this.isOpen = true;\n this.focusedIndex = -1;\n if ( this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n\n // Update dropdown position based on viewport\n this._updateDropdownPosition();\n\n // Focus search input if searchable\n if ( this.searchable ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n }\n } );\n }\n\n this.dispatchEvent( new CustomEvent( 'open' ) );\n }\n\n /**\n * Closes the dropdown\n */\n close (): void {\n this.isOpen = false;\n this.focusedIndex = -1;\n this.searchValue = '';\n\n // Reset filtered options when closing\n if ( this.searchable && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Clear any inline positioning styles\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( dropdown ) {\n dropdown.style.top = '';\n dropdown.style.left = '';\n dropdown.style.width = '';\n dropdown.style.maxHeight = '';\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'close' ) );\n }\n\n /**\n * Toggles the dropdown open/closed state\n */\n toggle (): void {\n if ( this.isOpen ) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Selects an option by its value\n */\n selectOption ( value: string ): void {\n const option = this.options.find( opt => opt.value === value );\n if ( !option ) return;\n\n if ( this.multiple ) {\n if ( !this.selectedOptions.find( opt => opt.value === value ) ) {\n this.selectedOptions.push( option );\n }\n } else {\n this.selectedOptions = [ option ];\n this.close();\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Deselects an option by its value\n */\n deselectOption ( value: string ): void {\n this.selectedOptions = this.selectedOptions.filter( opt => opt.value !== value );\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Returns an array of currently selected options\n */\n getSelectedOptions (): SelectOption[] {\n return [ ...this.selectedOptions ];\n }\n\n /**\n * Sets the options for the select component\n */\n setOptions ( options: SelectOption[] ): void {\n this.options = options;\n this.filteredOptions = [ ...options ];\n this.selectedOptions = [];\n this.render();\n }\n\n /**\n * Handles search functionality\n */\n private handleSearch ( query: string ): void {\n this.searchValue = query;\n this.filteredOptions = this.options.filter( option =>\n option.label.toLowerCase().includes( query.toLowerCase() )\n );\n this.focusedIndex = -1;\n this.render();\n this.dispatchEvent( new CustomEvent( 'search', { detail: { query } } ) );\n }\n\n /**\n * Updates the visual focus state without full re-render\n */\n private updateFocusedOption (): void {\n const options = this.shadowRoot.querySelectorAll( '.option' );\n\n // Remove focused class from all options\n options.forEach( option => option.classList.remove( 'focused' ) );\n\n // Add focused class to current option\n if ( this.focusedIndex >= 0 && this.focusedIndex < options.length ) {\n options[ this.focusedIndex ].classList.add( 'focused' );\n }\n\n this.scrollToFocusedOption();\n }\n\n /**\n * Scrolls the focused option into view\n */\n private scrollToFocusedOption (): void {\n if ( this.focusedIndex < 0 ) return;\n\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n const focusedOption = this.shadowRoot.querySelector( '.option.focused' ) as HTMLElement;\n\n if ( dropdown && focusedOption ) {\n const dropdownRect = dropdown.getBoundingClientRect();\n const optionRect = focusedOption.getBoundingClientRect();\n\n // Check if option is above visible area\n if ( optionRect.top < dropdownRect.top ) {\n dropdown.scrollTop -= ( dropdownRect.top - optionRect.top );\n }\n // Check if option is below visible area\n else if ( optionRect.bottom > dropdownRect.bottom ) {\n dropdown.scrollTop += ( optionRect.bottom - dropdownRect.bottom );\n }\n }\n } );\n }\n\n /**\n * Calculates the optimal dropdown position based on viewport constraints\n */\n private _calculateDropdownPosition (): { top: number; left: number; width: number; maxHeight: number; } | null {\n const trigger = this.shadowRoot.querySelector( '.select-trigger' ) as HTMLElement;\n if ( !trigger ) return null;\n\n const triggerRect = trigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n const dropdownMaxHeight = 200;\n const dropdownPadding = 10;\n const margin = 2;\n\n // Calculate available space\n const spaceBelow = viewportHeight - triggerRect.bottom;\n const spaceAbove = triggerRect.top;\n\n // Determine if dropdown should open upward\n const shouldOpenUpward = spaceBelow < dropdownMaxHeight + dropdownPadding && spaceAbove > spaceBelow;\n\n // Calculate dimensions\n const width = triggerRect.width;\n const left = Math.max( 0, Math.min( triggerRect.left, viewportWidth - width ) );\n\n let top: number;\n let maxHeight: number;\n\n if ( shouldOpenUpward ) {\n // Position above the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceAbove - dropdownPadding );\n top = triggerRect.top - maxHeight - margin;\n } else {\n // Position below the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceBelow - dropdownPadding );\n top = triggerRect.bottom + margin;\n }\n\n return {\n top: Math.max( 0, top ),\n left,\n width,\n maxHeight: Math.max( 100, maxHeight ) // Ensure minimum height\n };\n }\n\n /**\n * Updates dropdown position using fixed positioning relative to viewport\n */\n private _updateDropdownPosition (): void {\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( !dropdown ) return;\n\n const position = this._calculateDropdownPosition();\n if ( !position ) return;\n\n // Apply calculated position as inline styles\n dropdown.style.top = `${ position.top }px`;\n dropdown.style.left = `${ position.left }px`;\n dropdown.style.width = `${ position.width }px`;\n dropdown.style.maxHeight = `${ position.maxHeight }px`;\n } );\n }\n\n /**\n * Handles keyboard navigation\n */\n private handleKeydown ( event: KeyboardEvent ): void {\n if ( this.disabled ) return;\n\n // Prevent double execution if event has already been handled\n if ( ( event as any )._smartSelectHandled ) return;\n ( event as any )._smartSelectHandled = true;\n\n switch ( event.key ) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( !this.isOpen ) {\n this.open();\n } else {\n // If searchable and search input is focused, move to first option\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n this.focusedIndex = 0;\n searchInput.blur(); // Blur search input to allow normal navigation\n // Focus the component to ensure it receives keyboard events\n this.focus();\n this.updateFocusedOption();\n return;\n }\n // Navigate through options\n const newIndex = Math.min( this.focusedIndex + 1, this.filteredOptions.length - 1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( this.isOpen ) {\n // If at first option and searchable, focus search input\n if ( this.focusedIndex === 0 && this.searchable ) {\n this.focusedIndex = -1;\n this.updateFocusedOption();\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n return;\n }\n // If searchable and search input is focused, do nothing\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n return;\n }\n // Navigate through options\n const newIndex = Math.max( this.focusedIndex - 1, -1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'Enter':\n event.preventDefault();\n if ( this.isOpen && this.focusedIndex >= 0 && this.focusedIndex < this.filteredOptions.length ) {\n this.selectOption( this.filteredOptions[ this.focusedIndex ].value );\n } else if ( !this.isOpen ) {\n this.open();\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.close();\n break;\n\n case 'Tab':\n this.close();\n break;\n }\n }\n\n /**\n * Binds all event listeners\n */\n private bindEvents (): void {\n // Listen for keydown events on both the component and shadow root\n const keydownHandler = this.handleKeydown.bind( this );\n this.addEventListener( 'keydown', keydownHandler );\n this.shadowRoot.addEventListener( 'keydown', keydownHandler as EventListener );\n\n // Use event delegation on the shadow root\n this.shadowRoot.addEventListener( 'click', ( e ) => {\n e.stopPropagation();\n const target = e.target as HTMLElement;\n\n if ( target.closest( '.remove-tag' ) ) {\n const value = ( target.closest( '.remove-tag' ) as HTMLElement ).dataset.value;\n if ( value ) this.deselectOption( value );\n } else if ( target.closest( '.option' ) ) {\n const value = ( target.closest( '.option' ) as HTMLElement ).dataset.value;\n if ( value ) this.selectOption( value );\n } else if ( target.closest( '.select-trigger' ) ) {\n this.toggle();\n }\n } );\n\n // Handle mouse hover on options to update focused index\n this.shadowRoot.addEventListener( 'mouseover', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.option' ) ) {\n const option = target.closest( '.option' ) as HTMLElement;\n const options = Array.from( this.shadowRoot.querySelectorAll( '.option' ) );\n const newFocusedIndex = options.indexOf( option );\n\n // Only update if the focused index actually changed\n if ( this.focusedIndex !== newFocusedIndex ) {\n // Remove focused class from current option\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n\n // Add focused class to new option\n option.classList.add( 'focused' );\n this.focusedIndex = newFocusedIndex;\n }\n }\n } );\n\n // Handle mouse leaving dropdown to clear focus\n this.shadowRoot.addEventListener( 'mouseleave', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.dropdown' ) ) {\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n this.focusedIndex = -1;\n }\n } );\n\n // Handle search input\n this.shadowRoot.addEventListener( 'input', ( e ) => {\n const target = e.target as HTMLInputElement;\n if ( target.classList.contains( 'search-input' ) ) {\n this.handleSearch( target.value );\n }\n } );\n\n // Close dropdown when clicking outside\n document.addEventListener( 'click', ( e ) => {\n if ( !this.contains( e.target as Node ) ) {\n this.close();\n }\n } );\n\n // Update dropdown position on window resize or scroll\n window.addEventListener( 'resize', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n } );\n\n window.addEventListener( 'scroll', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n }, true ); // Use capture to catch all scroll events\n }\n\n /**\n * Renders the component\n */\n private render (): void {\n // Initialize filteredOptions if not set\n if ( this.filteredOptions.length === 0 && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Remember if search input was focused before render\n const wasSearchFocused = this.shadowRoot.querySelector( '.search-input' ) === this.shadowRoot.activeElement;\n\n const displayText = this.selectedOptions.length > 0\n ? ( this.multiple\n ? `${ this.selectedOptions.length } selected`\n : this.selectedOptions[ 0 ].label )\n : this.placeholder;\n\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: inline-block;\n position: relative;\n min-width: 200px;\n font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--font-size, 14px);\n outline: none;\n }\n\n :host(:focus) .select-trigger {\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n :host([disabled]) {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .select-container {\n position: relative;\n }\n\n .select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--padding, 8px 12px);\n border: var(--border, 1px solid #ccc);\n border-radius: var(--border-radius, 4px);\n background: var(--background, white);\n cursor: pointer;\n min-height: 36px;\n box-sizing: border-box;\n color: #333;\n user-select: none;\n }\n\n .select-trigger:focus {\n outline: none;\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n .select-trigger[disabled] {\n cursor: not-allowed;\n }\n\n .selected-content {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n flex: 1;\n }\n\n .tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--tag-background, #e9ecef);\n border-radius: var(--tag-border-radius, 12px);\n font-size: 12px;\n color: var(--tag-color, #495057);\n user-select: none;\n }\n\n .remove-tag {\n cursor: pointer;\n color: var(--remove-color, #6c757d);\n font-weight: bold;\n font-size: 14px;\n }\n\n .remove-tag:hover {\n color: var(--remove-hover-color, #dc3545);\n }\n\n .arrow {\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 5px solid var(--arrow-color, #666);\n transition: transform 0.2s;\n }\n\n .arrow.open {\n transform: rotate(180deg);\n }\n\n .dropdown {\n position: fixed;\n z-index: 99999;\n background: var(--dropdown-background, white);\n border: var(--dropdown-border, 1px solid #ccc);\n border-radius: var(--dropdown-border-radius, 4px);\n box-shadow: var(--dropdown-shadow, 0 2px 8px rgba(0, 0, 0, 0.1));\n max-height: 200px;\n overflow-y: auto;\n scroll-behavior: smooth;\n color: #333;\n }\n\n .search-input {\n width: 100%;\n padding: 8px 12px;\n border: none;\n border-bottom: 1px solid #eee;\n font-size: 14px;\n outline: none;\n box-sizing: border-box;\n }\n\n .option {\n padding: 8px 12px;\n cursor: pointer;\n color: var(--option-color, #333);\n transition: background-color 0.2s;\n user-select: none;\n }\n\n .option:hover {\n background-color: var(--option-hover-background, #f8f9fa);\n }\n\n .option.focused {\n background-color: var(--option-focused-background, #007bff);\n color: var(--option-focused-color, white);\n }\n\n .option.selected {\n background-color: var(--option-selected-background, #e3f2fd);\n color: var(--option-selected-color, #1976d2);\n }\n\n .no-options {\n padding: 8px 12px;\n color: var(--no-options-color, #6c757d);\n font-style: italic;\n }\n </style>\n\n <div class=\"select-container\">\n <div class=\"select-trigger\" tabindex=\"-1\">\n <div class=\"selected-content\">\n ${ this.multiple && this.selectedOptions.length > 0\n ? this.selectedOptions.map( option => `\n <span class=\"tag\">\n ${ option.label }\n <span class=\"remove-tag\" data-value=\"${ option.value }\">×</span>\n </span>\n `).join( '' )\n : `<span>${ displayText }</span>`\n }\n </div>\n <div class=\"arrow ${ this.isOpen ? 'open' : '' }\"></div>\n </div>\n\n ${ this.isOpen ? `\n <div class=\"dropdown\">\n ${ this.searchable ? `\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search options...\"\n value=\"${ this.searchValue }\"\n >\n ` : '' }\n\n ${ this.filteredOptions.length > 0\n ? this.filteredOptions.map( ( option, index ) => `\n <div\n class=\"option ${ this.selectedOptions.find( selected => selected.value === option.value ) ? 'selected' : '' } ${ index === this.focusedIndex ? 'focused' : '' }\"\n data-value=\"${ option.value }\"\n >\n ${ option.label }\n </div>\n `).join( '' )\n : '<div class=\"no-options\">No options available</div>'\n }\n </div>\n ` : '' }\n </div>\n `;\n\n // Re-focus search input if it was previously focused\n if ( wasSearchFocused && this.searchable && this.isOpen ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n // Restore cursor position to the end\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n }\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst defineSmartSelect = ( tagName: string = 'liwe3-select' ): void => {\n if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {\n customElements.define( tagName, SmartSelectElement );\n }\n};\n\n// Auto-register with default tag name\ndefineSmartSelect();\n\nexport { defineSmartSelect };\n"],"names":["SmartSelectElement","name","oldValue","newValue","value","opt","val","option","optionsAttr","e","opts","searchInput","dropdown","options","query","focusedOption","dropdownRect","optionRect","trigger","triggerRect","viewportHeight","viewportWidth","dropdownMaxHeight","dropdownPadding","margin","spaceBelow","spaceAbove","shouldOpenUpward","width","left","top","maxHeight","position","event","newIndex","keydownHandler","target","newFocusedIndex","currentFocused","wasSearchFocused","displayText","index","selected","defineSmartSelect","tagName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SmartSelect.js","sources":["../src/SmartSelect.ts"],"sourcesContent":["/**\n * SmartSelect Web Component\n * A customizable select dropdown with search, multi-select, and keyboard navigation\n */\n\nexport type SelectOption = {\n value: string;\n label: string;\n image?: string;\n};\n\nexport class SmartSelectElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private isOpen: boolean = false;\n private selectedOptions: SelectOption[] = [];\n private filteredOptions: SelectOption[] = [];\n private focusedIndex: number = -1;\n private searchValue: string = '';\n private keyboardNavigating: boolean = false;\n private keyboardTimer?: number;\n\n constructor () {\n super();\n this.attachShadow( { mode: 'open' } );\n\n // Make component focusable\n if ( !this.hasAttribute( 'tabindex' ) ) {\n this.setAttribute( 'tabindex', '0' );\n }\n\n this.render();\n this.bindEvents();\n }\n\n static get observedAttributes (): string[] {\n return [ 'multiple', 'searchable', 'placeholder', 'disabled', 'value', 'options' ];\n }\n\n attributeChangedCallback ( name: string, oldValue: string | null, newValue: string | null ): void {\n if ( oldValue !== newValue ) {\n if ( name === 'options' ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n }\n }\n\n get multiple (): boolean {\n return this.hasAttribute( 'multiple' );\n }\n\n set multiple ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'multiple', '' );\n } else {\n this.removeAttribute( 'multiple' );\n }\n }\n\n get searchable (): boolean {\n return this.hasAttribute( 'searchable' );\n }\n\n set searchable ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'searchable', '' );\n } else {\n this.removeAttribute( 'searchable' );\n }\n }\n\n get placeholder (): string {\n return this.getAttribute( 'placeholder' ) || 'Select an option';\n }\n\n set placeholder ( value: string ) {\n this.setAttribute( 'placeholder', value );\n }\n\n get disabled (): boolean {\n return this.hasAttribute( 'disabled' );\n }\n\n set disabled ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'disabled', '' );\n } else {\n this.removeAttribute( 'disabled' );\n }\n }\n\n get value (): string | string[] {\n if ( this.multiple ) {\n return this.selectedOptions.map( opt => opt.value );\n }\n return this.selectedOptions.length > 0 ? this.selectedOptions[ 0 ].value : '';\n }\n\n set value ( val: string | string[] ) {\n if ( this.multiple && Array.isArray( val ) ) {\n this.selectedOptions = this.options.filter( opt => val.includes( opt.value ) );\n } else {\n const option = this.options.find( opt => opt.value === val );\n this.selectedOptions = option ? [ option ] : [];\n }\n this.render();\n }\n\n get options (): SelectOption[] {\n const optionsAttr = this.getAttribute( 'options' );\n if ( optionsAttr ) {\n try {\n return JSON.parse( optionsAttr );\n } catch ( e ) {\n console.error( 'Invalid options format:', e );\n return [];\n }\n }\n return [];\n }\n\n set options ( opts: SelectOption[] ) {\n this.setAttribute( 'options', JSON.stringify( opts ) );\n }\n\n /**\n * Opens the dropdown\n */\n open (): void {\n if ( this.disabled ) return;\n this.isOpen = true;\n this.focusedIndex = -1;\n if ( this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n\n // Update dropdown position based on viewport\n this._updateDropdownPosition();\n\n // Focus search input if searchable\n if ( this.searchable ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n }\n } );\n }\n\n this.dispatchEvent( new CustomEvent( 'open' ) );\n }\n\n /**\n * Closes the dropdown\n */\n close (): void {\n this.isOpen = false;\n this.focusedIndex = -1;\n this.searchValue = '';\n\n // Reset filtered options when closing\n if ( this.searchable && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Clear any inline positioning styles\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( dropdown ) {\n dropdown.style.top = '';\n dropdown.style.left = '';\n dropdown.style.width = '';\n dropdown.style.maxHeight = '';\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'close' ) );\n }\n\n /**\n * Toggles the dropdown open/closed state\n */\n toggle (): void {\n if ( this.isOpen ) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Selects an option by its value\n */\n selectOption ( value: string ): void {\n const option = this.options.find( opt => opt.value === value );\n if ( !option ) return;\n\n if ( this.multiple ) {\n if ( !this.selectedOptions.find( opt => opt.value === value ) ) {\n this.selectedOptions.push( option );\n }\n } else {\n this.selectedOptions = [ option ];\n this.close();\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Deselects an option by its value\n */\n deselectOption ( value: string ): void {\n this.selectedOptions = this.selectedOptions.filter( opt => opt.value !== value );\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Returns an array of currently selected options\n */\n getSelectedOptions (): SelectOption[] {\n return [ ...this.selectedOptions ];\n }\n\n /**\n * Sets the options for the select component\n */\n setOptions ( options: SelectOption[] ): void {\n this.options = options;\n this.filteredOptions = [ ...options ];\n this.selectedOptions = [];\n this.render();\n }\n\n /**\n * Handles search functionality\n */\n private handleSearch ( query: string ): void {\n this.searchValue = query;\n this.filteredOptions = this.options.filter( option =>\n option.label.toLowerCase().includes( query.toLowerCase() )\n );\n this.focusedIndex = -1;\n this.render();\n this.dispatchEvent( new CustomEvent( 'search', { detail: { query } } ) );\n }\n\n /**\n * Updates the visual focus state without full re-render\n */\n private updateFocusedOption (): void {\n const options = this.shadowRoot.querySelectorAll( '.option' );\n\n // Remove focused class from all options\n options.forEach( option => option.classList.remove( 'focused' ) );\n\n // Add focused class to current option\n if ( this.focusedIndex >= 0 && this.focusedIndex < options.length ) {\n options[ this.focusedIndex ].classList.add( 'focused' );\n }\n\n this.scrollToFocusedOption();\n }\n\n /**\n * Scrolls the focused option into view\n */\n private scrollToFocusedOption (): void {\n if ( this.focusedIndex < 0 ) return;\n\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n const focusedOption = this.shadowRoot.querySelector( '.option.focused' ) as HTMLElement;\n\n if ( dropdown && focusedOption ) {\n const dropdownRect = dropdown.getBoundingClientRect();\n const optionRect = focusedOption.getBoundingClientRect();\n\n // Check if option is above visible area\n if ( optionRect.top < dropdownRect.top ) {\n dropdown.scrollTop -= ( dropdownRect.top - optionRect.top );\n }\n // Check if option is below visible area\n else if ( optionRect.bottom > dropdownRect.bottom ) {\n dropdown.scrollTop += ( optionRect.bottom - dropdownRect.bottom );\n }\n }\n } );\n }\n\n /**\n * Calculates the optimal dropdown position based on viewport constraints\n */\n private _calculateDropdownPosition (): { top: number; left: number; width: number; maxHeight: number; } | null {\n const trigger = this.shadowRoot.querySelector( '.select-trigger' ) as HTMLElement;\n if ( !trigger ) return null;\n\n const triggerRect = trigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n const dropdownMaxHeight = 200;\n const dropdownPadding = 10;\n const margin = 2;\n\n // Calculate available space\n const spaceBelow = viewportHeight - triggerRect.bottom;\n const spaceAbove = triggerRect.top;\n\n // Determine if dropdown should open upward\n const shouldOpenUpward = spaceBelow < dropdownMaxHeight + dropdownPadding && spaceAbove > spaceBelow;\n\n // Calculate dimensions\n const width = triggerRect.width;\n const left = Math.max( 0, Math.min( triggerRect.left, viewportWidth - width ) );\n\n let top: number;\n let maxHeight: number;\n\n if ( shouldOpenUpward ) {\n // Position above the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceAbove - dropdownPadding );\n top = triggerRect.top - maxHeight - margin;\n } else {\n // Position below the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceBelow - dropdownPadding );\n top = triggerRect.bottom + margin;\n }\n\n return {\n top: Math.max( 0, top ),\n left,\n width,\n maxHeight: Math.max( 100, maxHeight ) // Ensure minimum height\n };\n }\n\n /**\n * Updates dropdown position using fixed positioning relative to viewport\n */\n private _updateDropdownPosition (): void {\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( !dropdown ) return;\n\n const position = this._calculateDropdownPosition();\n if ( !position ) return;\n\n // Apply calculated position as inline styles\n dropdown.style.top = `${ position.top }px`;\n dropdown.style.left = `${ position.left }px`;\n dropdown.style.width = `${ position.width }px`;\n dropdown.style.maxHeight = `${ position.maxHeight }px`;\n } );\n }\n\n /**\n * Handles keyboard navigation\n */\n private handleKeydown ( event: KeyboardEvent ): void {\n if ( this.disabled ) return;\n\n // Prevent double execution if event has already been handled\n if ( ( event as any )._smartSelectHandled ) return;\n ( event as any )._smartSelectHandled = true;\n\n switch ( event.key ) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( !this.isOpen ) {\n this.open();\n } else {\n // If searchable and search input is focused, move to first option\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n this.focusedIndex = 0;\n searchInput.blur(); // Blur search input to allow normal navigation\n // Focus the component to ensure it receives keyboard events\n this.focus();\n this.updateFocusedOption();\n return;\n }\n // Navigate through options\n const newIndex = Math.min( this.focusedIndex + 1, this.filteredOptions.length - 1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( this.isOpen ) {\n // If at first option and searchable, focus search input\n if ( this.focusedIndex === 0 && this.searchable ) {\n this.focusedIndex = -1;\n this.updateFocusedOption();\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n return;\n }\n // If searchable and search input is focused, do nothing\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n return;\n }\n // Navigate through options\n const newIndex = Math.max( this.focusedIndex - 1, -1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'Enter':\n event.preventDefault();\n if ( this.isOpen && this.focusedIndex >= 0 && this.focusedIndex < this.filteredOptions.length ) {\n this.selectOption( this.filteredOptions[ this.focusedIndex ].value );\n } else if ( !this.isOpen ) {\n this.open();\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.close();\n break;\n\n case 'Tab':\n this.close();\n break;\n }\n }\n\n /**\n * Binds all event listeners\n */\n private bindEvents (): void {\n // Listen for keydown events on both the component and shadow root\n const keydownHandler = this.handleKeydown.bind( this );\n this.addEventListener( 'keydown', keydownHandler );\n this.shadowRoot.addEventListener( 'keydown', keydownHandler as EventListener );\n\n // Use event delegation on the shadow root\n this.shadowRoot.addEventListener( 'click', ( e ) => {\n e.stopPropagation();\n const target = e.target as HTMLElement;\n\n if ( target.closest( '.remove-tag' ) ) {\n const value = ( target.closest( '.remove-tag' ) as HTMLElement ).dataset.value;\n if ( value ) this.deselectOption( value );\n } else if ( target.closest( '.option' ) ) {\n const value = ( target.closest( '.option' ) as HTMLElement ).dataset.value;\n if ( value ) this.selectOption( value );\n } else if ( target.closest( '.select-trigger' ) ) {\n this.toggle();\n }\n } );\n\n // Handle mouse hover on options to update focused index\n this.shadowRoot.addEventListener( 'mouseover', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.option' ) ) {\n const option = target.closest( '.option' ) as HTMLElement;\n const options = Array.from( this.shadowRoot.querySelectorAll( '.option' ) );\n const newFocusedIndex = options.indexOf( option );\n\n // Only update if the focused index actually changed\n if ( this.focusedIndex !== newFocusedIndex ) {\n // Remove focused class from current option\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n\n // Add focused class to new option\n option.classList.add( 'focused' );\n this.focusedIndex = newFocusedIndex;\n }\n }\n } );\n\n // Handle mouse leaving dropdown to clear focus\n this.shadowRoot.addEventListener( 'mouseleave', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.dropdown' ) ) {\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n this.focusedIndex = -1;\n }\n } );\n\n // Handle search input\n this.shadowRoot.addEventListener( 'input', ( e ) => {\n const target = e.target as HTMLInputElement;\n if ( target.classList.contains( 'search-input' ) ) {\n this.handleSearch( target.value );\n }\n } );\n\n // Close dropdown when clicking outside\n document.addEventListener( 'click', ( e ) => {\n if ( !this.contains( e.target as Node ) ) {\n this.close();\n }\n } );\n\n // Update dropdown position on window resize or scroll\n window.addEventListener( 'resize', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n } );\n\n window.addEventListener( 'scroll', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n }, true ); // Use capture to catch all scroll events\n }\n\n /**\n * Renders the component\n */\n private render (): void {\n // Initialize filteredOptions if not set\n if ( this.filteredOptions.length === 0 && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Remember if search input was focused before render\n const wasSearchFocused = this.shadowRoot.querySelector( '.search-input' ) === this.shadowRoot.activeElement;\n\n const displayText = this.selectedOptions.length > 0\n ? ( this.multiple\n ? `${ this.selectedOptions.length } selected`\n : this.selectedOptions[ 0 ].label )\n : this.placeholder;\n\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: inline-block;\n position: relative;\n min-width: 200px;\n font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--font-size, 14px);\n outline: none;\n }\n\n :host(:focus) .select-trigger {\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n :host([disabled]) {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .select-container {\n position: relative;\n }\n\n .select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--padding, 8px 12px);\n border: var(--border, 1px solid #ccc);\n border-radius: var(--border-radius, 4px);\n background: var(--background, white);\n cursor: pointer;\n min-height: 36px;\n box-sizing: border-box;\n color: #333;\n user-select: none;\n }\n\n .select-trigger:focus {\n outline: none;\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n .select-trigger[disabled] {\n cursor: not-allowed;\n }\n\n .selected-content {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n flex: 1;\n }\n\n .tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--tag-background, #e9ecef);\n border-radius: var(--tag-border-radius, 12px);\n font-size: 12px;\n color: var(--tag-color, #495057);\n user-select: none;\n }\n\n .remove-tag {\n cursor: pointer;\n color: var(--remove-color, #6c757d);\n font-weight: bold;\n font-size: 14px;\n }\n\n .remove-tag:hover {\n color: var(--remove-hover-color, #dc3545);\n }\n\n .arrow {\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 5px solid var(--arrow-color, #666);\n transition: transform 0.2s;\n }\n\n .arrow.open {\n transform: rotate(180deg);\n }\n\n .dropdown {\n position: fixed;\n z-index: 99999;\n background: var(--dropdown-background, white);\n border: var(--dropdown-border, 1px solid #ccc);\n border-radius: var(--dropdown-border-radius, 4px);\n box-shadow: var(--dropdown-shadow, 0 2px 8px rgba(0, 0, 0, 0.1));\n max-height: 200px;\n overflow-y: auto;\n scroll-behavior: smooth;\n color: #333;\n }\n\n .search-input {\n width: 100%;\n padding: 8px 12px;\n border: none;\n border-bottom: 1px solid #eee;\n font-size: 14px;\n outline: none;\n box-sizing: border-box;\n }\n\n .option {\n padding: 8px 12px;\n cursor: pointer;\n color: var(--option-color, #333);\n transition: background-color 0.2s;\n user-select: none;\n }\n\n .option:hover {\n background-color: var(--option-hover-background, #f8f9fa);\n }\n\n .option.focused {\n background-color: var(--option-focused-background, #007bff);\n color: var(--option-focused-color, white);\n }\n\n .option.selected {\n background-color: var(--option-selected-background, #e3f2fd);\n color: var(--option-selected-color, #1976d2);\n }\n\n .option-content {\n display: flex;\n align-items: center;\n }\n\n .option-image {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n object-fit: cover;\n margin-right: 8px;\n flex-shrink: 0;\n }\n\n .tag-image {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n object-fit: cover;\n margin-right: 4px;\n flex-shrink: 0;\n }\n\n .single-value {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .single-value-image {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n object-fit: cover;\n }\n\n .no-options {\n padding: 8px 12px;\n color: var(--no-options-color, #6c757d);\n font-style: italic;\n }\n </style>\n\n <div class=\"select-container\">\n <div class=\"select-trigger\" tabindex=\"-1\">\n <div class=\"selected-content\">\n ${ this.multiple && this.selectedOptions.length > 0\n ? this.selectedOptions.map( option => `\n <span class=\"tag\">\n ${ option.image ? `<img src=\"${ option.image }\" class=\"tag-image\" alt=\"\">` : '' }\n ${ option.label }\n <span class=\"remove-tag\" data-value=\"${ option.value }\">×</span>\n </span>\n `).join( '' )\n : this.selectedOptions.length > 0\n ? `<div class=\"single-value\">\n ${ this.selectedOptions[ 0 ].image ? `<img src=\"${ this.selectedOptions[ 0 ].image }\" class=\"single-value-image\" alt=\"\">` : '' }\n <span>${ this.selectedOptions[ 0 ].label }</span>\n </div>`\n : `<span>${ displayText }</span>`\n }\n </div>\n <div class=\"arrow ${ this.isOpen ? 'open' : '' }\"></div>\n </div>\n\n ${ this.isOpen ? `\n <div class=\"dropdown\">\n ${ this.searchable ? `\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search options...\"\n value=\"${ this.searchValue }\"\n >\n ` : '' }\n\n ${ this.filteredOptions.length > 0\n ? this.filteredOptions.map( ( option, index ) => `\n <div\n class=\"option ${ this.selectedOptions.find( selected => selected.value === option.value ) ? 'selected' : '' } ${ index === this.focusedIndex ? 'focused' : '' }\"\n data-value=\"${ option.value }\"\n >\n <div class=\"option-content\">\n ${ option.image ? `<img src=\"${ option.image }\" class=\"option-image\" alt=\"\">` : '' }\n <span>${ option.label }</span>\n </div>\n </div>\n `).join( '' )\n : '<div class=\"no-options\">No options available</div>'\n }\n </div>\n ` : '' }\n </div>\n `;\n\n // Re-focus search input if it was previously focused\n if ( wasSearchFocused && this.searchable && this.isOpen ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n // Restore cursor position to the end\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n }\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst defineSmartSelect = ( tagName: string = 'liwe3-select' ): void => {\n if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {\n customElements.define( tagName, SmartSelectElement );\n }\n};\n\n// Auto-register with default tag name\ndefineSmartSelect();\n\nexport { defineSmartSelect };\n"],"names":["SmartSelectElement","name","oldValue","newValue","value","opt","val","option","optionsAttr","e","opts","searchInput","dropdown","options","query","focusedOption","dropdownRect","optionRect","trigger","triggerRect","viewportHeight","viewportWidth","dropdownMaxHeight","dropdownPadding","margin","spaceBelow","spaceAbove","shouldOpenUpward","width","left","top","maxHeight","position","event","newIndex","keydownHandler","target","newFocusedIndex","currentFocused","wasSearchFocused","displayText","index","selected","defineSmartSelect","tagName"],"mappings":"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"}
|
package/dist/Toast.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast Web Component
|
|
3
|
+
* A customizable toast notification system with multiple types, icons, buttons, and auto-dismiss
|
|
4
|
+
*/
|
|
5
|
+
export type ToastType = 'info' | 'warning' | 'error' | 'success';
|
|
6
|
+
export type ToastPosition = 'TL' | 'T' | 'TR' | 'BL' | 'B' | 'BR';
|
|
7
|
+
export type ToastButton = {
|
|
8
|
+
label: string;
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
};
|
|
11
|
+
export type ToastConfig = {
|
|
12
|
+
title: string;
|
|
13
|
+
text: string;
|
|
14
|
+
type?: ToastType;
|
|
15
|
+
icon?: string;
|
|
16
|
+
buttons?: ToastButton[];
|
|
17
|
+
closable?: boolean;
|
|
18
|
+
duration?: number;
|
|
19
|
+
position?: ToastPosition;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
};
|
|
22
|
+
export declare class ToastElement extends HTMLElement {
|
|
23
|
+
shadowRoot: ShadowRoot;
|
|
24
|
+
private config;
|
|
25
|
+
private autoCloseTimer?;
|
|
26
|
+
private remainingTime;
|
|
27
|
+
private pauseTime;
|
|
28
|
+
private progressBar?;
|
|
29
|
+
constructor();
|
|
30
|
+
static get observedAttributes(): string[];
|
|
31
|
+
attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
|
|
32
|
+
connectedCallback(): void;
|
|
33
|
+
disconnectedCallback(): void;
|
|
34
|
+
get title(): string;
|
|
35
|
+
set title(value: string);
|
|
36
|
+
get text(): string;
|
|
37
|
+
set text(value: string);
|
|
38
|
+
get type(): ToastType;
|
|
39
|
+
set type(value: ToastType);
|
|
40
|
+
get icon(): string | undefined;
|
|
41
|
+
set icon(value: string | undefined);
|
|
42
|
+
get closable(): boolean;
|
|
43
|
+
set closable(value: boolean);
|
|
44
|
+
get duration(): number;
|
|
45
|
+
set duration(value: number);
|
|
46
|
+
get buttons(): ToastButton[];
|
|
47
|
+
set buttons(value: ToastButton[]);
|
|
48
|
+
/**
|
|
49
|
+
* Shows the toast with the given configuration
|
|
50
|
+
*/
|
|
51
|
+
show(config: ToastConfig): void;
|
|
52
|
+
/**
|
|
53
|
+
* Closes the toast
|
|
54
|
+
*/
|
|
55
|
+
close(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Starts the auto-close timer if duration is set
|
|
58
|
+
*/
|
|
59
|
+
private startAutoCloseTimer;
|
|
60
|
+
/**
|
|
61
|
+
* Pauses the auto-close timer
|
|
62
|
+
*/
|
|
63
|
+
private pauseAutoCloseTimer;
|
|
64
|
+
/**
|
|
65
|
+
* Resumes the auto-close timer
|
|
66
|
+
*/
|
|
67
|
+
private resumeAutoCloseTimer;
|
|
68
|
+
/**
|
|
69
|
+
* Clears the auto-close timer
|
|
70
|
+
*/
|
|
71
|
+
private clearAutoCloseTimer;
|
|
72
|
+
/**
|
|
73
|
+
* Starts the progress bar animation
|
|
74
|
+
*/
|
|
75
|
+
private startProgressBarAnimation;
|
|
76
|
+
/**
|
|
77
|
+
* Pauses the progress bar animation
|
|
78
|
+
*/
|
|
79
|
+
private pauseProgressBarAnimation;
|
|
80
|
+
/**
|
|
81
|
+
* Resumes the progress bar animation
|
|
82
|
+
*/
|
|
83
|
+
private resumeProgressBarAnimation;
|
|
84
|
+
/**
|
|
85
|
+
* Gets the color scheme for the toast type
|
|
86
|
+
*/
|
|
87
|
+
private getTypeColors;
|
|
88
|
+
/**
|
|
89
|
+
* Gets the default icon for the toast type
|
|
90
|
+
*/
|
|
91
|
+
private getDefaultIcon;
|
|
92
|
+
/**
|
|
93
|
+
* Binds all event listeners
|
|
94
|
+
*/
|
|
95
|
+
private bindEvents;
|
|
96
|
+
/**
|
|
97
|
+
* Renders the component
|
|
98
|
+
*/
|
|
99
|
+
private render;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Conditionally defines the custom element if in a browser environment.
|
|
103
|
+
*/
|
|
104
|
+
declare const defineToast: (tagName?: string) => void;
|
|
105
|
+
/**
|
|
106
|
+
* Shows a toast notification with the given configuration.
|
|
107
|
+
* This is the recommended way to display toasts.
|
|
108
|
+
*
|
|
109
|
+
* @param config - The toast configuration
|
|
110
|
+
* @returns The toast element instance
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* import { toastAdd } from '@liwe3/webcomponents';
|
|
115
|
+
*
|
|
116
|
+
* toastAdd({
|
|
117
|
+
* title: 'Success!',
|
|
118
|
+
* text: 'Your changes have been saved.',
|
|
119
|
+
* type: 'success',
|
|
120
|
+
* duration: 5000,
|
|
121
|
+
* position: 'TR' // Optional: top-right (default)
|
|
122
|
+
* });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare const toastAdd: (config: ToastConfig) => ToastElement;
|
|
126
|
+
export { defineToast, toastAdd };
|
|
127
|
+
//# sourceMappingURL=Toast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../src/Toast.ts"],"names":[],"mappings":"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"}
|