@designbasekorea/ui-wc 0.8.13 → 0.9.2
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/CHANGELOG.md +64 -0
- package/LICENSE-PAYMENT-LOGOS.md +437 -0
- package/README.md +67 -3
- package/dist/esm/components/date-picker-utils.js +1 -1
- package/dist/esm/components/db-accordion.js +1 -1
- package/dist/esm/components/db-achievement-list.d.ts +31 -0
- package/dist/esm/components/db-achievement-list.js +1 -0
- package/dist/esm/components/db-achievement-modal.d.ts +38 -0
- package/dist/esm/components/db-achievement-modal.js +1 -0
- package/dist/esm/components/db-activity-heatmap.js +1 -1
- package/dist/esm/components/db-ad-banner.js +1 -1
- package/dist/esm/components/db-ai-chain-of-thought.d.ts +14 -0
- package/dist/esm/components/db-ai-chain-of-thought.js +1 -0
- package/dist/esm/components/db-ai-chat.js +1 -1
- package/dist/esm/components/db-ai-checkpoint.d.ts +14 -0
- package/dist/esm/components/db-ai-checkpoint.js +1 -0
- package/dist/esm/components/db-ai-confirmation.d.ts +14 -0
- package/dist/esm/components/db-ai-confirmation.js +1 -0
- package/dist/esm/components/db-ai-context.d.ts +15 -0
- package/dist/esm/components/db-ai-context.js +1 -0
- package/dist/esm/components/db-ai-conversation.d.ts +21 -0
- package/dist/esm/components/db-ai-conversation.js +1 -0
- package/dist/esm/components/db-ai-execution-quote.d.ts +41 -0
- package/dist/esm/components/db-ai-execution-quote.js +1 -0
- package/dist/esm/components/db-ai-file-tree.d.ts +14 -0
- package/dist/esm/components/db-ai-file-tree.js +1 -0
- package/dist/esm/components/db-ai-glow.d.ts +1 -1
- package/dist/esm/components/db-ai-inline-citation.d.ts +16 -0
- package/dist/esm/components/db-ai-inline-citation.js +1 -0
- package/dist/esm/components/db-ai-message.d.ts +19 -0
- package/dist/esm/components/db-ai-message.js +1 -0
- package/dist/esm/components/db-ai-model-select.d.ts +36 -0
- package/dist/esm/components/db-ai-model-select.js +1 -0
- package/dist/esm/components/db-ai-plan.d.ts +14 -0
- package/dist/esm/components/db-ai-plan.js +1 -0
- package/dist/esm/components/db-ai-queue.d.ts +14 -0
- package/dist/esm/components/db-ai-queue.js +1 -0
- package/dist/esm/components/db-ai-reasoning.d.ts +14 -0
- package/dist/esm/components/db-ai-reasoning.js +1 -0
- package/dist/esm/components/db-ai-sources.d.ts +14 -0
- package/dist/esm/components/db-ai-sources.js +1 -0
- package/dist/esm/components/db-ai-stack-trace.d.ts +14 -0
- package/dist/esm/components/db-ai-stack-trace.js +1 -0
- package/dist/esm/components/db-ai-subagent.d.ts +14 -0
- package/dist/esm/components/db-ai-subagent.js +1 -0
- package/dist/esm/components/db-ai-task-status.d.ts +26 -0
- package/dist/esm/components/db-ai-task-status.js +1 -0
- package/dist/esm/components/db-ai-task.d.ts +14 -0
- package/dist/esm/components/db-ai-task.js +1 -0
- package/dist/esm/components/db-ai-terminal.d.ts +14 -0
- package/dist/esm/components/db-ai-terminal.js +1 -0
- package/dist/esm/components/db-ai-test-results.d.ts +14 -0
- package/dist/esm/components/db-ai-test-results.js +1 -0
- package/dist/esm/components/db-ai-tool.d.ts +14 -0
- package/dist/esm/components/db-ai-tool.js +1 -0
- package/dist/esm/components/db-alert.js +1 -1
- package/dist/esm/components/db-app-badge.js +1 -1
- package/dist/esm/components/db-attachment-list.d.ts +38 -0
- package/dist/esm/components/db-attachment-list.js +1 -0
- package/dist/esm/components/db-audio-player.js +1 -1
- package/dist/esm/components/db-bento-card.d.ts +55 -0
- package/dist/esm/components/db-bento-card.js +1 -0
- package/dist/esm/components/db-bento-grid.d.ts +25 -0
- package/dist/esm/components/db-bento-grid.js +1 -0
- package/dist/esm/components/db-boarding-pass-stack.d.ts +49 -0
- package/dist/esm/components/db-boarding-pass-stack.js +1 -0
- package/dist/esm/components/db-boarding-pass.d.ts +32 -0
- package/dist/esm/components/db-boarding-pass.js +1 -0
- package/dist/esm/components/db-border-beam.d.ts +52 -0
- package/dist/esm/components/db-border-beam.js +1 -0
- package/dist/esm/components/db-bottom-navigation.js +1 -1
- package/dist/esm/components/db-bottom-sheet.js +1 -1
- package/dist/esm/components/db-breadcrumbs.js +1 -1
- package/dist/esm/components/db-button.d.ts +1 -1
- package/dist/esm/components/db-button.js +1 -1
- package/dist/esm/components/db-calendar.js +1 -1
- package/dist/esm/components/db-card.js +1 -1
- package/dist/esm/components/db-carousel.d.ts +3 -3
- package/dist/esm/components/db-carousel.js +1 -1
- package/dist/esm/components/db-chat-composer.d.ts +2 -0
- package/dist/esm/components/db-chat-composer.js +1 -1
- package/dist/esm/components/db-chat-message.d.ts +1 -1
- package/dist/esm/components/db-chat-orb.d.ts +1 -1
- package/dist/esm/components/db-chat-room.js +1 -1
- package/dist/esm/components/db-chat-widget.d.ts +1 -1
- package/dist/esm/components/db-chat-widget.js +1 -1
- package/dist/esm/components/db-chip.js +1 -1
- package/dist/esm/components/db-code-block.js +1 -1
- package/dist/esm/components/db-color-picker.d.ts +1 -0
- package/dist/esm/components/db-color-picker.js +1 -1
- package/dist/esm/components/db-confetti.d.ts +90 -0
- package/dist/esm/components/db-confetti.js +1 -0
- package/dist/esm/components/db-context-bar.d.ts +13 -0
- package/dist/esm/components/db-context-bar.js +1 -0
- package/dist/esm/components/db-credit-balance.d.ts +17 -0
- package/dist/esm/components/db-credit-balance.js +1 -0
- package/dist/esm/components/db-credit-card.d.ts +51 -0
- package/dist/esm/components/db-credit-card.js +1 -0
- package/dist/esm/components/db-credit-dropdown.d.ts +31 -0
- package/dist/esm/components/db-credit-dropdown.js +1 -0
- package/dist/esm/components/db-credit-history.d.ts +31 -0
- package/dist/esm/components/db-credit-history.js +1 -0
- package/dist/esm/components/db-credit-recharge-modal.d.ts +35 -0
- package/dist/esm/components/db-credit-recharge-modal.js +1 -0
- package/dist/esm/components/db-credit-recharge.d.ts +39 -0
- package/dist/esm/components/db-credit-recharge.js +1 -0
- package/dist/esm/components/db-date-picker.js +1 -1
- package/dist/esm/components/db-design-canvas.d.ts +137 -0
- package/dist/esm/components/db-design-canvas.js +1 -0
- package/dist/esm/components/db-design-toolbar.d.ts +60 -0
- package/dist/esm/components/db-design-toolbar.js +1 -0
- package/dist/esm/components/db-device-mockup.d.ts +100 -0
- package/dist/esm/components/db-device-mockup.js +1 -0
- package/dist/esm/components/db-download-progress.d.ts +1 -1
- package/dist/esm/components/db-download-progress.js +1 -1
- package/dist/esm/components/db-drawer.js +1 -1
- package/dist/esm/components/db-dropdown.d.ts +2 -0
- package/dist/esm/components/db-dropdown.js +1 -1
- package/dist/esm/components/db-dropzone.js +1 -1
- package/dist/esm/components/db-email-verify.d.ts +1 -7
- package/dist/esm/components/db-email-verify.js +1 -1
- package/dist/esm/components/db-file-uploader.d.ts +10 -0
- package/dist/esm/components/db-file-uploader.js +1 -1
- package/dist/esm/components/db-filter-bar.d.ts +34 -0
- package/dist/esm/components/db-filter-bar.js +1 -0
- package/dist/esm/components/db-flight-status.d.ts +33 -0
- package/dist/esm/components/db-flight-status.js +1 -0
- package/dist/esm/components/db-floating-action-button.d.ts +24 -14
- package/dist/esm/components/db-floating-action-button.js +1 -1
- package/dist/esm/components/db-footer.d.ts +3 -0
- package/dist/esm/components/db-footer.js +1 -1
- package/dist/esm/components/db-gantt.js +1 -1
- package/dist/esm/components/db-glyph-matrix.d.ts +58 -0
- package/dist/esm/components/db-glyph-matrix.js +1 -0
- package/dist/esm/components/db-icons.d.ts +1 -1
- package/dist/esm/components/db-icons.js +1 -1
- package/dist/esm/components/db-image-list.d.ts +2 -0
- package/dist/esm/components/db-image-list.js +1 -1
- package/dist/esm/components/db-indicator.js +1 -1
- package/dist/esm/components/db-input.js +1 -1
- package/dist/esm/components/db-kanban.js +1 -1
- package/dist/esm/components/db-lightbox.d.ts +4 -0
- package/dist/esm/components/db-lightbox.js +1 -1
- package/dist/esm/components/db-markdown-editor.js +1 -1
- package/dist/esm/components/db-marquee.d.ts +2 -10
- package/dist/esm/components/db-marquee.js +1 -1
- package/dist/esm/components/db-menu-item.d.ts +2 -0
- package/dist/esm/components/db-menu-item.js +1 -1
- package/dist/esm/components/db-modal.d.ts +1 -1
- package/dist/esm/components/db-modal.js +1 -1
- package/dist/esm/components/db-navbar.d.ts +3 -1
- package/dist/esm/components/db-navbar.js +1 -1
- package/dist/esm/components/db-navigation-rail.d.ts +1 -1
- package/dist/esm/components/db-node.d.ts +83 -0
- package/dist/esm/components/db-node.js +1 -0
- package/dist/esm/components/db-notification-dropdown.d.ts +30 -0
- package/dist/esm/components/db-notification-dropdown.js +1 -0
- package/dist/esm/components/db-notification-inbox.d.ts +25 -0
- package/dist/esm/components/db-notification-inbox.js +1 -0
- package/dist/esm/components/db-onboarding-modal.js +1 -1
- package/dist/esm/components/db-otp-input.d.ts +1 -1
- package/dist/esm/components/db-otp-input.js +1 -1
- package/dist/esm/components/db-page-header.d.ts +8 -0
- package/dist/esm/components/db-page-header.js +1 -1
- package/dist/esm/components/db-pagination.js +1 -1
- package/dist/esm/components/db-phone-verify.d.ts +1 -1
- package/dist/esm/components/db-phone-verify.js +1 -1
- package/dist/esm/components/db-plan-overview.d.ts +26 -0
- package/dist/esm/components/db-plan-overview.js +1 -0
- package/dist/esm/components/db-popover.js +1 -1
- package/dist/esm/components/db-progress-step.js +1 -1
- package/dist/esm/components/db-progressive-blur.d.ts +35 -0
- package/dist/esm/components/db-progressive-blur.js +1 -0
- package/dist/esm/components/db-receipt.d.ts +37 -0
- package/dist/esm/components/db-receipt.js +1 -0
- package/dist/esm/components/db-resizable-panels.js +1 -1
- package/dist/esm/components/db-search-bar.js +1 -1
- package/dist/esm/components/db-section-testimonials.d.ts +1 -4
- package/dist/esm/components/db-section-testimonials.js +1 -1
- package/dist/esm/components/db-segment-control.d.ts +0 -4
- package/dist/esm/components/db-segment-control.js +1 -1
- package/dist/esm/components/db-select.js +1 -1
- package/dist/esm/components/db-share.js +1 -1
- package/dist/esm/components/db-sidebar.d.ts +14 -1
- package/dist/esm/components/db-sidebar.js +1 -1
- package/dist/esm/components/db-split-view.d.ts +2 -0
- package/dist/esm/components/db-split-view.js +1 -1
- package/dist/esm/components/db-stepper.d.ts +0 -3
- package/dist/esm/components/db-stepper.js +1 -1
- package/dist/esm/components/db-table.js +1 -1
- package/dist/esm/components/db-testimonial.js +1 -1
- package/dist/esm/components/db-textarea.js +1 -1
- package/dist/esm/components/db-ticket.d.ts +18 -0
- package/dist/esm/components/db-ticket.js +1 -0
- package/dist/esm/components/db-time-picker.d.ts +1 -1
- package/dist/esm/components/db-time-picker.js +1 -1
- package/dist/esm/components/db-tool-button.d.ts +57 -0
- package/dist/esm/components/db-tool-button.js +1 -0
- package/dist/esm/components/db-top-banner.js +1 -1
- package/dist/esm/components/db-tutorial.js +1 -1
- package/dist/esm/components/db-tweet-card.d.ts +101 -0
- package/dist/esm/components/db-tweet-card.js +1 -0
- package/dist/esm/components/db-two-factor.d.ts +5 -11
- package/dist/esm/components/db-two-factor.js +1 -1
- package/dist/esm/components/db-video-player.js +1 -1
- package/dist/esm/components/db-voice-control.d.ts +25 -0
- package/dist/esm/components/db-voice-control.js +1 -0
- package/dist/esm/components/db-workspace-invite-modal.d.ts +51 -0
- package/dist/esm/components/db-workspace-invite-modal.js +1 -0
- package/dist/esm/components/db-workspace-members.d.ts +36 -0
- package/dist/esm/components/db-workspace-members.js +1 -0
- package/dist/esm/components/db-youtube-player.js +1 -1
- package/dist/esm/components/device-mockup-style.js +1 -0
- package/dist/esm/components/flight-route.js +1 -0
- package/dist/esm/components/input-validation.js +1 -1
- package/dist/esm/components/node-style.js +1 -0
- package/dist/esm/components/paper-card.d.ts +26 -0
- package/dist/esm/components/paper-card.js +1 -0
- package/dist/esm/components/random-gradient-palettes.d.ts +4 -0
- package/dist/esm/core/ai-element.d.ts +47 -0
- package/dist/esm/core/ai-element.js +1 -0
- package/dist/esm/core/credit-ui.js +1 -0
- package/dist/esm/core/i18n-copy.js +1 -0
- package/dist/esm/core/i18n-en.js +1 -0
- package/dist/esm/core/i18n.d.ts +206 -1
- package/dist/esm/core/i18n.js +1 -1
- package/dist/esm/core/notification-ui.d.ts +17 -0
- package/dist/esm/core/notification-ui.js +1 -0
- package/dist/esm/helpers/payment-brands.d.ts +56 -0
- package/dist/esm/helpers/payment-brands.js +1 -0
- package/dist/esm/helpers/toast.d.ts +2 -3
- package/dist/esm/index.d.ts +63 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-AGE5BHQ4.js +1 -0
- package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-GM4TCAP5.js +1 -0
- package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-GSN4JZN7.js +1 -0
- package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-VJJVQGV4.js +1 -0
- package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-YWLMJGDH.js +1 -0
- package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
- package/dist/esm/ui-contracts/dist/components.d.d.ts +76 -0
- package/dist/esm/ui-contracts/dist/primitives.d.d.ts +21 -0
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2223 -63
- package/dist/index.esm.js +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +793 -6726
- package/dist/react/index.esm.js +1 -1
- package/package.json +16 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isDbIconName as t,createDbIcon as e}from"./db-icons.js";import"./db-button.js";import"./db-chip.js";const s=[{id:"image",label:"사진·이미지",icon:"gallery",accept:"image/*",multiple:!0},{id:"video",label:"동영상",icon:"video",accept:"video/*",multiple:!0},{id:"file",label:"문서·파일",icon:"file-new",accept:"",multiple:!0},{id:"camera",label:"카메라 촬영",icon:"camera",accept:"image/*",capture:"environment"}];function kindFromMime(t){return t.startsWith("image/")?"image":t.startsWith("video/")?"video":"file"}const i={image:"gallery",video:"video",file:"file-new"};let a=0;class DbChatComposer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this._tools=[],this._attachOptions=s,this._attachments=[],this.attachMenuOpen=!1,this.dragDepth=0,this.handleAttachClick=()=>{this.disabled||(this.attachMenuOpen?this.closeAttachMenu():this.openAttachMenu())},this.handleOutsideClick=t=>{this.attachMenuOpen&&(this.attachWrap.contains(t.target)||this.closeAttachMenu())},this.handleFilePicked=()=>{const t=Array.from(this.fileInput.files||[]);if(!t.length)return;const e=this.fileInput.dataset.type||"file";this.addFiles(t,e)},this.handleDragEnter=t=>{this.hasAttribute("attach")&&!this.disabled&&this.hasDragFiles(t)&&(t.preventDefault(),this.dragDepth+=1,this.showDropOverlay(!0))},this.handleDragOver=t=>{this.hasAttribute("attach")&&!this.disabled&&this.hasDragFiles(t)&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handleDragLeave=t=>{this.hasAttribute("attach")&&this.hasDragFiles(t)&&(this.dragDepth=Math.max(0,this.dragDepth-1),0===this.dragDepth&&this.showDropOverlay(!1))},this.handleDrop=t=>{if(!this.hasAttribute("attach")||this.disabled)return;if(!this.hasDragFiles(t))return;t.preventDefault(),t.stopPropagation(),this.dragDepth=0,this.showDropOverlay(!1);const e=Array.from(t.dataTransfer?.files||[]);e.length&&(this.addFiles(e,"drop"),this.focusInput())},this.handleInput=()=>{this.autoGrow(),this.syncSendState(),this.dispatchEvent(new CustomEvent("db-input",{detail:{value:this.field.value},bubbles:!0,composed:!0}))},this.handleKeyDown=t=>{if("Escape"===t.key&&this.attachMenuOpen)return t.preventDefault(),void this.closeAttachMenu();"Enter"!==t.key||t.shiftKey||t.isComposing||(t.preventDefault(),this.submit())},this.handleSendClick=()=>{this.disabled||(this.streaming?this.dispatchEvent(new CustomEvent("db-stop",{bubbles:!0,composed:!0})):this.submit())}}static get observedAttributes(){return["placeholder","value","disabled","streaming","voice","attach","max-length","send-label"]}get value(){return this.field?.value??this.getAttribute("value")??""}set value(t){this.field&&(this.field.value=t,this.autoGrow(),this.syncSendState()),t?this.setAttribute("value",t):this.removeAttribute("value")}get streaming(){return this.hasAttribute("streaming")}set streaming(t){this.toggleAttribute("streaming",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get tools(){return this._tools}set tools(t){this._tools=Array.isArray(t)?t:[],this.built&&this.renderTools()}get attachOptions(){return this._attachOptions}set attachOptions(t){this._attachOptions=Array.isArray(t)&&t.length?t:s,this.built&&this.renderAttachMenu()}get attachments(){return this._attachments.map(t=>({...t}))}connectedCallback(){this.built||this.build(),this.sync(),requestAnimationFrame(()=>this.autoGrow()),document.addEventListener("click",this.handleOutsideClick,!0),this.addEventListener("dragenter",this.handleDragEnter),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop)}disconnectedCallback(){document.removeEventListener("click",this.handleOutsideClick,!0),this.removeEventListener("dragenter",this.handleDragEnter),this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop),this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview))}attributeChangedCallback(t){if(this.built){if("value"===t){const t=this.getAttribute("value")||"";return this.field.value!==t&&(this.field.value=t,this.autoGrow()),void this.syncSendState()}"streaming"!==t?this.sync():this.renderSendButton()}}build(){this.built=!0,this.className="designbase-wc-chat-composer",this.replaceChildren();const t=document.createElement("div");t.className="designbase-wc-chat-composer__surface",this.attachmentsEl=document.createElement("div"),this.attachmentsEl.className="designbase-wc-chat-composer__attachments",this.attachmentsEl.hidden=!0;const e=document.createElement("div");e.className="designbase-wc-chat-composer__field-row",this.field=document.createElement("textarea"),this.field.className="designbase-wc-chat-composer__field",this.field.rows=1,this.field.addEventListener("input",this.handleInput),this.field.addEventListener("keydown",this.handleKeyDown),e.append(this.field);const s=document.createElement("div");s.className="designbase-wc-chat-composer__toolbar";const i=document.createElement("div");i.className="designbase-wc-chat-composer__leading",this.attachWrap=document.createElement("div"),this.attachWrap.className="designbase-wc-chat-composer__attach-wrap",this.attachBtn=document.createElement("db-button"),this.attachBtn.className="designbase-wc-chat-composer__attach",this.attachBtn.setAttribute("variant","ghost"),this.attachBtn.setAttribute("icon-only",""),this.attachBtn.setAttribute("radius","pill"),this.attachBtn.setAttribute("size","m"),this.attachBtn.setAttribute("start-icon","paperclip"),this.attachBtn.setAttribute("aria-label","첨부"),this.attachBtn.setAttribute("aria-haspopup","menu"),this.attachBtn.addEventListener("db-click",this.handleAttachClick),this.attachMenu=document.createElement("div"),this.attachMenu.className="designbase-wc-chat-composer__attach-menu",this.attachMenu.setAttribute("role","menu"),this.attachMenu.hidden=!0,this.attachWrap.append(this.attachBtn,this.attachMenu),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.className="designbase-wc-chat-composer__file-input",this.fileInput.hidden=!0,this.fileInput.addEventListener("change",this.handleFilePicked),this.toolsEl=document.createElement("div"),this.toolsEl.className="designbase-wc-chat-composer__tools",i.append(this.attachWrap,this.toolsEl);const a=document.createElement("div");a.className="designbase-wc-chat-composer__trailing",this.micBtn=this.makeIconButton("microphone","음성 입력","db-voice"),this.micBtn.classList.add("designbase-wc-chat-composer__mic"),this.sendBtn=document.createElement("db-button"),this.sendBtn.className="designbase-wc-chat-composer__send",this.sendBtn.setAttribute("icon-only",""),this.sendBtn.setAttribute("radius","pill"),this.sendBtn.setAttribute("size","m"),this.sendBtn.addEventListener("db-click",this.handleSendClick),a.append(this.micBtn,this.sendBtn),s.append(i,a),t.append(this.attachmentsEl,e,s),this.dropOverlay=document.createElement("div"),this.dropOverlay.className="designbase-wc-chat-composer__drop-overlay",this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true"),this.dropOverlay.innerHTML='<span class="designbase-wc-chat-composer__drop-label">파일을 놓으면 첨부됩니다</span>',this.append(t,this.dropOverlay,this.fileInput),this.renderTools(),this.renderAttachMenu(),this.renderSendButton()}makeIconButton(t,e,s){const i=document.createElement("db-button");return i.setAttribute("variant","ghost"),i.setAttribute("icon-only",""),i.setAttribute("radius","pill"),i.setAttribute("size","m"),i.setAttribute("start-icon",t),i.setAttribute("aria-label",e),i.addEventListener("db-click",()=>{this.disabled||this.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0}))}),i}sync(){this.classList.toggle("designbase-wc-chat-composer--disabled",this.disabled),this.classList.toggle("designbase-wc-chat-composer--streaming",this.streaming),this.field.placeholder=this.getAttribute("placeholder")||"메시지를 입력하세요…",this.field.disabled=this.disabled;const t=this.getAttribute("max-length");t?this.field.maxLength=Number(t):this.field.removeAttribute("maxlength"),this.attachWrap.hidden=!this.hasAttribute("attach"),this.micBtn.hidden=!this.hasAttribute("voice"),this.hasAttribute("attach")||this.closeAttachMenu(),this.autoGrow(),this.syncSendState()}renderTools(){this.toolsEl.replaceChildren();for(const e of this._tools){const s=document.createElement("db-chip");s.setAttribute("label",e.label),s.setAttribute("size","s"),s.setAttribute("clickable",""),s.setAttribute("variant",e.active?"primary":"default"),e.icon&&t(e.icon)&&s.setAttribute("start-icon",e.icon),s.setAttribute("aria-pressed",String(!!e.active)),s.addEventListener("db-click",()=>{this.disabled||(e.active=!e.active,s.setAttribute("variant",e.active?"primary":"default"),s.setAttribute("aria-pressed",String(!!e.active)),this.dispatchEvent(new CustomEvent("db-tool-toggle",{detail:{id:e.id,active:!!e.active},bubbles:!0,composed:!0})))}),this.toolsEl.append(s)}}renderAttachMenu(){this.attachMenu.replaceChildren();for(const s of this._attachOptions){const i=document.createElement("button");i.type="button",i.className="designbase-wc-chat-composer__attach-item",i.setAttribute("role","menuitem");const a=document.createElement("span");a.className="designbase-wc-chat-composer__attach-item-icon",s.icon&&t(s.icon)&&a.append(e(s.icon,18));const n=document.createElement("span");n.className="designbase-wc-chat-composer__attach-item-text";const h=document.createElement("span");if(h.className="designbase-wc-chat-composer__attach-item-label",h.textContent=s.label,n.append(h),s.description){const t=document.createElement("span");t.className="designbase-wc-chat-composer__attach-item-desc",t.textContent=s.description,n.append(t)}i.append(a,n),i.addEventListener("click",()=>this.chooseAttachOption(s)),this.attachMenu.append(i)}}renderAttachments(){this.attachmentsEl.replaceChildren(),this.attachmentsEl.hidden=0===this._attachments.length;for(const t of this._attachments){if("image"===t.kind&&t.preview){const s=document.createElement("div");s.className="designbase-wc-chat-composer__thumb";const i=document.createElement("img");i.src=t.preview,i.alt=t.name;const a=document.createElement("button");a.type="button",a.className="designbase-wc-chat-composer__thumb-remove",a.setAttribute("aria-label",`${t.name} 제거`),a.append(e("close",12)),a.addEventListener("click",()=>this.removeAttachment(t.id)),s.append(i,a),this.attachmentsEl.append(s);continue}const s=document.createElement("db-chip");s.setAttribute("label",t.name),s.setAttribute("size","s"),s.setAttribute("variant","neutral"),s.setAttribute("deletable",""),s.setAttribute("start-icon",i[t.kind]),s.addEventListener("db-delete",()=>this.removeAttachment(t.id)),this.attachmentsEl.append(s)}this.syncSendState()}renderSendButton(){const t=this.streaming;this.sendBtn.setAttribute("variant",t?"secondary":"primary"),this.sendBtn.setAttribute("start-icon",t?"stop":"send"),this.sendBtn.setAttribute("aria-label",t?"생성 중지":"전송"),this.syncSendState()}syncSendState(){if(!this.sendBtn)return;const t=0===this.value.trim().length&&0===this._attachments.length,e=this.disabled||!this.streaming&&t;this.sendBtn.toggleAttribute("disabled",e)}autoGrow(){this.field&&0!==this.field.clientWidth&&(this.field.style.height="auto",this.field.style.height=`${Math.min(this.field.scrollHeight,200)}px`)}openAttachMenu(){this.attachMenuOpen=!0,this.attachMenu.hidden=!1,this.attachBtn.setAttribute("aria-expanded","true"),this.classList.add("designbase-wc-chat-composer--attach-open"),requestAnimationFrame(()=>this.attachMenu.classList.add("designbase-wc-chat-composer__attach-menu--visible"))}closeAttachMenu(){this.attachMenuOpen&&(this.attachMenuOpen=!1,this.attachMenu.hidden=!0,this.attachMenu.classList.remove("designbase-wc-chat-composer__attach-menu--visible"),this.attachBtn.setAttribute("aria-expanded","false"),this.classList.remove("designbase-wc-chat-composer--attach-open"))}chooseAttachOption(t){this.closeAttachMenu(),this.dispatchEvent(new CustomEvent("db-attach",{detail:{type:t.id},bubbles:!0,composed:!0})),void 0!==t.accept&&(this.fileInput.value="",this.fileInput.accept=t.accept,this.fileInput.multiple=!!t.multiple,t.capture?this.fileInput.setAttribute("capture",t.capture):this.fileInput.removeAttribute("capture"),this.fileInput.dataset.type=t.id,this.fileInput.click())}hasDragFiles(t){return Array.from(t.dataTransfer?.types||[]).includes("Files")}showDropOverlay(t){this.dropOverlay&&(this.dropOverlay.hidden=!t,this.classList.toggle("designbase-wc-chat-composer--drop-active",t))}addFile(t){if(this.hasAttachment(t))return!1;const e=kindFromMime(t.type),s={id:`att-${a+=1}`,name:t.name,size:t.size,mime:t.type,kind:e,file:t,preview:"image"===e?URL.createObjectURL(t):void 0};return this._attachments.push(s),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-add",{detail:{attachment:this.attachmentMeta(s)},bubbles:!0,composed:!0})),!0}hasAttachment(t){return this._attachments.some(e=>e.file&&e.file.name===t.name&&e.file.size===t.size&&e.file.type===t.type&&e.file.lastModified===t.lastModified)}attachmentMeta(t){return{id:t.id,name:t.name,size:t.size,mime:t.mime,kind:t.kind,file:t.file,preview:t.preview}}submit(){const t=this.field.value.trim();if(!t&&0===this._attachments.length||this.disabled||this.streaming)return;const e=new CustomEvent("db-send",{detail:{value:t,attachments:this._attachments.map(t=>this.attachmentMeta(t))},bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(e)&&(this.clear(),this.clearAttachments())}addFiles(t,e="file"){const s=Array.from(t||[]).filter(Boolean);if(!s.length)return;const i=s.filter(t=>this.addFile(t));i.length&&this.dispatchEvent(new CustomEvent("db-attach-files",{detail:{type:e,files:i},bubbles:!0,composed:!0}))}send(){this.submit()}clear(){this.value=""}removeAttachment(t){const e=this._attachments.find(e=>e.id===t);e&&(e.preview&&URL.revokeObjectURL(e.preview),this._attachments=this._attachments.filter(e=>e.id!==t),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-remove",{detail:{id:t},bubbles:!0,composed:!0})))}clearAttachments(){this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview)),this._attachments=[],this.renderAttachments()}addAttachment(t){if(t instanceof File)return void this.addFile(t);if(t.file&&this.hasAttachment(t.file))return;const e=kindFromMime(t.mime);this._attachments.push({id:`att-${a+=1}`,kind:e,...t}),this.renderAttachments()}focusInput(){this.field?.focus()}setStreaming(t){this.streaming=t}}customElements.get("db-chat-composer")||customElements.define("db-chat-composer",DbChatComposer);export{DbChatComposer};
|
|
1
|
+
import{dbText as t,dbFormat as e}from"../core/i18n.js";import{isDbIconName as s,createDbIcon as i}from"./db-icons.js";import"./db-button.js";import"./db-chip.js";import"./db-voice-control.js";const DEFAULT_ATTACH_OPTIONS=()=>[{id:"image",label:t("사진·이미지"),icon:"gallery",accept:"image/*",multiple:!0},{id:"video",label:t("동영상"),icon:"video",accept:"video/*",multiple:!0},{id:"file",label:t("문서·파일"),icon:"file-new",accept:"",multiple:!0},{id:"camera",label:t("카메라 촬영"),icon:"camera",accept:"image/*",capture:"environment"}];function kindFromMime(t){return t.startsWith("image/")?"image":t.startsWith("video/")?"video":"file"}const a={image:"gallery",video:"video",file:"file-new"};let n=0;class DbChatComposer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this._tools=[],this._attachOptions=DEFAULT_ATTACH_OPTIONS(),this._attachments=[],this.attachMenuOpen=!1,this.dragDepth=0,this.handleAttachClick=()=>{this.disabled||(this.attachMenuOpen?this.closeAttachMenu():this.openAttachMenu())},this.handleOutsideClick=t=>{this.attachMenuOpen&&(this.attachWrap.contains(t.target)||this.closeAttachMenu())},this.handleFilePicked=()=>{const t=Array.from(this.fileInput.files||[]);if(!t.length)return;const e=this.fileInput.dataset.type||"file";this.addFiles(t,e)},this.handleDragEnter=t=>{this.hasAttribute("attach")&&!this.disabled&&this.hasDragFiles(t)&&(t.preventDefault(),this.dragDepth+=1,this.showDropOverlay(!0))},this.handleDragOver=t=>{this.hasAttribute("attach")&&!this.disabled&&this.hasDragFiles(t)&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handleDragLeave=t=>{this.hasAttribute("attach")&&this.hasDragFiles(t)&&(this.dragDepth=Math.max(0,this.dragDepth-1),0===this.dragDepth&&this.showDropOverlay(!1))},this.handleDrop=t=>{if(!this.hasAttribute("attach")||this.disabled)return;if(!this.hasDragFiles(t))return;t.preventDefault(),t.stopPropagation(),this.dragDepth=0,this.showDropOverlay(!1);const e=Array.from(t.dataTransfer?.files||[]);e.length&&(this.addFiles(e,"drop"),this.focusInput())},this.handleInput=()=>{this.autoGrow(),this.syncSendState(),this.dispatchEvent(new CustomEvent("db-input",{detail:{value:this.field.value},bubbles:!0,composed:!0}))},this.handleKeyDown=t=>"Escape"===t.key&&this.voiceActive?(t.preventDefault(),void this.dispatchEvent(new CustomEvent("db-voice",{detail:{action:"cancel"},bubbles:!0,composed:!0}))):"Escape"===t.key&&this.attachMenuOpen?(t.preventDefault(),void this.closeAttachMenu()):void("Enter"!==t.key||t.shiftKey||t.isComposing||(t.preventDefault(),this.submit())),this.handleSendClick=()=>{this.disabled||this.voiceActive||(this.streaming?this.dispatchEvent(new CustomEvent("db-stop",{bubbles:!0,composed:!0})):this.submit())}}static get observedAttributes(){return["placeholder","value","disabled","streaming","voice","attach","max-length","send-label","voice-state","voice-message","voice-disabled"]}get value(){return this.field?.value??this.getAttribute("value")??""}set value(t){this.field&&(this.field.value=t,this.autoGrow(),this.syncSendState()),t?this.setAttribute("value",t):this.removeAttribute("value")}get streaming(){return this.hasAttribute("streaming")}set streaming(t){this.toggleAttribute("streaming",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get tools(){return this._tools}set tools(t){this._tools=Array.isArray(t)?t:[],this.built&&this.renderTools()}get attachOptions(){return this._attachOptions}set attachOptions(t){this._attachOptions=Array.isArray(t)&&t.length?t:DEFAULT_ATTACH_OPTIONS(),this.built&&this.renderAttachMenu()}get attachments(){return this._attachments.map(t=>({...t}))}connectedCallback(){this.built||this.build(),this.sync(),requestAnimationFrame(()=>this.autoGrow()),document.addEventListener("click",this.handleOutsideClick,!0),this.addEventListener("dragenter",this.handleDragEnter),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop)}disconnectedCallback(){document.removeEventListener("click",this.handleOutsideClick,!0),this.removeEventListener("dragenter",this.handleDragEnter),this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop),this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview))}attributeChangedCallback(t){if(this.built){if("value"===t){const t=this.getAttribute("value")||"";return this.field.value!==t&&(this.field.value=t,this.autoGrow()),void this.syncSendState()}"streaming"!==t?this.sync():this.renderSendButton()}}build(){this.built=!0,this.className="designbase-wc-chat-composer",this.replaceChildren();const e=document.createElement("div");e.className="designbase-wc-chat-composer__surface",this.attachmentsEl=document.createElement("div"),this.attachmentsEl.className="designbase-wc-chat-composer__attachments",this.attachmentsEl.hidden=!0;const s=document.createElement("div");s.className="designbase-wc-chat-composer__field-row",this.field=document.createElement("textarea"),this.field.className="designbase-wc-chat-composer__field",this.field.rows=1,this.field.addEventListener("input",this.handleInput),this.field.addEventListener("keydown",this.handleKeyDown),s.append(this.field);const i=document.createElement("div");i.className="designbase-wc-chat-composer__toolbar";const a=document.createElement("div");a.className="designbase-wc-chat-composer__leading",this.attachWrap=document.createElement("div"),this.attachWrap.className="designbase-wc-chat-composer__attach-wrap",this.attachBtn=document.createElement("db-button"),this.attachBtn.className="designbase-wc-chat-composer__attach",this.attachBtn.setAttribute("variant","ghost"),this.attachBtn.setAttribute("icon-only",""),this.attachBtn.setAttribute("radius","pill"),this.attachBtn.setAttribute("size","m"),this.attachBtn.setAttribute("start-icon","paperclip"),this.attachBtn.setAttribute("aria-label",t("첨부")),this.attachBtn.setAttribute("aria-haspopup","menu"),this.attachBtn.addEventListener("db-click",this.handleAttachClick),this.attachMenu=document.createElement("div"),this.attachMenu.className="designbase-wc-chat-composer__attach-menu",this.attachMenu.setAttribute("role","menu"),this.attachMenu.hidden=!0,this.attachWrap.append(this.attachBtn,this.attachMenu),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.className="designbase-wc-chat-composer__file-input",this.fileInput.hidden=!0,this.fileInput.addEventListener("change",this.handleFilePicked),this.toolsEl=document.createElement("div"),this.toolsEl.className="designbase-wc-chat-composer__tools",a.append(this.attachWrap,this.toolsEl);const n=document.createElement("div");n.className="designbase-wc-chat-composer__trailing",this.micBtn=document.createElement("db-voice-control"),this.micBtn.setAttribute("compact","");for(const t of["start","stop","cancel"])this.micBtn.addEventListener(`db-${t}`,e=>{e.stopPropagation(),this.disabled||this.streaming||this.hasAttribute("voice-disabled")||this.dispatchEvent(new CustomEvent("db-voice",{detail:{action:t},bubbles:!0,composed:!0}))});this.micBtn.classList.add("designbase-wc-chat-composer__mic"),this.sendBtn=document.createElement("db-button"),this.sendBtn.className="designbase-wc-chat-composer__send",this.sendBtn.setAttribute("icon-only",""),this.sendBtn.setAttribute("radius","pill"),this.sendBtn.setAttribute("size","m"),this.sendBtn.addEventListener("db-click",this.handleSendClick),n.append(this.micBtn,this.sendBtn),i.append(a,n),e.append(this.attachmentsEl,s,i),this.dropOverlay=document.createElement("div"),this.dropOverlay.className="designbase-wc-chat-composer__drop-overlay",this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true"),this.dropOverlay.innerHTML='<span class="designbase-wc-chat-composer__drop-label">파일을 놓으면 첨부됩니다</span>',this.append(e,this.dropOverlay,this.fileInput),this.renderTools(),this.renderAttachMenu(),this.renderSendButton()}makeIconButton(t,e,s){const i=document.createElement("db-button");return i.setAttribute("variant","ghost"),i.setAttribute("icon-only",""),i.setAttribute("radius","pill"),i.setAttribute("size","m"),i.setAttribute("start-icon",t),i.setAttribute("aria-label",e),i.addEventListener("db-click",()=>{this.disabled||this.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0}))}),i}sync(){this.classList.toggle("designbase-wc-chat-composer--disabled",this.disabled),this.classList.toggle("designbase-wc-chat-composer--streaming",this.streaming),this.field.placeholder=this.getAttribute("placeholder")||"메시지를 입력하세요…",this.field.disabled=this.disabled,this.field.readOnly=this.voiceActive;const t=this.getAttribute("max-length");t?this.field.maxLength=Number(t):this.field.removeAttribute("maxlength"),this.attachWrap.hidden=!this.hasAttribute("attach"),this.micBtn.hidden=!this.hasAttribute("voice"),this.micBtn.setAttribute("state",this.getAttribute("voice-state")||"idle"),this.micBtn.setAttribute("message",this.getAttribute("voice-message")||""),this.micBtn.toggleAttribute("disabled",this.disabled||this.streaming||this.hasAttribute("voice-disabled")),this.hasAttribute("attach")||this.closeAttachMenu(),this.autoGrow(),this.syncSendState()}renderTools(){this.toolsEl.replaceChildren();for(const t of this._tools){const e=document.createElement("db-chip");e.setAttribute("label",t.label),e.setAttribute("size","s"),e.setAttribute("clickable",""),e.setAttribute("variant",t.active?"primary":"default"),t.icon&&s(t.icon)&&e.setAttribute("start-icon",t.icon),e.setAttribute("aria-pressed",String(!!t.active)),e.addEventListener("db-click",()=>{this.disabled||(t.active=!t.active,e.setAttribute("variant",t.active?"primary":"default"),e.setAttribute("aria-pressed",String(!!t.active)),this.dispatchEvent(new CustomEvent("db-tool-toggle",{detail:{id:t.id,active:!!t.active},bubbles:!0,composed:!0})))}),this.toolsEl.append(e)}}renderAttachMenu(){this.attachMenu.replaceChildren();for(const t of this._attachOptions){const e=document.createElement("button");e.type="button",e.className="designbase-wc-chat-composer__attach-item",e.setAttribute("role","menuitem");const a=document.createElement("span");a.className="designbase-wc-chat-composer__attach-item-icon",t.icon&&s(t.icon)&&a.append(i(t.icon,18));const n=document.createElement("span");n.className="designbase-wc-chat-composer__attach-item-text";const h=document.createElement("span");if(h.className="designbase-wc-chat-composer__attach-item-label",h.textContent=t.label,n.append(h),t.description){const e=document.createElement("span");e.className="designbase-wc-chat-composer__attach-item-desc",e.textContent=t.description,n.append(e)}e.append(a,n),e.addEventListener("click",()=>this.chooseAttachOption(t)),this.attachMenu.append(e)}}renderAttachments(){this.attachmentsEl.replaceChildren(),this.attachmentsEl.hidden=0===this._attachments.length;for(const t of this._attachments){if("image"===t.kind&&t.preview){const s=document.createElement("div");s.className="designbase-wc-chat-composer__thumb";const a=document.createElement("img");a.src=t.preview,a.alt=t.name;const n=document.createElement("button");n.type="button",n.className="designbase-wc-chat-composer__thumb-remove",n.setAttribute("aria-label",e("{0} 제거",[t.name])),n.append(i("close",12)),n.addEventListener("click",()=>this.removeAttachment(t.id)),s.append(a,n),this.attachmentsEl.append(s);continue}const s=document.createElement("db-chip");s.setAttribute("label",t.name),s.setAttribute("size","s"),s.setAttribute("variant","neutral"),s.setAttribute("deletable",""),s.setAttribute("start-icon",a[t.kind]),s.addEventListener("db-delete",()=>this.removeAttachment(t.id)),this.attachmentsEl.append(s)}this.syncSendState()}renderSendButton(){const t=this.streaming;this.sendBtn.setAttribute("variant",t?"secondary":"primary"),this.sendBtn.setAttribute("start-icon",t?"stop":"send"),this.sendBtn.setAttribute("aria-label",t?"생성 중지":"전송"),this.syncSendState()}get voiceActive(){return this.hasAttribute("voice")&&["starting","listening","stopping"].includes(this.getAttribute("voice-state")||"idle")}syncSendState(){if(!this.sendBtn)return;const t=0===this.value.trim().length&&0===this._attachments.length,e=this.disabled||this.voiceActive||!this.streaming&&t;this.sendBtn.toggleAttribute("disabled",e)}autoGrow(){this.field&&0!==this.field.clientWidth&&(this.field.style.height="auto",this.field.style.height=`${Math.min(this.field.scrollHeight,200)}px`)}openAttachMenu(){this.attachMenuOpen=!0,this.attachMenu.hidden=!1,this.attachBtn.setAttribute("aria-expanded","true"),this.classList.add("designbase-wc-chat-composer--attach-open"),requestAnimationFrame(()=>this.attachMenu.classList.add("designbase-wc-chat-composer__attach-menu--visible"))}closeAttachMenu(){this.attachMenuOpen&&(this.attachMenuOpen=!1,this.attachMenu.hidden=!0,this.attachMenu.classList.remove("designbase-wc-chat-composer__attach-menu--visible"),this.attachBtn.setAttribute("aria-expanded","false"),this.classList.remove("designbase-wc-chat-composer--attach-open"))}chooseAttachOption(t){this.closeAttachMenu(),this.dispatchEvent(new CustomEvent("db-attach",{detail:{type:t.id},bubbles:!0,composed:!0})),void 0!==t.accept&&(this.fileInput.value="",this.fileInput.accept=t.accept,this.fileInput.multiple=!!t.multiple,t.capture?this.fileInput.setAttribute("capture",t.capture):this.fileInput.removeAttribute("capture"),this.fileInput.dataset.type=t.id,this.fileInput.click())}hasDragFiles(t){return Array.from(t.dataTransfer?.types||[]).includes("Files")}showDropOverlay(t){this.dropOverlay&&(this.dropOverlay.hidden=!t,this.classList.toggle("designbase-wc-chat-composer--drop-active",t))}addFile(t){if(this.hasAttachment(t))return!1;const e=kindFromMime(t.type),s={id:`att-${n+=1}`,name:t.name,size:t.size,mime:t.type,kind:e,file:t,preview:"image"===e?URL.createObjectURL(t):void 0};return this._attachments.push(s),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-add",{detail:{attachment:this.attachmentMeta(s)},bubbles:!0,composed:!0})),!0}hasAttachment(t){return this._attachments.some(e=>e.file&&e.file.name===t.name&&e.file.size===t.size&&e.file.type===t.type&&e.file.lastModified===t.lastModified)}attachmentMeta(t){return{id:t.id,name:t.name,size:t.size,mime:t.mime,kind:t.kind,file:t.file,preview:t.preview}}submit(){const t=this.field.value.trim();if(!t&&0===this._attachments.length||this.disabled||this.streaming||this.voiceActive)return;const e=new CustomEvent("db-send",{detail:{value:t,attachments:this._attachments.map(t=>this.attachmentMeta(t))},bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(e)&&(this.clear(),this.clearAttachments())}addFiles(t,e="file"){const s=Array.from(t||[]).filter(Boolean);if(!s.length)return;const i=s.filter(t=>this.addFile(t));i.length&&this.dispatchEvent(new CustomEvent("db-attach-files",{detail:{type:e,files:i},bubbles:!0,composed:!0}))}send(){this.submit()}clear(){this.value=""}removeAttachment(t){const e=this._attachments.find(e=>e.id===t);e&&(e.preview&&URL.revokeObjectURL(e.preview),this._attachments=this._attachments.filter(e=>e.id!==t),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-remove",{detail:{id:t},bubbles:!0,composed:!0})))}clearAttachments(){this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview)),this._attachments=[],this.renderAttachments()}addAttachment(t){if(t instanceof File)return void this.addFile(t);if(t.file&&this.hasAttachment(t.file))return;const e=kindFromMime(t.mime);this._attachments.push({id:`att-${n+=1}`,kind:e,...t}),this.renderAttachments()}focusInput(){this.field?.focus()}setStreaming(t){this.streaming=t}}customElements.get("db-chat-composer")||customElements.define("db-chat-composer",DbChatComposer);export{DbChatComposer};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CHAT_ORB_SIZES, CHAT_ORB_VARIANTS, CHAT_ORB_STATES, CHAT_ORB_THEMES } from '
|
|
1
|
+
import { CHAT_ORB_SIZES, CHAT_ORB_VARIANTS, CHAT_ORB_STATES, CHAT_ORB_THEMES } from '../ui-contracts/dist/components.d.js';
|
|
2
2
|
|
|
3
3
|
type OrbSize = (typeof CHAT_ORB_SIZES)[number];
|
|
4
4
|
type OrbVariant = (typeof CHAT_ORB_VARIANTS)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{dbText as t}from"../core/i18n.js";import{createDbIcon as e}from"./db-icons.js";import{sanitizeHtmlFragment as s}from"../core/safe-html.js";import"./db-avatar.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-chat-composer.js";const r="designbase-wc-chat-room";let a=0;function el(t,e,s){const r=document.createElement(t);return e&&(r.className=e),null!=s&&(r.textContent=s),r}class DbChatRoom extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.dragDepth=0,this._messages=[],this.handleDragEnter=t=>{this.hasDragFiles(t)&&(t.preventDefault(),this.dragDepth+=1,this.showDropOverlay(!0))},this.handleDragOver=t=>{this.hasDragFiles(t)&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handleDragLeave=()=>{this.dragDepth=Math.max(0,this.dragDepth-1),0===this.dragDepth&&this.showDropOverlay(!1)},this.handleDrop=t=>{if(!this.hasDragFiles(t))return;t.preventDefault(),t.stopPropagation(),this.dragDepth=0,this.showDropOverlay(!1);const e=Array.from(t.dataTransfer?.files||[]);e.length&&this.composer&&(this.composer.addFiles(e,"drop"),this.composer.focusInput())}}static get observedAttributes(){return["peer-name","peer-avatar","peer-initials","peer-status","placeholder","online","show-back"]}set messages(t){this._messages=Array.isArray(t)?t.map(t=>({id:`r${a+=1}`,...t})):[],this.built&&this.renderThread()}get messages(){return this._messages}connectedCallback(){this.built||this.build(),this.sync(),this.addEventListener("dragenter",this.handleDragEnter),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop)}disconnectedCallback(){this.removeEventListener("dragenter",this.handleDragEnter),this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop)}attributeChangedCallback(){this.built&&this.sync()}build(){this.built=!0,this.className=r,this.replaceChildren(),this.headerEl=el("div",`${r}__header`),this.scrollEl=el("div",`${r}__scroll`),this.threadEl=el("div",`${r}__thread`),this.scrollEl.append(this.threadEl),this.dockEl=el("div",`${r}__dock`),this.dropOverlay=el("div",`${r}__drop-overlay`),this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true"),this.dropOverlay.append(el("span",`${r}__drop-label`,"파일을 놓으면 첨부됩니다")),this.composer=document.createElement("db-chat-composer"),this.composer.setAttribute("attach",""),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…"),this.composer.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail,s=e?.value?.trim()||"",r=e?.attachments||[];if(!s&&!r.length)return;const a=r.map(t=>({name:t.name,kind:t.kind,mime:t.mime,url:t.file?URL.createObjectURL(t.file):t.preview}));this.dispatchEvent(new CustomEvent("db-send",{detail:{value:s,attachments:a},bubbles:!0,composed:!0})),this.addMessage({mine:!0,text:s,time:this.nowLabel(),attachments:a.length?a:void 0})}),this.dockEl.append(this.composer),this.append(this.headerEl,this.scrollEl,this.dockEl,this.dropOverlay),this.renderThread()}sync(){this.renderHeader(),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…")}renderHeader(){if(this.headerEl.replaceChildren(),this.hasAttribute("show-back")){const s=el("button",`${r}__back`);s.type="button",s.setAttribute("aria-label",t("뒤로")),s.append(e("chevron-left",20)),s.addEventListener("click",()=>this.dispatchEvent(new CustomEvent("db-back",{bubbles:!0,composed:!0}))),this.headerEl.append(s)}const s=document.createElement("db-avatar");s.className=`${r}__avatar`,s.setAttribute("size","m"),s.setAttribute("variant","primary");const a=this.getAttribute("peer-avatar"),i=this.getAttribute("peer-initials");a?s.setAttribute("src",a):i&&s.setAttribute("initials",i),this.hasAttribute("online")&&s.setAttribute("status","online");const n=el("div",`${r}__meta`);n.append(el("span",`${r}__name`,this.getAttribute("peer-name")||"대화"));const h=this.getAttribute("peer-status")||(this.hasAttribute("online")?"온라인":"");h&&n.append(el("span",`${r}__status`,h)),this.headerEl.append(s,n)}renderThread(){this.threadEl.replaceChildren(),this._messages.forEach(t=>this.threadEl.append(this.buildMessage(t))),this.typing&&this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())}buildMessage(t){const e=el("db-chat-message");return e.dataset.id=t.id||"",e.setAttribute("role",t.mine?"user":"assistant"),!t.mine&&t.name&&e.setAttribute("name",t.name),t.time&&e.setAttribute("timestamp",t.time),!t.mine&&t.avatarSrc&&e.setAttribute("avatar-src",t.avatarSrc),!t.mine&&t.avatarInitials&&e.setAttribute("avatar-initials",t.avatarInitials),null!=t.html?e.replaceChildren(s(t.html)):e.textContent=t.text||"",t.attachments?.length&&(e.attachments=t.attachments),e}buildTyping(){const t=el("db-chat-typing");return t.setAttribute("name",this.getAttribute("peer-name")||""),t.setAttribute("avatar-initials",this.getAttribute("peer-initials")||""),t}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}nowLabel(){const t=new Date,e=t.getHours();return`${e<12?"오전":"오후"} ${e%12==0?12:e%12}:${t.getMinutes().toString().padStart(2,"0")}`}hasDragFiles(t){return Array.from(t.dataTransfer?.types||[]).includes("Files")}showDropOverlay(t){this.dropOverlay.hidden=!t,this.classList.toggle(`${r}--drop-active`,t)}addMessage(t){const e={id:`r${a+=1}`,...t};this._messages.push(e);const s=this.threadEl.querySelector("db-chat-typing"),r=this.buildMessage(e);return s?this.threadEl.insertBefore(r,s):this.threadEl.append(r),requestAnimationFrame(()=>this.scrollToEnd()),e.id}setMessages(t){this.messages=t}setTyping(t){if(this.typing=Boolean(t),!this.built)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())):!this.typing&&e&&e.remove()}focusInput(){this.composer?.focusInput()}getComposer(){return this.composer}}customElements.get("db-chat-room")||customElements.define("db-chat-room",DbChatRoom);export{DbChatRoom};
|
|
@@ -6,7 +6,7 @@ import { DbChatSuggestion } from './db-chat-suggestions.js';
|
|
|
6
6
|
import './db-chat-message.js';
|
|
7
7
|
import './db-chat-typing.js';
|
|
8
8
|
import './db-avatar.js';
|
|
9
|
-
import { CHAT_WIDGET_VIEWS, CHAT_WIDGET_POSITIONS } from '
|
|
9
|
+
import { CHAT_WIDGET_VIEWS, CHAT_WIDGET_POSITIONS } from '../ui-contracts/dist/components.d.js';
|
|
10
10
|
|
|
11
11
|
type ChatView = (typeof CHAT_WIDGET_VIEWS)[number];
|
|
12
12
|
type ChatPosition = (typeof CHAT_WIDGET_POSITIONS)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createDbIcon as t,isDbIconName as e}from"./db-icons.js";import"./db-chat-orb.js";import"./db-badge.js";import"./db-chat-welcome.js";import"./db-chat-composer.js";import"./db-chat-suggestions.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-avatar.js";import{CHAT_WIDGET_VIEWS as i,CHAT_WIDGET_POSITIONS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const n=[{id:"products",title:"제품·정책 알아보기",description:"AI가 정확하게 안내해 드려요",icon:"sparkle",action:"help"},{id:"services",title:"상담원과 대화하기",description:"지금 바로 대화를 시작하세요",icon:"mail",action:"conversation"}],a=[{id:"products",title:"제품·정책 알아보기"},{id:"services",title:"서비스 상담하기"},{id:"refund",title:"환불·결제 문의"},{id:"account",title:"계정·로그인 도움"}],r=[{id:"m1",role:"assistant",name:"상담원",text:"안녕하세요. 무엇을 도와드릴까요? 제품과 정책을 24시간 정확하게 안내해 드릴게요.",time:"오후 12:31"},{id:"m2",role:"assistant",name:"상담원",text:"아래에서 원하시는 도움을 선택하시거나, 자유롭게 메시지를 입력해 주세요.",time:"오후 12:31",actions:"like,dislike"}],o=[{id:"c1",name:"디지털 상담원",avatarInitials:"상",preview:"무엇을 도와드릴까요? 24시간 대기중이에요.",time:"방금",unread:1,online:!0},{id:"c2",name:"결제·환불 지원",avatarInitials:"결",preview:"환불 요청이 정상 접수되었습니다.",time:"어제"},{id:"c3",name:"온보딩 도우미",avatarInitials:"온",preview:"설치가 완료되면 알려드릴게요.",time:"3일 전"}],l=[{id:"a1",title:"시작하기 — 5분 만에 설정 완료",description:"계정 생성부터 첫 배포까지",icon:"sparkle",body:"계정을 만들고 첫 프로젝트를 배포하기까지 약 5분이면 충분합니다.\n\n1. 회원가입 후 이메일 인증\n2. 워크스페이스 생성\n3. 템플릿 선택 또는 빈 프로젝트 시작\n4. 배포 버튼을 눌러 미리보기 URL 확인"},{id:"a2",title:"요금제와 결제 안내",description:"플랜 비교 · 청구 주기",icon:"star",body:"Free · Pro · Team 플랜을 제공합니다. 청구는 월간 또는 연간으로 선택할 수 있으며, 연간 결제는 할인율이 적용됩니다.\n\n결제 수단 변경과 영수증 다운로드는 설정 > 결제에서 가능합니다."},{id:"a3",title:"팀원 초대와 권한 관리",description:"역할별 접근 권한 설정",icon:"user",body:"Owner · Admin · Member · Viewer 역할을 지원합니다.\n\n설정 > 팀원에서 이메일로 초대하고, 역할은 언제든 변경할 수 있습니다."},{id:"a4",title:"데이터 내보내기·백업",description:"안전하게 보관하는 방법",icon:"download",body:"프로젝트 데이터는 JSON · CSV로 내보낼 수 있습니다. 자동 백업은 Pro 이상에서 매일 실행되며, 설정 > 데이터에서 수동 백업도 가능합니다."}],h={chat:'<path d="M4 5.5A1.5 1.5 0 0 1 5.5 4h13A1.5 1.5 0 0 1 20 5.5v9a1.5 1.5 0 0 1-1.5 1.5H9l-4 3.5V16H5.5A1.5 1.5 0 0 1 4 14.5z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>',home:'<path d="M4 10.5 12 4l8 6.5M6 9.5V19a1 1 0 0 0 1 1h3.5v-4.5a1.5 1.5 0 0 1 3 0V20H17a1 1 0 0 0 1-1V9.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>',help:'<circle cx="12" cy="12" r="8.25" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M9.6 9.4a2.5 2.5 0 0 1 4.9.6c0 1.7-2.5 2-2.5 3.6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="12" cy="16.6" r="0.95" fill="currentColor"/>',"close-x":'<path d="m6.5 6.5 11 11m0-11-11 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>'};function widgetIcon(t,e=20){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox","0 0 24 24"),i.setAttribute("width",String(e)),i.setAttribute("height",String(e)),i.setAttribute("fill","none"),i.setAttribute("aria-hidden","true"),i.setAttribute("focusable","false"),i.innerHTML=h[t],i}function el(t,e,i){const s=document.createElement(t);return e&&(s.className=e),null!=i&&(s.textContent=i),s}function parseJson(t,e){if(!t)return e;try{const i=JSON.parse(t);return Array.isArray(i)?i:e}catch{return e}}const d="designbase-wc-chat";class DbChatWidget extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.helpQuery="",this._activeArticle=null,this._messages=r,this._conversations=o,this._menuItems=n,this._quickReplies=a,this._articles=l,this.composer=null,this.threadEl=null,this.handleDocumentKeyDown=t=>{"Escape"===t.key&&this.open&&(t.preventDefault(),this.close(),this.launcher?.focus())},this.panelDragDepth=0,this.handlePanelDragEnter=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),this.panelDragDepth+=1,this.bodyEl.classList.add(`${d}__body--drop-active`))},this.handlePanelDragOver=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handlePanelDragLeave=()=>{this.panelDragDepth=Math.max(0,this.panelDragDepth-1),0===this.panelDragDepth&&this.bodyEl.classList.remove(`${d}__body--drop-active`)},this.handlePanelDrop=t=>{if("conversation"!==this.view||!this.composer)return;if(!Array.from(t.dataTransfer?.types||[]).includes("Files"))return;t.preventDefault(),t.stopPropagation(),this.panelDragDepth=0,this.bodyEl.classList.remove(`${d}__body--drop-active`);const e=Array.from(t.dataTransfer?.files||[]);e.length&&(this.composer.addFiles(e,"drop"),this.composer.focusInput())}}static get observedAttributes(){return["open","view","position","agent-name","agent-avatar","agent-status","greeting","title","subtitle","placeholder","unread","accent","orb-variant","launcher-label"]}get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",Boolean(t))}get view(){const t=this.getAttribute("view");return i.includes(t)?t:"home"}set view(t){this.setAttribute("view",t)}get position(){const t=this.getAttribute("position");return s.includes(t)?t:"bottom-right"}set position(t){this.setAttribute("position",t)}get unread(){return Number(this.getAttribute("unread")||0)}set unread(t){this.setAttribute("unread",String(t))}set messages(t){this._messages=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get messages(){return this._messages}set conversations(t){this._conversations=Array.isArray(t)?t:[],this.built&&this.renderBody()}get conversations(){return this._conversations}set menuItems(t){this._menuItems=Array.isArray(t)?t:[],this.built&&"home"===this.view&&this.renderBody()}get menuItems(){return this._menuItems}set quickReplies(t){this._quickReplies=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get quickReplies(){return this._quickReplies}set articles(t){this._articles=Array.isArray(t)?t:[],!this.built||"help"!==this.view&&"article"!==this.view||this.renderBody()}get articles(){return this._articles}connectedCallback(){this.hydrateFromAttributes(),this.build(),this.sync(),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){document.removeEventListener("keydown",this.handleDocumentKeyDown)}attributeChangedCallback(t,e,i){if(this.built&&e!==i){if("open"!==t)return"view"===t?(this.sync(),void this.dispatch("db-view-change",{view:this.view})):void this.sync();this.syncOpenState()}}hydrateFromAttributes(){this._messages=parseJson(this.getAttribute("messages"),this._messages),this._conversations=parseJson(this.getAttribute("conversations"),this._conversations),this._menuItems=parseJson(this.getAttribute("menu-items"),this._menuItems),this._quickReplies=parseJson(this.getAttribute("quick-replies"),this._quickReplies),this._articles=parseJson(this.getAttribute("articles"),this._articles)}build(){if(this.built)return;this.built=!0,this.classList.add(d),this.replaceChildren(),this.launcher=el("button",`${d}__launcher`),this.launcher.type="button",this.launcher.setAttribute("aria-haspopup","dialog");const t=el("span",`${d}__launcher-icon ${d}__launcher-icon--open`),e=el("db-chat-orb");e.setAttribute("size","s"),e.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),e.setAttribute("state","idle"),t.append(e);const i=el("span",`${d}__launcher-icon ${d}__launcher-icon--close`);i.append(widgetIcon("close-x",24)),this.launcherBadge=document.createElement("db-badge"),this.launcherBadge.className=`${d}__launcher-badge`,this.launcherBadge.setAttribute("type","number"),this.launcherBadge.setAttribute("variant","danger"),this.launcherBadge.setAttribute("size","s"),this.launcherBadge.setAttribute("max-count","99"),this.launcher.append(t,i),this.launcher.addEventListener("click",()=>this.toggle()),this.panel=el("div",`${d}__panel`),this.panel.setAttribute("role","dialog"),this.panel.setAttribute("aria-modal","false"),this.headerEl=el("div",`${d}__header`),this.bodyEl=el("div",`${d}__body`),this.footerEl=el("div",`${d}__footer`),this.navEl=el("div",`${d}__nav`),this.panel.append(this.headerEl,this.bodyEl,this.footerEl,this.navEl),this.append(this.panel,this.launcher,this.launcherBadge),this.bodyEl.addEventListener("dragenter",this.handlePanelDragEnter),this.bodyEl.addEventListener("dragover",this.handlePanelDragOver),this.bodyEl.addEventListener("dragleave",this.handlePanelDragLeave),this.bodyEl.addEventListener("drop",this.handlePanelDrop)}sync(){s.forEach(t=>this.classList.toggle(`${d}--${t}`,t===this.position)),i.forEach(t=>this.classList.toggle(`${d}--view-${t}`,t===this.view));const t=this.getAttribute("accent");t?this.style.setProperty("--dbchat-accent",t):this.style.removeProperty("--dbchat-accent"),this.renderBadge(),this.renderHeader(),this.renderBody(),this.renderFooter(),this.renderNav(),this.syncOpenState()}syncOpenState(){const t=this.open;this.classList.toggle(`${d}--open`,t),this.launcher.setAttribute("aria-expanded",String(t)),this.panel.setAttribute("aria-hidden",String(!t)),t?this.panel.removeAttribute("inert"):this.panel.setAttribute("inert",""),this.launcher.setAttribute("aria-label",this.getAttribute("launcher-label")||(t?"상담창 닫기":"상담 시작하기")),t&&"conversation"===this.view&&requestAnimationFrame(()=>this.composer?.focusInput())}renderBadge(){const t=this.unread;t>0?this.launcherBadge.setAttribute("count",String(t)):this.launcherBadge.removeAttribute("count"),this.launcherBadge.hidden=t<=0}renderHeader(){this.headerEl.replaceChildren();const e=this.view,i=this.getAttribute("agent-name")||"디지털 상담원",s=el("div",`${d}__header-bar`);if("conversation"===e){const e=this.iconButton(t("chevron-left",20),"뒤로",()=>this.setView("messages")),n=el("div",`${d}__header-identity`),a=el("db-chat-orb");a.className=`${d}__header-orb`,a.setAttribute("size","xs"),a.setAttribute("variant",this.getAttribute("orb-variant")||"violet");const r=el("div",`${d}__header-meta`);r.append(el("span",`${d}__header-name`,i),el("span",`${d}__header-status`,this.getAttribute("agent-status")||"보통 몇 분 내 응답")),n.append(a,r),s.append(e,n,this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else if("article"===e){const e=this.iconButton(t("chevron-left",20),"뒤로",()=>{this._activeArticle=null,this.setView("help")});s.append(e,el("span",`${d}__header-title`,this._activeArticle?.title||"도움말"),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else{const t="home"===e?"홈":"messages"===e?"메시지":"도움말";s.append(el("span",`${d}__header-title`,t),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}this.headerEl.append(s)}iconButton(t,e,i){const s=el("button",`${d}__icon-btn`);return s.type="button",s.setAttribute("aria-label",e),s.append(t),s.addEventListener("click",i),s}renderBody(){if(this.built)switch(this.threadEl=null,this.bodyEl.replaceChildren(),this.bodyEl.classList.toggle(`${d}__body--thread`,"conversation"===this.view),this.bodyEl.classList.toggle(`${d}__body--article`,"article"===this.view),this.view){case"home":this.renderHome();break;case"messages":this.renderMessages();break;case"conversation":this.renderConversation();break;case"help":this.renderHelp();break;case"article":this.renderArticle()}}renderHome(){const e=el("db-chat-welcome",`${d}__welcome`);e.setAttribute("greeting",this.getAttribute("greeting")||"안녕하세요"),e.setAttribute("title",this.getAttribute("title")||"무엇을 도와드릴까요?"),e.setAttribute("highlight",this.getAttribute("highlight")||this.getAttribute("title")||"도와드릴까요"),e.setAttribute("subtitle",this.getAttribute("subtitle")||"제품·정책을 24시간 정확하게 안내해 드릴게요."),e.setAttribute("orb-variant",this.getAttribute("orb-variant")||"violet"),e.setAttribute("align","left"),e.setAttribute("orb-size","m"),this.bodyEl.append(e);const i=this._conversations[0];if(i){const e=el("button",`${d}__recent`);e.type="button";const s=el("db-avatar",`${d}__recent-avatar`);s.setAttribute("size","s"),s.setAttribute("variant","primary"),i.avatarSrc?s.setAttribute("src",i.avatarSrc):s.setAttribute("initials",i.avatarInitials||i.name.charAt(0)||"상"),i.online&&s.setAttribute("status","online"),i.unread&&(s.setAttribute("badge",String(i.unread)),s.setAttribute("type","number"),s.setAttribute("badge-variant","primary"),s.setAttribute("badge-max-count","99"));const n=el("div",`${d}__recent-body`);n.append(el("span",`${d}__recent-title`,"최근 대화 이어가기"),el("span",`${d}__recent-preview`,i.preview||"")),e.append(s,n,t("chevron-right",18)),e.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:i.id}),this.setView("conversation")}),this.bodyEl.append(e)}else this.bodyEl.append(this.buildStartChatButton("대화 시작하기","primary"));if(this._menuItems.length){const t=el("db-chat-suggestions",`${d}__menu`);t.setAttribute("layout","card"),t.setAttribute("columns","1"),t.items=this._menuItems,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,i=this._menuItems.find(t=>(t.id??t.title)===e.id),s=i?.action??"none";this.dispatch("db-menu-select",{...e,action:s}),"conversation"===s?this.startConversation():"help"===s&&this.setView("help")}),this.bodyEl.append(t)}}renderMessages(){if(!this._conversations.length)return void this.bodyEl.append(this.buildEmpty("아직 대화가 없어요","궁금한 점이 있으면 새 대화를 시작해 보세요.",{ctaLabel:"대화 시작하기",onCta:()=>this.startConversation()}));const t=el("div",`${d}__conv-list`);this._conversations.forEach(e=>{const i=el("button",`${d}__conv`);i.type="button";const s=el("db-avatar",`${d}__conv-avatar`);s.setAttribute("size","m"),s.setAttribute("variant","primary"),e.avatarSrc?s.setAttribute("src",e.avatarSrc):s.setAttribute("initials",e.avatarInitials||e.name.charAt(0)||"?"),e.online&&s.setAttribute("status","online");const n=el("div",`${d}__conv-body`),a=el("div",`${d}__conv-top`);if(a.append(el("span",`${d}__conv-name`,e.name)),e.time&&a.append(el("span",`${d}__conv-time`,e.time)),n.append(a,el("span",`${d}__conv-preview`,e.preview||"")),i.append(s,n),e.unread){const t=el("db-badge",`${d}__conv-unread`);t.setAttribute("type","number"),t.setAttribute("variant","primary"),t.setAttribute("size","s"),t.setAttribute("max-count","99"),t.setAttribute("count",String(e.unread)),i.append(t)}i.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:e.id}),this.setView("conversation")}),t.append(i)}),this.bodyEl.append(t)}renderConversation(){const t=el("div",`${d}__thread`);if(this._messages.forEach(e=>t.append(this.buildMessage(e))),this.threadEl=t,this.bodyEl.append(t),this.typing&&t.append(this.buildTyping()),this._quickReplies.length){const t=el("db-chat-suggestions");t.setAttribute("data-dbchat","quick"),t.setAttribute("layout","chip"),t.items=this._quickReplies,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail;this.dispatch("db-quick-reply",e),this.addMessage({role:"user",text:e.title,time:this.nowLabel()})}),this.bodyEl.append(t)}requestAnimationFrame(()=>this.scrollThreadToEnd())}renderHelp(){const e=el("div",`${d}__help-search`),i=el("span",`${d}__help-search-icon`);i.append(t("search",18));const s=el("input",`${d}__help-input`);s.type="search",s.placeholder="도움말 검색…",s.value=this.helpQuery,s.addEventListener("input",()=>{this.helpQuery=s.value,this.renderHelpResults()}),e.append(i,s),this.bodyEl.append(e);const n=el("div",`${d}__help-results`);this.bodyEl.append(n),this.renderHelpResults()}renderHelpResults(){const t=this.bodyEl.querySelector(`.${d}__help-results`);if(!t)return;t.replaceChildren();const e=this.helpQuery.trim().toLowerCase(),i=e?this._articles.filter(t=>t.title.toLowerCase().includes(e)||(t.description||"").toLowerCase().includes(e)||(t.body||"").toLowerCase().includes(e)):this._articles;if(!i.length)return void t.append(this.buildEmpty(e?"검색 결과가 없어요":"문서가 없어요",e?"다른 검색어로 다시 시도해 보세요.":"도움말 문서가 준비되면 여기에 표시됩니다."));const s=el("db-chat-suggestions",`${d}__articles`);s.setAttribute("layout","card"),s.setAttribute("columns","1"),s.items=i,s.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,s=this._articles.find(t=>(t.id??t.title)===e.id)||i.find(t=>(t.id??t.title)===e.id);this.dispatch("db-article-select",{id:e.id,title:e.title}),s&&this.openArticle(s)}),t.append(s)}renderArticle(){const i=this._activeArticle;if(!i)return void this.setView("help");const s=el("article",`${d}__article`);if(i.icon&&e(i.icon)){const e=el("div",`${d}__article-icon`);e.append(t(i.icon,22)),s.append(e)}s.append(el("h2",`${d}__article-title`,i.title)),i.description&&s.append(el("p",`${d}__article-desc`,i.description));const n=el("div",`${d}__article-body`);(i.body||i.description||"").split(/\n{2,}/).forEach(t=>{const e=el("p",void 0,t.trim());e.textContent&&n.append(e)}),s.append(n);const a=this.buildStartChatButton("이 내용으로 문의하기","secondary");s.append(a),this.bodyEl.append(s)}openArticle(t){this._activeArticle=t,this.setView("article")}startConversation(){this.dispatch("db-start-chat"),this.setView("conversation")}buildMessage(t){const e="user"===t.role?"user":"assistant",i=el("db-chat-message");return i.setAttribute("role",e),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time),t.avatarSrc&&i.setAttribute("avatar-src",t.avatarSrc),t.avatarInitials&&i.setAttribute("avatar-initials",t.avatarInitials),t.actions&&i.setAttribute("actions",t.actions),i.textContent=t.text||"",t.attachments?.length&&(i.attachments=t.attachments),i.addEventListener("db-action",t=>{t.stopPropagation(),this.dispatch("db-message-action",t.detail)}),i}buildTyping(){const t=el("db-chat-typing",`${d}__typing`);return t.setAttribute("name",this.getAttribute("agent-name")||"상담원"),t.setAttribute("label","입력 중"),t}buildEmpty(t,e,i){const s=el("div",`${d}__empty`),n=el("db-chat-orb",`${d}__empty-orb`);return n.setAttribute("size","m"),n.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),s.append(n,el("p",`${d}__empty-title`,t),el("p",`${d}__empty-desc`,e)),i?.ctaLabel&&i.onCta&&s.append(this.buildStartChatButton(i.ctaLabel,"primary",i.onCta)),s}buildStartChatButton(t,e="primary",i){const s=el("button",`${d}__cta ${d}__cta--${e}`);return s.type="button",s.append(widgetIcon("chat",18),el("span",void 0,t)),s.addEventListener("click",()=>i?i():this.startConversation()),s}renderFooter(){const t="conversation"===this.view;if(this.footerEl.hidden=!t,!t)return this.composer=null,void this.footerEl.replaceChildren();if(!this.composer){this.footerEl.replaceChildren();const t=el("db-chat-composer",`${d}__composer`);t.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지를 입력하세요…"),t.setAttribute("attach",""),t.setAttribute("voice",""),t.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail,i=e?.value?.trim()||"",s=e?.attachments||[];if(!i&&!s.length)return;const n=s.map(t=>({name:t.name,kind:t.kind,mime:t.mime,url:t.file?URL.createObjectURL(t.file):t.preview}));this.dispatch("db-send",{text:i,attachments:n}),this.addMessage({role:"user",text:i,time:this.nowLabel(),attachments:n.length?n:void 0})}),t.addEventListener("db-stop",t=>{t.stopPropagation(),this.dispatch("db-stop")}),t.addEventListener("db-attach",t=>{t.stopPropagation(),this.dispatch("db-attach")}),t.addEventListener("db-voice",t=>{t.stopPropagation(),this.dispatch("db-voice")}),this.composer=t,this.footerEl.append(t)}}renderNav(){this.navEl.replaceChildren();const t="conversation"!==this.view&&"article"!==this.view;if(this.navEl.hidden=!t,!t)return;[{view:"home",icon:"home",label:"홈"},{view:"messages",icon:"chat",label:"메시지"},{view:"help",icon:"help",label:"도움말"}].forEach(t=>{const e=el("button",`${d}__tab`);e.type="button";const i=this.view===t.view;if(e.classList.toggle(`${d}__tab--active`,i),e.setAttribute("aria-current",String(i)),e.append(widgetIcon(t.icon,22),el("span",`${d}__tab-label`,t.label)),"messages"===t.view&&this.unread>0){const t=el("db-badge",`${d}__tab-badge`);t.setAttribute("type","dot"),t.setAttribute("variant","danger"),t.setAttribute("size","s"),e.append(t)}e.addEventListener("click",()=>{"help"===t.view&&(this.helpQuery=""),this.setView(t.view)}),this.navEl.append(e)})}scrollThreadToEnd(){this.bodyEl.scrollTop=this.bodyEl.scrollHeight}nowLabel(){const t=new Date,e=t.getHours();return`${e<12?"오전":"오후"} ${e%12==0?12:e%12}:${t.getMinutes().toString().padStart(2,"0")}`}dispatch(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}openWidget(){this.open||(this.open=!0,this.dispatch("db-open"),this.dispatch("db-toggle",{open:!0}))}close(){this.open&&(this.open=!1,this.dispatch("db-close"),this.dispatch("db-toggle",{open:!1}))}toggle(){this.open?this.close():this.openWidget()}setView(t){this.view!==t&&(this.view=t)}addMessage(t){if(this._messages=[...this._messages,{id:`m${Date.now()}`,...t}],"conversation"===this.view)if(this.built&&this.threadEl){const e=this.threadEl.querySelector("db-chat-typing");this.threadEl.insertBefore(this.buildMessage(t),e),requestAnimationFrame(()=>this.scrollThreadToEnd())}else this.built&&this.renderBody();else this.view="conversation"}setTyping(t){if(this.typing=Boolean(t),!this.built||"conversation"!==this.view||!this.threadEl)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollThreadToEnd())):!this.typing&&e&&e.remove()}setStreaming(t){this.composer?.setStreaming(Boolean(t))}setMessages(t){this.messages=t}}customElements.get("db-chat-widget")||customElements.define("db-chat-widget",DbChatWidget);export{DbChatWidget};
|
|
1
|
+
import{dbText as t}from"../core/i18n.js";import{createDbIcon as e,isDbIconName as i}from"./db-icons.js";import"./db-chat-orb.js";import"./db-badge.js";import"./db-chat-welcome.js";import"./db-chat-composer.js";import"./db-chat-suggestions.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-avatar.js";import{CHAT_WIDGET_VIEWS as s,CHAT_WIDGET_POSITIONS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r=[{id:"c1",name:"디지털 상담원",avatarInitials:"상",preview:"무엇을 도와드릴까요? 24시간 대기중이에요.",time:"방금",unread:1,online:!0},{id:"c2",name:"결제·환불 지원",avatarInitials:"결",preview:"환불 요청이 정상 접수되었습니다.",time:"어제"},{id:"c3",name:"온보딩 도우미",avatarInitials:"온",preview:"설치가 완료되면 알려드릴게요.",time:"3일 전"}],a={chat:'<path d="M4 5.5A1.5 1.5 0 0 1 5.5 4h13A1.5 1.5 0 0 1 20 5.5v9a1.5 1.5 0 0 1-1.5 1.5H9l-4 3.5V16H5.5A1.5 1.5 0 0 1 4 14.5z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>',home:'<path d="M4 10.5 12 4l8 6.5M6 9.5V19a1 1 0 0 0 1 1h3.5v-4.5a1.5 1.5 0 0 1 3 0V20H17a1 1 0 0 0 1-1V9.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>',help:'<circle cx="12" cy="12" r="8.25" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M9.6 9.4a2.5 2.5 0 0 1 4.9.6c0 1.7-2.5 2-2.5 3.6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="12" cy="16.6" r="0.95" fill="currentColor"/>',"close-x":'<path d="m6.5 6.5 11 11m0-11-11 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>'};function widgetIcon(t,e=20){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox","0 0 24 24"),i.setAttribute("width",String(e)),i.setAttribute("height",String(e)),i.setAttribute("fill","none"),i.setAttribute("aria-hidden","true"),i.setAttribute("focusable","false"),i.innerHTML=a[t],i}function el(t,e,i){const s=document.createElement(t);return e&&(s.className=e),null!=i&&(s.textContent=i),s}function parseJson(t,e){if(!t)return e;try{const i=JSON.parse(t);return Array.isArray(i)?i:e}catch{return e}}const o="designbase-wc-chat";class DbChatWidget extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.helpQuery="",this._activeArticle=null,this._messages=[{id:"m1",role:"assistant",name:"상담원",text:t("안녕하세요. 무엇을 도와드릴까요? 제품과 정책을 24시간 정확하게 안내해 드릴게요."),time:"오후 12:31"},{id:"m2",role:"assistant",name:"상담원",text:t("아래에서 원하시는 도움을 선택하시거나, 자유롭게 메시지를 입력해 주세요."),time:"오후 12:31",actions:"like,dislike"}],this._conversations=r,this._menuItems=[{id:"products",title:t("제품·정책 알아보기"),description:t("AI가 정확하게 안내해 드려요"),icon:"sparkle",action:"help"},{id:"services",title:t("상담원과 대화하기"),description:t("지금 바로 대화를 시작하세요"),icon:"mail",action:"conversation"}],this._quickReplies=[{id:"products",title:t("제품·정책 알아보기")},{id:"services",title:t("서비스 상담하기")},{id:"refund",title:t("환불·결제 문의")},{id:"account",title:t("계정·로그인 도움")}],this._articles=[{id:"a1",title:t("시작하기 — 5분 만에 설정 완료"),description:t("계정 생성부터 첫 배포까지"),icon:"sparkle",body:"계정을 만들고 첫 프로젝트를 배포하기까지 약 5분이면 충분합니다.\n\n1. 회원가입 후 이메일 인증\n2. 워크스페이스 생성\n3. 템플릿 선택 또는 빈 프로젝트 시작\n4. 배포 버튼을 눌러 미리보기 URL 확인"},{id:"a2",title:t("요금제와 결제 안내"),description:t("플랜 비교 · 청구 주기"),icon:"star",body:"Free · Pro · Team 플랜을 제공합니다. 청구는 월간 또는 연간으로 선택할 수 있으며, 연간 결제는 할인율이 적용됩니다.\n\n결제 수단 변경과 영수증 다운로드는 설정 > 결제에서 가능합니다."},{id:"a3",title:t("팀원 초대와 권한 관리"),description:t("역할별 접근 권한 설정"),icon:"user",body:"Owner · Admin · Member · Viewer 역할을 지원합니다.\n\n설정 > 팀원에서 이메일로 초대하고, 역할은 언제든 변경할 수 있습니다."},{id:"a4",title:t("데이터 내보내기·백업"),description:t("안전하게 보관하는 방법"),icon:"download",body:"프로젝트 데이터는 JSON · CSV로 내보낼 수 있습니다. 자동 백업은 Pro 이상에서 매일 실행되며, 설정 > 데이터에서 수동 백업도 가능합니다."}],this.composer=null,this.threadEl=null,this.handleDocumentKeyDown=t=>{"Escape"===t.key&&this.open&&(t.preventDefault(),this.close(),this.launcher?.focus())},this.panelDragDepth=0,this.handlePanelDragEnter=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),this.panelDragDepth+=1,this.bodyEl.classList.add(`${o}__body--drop-active`))},this.handlePanelDragOver=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handlePanelDragLeave=()=>{this.panelDragDepth=Math.max(0,this.panelDragDepth-1),0===this.panelDragDepth&&this.bodyEl.classList.remove(`${o}__body--drop-active`)},this.handlePanelDrop=t=>{if("conversation"!==this.view||!this.composer)return;if(!Array.from(t.dataTransfer?.types||[]).includes("Files"))return;t.preventDefault(),t.stopPropagation(),this.panelDragDepth=0,this.bodyEl.classList.remove(`${o}__body--drop-active`);const e=Array.from(t.dataTransfer?.files||[]);e.length&&(this.composer.addFiles(e,"drop"),this.composer.focusInput())}}static get observedAttributes(){return["open","view","position","agent-name","agent-avatar","agent-status","greeting","title","subtitle","placeholder","unread","accent","orb-variant","launcher-label"]}get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",Boolean(t))}get view(){const t=this.getAttribute("view");return s.includes(t)?t:"home"}set view(t){this.setAttribute("view",t)}get position(){const t=this.getAttribute("position");return n.includes(t)?t:"bottom-right"}set position(t){this.setAttribute("position",t)}get unread(){return Number(this.getAttribute("unread")||0)}set unread(t){this.setAttribute("unread",String(t))}set messages(t){this._messages=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get messages(){return this._messages}set conversations(t){this._conversations=Array.isArray(t)?t:[],this.built&&this.renderBody()}get conversations(){return this._conversations}set menuItems(t){this._menuItems=Array.isArray(t)?t:[],this.built&&"home"===this.view&&this.renderBody()}get menuItems(){return this._menuItems}set quickReplies(t){this._quickReplies=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get quickReplies(){return this._quickReplies}set articles(t){this._articles=Array.isArray(t)?t:[],!this.built||"help"!==this.view&&"article"!==this.view||this.renderBody()}get articles(){return this._articles}connectedCallback(){this.hydrateFromAttributes(),this.build(),this.sync(),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){document.removeEventListener("keydown",this.handleDocumentKeyDown)}attributeChangedCallback(t,e,i){if(this.built&&e!==i){if("open"!==t)return"view"===t?(this.sync(),void this.dispatch("db-view-change",{view:this.view})):void this.sync();this.syncOpenState()}}hydrateFromAttributes(){this._messages=parseJson(this.getAttribute("messages"),this._messages),this._conversations=parseJson(this.getAttribute("conversations"),this._conversations),this._menuItems=parseJson(this.getAttribute("menu-items"),this._menuItems),this._quickReplies=parseJson(this.getAttribute("quick-replies"),this._quickReplies),this._articles=parseJson(this.getAttribute("articles"),this._articles)}build(){if(this.built)return;this.built=!0,this.classList.add(o),this.replaceChildren(),this.launcher=el("button",`${o}__launcher`),this.launcher.type="button",this.launcher.setAttribute("aria-haspopup","dialog");const t=el("span",`${o}__launcher-icon ${o}__launcher-icon--open`),e=el("db-chat-orb");e.setAttribute("size","s"),e.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),e.setAttribute("state","idle"),t.append(e);const i=el("span",`${o}__launcher-icon ${o}__launcher-icon--close`);i.append(widgetIcon("close-x",24)),this.launcherBadge=document.createElement("db-badge"),this.launcherBadge.className=`${o}__launcher-badge`,this.launcherBadge.setAttribute("type","number"),this.launcherBadge.setAttribute("variant","danger"),this.launcherBadge.setAttribute("size","s"),this.launcherBadge.setAttribute("max-count","99"),this.launcher.append(t,i),this.launcher.addEventListener("click",()=>this.toggle()),this.panel=el("div",`${o}__panel`),this.panel.setAttribute("role","dialog"),this.panel.setAttribute("aria-modal","false"),this.headerEl=el("div",`${o}__header`),this.bodyEl=el("div",`${o}__body`),this.footerEl=el("div",`${o}__footer`),this.navEl=el("div",`${o}__nav`),this.panel.append(this.headerEl,this.bodyEl,this.footerEl,this.navEl),this.append(this.panel,this.launcher,this.launcherBadge),this.bodyEl.addEventListener("dragenter",this.handlePanelDragEnter),this.bodyEl.addEventListener("dragover",this.handlePanelDragOver),this.bodyEl.addEventListener("dragleave",this.handlePanelDragLeave),this.bodyEl.addEventListener("drop",this.handlePanelDrop)}sync(){n.forEach(t=>this.classList.toggle(`${o}--${t}`,t===this.position)),s.forEach(t=>this.classList.toggle(`${o}--view-${t}`,t===this.view));const t=this.getAttribute("accent");t?this.style.setProperty("--dbchat-accent",t):this.style.removeProperty("--dbchat-accent"),this.renderBadge(),this.renderHeader(),this.renderBody(),this.renderFooter(),this.renderNav(),this.syncOpenState()}syncOpenState(){const t=this.open;this.classList.toggle(`${o}--open`,t),this.launcher.setAttribute("aria-expanded",String(t)),this.panel.setAttribute("aria-hidden",String(!t)),t?this.panel.removeAttribute("inert"):this.panel.setAttribute("inert",""),this.launcher.setAttribute("aria-label",this.getAttribute("launcher-label")||(t?"상담창 닫기":"상담 시작하기")),t&&"conversation"===this.view&&requestAnimationFrame(()=>this.composer?.focusInput())}renderBadge(){const t=this.unread;t>0?this.launcherBadge.setAttribute("count",String(t)):this.launcherBadge.removeAttribute("count"),this.launcherBadge.hidden=t<=0}renderHeader(){this.headerEl.replaceChildren();const t=this.view,i=this.getAttribute("agent-name")||"디지털 상담원",s=el("div",`${o}__header-bar`);if("conversation"===t){const t=this.iconButton(e("chevron-left",20),"뒤로",()=>this.setView("messages")),n=el("div",`${o}__header-identity`),r=el("db-chat-orb");r.className=`${o}__header-orb`,r.setAttribute("size","xs"),r.setAttribute("variant",this.getAttribute("orb-variant")||"violet");const a=el("div",`${o}__header-meta`);a.append(el("span",`${o}__header-name`,i),el("span",`${o}__header-status`,this.getAttribute("agent-status")||"보통 몇 분 내 응답")),n.append(r,a),s.append(t,n,this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else if("article"===t){const t=this.iconButton(e("chevron-left",20),"뒤로",()=>{this._activeArticle=null,this.setView("help")});s.append(t,el("span",`${o}__header-title`,this._activeArticle?.title||"도움말"),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else{const e="home"===t?"홈":"messages"===t?"메시지":"도움말";s.append(el("span",`${o}__header-title`,e),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}this.headerEl.append(s)}iconButton(t,e,i){const s=el("button",`${o}__icon-btn`);return s.type="button",s.setAttribute("aria-label",e),s.append(t),s.addEventListener("click",i),s}renderBody(){if(this.built)switch(this.threadEl=null,this.bodyEl.replaceChildren(),this.bodyEl.classList.toggle(`${o}__body--thread`,"conversation"===this.view),this.bodyEl.classList.toggle(`${o}__body--article`,"article"===this.view),this.view){case"home":this.renderHome();break;case"messages":this.renderMessages();break;case"conversation":this.renderConversation();break;case"help":this.renderHelp();break;case"article":this.renderArticle()}}renderHome(){const t=el("db-chat-welcome",`${o}__welcome`);t.setAttribute("greeting",this.getAttribute("greeting")||"안녕하세요"),t.setAttribute("title",this.getAttribute("title")||"무엇을 도와드릴까요?"),t.setAttribute("highlight",this.getAttribute("highlight")||this.getAttribute("title")||"도와드릴까요"),t.setAttribute("subtitle",this.getAttribute("subtitle")||"제품·정책을 24시간 정확하게 안내해 드릴게요."),t.setAttribute("orb-variant",this.getAttribute("orb-variant")||"violet"),t.setAttribute("align","left"),t.setAttribute("orb-size","m"),this.bodyEl.append(t);const i=this._conversations[0];if(i){const t=el("button",`${o}__recent`);t.type="button";const s=el("db-avatar",`${o}__recent-avatar`);s.setAttribute("size","s"),s.setAttribute("variant","primary"),i.avatarSrc?s.setAttribute("src",i.avatarSrc):s.setAttribute("initials",i.avatarInitials||i.name.charAt(0)||"상"),i.online&&s.setAttribute("status","online"),i.unread&&(s.setAttribute("badge",String(i.unread)),s.setAttribute("type","number"),s.setAttribute("badge-variant","primary"),s.setAttribute("badge-max-count","99"));const n=el("div",`${o}__recent-body`);n.append(el("span",`${o}__recent-title`,"최근 대화 이어가기"),el("span",`${o}__recent-preview`,i.preview||"")),t.append(s,n,e("chevron-right",18)),t.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:i.id}),this.setView("conversation")}),this.bodyEl.append(t)}else this.bodyEl.append(this.buildStartChatButton("대화 시작하기","primary"));if(this._menuItems.length){const t=el("db-chat-suggestions",`${o}__menu`);t.setAttribute("layout","card"),t.setAttribute("columns","1"),t.items=this._menuItems,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,i=this._menuItems.find(t=>(t.id??t.title)===e.id),s=i?.action??"none";this.dispatch("db-menu-select",{...e,action:s}),"conversation"===s?this.startConversation():"help"===s&&this.setView("help")}),this.bodyEl.append(t)}}renderMessages(){if(!this._conversations.length)return void this.bodyEl.append(this.buildEmpty("아직 대화가 없어요","궁금한 점이 있으면 새 대화를 시작해 보세요.",{ctaLabel:t("대화 시작하기"),onCta:()=>this.startConversation()}));const e=el("div",`${o}__conv-list`);this._conversations.forEach(t=>{const i=el("button",`${o}__conv`);i.type="button";const s=el("db-avatar",`${o}__conv-avatar`);s.setAttribute("size","m"),s.setAttribute("variant","primary"),t.avatarSrc?s.setAttribute("src",t.avatarSrc):s.setAttribute("initials",t.avatarInitials||t.name.charAt(0)||"?"),t.online&&s.setAttribute("status","online");const n=el("div",`${o}__conv-body`),r=el("div",`${o}__conv-top`);if(r.append(el("span",`${o}__conv-name`,t.name)),t.time&&r.append(el("span",`${o}__conv-time`,t.time)),n.append(r,el("span",`${o}__conv-preview`,t.preview||"")),i.append(s,n),t.unread){const e=el("db-badge",`${o}__conv-unread`);e.setAttribute("type","number"),e.setAttribute("variant","primary"),e.setAttribute("size","s"),e.setAttribute("max-count","99"),e.setAttribute("count",String(t.unread)),i.append(e)}i.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:t.id}),this.setView("conversation")}),e.append(i)}),this.bodyEl.append(e)}renderConversation(){const t=el("div",`${o}__thread`);if(this._messages.forEach(e=>t.append(this.buildMessage(e))),this.threadEl=t,this.bodyEl.append(t),this.typing&&t.append(this.buildTyping()),this._quickReplies.length){const t=el("db-chat-suggestions");t.setAttribute("data-dbchat","quick"),t.setAttribute("layout","chip"),t.items=this._quickReplies,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail;this.dispatch("db-quick-reply",e),this.addMessage({role:"user",text:e.title,time:this.nowLabel()})}),this.bodyEl.append(t)}requestAnimationFrame(()=>this.scrollThreadToEnd())}renderHelp(){const i=el("div",`${o}__help-search`),s=el("span",`${o}__help-search-icon`);s.append(e("search",18));const n=el("input",`${o}__help-input`);n.type="search",n.placeholder=t("도움말 검색…"),n.value=this.helpQuery,n.addEventListener("input",()=>{this.helpQuery=n.value,this.renderHelpResults()}),i.append(s,n),this.bodyEl.append(i);const r=el("div",`${o}__help-results`);this.bodyEl.append(r),this.renderHelpResults()}renderHelpResults(){const t=this.bodyEl.querySelector(`.${o}__help-results`);if(!t)return;t.replaceChildren();const e=this.helpQuery.trim().toLowerCase(),i=e?this._articles.filter(t=>t.title.toLowerCase().includes(e)||(t.description||"").toLowerCase().includes(e)||(t.body||"").toLowerCase().includes(e)):this._articles;if(!i.length)return void t.append(this.buildEmpty(e?"검색 결과가 없어요":"문서가 없어요",e?"다른 검색어로 다시 시도해 보세요.":"도움말 문서가 준비되면 여기에 표시됩니다."));const s=el("db-chat-suggestions",`${o}__articles`);s.setAttribute("layout","card"),s.setAttribute("columns","1"),s.items=i,s.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,s=this._articles.find(t=>(t.id??t.title)===e.id)||i.find(t=>(t.id??t.title)===e.id);this.dispatch("db-article-select",{id:e.id,title:e.title}),s&&this.openArticle(s)}),t.append(s)}renderArticle(){const t=this._activeArticle;if(!t)return void this.setView("help");const s=el("article",`${o}__article`);if(t.icon&&i(t.icon)){const i=el("div",`${o}__article-icon`);i.append(e(t.icon,22)),s.append(i)}s.append(el("h2",`${o}__article-title`,t.title)),t.description&&s.append(el("p",`${o}__article-desc`,t.description));const n=el("div",`${o}__article-body`);(t.body||t.description||"").split(/\n{2,}/).forEach(t=>{const e=el("p",void 0,t.trim());e.textContent&&n.append(e)}),s.append(n);const r=this.buildStartChatButton("이 내용으로 문의하기","secondary");s.append(r),this.bodyEl.append(s)}openArticle(t){this._activeArticle=t,this.setView("article")}startConversation(){this.dispatch("db-start-chat"),this.setView("conversation")}buildMessage(t){const e="user"===t.role?"user":"assistant",i=el("db-chat-message");return i.setAttribute("role",e),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time),t.avatarSrc&&i.setAttribute("avatar-src",t.avatarSrc),t.avatarInitials&&i.setAttribute("avatar-initials",t.avatarInitials),t.actions&&i.setAttribute("actions",t.actions),i.textContent=t.text||"",t.attachments?.length&&(i.attachments=t.attachments),i.addEventListener("db-action",t=>{t.stopPropagation(),this.dispatch("db-message-action",t.detail)}),i}buildTyping(){const t=el("db-chat-typing",`${o}__typing`);return t.setAttribute("name",this.getAttribute("agent-name")||"상담원"),t.setAttribute("label","입력 중"),t}buildEmpty(t,e,i){const s=el("div",`${o}__empty`),n=el("db-chat-orb",`${o}__empty-orb`);return n.setAttribute("size","m"),n.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),s.append(n,el("p",`${o}__empty-title`,t),el("p",`${o}__empty-desc`,e)),i?.ctaLabel&&i.onCta&&s.append(this.buildStartChatButton(i.ctaLabel,"primary",i.onCta)),s}buildStartChatButton(t,e="primary",i){const s=el("button",`${o}__cta ${o}__cta--${e}`);return s.type="button",s.append(widgetIcon("chat",18),el("span",void 0,t)),s.addEventListener("click",()=>i?i():this.startConversation()),s}renderFooter(){const t="conversation"===this.view;if(this.footerEl.hidden=!t,!t)return this.composer=null,void this.footerEl.replaceChildren();if(!this.composer){this.footerEl.replaceChildren();const t=el("db-chat-composer",`${o}__composer`);t.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지를 입력하세요…"),t.setAttribute("attach",""),t.setAttribute("voice",""),t.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail,i=e?.value?.trim()||"",s=e?.attachments||[];if(!i&&!s.length)return;const n=s.map(t=>({name:t.name,kind:t.kind,mime:t.mime,url:t.file?URL.createObjectURL(t.file):t.preview}));this.dispatch("db-send",{text:i,attachments:n}),this.addMessage({role:"user",text:i,time:this.nowLabel(),attachments:n.length?n:void 0})}),t.addEventListener("db-stop",t=>{t.stopPropagation(),this.dispatch("db-stop")}),t.addEventListener("db-attach",t=>{t.stopPropagation(),this.dispatch("db-attach")}),t.addEventListener("db-voice",t=>{t.stopPropagation(),this.dispatch("db-voice")}),this.composer=t,this.footerEl.append(t)}}renderNav(){this.navEl.replaceChildren();const e="conversation"!==this.view&&"article"!==this.view;if(this.navEl.hidden=!e,!e)return;[{view:"home",icon:"home",label:t("홈")},{view:"messages",icon:"chat",label:t("메시지")},{view:"help",icon:"help",label:t("도움말")}].forEach(t=>{const e=el("button",`${o}__tab`);e.type="button";const i=this.view===t.view;if(e.classList.toggle(`${o}__tab--active`,i),e.setAttribute("aria-current",String(i)),e.append(widgetIcon(t.icon,22),el("span",`${o}__tab-label`,t.label)),"messages"===t.view&&this.unread>0){const t=el("db-badge",`${o}__tab-badge`);t.setAttribute("type","dot"),t.setAttribute("variant","danger"),t.setAttribute("size","s"),e.append(t)}e.addEventListener("click",()=>{"help"===t.view&&(this.helpQuery=""),this.setView(t.view)}),this.navEl.append(e)})}scrollThreadToEnd(){this.bodyEl.scrollTop=this.bodyEl.scrollHeight}nowLabel(){const t=new Date,e=t.getHours();return`${e<12?"오전":"오후"} ${e%12==0?12:e%12}:${t.getMinutes().toString().padStart(2,"0")}`}dispatch(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}openWidget(){this.open||(this.open=!0,this.dispatch("db-open"),this.dispatch("db-toggle",{open:!0}))}close(){this.open&&(this.open=!1,this.dispatch("db-close"),this.dispatch("db-toggle",{open:!1}))}toggle(){this.open?this.close():this.openWidget()}setView(t){this.view!==t&&(this.view=t)}addMessage(t){if(this._messages=[...this._messages,{id:`m${Date.now()}`,...t}],"conversation"===this.view)if(this.built&&this.threadEl){const e=this.threadEl.querySelector("db-chat-typing");this.threadEl.insertBefore(this.buildMessage(t),e),requestAnimationFrame(()=>this.scrollThreadToEnd())}else this.built&&this.renderBody();else this.view="conversation"}setTyping(t){if(this.typing=Boolean(t),!this.built||"conversation"!==this.view||!this.threadEl)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollThreadToEnd())):!this.typing&&e&&e.remove()}setStreaming(t){this.composer?.setStreaming(Boolean(t))}setMessages(t){this.messages=t}}customElements.get("db-chat-widget")||customElements.define("db-chat-widget",DbChatWidget);export{DbChatWidget};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isDbIconName as
|
|
1
|
+
import{dbFormat as e}from"../core/i18n.js";import{isDbIconName as t,createDbIcon as i}from"./db-icons.js";import{CHIP_SIZES as s,CHIP_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbChip extends HTMLElement{static get observedAttributes(){return["label","size","variant","deletable","disabled","selected","full-width","clickable","start-icon","end-icon"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}iconSize(e){return"s"===e?12:"l"===e?20:16}render(){const a=this.getAttribute("label")||this.textContent?.trim()||"",c=this.getAttribute("size")||"m",d=this.getAttribute("variant")||"default",l=this.hasAttribute("disabled"),b=this.hasAttribute("deletable"),r=this.hasAttribute("selected"),o=this.hasAttribute("full-width"),h=this.hasAttribute("clickable"),p=this.getAttribute("start-icon"),u=this.getAttribute("end-icon"),m=s.includes(c)?c:"m",g=n.includes(d)?d:"default",w=this.iconSize(m);if(this.className=["designbase-wc-chip",`designbase-wc-chip--${m}`,`designbase-wc-chip--${g}`,b&&"designbase-wc-chip--deletable",l&&"designbase-wc-chip--disabled",r&&"designbase-wc-chip--selected",h&&"designbase-wc-chip--clickable",o&&"designbase-wc-chip--full-width"].filter(Boolean).join(" "),this.replaceChildren(),h&&!l?(this.setAttribute("role","button"),this.tabIndex=0,this.setAttribute("aria-pressed",String(r))):(this.removeAttribute("role"),this.removeAttribute("tabindex"),this.removeAttribute("aria-pressed")),p&&t(p)){const e=document.createElement("span");e.className="designbase-wc-chip__start-icon",e.append(i(p,w)),this.append(e)}const A=document.createElement("span");if(A.className="designbase-wc-chip__label",A.textContent=a,this.append(A),u&&t(u)&&!b){const e=document.createElement("span");e.className="designbase-wc-chip__end-icon",e.append(i(u,w)),this.append(e)}if(b){const t=document.createElement("button");t.type="button",t.className="designbase-wc-chip__delete-button",t.disabled=l,t.setAttribute("aria-label",e("{0} 삭제",[a])),t.append(i("close",w)),t.addEventListener("click",e=>{e.stopPropagation(),l||this.dispatchEvent(new CustomEvent("db-delete",{bubbles:!0,composed:!0}))}),this.append(t)}this.onclick=()=>{!l&&h&&this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0}))},this.onkeydown=e=>{"Enter"!==e.key&&" "!==e.key||l||!h||(e.preventDefault(),this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0})))}}}customElements.get("db-chip")||customElements.define("db-chip",DbChip);export{DbChip};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ensureHighlightReady as
|
|
1
|
+
import{dbFormat as e}from"../core/i18n.js";import{ensureHighlightReady as t,highlightCodeSync as i,formatLanguageLabel as s}from"./highlight-code.js";const o="designbase-wc-code-block";class DbCodeBlock extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.renderToken=0,this.internalActiveId="",this.copied=!1,this.editedCodes={},this.handleSegmentChange=e=>{const t=e,i=t.detail?.value;i&&(this.hasAttribute("active-id")||(this.internalActiveId=i),this.dispatchEvent(new CustomEvent("db-active-id-change",{detail:{activeId:i},bubbles:!0,composed:!0})),this.render())}}static get observedAttributes(){return["items","code","language","default-active-id","active-id","show-language","highlight","theme","show-copy","copied-label","copy-label","min-height","max-height","overflow","editable"]}connectedCallback(){this.style.display="block",this.built||(this.built=!0,this.internalActiveId=this.getAttribute("default-active-id")||this.getResolvedItems()[0]?.id||""),this.render(),t().then(()=>{this.isConnected&&this.built&&this.render()}),this.addEventListener("db-change",this.handleSegmentChange)}disconnectedCallback(){this.removeEventListener("db-change",this.handleSegmentChange),this.copyTimeout&&window.clearTimeout(this.copyTimeout)}attributeChangedCallback(e){if(this.built)if("active-id"!==e){if("items"===e||"code"===e||"language"===e){const e=this.getResolvedItems();e.find(e=>e.id===this.getActiveId())||(this.internalActiveId=e[0]?.id||"")}this.render()}else this.render()}getBool(e,t=!0){if(!this.hasAttribute(e))return t;const i=this.getAttribute(e);return""===i||"true"===i}getResolvedItems(){const e=this.getAttribute("items");if(e)try{const t=JSON.parse(e);if(Array.isArray(t)&&t.length>0)return t}catch{}const t=this.getAttribute("code");if(t){const e=this.getAttribute("language")||"tsx";return[{id:"default",label:e,code:t,language:e}]}return[]}getActiveId(){return this.getAttribute("active-id")||this.internalActiveId}getActiveItem(){const e=this.getResolvedItems();return e.find(e=>e.id===this.getActiveId())||e[0]}toCssSize(e){if(e)return/^\d+$/.test(e)?`${e}px`:e}async handleCopy(){const e=this.getActiveItem(),t=this.getBool("editable",!1),i=(t?this.editedCodes[e?.id??""]??e?.code:e?.code)?.trim();if(i){try{await navigator.clipboard.writeText(i)}catch{const e=document.createElement("textarea");e.value=i,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select(),document.execCommand("copy"),e.remove()}this.copied=!0,this.render(),this.copyTimeout&&window.clearTimeout(this.copyTimeout),this.copyTimeout=window.setTimeout(()=>{this.copied=!1,this.render()},1600)}}render(){const e=++this.renderToken;this.renderNow(e)}renderNow(t){const n=this.getActiveItem();if(!n)return void this.replaceChildren();const d=this.getResolvedItems(),c=d.length>1,a=this.getAttribute("language")||"tsx",r=n.language??a,l=this.getBool("show-language",!0),h=this.getBool("highlight",!0),u=this.getAttribute("theme")||"auto",m=this.getBool("show-copy",!0),b=this.getAttribute("copied-label")||"복사됨",g=this.getAttribute("copy-label")||"복사",p=this.getAttribute("overflow")||"scroll-x",v=this.getBool("editable",!1),A=v?this.editedCodes[n.id]??n.code:n.code,y=A.trim();let f=null;if(h&&A)try{f=i(A,r)}catch{f=null}if(t!==this.renderToken)return;const C=this.toCssSize(this.getAttribute("min-height")),E=this.toCssSize(this.getAttribute("max-height"));C?this.style.setProperty("--db-code-block-min-height",C):this.style.removeProperty("--db-code-block-min-height"),E?this.style.setProperty("--db-code-block-max-height",E):this.style.removeProperty("--db-code-block-max-height"),this.className=[o,`${o}--overflow-${p}`,"auto"!==u&&`${o}--theme-${u}`,v&&`${o}--editable`].filter(Boolean).join(" ");const _=document.createElement("div");_.className=`${o}__toolbar`;const x=document.createElement("div");if(x.className=`${o}__toolbar-left`,c){const e=document.createElement("db-segment-control");e.setAttribute("size","s"),e.setAttribute("options",JSON.stringify(d.map(e=>({value:e.id,label:e.label})))),e.setAttribute("value",this.getActiveId()),x.append(e)}if(l){const e=document.createElement("span");e.className=`${o}__language`,e.textContent=s(r),x.append(e)}if(_.append(x),m){const e=document.createElement("db-button");e.setAttribute("type","button"),e.setAttribute("variant","tertiary"),e.setAttribute("size","s"),e.setAttribute("start-icon",this.copied?"done":"copy"),e.textContent=this.copied?b:g,e.addEventListener("db-click",()=>this.handleCopy()),_.append(e)}const $=document.createElement("div");if($.className=`${o}__body`,c&&n.description){const e=document.createElement("p");e.className=`${o}__caption`,e.textContent=n.description,$.append(e)}if(v){void 0===this.editedCodes[n.id]&&(this.editedCodes[n.id]=n.code);const t=document.createElement("div");t.className=`${o}__editor`;const d=document.createElement("pre");d.className=`${o}__pre ${o}__pre--mirror`,d.setAttribute("aria-hidden","true");const c=document.createElement("code");c.className=`${o}__code language-${r}`,f?c.innerHTML=f:c.textContent=A,d.append(c);const a=document.createElement("textarea");a.className=`${o}__textarea`,a.value=A,a.spellcheck=!1,a.setAttribute("autocapitalize","off"),a.setAttribute("autocorrect","off"),a.setAttribute("aria-label",e("{0} 코드 편집",[s(r)])),a.addEventListener("input",()=>{this.editedCodes[n.id]=a.value;const e=h&&a.value?i(a.value,r):null;e?c.innerHTML=e:c.textContent=a.value,this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:a.value,itemId:n.id},bubbles:!0,composed:!0}))}),t.append(d,a),$.append(t)}else{const e=document.createElement("pre");e.className=`${o}__pre`;const t=document.createElement("code");t.className=`${o}__code language-${r}`,f?t.innerHTML=f:t.textContent=y,e.append(t),$.append(e)}this.replaceChildren(_,$)}}customElements.get("db-code-block")||customElements.define("db-code-block",DbCodeBlock);export{DbCodeBlock};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hexToRgb as t,rgbToHsv as e,hsvToRgb as i,toHex as s,rgbToHsl as l,formatColor as n,normalizeHex as o,isHex as r,clamp as a}from"./color-picker-utils.js";import{getDbComponentIconSize as h,createDbIcon as d}from"./db-icons.js";import{rafThrottle as p}from"../core/dom.js";import{coerceToContract as u}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{COLOR_PICKER_SIZES as c,COLOR_PICKER_POSITIONS as g}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const m=c,E=["dropdown","modal"],b=["input","swatch"],v=g,y="#006FFF",I=t(y)??{r:0,g:111,b:255},w=e(I.r,I.g,I.b),A=JSON.stringify([{label:"HEX",value:"hex"},{label:"RGB",value:"rgb"},{label:"RGBA",value:"rgba"},{label:"HSL",value:"hsl"},{label:"HSLA",value:"hsla"}]);class DbColorPicker extends HTMLElement{constructor(){super(...arguments),this.h=w.h,this.s=w.s,this.v=w.v,this.a=100,this.open=!1,this.format="hex",this.colorInput=y,this.alphaInput="100",this.isCopied=!1,this.tempColor="",this.dragging=null,this.mounted=!1,this.copiedTimer=null,this.debounceTimer=null,this.lastEmittedHex="",this.suppressNextValueSync=!1,this.didMount=!1,this.triggerInputFocused=!1,this.panelInputFocused=!1,this.panelAlphaInputFocused=!1,this.handleOutsideClick=t=>{if(!this.open||"dropdown"!==this.getOption("type",E,"dropdown"))return;const e=t.target;this.contains(e)||this.formatSelectEl?.contains(e)||(this.open=!1,this.update())},this.handleDocumentMouseMove=p(t=>{"area"===this.dragging&&(this.getBool("disabled")||this.getBool("readonly")||this.updateFromArea(t.clientX,t.clientY))}),this.handleDocumentMouseUp=()=>{"area"===this.dragging&&(this.dragging=null,this.onColorStateChanged(!0))},this.handleModalCancel=()=>{const i=t(this.tempColor);if(i){const t=e(i.r,i.g,i.b);this.h=t.h,this.s=t.s,this.v=t.v}this.open=!1,this.update(),this.dispatchEvent(new CustomEvent("db-cancel",{bubbles:!0,composed:!0}))},this.handleModalApply=()=>{this.open=!1,this.actuallyEmit(),this.dispatchEvent(new CustomEvent("db-apply",{detail:{value:this.getOutward()},bubbles:!0,composed:!0})),this.update()}}static get observedAttributes(){return["size","type","trigger","position","value","default-value","show-input","show-alpha","show-format-selector","show-copy-button","show-eyedropper","disabled","readonly","full-width","error","on-change-format","change-debounce-ms","emit-during-drag"]}connectedCallback(){this.mounted||this.mount(),this.syncFromValueAttr(!0),this.update(),document.addEventListener("mousedown",this.handleOutsideClick),document.addEventListener("mousemove",this.handleDocumentMouseMove),document.addEventListener("mouseup",this.handleDocumentMouseUp)}disconnectedCallback(){document.removeEventListener("mousedown",this.handleOutsideClick),document.removeEventListener("mousemove",this.handleDocumentMouseMove),document.removeEventListener("mouseup",this.handleDocumentMouseUp),this.copiedTimer&&clearTimeout(this.copiedTimer),this.debounceTimer&&clearTimeout(this.debounceTimer)}attributeChangedCallback(t){this.mounted&&("value"!==t&&"default-value"!==t||this.syncFromValueAttr(!1),this.update())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return""===i||"true"===i}getOption(t,e,i){return u(this.getAttribute(t),e,i)}getIconSize(){return h(this.getOption("size",m,"m"))}getRgb(){return i(this.h,this.s,this.v)}getHex(){const t=this.getRgb();return s(t.r,t.g,t.b)}getHsl(){const t=this.getRgb();return l(t.r,t.g,t.b)}getUiFormatted(){const t=this.getRgb(),e=this.getHex(),i=this.getHsl();return n(this.format,t,e,i,this.a)}getOutward(){return"hex"===("ui"===this.getAttribute("on-change-format")?"ui":"hex")?this.getHex():this.getUiFormatted()}syncFromValueAttr(i){const s=this.getAttribute("value")??(i?this.getAttribute("default-value"):null)??(i?y:null);if(null==s)return;const l=o(s);if(!r(l))return;if(!i&&this.suppressNextValueSync&&l===this.lastEmittedHex)return void(this.suppressNextValueSync=!1);const n=t(l);if(!n)return;const a=e(n.r,n.g,n.b);this.h=a.h,this.s=a.s,this.v=a.v,this.colorInput=l,this.lastEmittedHex=l}actuallyEmit(){const t=o(this.getHex()),e=this.hasAttribute("value")&&r(this.getAttribute("value")||"")?o(this.getAttribute("value")||""):null;e&&e===t||this.lastEmittedHex!==t&&(this.lastEmittedHex=t,this.suppressNextValueSync=!0,this.setAttribute("value",t),this.dispatchEvent(new CustomEvent("db-change",{detail:{value:this.getOutward()},bubbles:!0,composed:!0})))}scheduleEmit(){const t=this.getBool("emit-during-drag",!0);if(this.dragging&&!t)return;const e=Number(this.getAttribute("change-debounce-ms")||"0");if(e>0)return this.debounceTimer&&clearTimeout(this.debounceTimer),void(this.debounceTimer=setTimeout(()=>this.actuallyEmit(),e));this.actuallyEmit()}onColorStateChanged(t=!1){this.didMount?(!t||this.getBool("emit-during-drag",!0),this.scheduleEmit()):this.didMount=!0}updateFromArea(t,e){const i=this.colorFieldEl;if(!i)return;const s=i.getBoundingClientRect(),l=a(t-s.left,0,s.width),n=a(e-s.top,0,s.height);this.s=l/s.width*100,this.v=100-n/s.height*100,this.updateVisuals(),this.onColorStateChanged()}getInputValue(t){return t?t.getAttribute("value")??"":""}setInputValue(t,e){t&&void 0!==e&&t.setAttribute("value",e)}getPanelControlSize(){return"s"===this.getOption("size",m,"m")?"s":"m"}commitColorInput(i){const s="trigger"===i?this.triggerInputEl?.value??"":this.getInputValue(this.panelValueInputEl),l=s.trim();if(/^#([0-9A-Fa-f]{6})$/.test(l)){const i=t(l),s=e(i.r,i.g,i.b);return this.h=s.h,this.s=s.s,this.v=s.v,this.colorInput=o(l),this.updateVisuals(),void this.onColorStateChanged()}let n=/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i.exec(s);if(n){const t=a(parseInt(n[1],10),0,255),i=a(parseInt(n[2],10),0,255),s=a(parseInt(n[3],10),0,255),l=e(t,i,s);return this.h=l.h,this.s=l.s,this.v=l.v,this.colorInput=`rgb(${t}, ${i}, ${s})`,this.updateVisuals(),void this.onColorStateChanged()}if(n=/^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0?\.\d+)\s*\)$/i.exec(l),n){const t=a(parseInt(n[1],10),0,255),i=a(parseInt(n[2],10),0,255),s=a(parseInt(n[3],10),0,255),l=parseFloat(n[4]),o=e(t,i,s);this.h=o.h,this.s=o.s,this.v=o.v,this.a=Math.round(100*l),this.alphaInput=String(this.a),this.colorInput=`rgba(${t}, ${i}, ${s}, ${l})`,this.updateVisuals(),this.onColorStateChanged()}}commitAlphaInput(){const t=Number(this.getInputValue(this.panelAlphaInputEl).trim()||this.alphaInput);if(!Number.isNaN(t)&&t>=0&&t<=100){const e=Math.round(t);this.a=e,this.alphaInput=String(e),this.updateVisuals(),this.onColorStateChanged()}else this.alphaInput=String(this.a),this.panelAlphaInputEl&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput)}async onCopy(){try{await navigator.clipboard.writeText(this.getUiFormatted()),this.isCopied=!0,this.updateCopyButtons(),this.copiedTimer&&clearTimeout(this.copiedTimer),this.copiedTimer=setTimeout(()=>{this.isCopied=!1,this.updateCopyButtons()},1600)}catch{}}async onEyedrop(){try{if("EyeDropper"in window){const i=new window.EyeDropper,{sRGBHex:s}=await i.open(),l=t(s);if(!l)return;const n=e(l.r,l.g,l.b);this.h=n.h,this.s=n.s,this.v=n.v,this.updateVisuals(),this.onColorStateChanged()}}catch{}}toggleOpen(){if(this.getBool("disabled")||this.getBool("readonly"))return;if("modal"===this.getOption("type",E,"dropdown"))return this.tempColor=this.getHex(),this.open=!0,void this.update();this.open=!this.open,this.update()}close(){this.open&&"dropdown"===this.getOption("type",E,"dropdown")&&(this.open=!1,this.update())}mount(){this.mounted=!0;const t=this.getIconSize();this.triggerEl=document.createElement("div"),this.triggerEl.className="designbase-wc-color-picker__trigger",this.triggerEl.addEventListener("click",()=>this.toggleOpen());const e=document.createElement("div");e.className="designbase-wc-color-picker__color-display",this.colorBoxEl=document.createElement("div"),this.colorBoxEl.className="designbase-wc-color-picker__color-box",e.append(this.colorBoxEl),this.triggerInputEl=document.createElement("input"),this.triggerInputEl.type="text",this.triggerInputEl.className="designbase-wc-color-picker__input",this.triggerInputEl.placeholder="#000000",this.triggerInputEl.addEventListener("click",t=>t.stopPropagation()),this.triggerInputEl.addEventListener("focus",()=>{this.triggerInputFocused=!0}),this.triggerInputEl.addEventListener("blur",()=>{this.triggerInputFocused=!1;const t="hex"===this.format?o(this.getUiFormatted()):this.getUiFormatted();this.triggerInputEl&&this.triggerInputEl.value!==t&&(this.triggerInputEl.value=t,this.colorInput=t)}),this.triggerInputEl.addEventListener("input",()=>{this.triggerInputEl&&(this.colorInput=this.triggerInputEl.value)}),this.triggerInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitColorInput("trigger")});const i=document.createElement("div");i.className="designbase-wc-color-picker__trigger-actions",this.triggerCopyBtn=document.createElement("button"),this.triggerCopyBtn.type="button",this.triggerCopyBtn.className="designbase-wc-color-picker__copy-button-inline",this.triggerCopyBtn.setAttribute("aria-label","Copy color value"),this.triggerCopyBtn.addEventListener("click",t=>{t.stopPropagation(),this.onCopy()}),this.toggleBtn=document.createElement("button"),this.toggleBtn.type="button",this.toggleBtn.className="designbase-wc-color-picker__toggle",this.toggleBtn.setAttribute("aria-label","Toggle color picker"),this.toggleBtn.append(d("chevron-down",t)),i.append(this.triggerCopyBtn,this.toggleBtn),this.triggerEl.append(e,this.triggerInputEl,i),this.dropdownEl=document.createElement("div"),this.dropdownEl.className="designbase-wc-color-picker__dropdown",this.dropdownEl.addEventListener("click",t=>t.stopPropagation()),this.dropdownEl.hidden=!0;const s=document.createElement("div");s.className="designbase-wc-color-picker__selector";const l=document.createElement("div");l.className="designbase-wc-color-picker__color-area",this.colorFieldEl=document.createElement("div"),this.colorFieldEl.className="designbase-wc-color-picker__color-field",this.colorPointerEl=document.createElement("div"),this.colorPointerEl.className="designbase-wc-color-picker__color-pointer",this.colorFieldEl.append(this.colorPointerEl),this.colorFieldEl.addEventListener("mousedown",t=>{this.getBool("disabled")||this.getBool("readonly")||(t.preventDefault(),this.dragging="area",this.updateFromArea(t.clientX,t.clientY))}),this.colorFieldEl.addEventListener("touchstart",t=>{if(this.getBool("disabled")||this.getBool("readonly"))return;this.dragging="area";const e=t.touches[0];this.updateFromArea(e.clientX,e.clientY)},{passive:!0}),this.colorFieldEl.addEventListener("touchmove",t=>{if("area"!==this.dragging)return;const e=t.touches[0];this.updateFromArea(e.clientX,e.clientY)},{passive:!0}),this.colorFieldEl.addEventListener("touchend",()=>{"area"===this.dragging&&(this.dragging=null,this.onColorStateChanged(!0))}),l.append(this.colorFieldEl);const n=document.createElement("div");n.className="designbase-wc-color-picker__controls",this.eyedropperBtnEl=document.createElement("db-button"),this.eyedropperBtnEl.setAttribute("variant","tertiary"),this.eyedropperBtnEl.setAttribute("size",this.getPanelControlSize()),this.eyedropperBtnEl.setAttribute("icon-only",""),this.eyedropperBtnEl.setAttribute("start-icon","eyedropper"),this.eyedropperBtnEl.setAttribute("aria-label","Eyedropper tool"),this.eyedropperBtnEl.addEventListener("click",()=>{this.onEyedrop()});const r=document.createElement("div");r.className="designbase-wc-color-picker__slider-container";const h=document.createElement("div");h.className="designbase-wc-color-picker__hue-slider",this.hueSliderEl=document.createElement("input"),this.hueSliderEl.type="range",this.hueSliderEl.min="0",this.hueSliderEl.max="360",this.hueSliderEl.className="designbase-wc-color-picker__slider designbase-wc-color-picker__slider--hue",this.hueSliderEl.addEventListener("input",()=>{this.h=parseFloat(this.hueSliderEl.value),this.updateVisuals(),this.onColorStateChanged()}),h.append(this.hueSliderEl),this.alphaSliderWrapEl=document.createElement("div"),this.alphaSliderWrapEl.className="designbase-wc-color-picker__alpha-slider",this.alphaSliderEl=document.createElement("input"),this.alphaSliderEl.type="range",this.alphaSliderEl.min="0",this.alphaSliderEl.max="100",this.alphaSliderEl.className="designbase-wc-color-picker__slider designbase-wc-color-picker__slider--alpha",this.alphaSliderEl.addEventListener("input",()=>{this.a=a(parseInt(this.alphaSliderEl.value,10),0,100),this.alphaInput=String(this.a),this.panelAlphaInputEl&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput),this.updateVisuals(),this.onColorStateChanged()}),this.alphaSliderWrapEl.append(this.alphaSliderEl),r.append(h,this.alphaSliderWrapEl),n.append(this.eyedropperBtnEl,r);const p=document.createElement("div");p.className="designbase-wc-color-picker__value-display",this.formatSelectEl=document.createElement("db-select"),this.formatSelectEl.setAttribute("size","s"),this.formatSelectEl.setAttribute("position","top"),this.formatSelectEl.setAttribute("show-clear-button","false"),this.formatSelectEl.setAttribute("options",A),this.formatSelectEl.addEventListener("db-change",t=>{const e=t.detail,i=e?.value;i&&(this.format=i,this.updateVisuals())}),this.panelValueInputEl=document.createElement("db-input"),this.panelValueInputEl.classList.add("designbase-wc-color-picker__value-input"),this.panelValueInputEl.setAttribute("size","s"),this.panelValueInputEl.setAttribute("placeholder","#000000"),this.panelValueInputEl.addEventListener("db-focus",()=>{this.panelInputFocused=!0}),this.panelValueInputEl.addEventListener("db-blur",()=>{this.panelInputFocused=!1;const t="hex"===this.format?o(this.getUiFormatted()):this.getUiFormatted();this.getInputValue(this.panelValueInputEl)!==t&&(this.setInputValue(this.panelValueInputEl,t),this.colorInput=t)}),this.panelValueInputEl.addEventListener("db-input",t=>{const e=t.detail;void 0!==e?.value&&(this.colorInput=e.value)}),this.panelValueInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitColorInput("panel")}),this.panelAlphaWrapEl=document.createElement("div"),this.panelAlphaWrapEl.className="designbase-wc-color-picker__alpha-input-wrap",this.panelAlphaInputEl=document.createElement("db-input"),this.panelAlphaInputEl.classList.add("designbase-wc-color-picker__alpha-input"),this.panelAlphaInputEl.setAttribute("size","s"),this.panelAlphaInputEl.setAttribute("placeholder","100"),this.panelAlphaInputEl.setAttribute("aria-label","Alpha percent"),this.panelAlphaInputEl.addEventListener("db-focus",()=>{this.panelAlphaInputFocused=!0}),this.panelAlphaInputEl.addEventListener("db-blur",()=>{this.panelAlphaInputFocused=!1,this.commitAlphaInput()}),this.panelAlphaInputEl.addEventListener("db-input",t=>{const e=t.detail,i=(e?.value??"").replace(/[^\d]/g,"").slice(0,3);i!==e?.value&&this.setInputValue(this.panelAlphaInputEl,i),this.alphaInput=i}),this.panelAlphaInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitAlphaInput()});const u=document.createElement("span");u.className="designbase-wc-color-picker__alpha-suffix",u.textContent="%",this.panelAlphaWrapEl.append(this.panelAlphaInputEl,u),this.panelCopyBtnEl=document.createElement("db-button"),this.panelCopyBtnEl.setAttribute("variant","tertiary"),this.panelCopyBtnEl.setAttribute("size","s"),this.panelCopyBtnEl.setAttribute("icon-only",""),this.panelCopyBtnEl.setAttribute("start-icon","copy"),this.panelCopyBtnEl.setAttribute("aria-label","Copy color value"),this.panelCopyBtnEl.addEventListener("click",()=>{this.onCopy()}),p.append(this.formatSelectEl,this.panelValueInputEl,this.panelAlphaWrapEl,this.panelCopyBtnEl),s.append(l,n,p),this.selectorEl=s,this.dropdownEl.append(s),this.modalEl=document.createElement("db-modal"),this.modalEl.setAttribute("size","s"),this.modalEl.setAttribute("title","색상 선택"),this.modalEl.setAttribute("close-on-escape","true"),this.modalEl.addEventListener("db-close",this.handleModalCancel),this.modalBodyEl=document.createElement("div"),this.modalBodyEl.className="designbase-wc-modal__body designbase-wc-color-picker__modal-body",this.modalBodyEl.setAttribute("data-modal-part","body");const c=document.createElement("div");c.className="designbase-wc-modal__footer",c.setAttribute("data-modal-part","footer");const g=document.createElement("db-button");g.setAttribute("variant","tertiary"),g.setAttribute("size","m"),g.textContent="취소",g.addEventListener("click",this.handleModalCancel);const m=document.createElement("db-button");m.setAttribute("variant","primary"),m.setAttribute("size","m"),m.textContent="적용",m.addEventListener("click",this.handleModalApply),c.append(g,m),this.modalEl.append(this.modalBodyEl,c),this.replaceChildren(this.triggerEl,this.dropdownEl,this.modalEl)}updateCopyButtons(){const t=this.getIconSize(),e=this.isCopied?"done":"copy";this.triggerCopyBtn&&this.triggerCopyBtn.replaceChildren(d(e,t)),this.panelCopyBtnEl&&this.panelCopyBtnEl.setAttribute("start-icon",e)}updateVisuals(){const t=this.getRgb(),e=this.getHex(),i=this.getBool("show-alpha",!0),s=this.getUiFormatted(),l="hex"===this.format?o(s):s;!this.triggerInputFocused&&this.triggerInputEl&&(this.triggerInputEl.value=l),!this.panelInputFocused&&this.panelValueInputEl&&this.setInputValue(this.panelValueInputEl,l),this.colorBoxEl&&(this.colorBoxEl.style.backgroundColor=i?`rgba(${t.r}, ${t.g}, ${t.b}, ${this.a/100})`:e),this.colorFieldEl&&(this.colorFieldEl.style.backgroundImage=`\n linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)),\n linear-gradient(to right, #ffffff, hsl(${this.h}, 100%, 50%))\n `),this.colorPointerEl&&(this.colorPointerEl.style.left=`${this.s}%`,this.colorPointerEl.style.top=100-this.v+"%",this.colorPointerEl.style.backgroundColor=`rgb(${t.r}, ${t.g}, ${t.b})`),this.hueSliderEl&&(this.hueSliderEl.value=String(this.h),this.hueSliderEl.style.background="linear-gradient(to right,\n hsl(0,100%,50%),\n hsl(60,100%,50%),\n hsl(120,100%,50%),\n hsl(180,100%,50%),\n hsl(240,100%,50%),\n hsl(300,100%,50%),\n hsl(360,100%,50%))"),this.alphaSliderEl&&(this.alphaSliderEl.value=String(this.a),this.alphaSliderEl.style.backgroundImage=`\n linear-gradient(45deg, var(--db-border-base) 25%, transparent 25%),\n linear-gradient(-45deg, var(--db-border-base) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--db-border-base) 75%),\n linear-gradient(-45deg, transparent 75%, var(--db-border-base) 75%),\n linear-gradient(to right, rgba(${t.r}, ${t.g}, ${t.b}, 0), rgba(${t.r}, ${t.g}, ${t.b}, 1))\n `,this.alphaSliderEl.style.backgroundSize="8px 8px,8px 8px,8px 8px,8px 8px,100% 100%",this.alphaSliderEl.style.backgroundPosition="0 0,0 4px,4px -4px,-4px 0,0 0",this.alphaSliderEl.style.backgroundColor="var(--db-surface-base)"),this.panelAlphaInputEl&&!this.panelAlphaInputFocused&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput),this.formatSelectEl&&this.formatSelectEl.getAttribute("value")!==this.format&&this.formatSelectEl.setAttribute("value",this.format),this.colorInput=l}update(){if(!this.mounted)return;const t=this.getOption("size",m,"m"),e=this.getOption("type",E,"dropdown"),i=this.getOption("position",v,"bottom-start"),s=this.getBool("disabled"),l=this.getBool("readonly"),n=this.getBool("show-input",!0),o=this.getBool("show-alpha",!0),r=this.getBool("show-format-selector",!0),a=this.getBool("show-copy-button",!0),h=this.getBool("show-eyedropper",!0),d="undefined"!=typeof window&&"EyeDropper"in window,p=this.getOption("trigger",b,"input"),u="swatch"===p;if(this.className=["designbase-wc-color-picker",`designbase-wc-color-picker--${t}`,`designbase-wc-color-picker--${i}`,`designbase-wc-color-picker--trigger-${p}`,this.open&&"dropdown"===e&&"designbase-wc-color-picker--open",this.getBool("full-width")&&"designbase-wc-color-picker--full-width",this.getBool("error")&&"designbase-wc-color-picker--error",s&&"designbase-wc-color-picker--disabled",l&&"designbase-wc-color-picker--readonly",!n&&"designbase-wc-color-picker--no-input"].filter(Boolean).join(" "),this.triggerInputEl?.setAttribute("aria-invalid",String(this.getBool("error"))),this.triggerInputEl&&(this.triggerInputEl.hidden=u||!n,this.triggerInputEl.disabled=s,this.triggerInputEl.readOnly=l),this.triggerCopyBtn&&(this.triggerCopyBtn.hidden=u||!(n&&a),this.triggerCopyBtn.disabled=s),this.toggleBtn&&(this.toggleBtn.hidden=u,this.toggleBtn.disabled=s),this.dropdownEl&&(this.dropdownEl.className=`designbase-wc-color-picker__dropdown designbase-wc-color-picker__dropdown--${i}`,this.dropdownEl.hidden=!("dropdown"===e&&this.open)),this.alphaSliderWrapEl&&(this.alphaSliderWrapEl.hidden=!o),this.panelAlphaWrapEl&&(this.panelAlphaWrapEl.hidden=!o),this.formatSelectEl&&(this.formatSelectEl.hidden=!r),this.panelCopyBtnEl&&(this.panelCopyBtnEl.hidden=!a),this.eyedropperBtnEl&&(this.eyedropperBtnEl.hidden=!(h&&d)),this.selectorEl){const t="modal"===e?this.modalBodyEl:this.dropdownEl;t&&this.selectorEl.parentElement!==t&&t.append(this.selectorEl)}this.modalEl&&("modal"===e&&this.open?this.modalEl.setAttribute("open","true"):this.modalEl.removeAttribute("open")),this.updateCopyButtons(),this.updateVisuals()}}customElements.get("db-color-picker")||customElements.define("db-color-picker",DbColorPicker);export{DbColorPicker};
|
|
1
|
+
import{dbText as t}from"../core/i18n.js";import{hexToRgb as e,rgbToHsv as i,hsvToRgb as s,toHex as l,rgbToHsl as n,formatColor as o,normalizeHex as r,isHex as a,clamp as h}from"./color-picker-utils.js";import{getDbComponentIconSize as d,createDbIcon as p}from"./db-icons.js";import{rafThrottle as u}from"../core/dom.js";import{coerceToContract as c}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{COLOR_PICKER_SIZES as g,COLOR_PICKER_POSITIONS as m}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const E=g,b=["dropdown","modal"],v=["input","swatch"],y=m,I="#006FFF",w=e(I)??{r:0,g:111,b:255},A=i(w.r,w.g,w.b),C=JSON.stringify([{label:"HEX",value:"hex"},{label:"RGB",value:"rgb"},{label:"RGBA",value:"rgba"},{label:"HSL",value:"hsl"},{label:"HSLA",value:"hsla"}]);class DbColorPicker extends HTMLElement{constructor(){super(...arguments),this.h=A.h,this.s=A.s,this.v=A.v,this.a=100,this.open=!1,this.format="hex",this.colorInput=I,this.alphaInput="100",this.isCopied=!1,this.tempColor="",this.dragging=null,this.mounted=!1,this.copiedTimer=null,this.debounceTimer=null,this.lastEmittedHex="",this.suppressNextValueSync=!1,this.didMount=!1,this.triggerInputFocused=!1,this.panelInputFocused=!1,this.panelAlphaInputFocused=!1,this.handleEscape=t=>{"Escape"!==t.key||t.defaultPrevented||t.isComposing||!this.open||"dropdown"===this.getOption("type",b,"dropdown")&&(t.preventDefault(),this.close(),this.toggleBtn?.focus())},this.handleOutsideClick=t=>{if(!this.open||"dropdown"!==this.getOption("type",b,"dropdown"))return;const e=t.target;this.contains(e)||this.formatSelectEl?.contains(e)||(this.open=!1,this.update())},this.handleDocumentMouseMove=u(t=>{"area"===this.dragging&&(this.getBool("disabled")||this.getBool("readonly")||this.updateFromArea(t.clientX,t.clientY))}),this.handleDocumentMouseUp=()=>{"area"===this.dragging&&(this.dragging=null,this.onColorStateChanged(!0))},this.handleModalCancel=()=>{const t=e(this.tempColor);if(t){const e=i(t.r,t.g,t.b);this.h=e.h,this.s=e.s,this.v=e.v}this.open=!1,this.update(),this.dispatchEvent(new CustomEvent("db-cancel",{bubbles:!0,composed:!0}))},this.handleModalApply=()=>{this.open=!1,this.actuallyEmit(),this.dispatchEvent(new CustomEvent("db-apply",{detail:{value:this.getOutward()},bubbles:!0,composed:!0})),this.update()}}static get observedAttributes(){return["size","type","trigger","position","value","default-value","show-input","show-alpha","show-format-selector","show-copy-button","show-eyedropper","disabled","readonly","full-width","error","on-change-format","change-debounce-ms","emit-during-drag"]}connectedCallback(){this.mounted||this.mount(),this.syncFromValueAttr(!0),this.update(),document.addEventListener("mousedown",this.handleOutsideClick),document.addEventListener("mousemove",this.handleDocumentMouseMove),document.addEventListener("mouseup",this.handleDocumentMouseUp),document.addEventListener("keydown",this.handleEscape)}disconnectedCallback(){document.removeEventListener("mousedown",this.handleOutsideClick),document.removeEventListener("mousemove",this.handleDocumentMouseMove),document.removeEventListener("mouseup",this.handleDocumentMouseUp),document.removeEventListener("keydown",this.handleEscape),this.copiedTimer&&clearTimeout(this.copiedTimer),this.debounceTimer&&clearTimeout(this.debounceTimer)}attributeChangedCallback(t){this.mounted&&("value"!==t&&"default-value"!==t||this.syncFromValueAttr(!1),this.update())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return""===i||"true"===i}getOption(t,e,i){return c(this.getAttribute(t),e,i)}getIconSize(){return d(this.getOption("size",E,"m"))}getRgb(){return s(this.h,this.s,this.v)}getHex(){const t=this.getRgb();return l(t.r,t.g,t.b)}getHsl(){const t=this.getRgb();return n(t.r,t.g,t.b)}getUiFormatted(){const t=this.getRgb(),e=this.getHex(),i=this.getHsl();return o(this.format,t,e,i,this.a)}getOutward(){return"hex"===("ui"===this.getAttribute("on-change-format")?"ui":"hex")?this.getHex():this.getUiFormatted()}syncFromValueAttr(t){const s=this.getAttribute("value")??(t?this.getAttribute("default-value"):null)??(t?I:null);if(null==s)return;const l=r(s);if(!a(l))return;if(!t&&this.suppressNextValueSync&&l===this.lastEmittedHex)return void(this.suppressNextValueSync=!1);const n=e(l);if(!n)return;const o=i(n.r,n.g,n.b);this.h=o.h,this.s=o.s,this.v=o.v,this.colorInput=l,this.lastEmittedHex=l}actuallyEmit(){const t=r(this.getHex()),e=this.hasAttribute("value")&&a(this.getAttribute("value")||"")?r(this.getAttribute("value")||""):null;e&&e===t||this.lastEmittedHex!==t&&(this.lastEmittedHex=t,this.suppressNextValueSync=!0,this.setAttribute("value",t),this.dispatchEvent(new CustomEvent("db-change",{detail:{value:this.getOutward()},bubbles:!0,composed:!0})))}scheduleEmit(){const t=this.getBool("emit-during-drag",!0);if(this.dragging&&!t)return;const e=Number(this.getAttribute("change-debounce-ms")||"0");if(e>0)return this.debounceTimer&&clearTimeout(this.debounceTimer),void(this.debounceTimer=setTimeout(()=>this.actuallyEmit(),e));this.actuallyEmit()}onColorStateChanged(t=!1){this.didMount?(!t||this.getBool("emit-during-drag",!0),this.scheduleEmit()):this.didMount=!0}updateFromArea(t,e){const i=this.colorFieldEl;if(!i)return;const s=i.getBoundingClientRect(),l=h(t-s.left,0,s.width),n=h(e-s.top,0,s.height);this.s=l/s.width*100,this.v=100-n/s.height*100,this.updateVisuals(),this.onColorStateChanged()}getInputValue(t){return t?t.getAttribute("value")??"":""}setInputValue(t,e){t&&void 0!==e&&t.setAttribute("value",e)}getPanelControlSize(){return"s"===this.getOption("size",E,"m")?"s":"m"}commitColorInput(t){const s="trigger"===t?this.triggerInputEl?.value??"":this.getInputValue(this.panelValueInputEl),l=s.trim();if(/^#([0-9A-Fa-f]{6})$/.test(l)){const t=e(l),s=i(t.r,t.g,t.b);return this.h=s.h,this.s=s.s,this.v=s.v,this.colorInput=r(l),this.updateVisuals(),void this.onColorStateChanged()}let n=/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i.exec(s);if(n){const t=h(parseInt(n[1],10),0,255),e=h(parseInt(n[2],10),0,255),s=h(parseInt(n[3],10),0,255),l=i(t,e,s);return this.h=l.h,this.s=l.s,this.v=l.v,this.colorInput=`rgb(${t}, ${e}, ${s})`,this.updateVisuals(),void this.onColorStateChanged()}if(n=/^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0?\.\d+)\s*\)$/i.exec(l),n){const t=h(parseInt(n[1],10),0,255),e=h(parseInt(n[2],10),0,255),s=h(parseInt(n[3],10),0,255),l=parseFloat(n[4]),o=i(t,e,s);this.h=o.h,this.s=o.s,this.v=o.v,this.a=Math.round(100*l),this.alphaInput=String(this.a),this.colorInput=`rgba(${t}, ${e}, ${s}, ${l})`,this.updateVisuals(),this.onColorStateChanged()}}commitAlphaInput(){const t=Number(this.getInputValue(this.panelAlphaInputEl).trim()||this.alphaInput);if(!Number.isNaN(t)&&t>=0&&t<=100){const e=Math.round(t);this.a=e,this.alphaInput=String(e),this.updateVisuals(),this.onColorStateChanged()}else this.alphaInput=String(this.a),this.panelAlphaInputEl&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput)}async onCopy(){try{await navigator.clipboard.writeText(this.getUiFormatted()),this.isCopied=!0,this.updateCopyButtons(),this.copiedTimer&&clearTimeout(this.copiedTimer),this.copiedTimer=setTimeout(()=>{this.isCopied=!1,this.updateCopyButtons()},1600)}catch{}}async onEyedrop(){try{if("EyeDropper"in window){const t=new window.EyeDropper,{sRGBHex:s}=await t.open(),l=e(s);if(!l)return;const n=i(l.r,l.g,l.b);this.h=n.h,this.s=n.s,this.v=n.v,this.updateVisuals(),this.onColorStateChanged()}}catch{}}toggleOpen(){if(this.getBool("disabled")||this.getBool("readonly"))return;if("modal"===this.getOption("type",b,"dropdown"))return this.tempColor=this.getHex(),this.open=!0,void this.update();this.open=!this.open,this.update()}close(){this.open&&"dropdown"===this.getOption("type",b,"dropdown")&&(this.open=!1,this.update())}mount(){this.mounted=!0;const e=this.getIconSize();this.triggerEl=document.createElement("div"),this.triggerEl.className="designbase-wc-color-picker__trigger",this.triggerEl.addEventListener("click",()=>this.toggleOpen());const i=document.createElement("div");i.className="designbase-wc-color-picker__color-display",this.colorBoxEl=document.createElement("div"),this.colorBoxEl.className="designbase-wc-color-picker__color-box",i.append(this.colorBoxEl),this.triggerInputEl=document.createElement("input"),this.triggerInputEl.type="text",this.triggerInputEl.className="designbase-wc-color-picker__input",this.triggerInputEl.placeholder="#000000",this.triggerInputEl.addEventListener("click",t=>t.stopPropagation()),this.triggerInputEl.addEventListener("focus",()=>{this.triggerInputFocused=!0}),this.triggerInputEl.addEventListener("blur",()=>{this.triggerInputFocused=!1;const t="hex"===this.format?r(this.getUiFormatted()):this.getUiFormatted();this.triggerInputEl&&this.triggerInputEl.value!==t&&(this.triggerInputEl.value=t,this.colorInput=t)}),this.triggerInputEl.addEventListener("input",()=>{this.triggerInputEl&&(this.colorInput=this.triggerInputEl.value)}),this.triggerInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitColorInput("trigger")});const s=document.createElement("div");s.className="designbase-wc-color-picker__trigger-actions",this.triggerCopyBtn=document.createElement("button"),this.triggerCopyBtn.type="button",this.triggerCopyBtn.className="designbase-wc-color-picker__copy-button-inline",this.triggerCopyBtn.setAttribute("aria-label","Copy color value"),this.triggerCopyBtn.addEventListener("click",t=>{t.stopPropagation(),this.onCopy()}),this.toggleBtn=document.createElement("button"),this.toggleBtn.type="button",this.toggleBtn.className="designbase-wc-color-picker__toggle",this.toggleBtn.setAttribute("aria-label","Toggle color picker"),this.toggleBtn.append(p("chevron-down",e)),s.append(this.triggerCopyBtn,this.toggleBtn),this.triggerEl.append(i,this.triggerInputEl,s),this.dropdownEl=document.createElement("div"),this.dropdownEl.className="designbase-wc-color-picker__dropdown",this.dropdownEl.addEventListener("click",t=>t.stopPropagation()),this.dropdownEl.hidden=!0;const l=document.createElement("div");l.className="designbase-wc-color-picker__selector";const n=document.createElement("div");n.className="designbase-wc-color-picker__color-area",this.colorFieldEl=document.createElement("div"),this.colorFieldEl.className="designbase-wc-color-picker__color-field",this.colorPointerEl=document.createElement("div"),this.colorPointerEl.className="designbase-wc-color-picker__color-pointer",this.colorFieldEl.append(this.colorPointerEl),this.colorFieldEl.addEventListener("mousedown",t=>{this.getBool("disabled")||this.getBool("readonly")||(t.preventDefault(),this.dragging="area",this.updateFromArea(t.clientX,t.clientY))}),this.colorFieldEl.addEventListener("touchstart",t=>{if(this.getBool("disabled")||this.getBool("readonly"))return;this.dragging="area";const e=t.touches[0];this.updateFromArea(e.clientX,e.clientY)},{passive:!0}),this.colorFieldEl.addEventListener("touchmove",t=>{if("area"!==this.dragging)return;const e=t.touches[0];this.updateFromArea(e.clientX,e.clientY)},{passive:!0}),this.colorFieldEl.addEventListener("touchend",()=>{"area"===this.dragging&&(this.dragging=null,this.onColorStateChanged(!0))}),n.append(this.colorFieldEl);const o=document.createElement("div");o.className="designbase-wc-color-picker__controls",this.eyedropperBtnEl=document.createElement("db-button"),this.eyedropperBtnEl.setAttribute("variant","tertiary"),this.eyedropperBtnEl.setAttribute("size",this.getPanelControlSize()),this.eyedropperBtnEl.setAttribute("icon-only",""),this.eyedropperBtnEl.setAttribute("start-icon","eyedropper"),this.eyedropperBtnEl.setAttribute("aria-label","Eyedropper tool"),this.eyedropperBtnEl.addEventListener("click",()=>{this.onEyedrop()});const a=document.createElement("div");a.className="designbase-wc-color-picker__slider-container";const d=document.createElement("div");d.className="designbase-wc-color-picker__hue-slider",this.hueSliderEl=document.createElement("input"),this.hueSliderEl.type="range",this.hueSliderEl.min="0",this.hueSliderEl.max="360",this.hueSliderEl.className="designbase-wc-color-picker__slider designbase-wc-color-picker__slider--hue",this.hueSliderEl.addEventListener("input",()=>{this.h=parseFloat(this.hueSliderEl.value),this.updateVisuals(),this.onColorStateChanged()}),d.append(this.hueSliderEl),this.alphaSliderWrapEl=document.createElement("div"),this.alphaSliderWrapEl.className="designbase-wc-color-picker__alpha-slider",this.alphaSliderEl=document.createElement("input"),this.alphaSliderEl.type="range",this.alphaSliderEl.min="0",this.alphaSliderEl.max="100",this.alphaSliderEl.className="designbase-wc-color-picker__slider designbase-wc-color-picker__slider--alpha",this.alphaSliderEl.addEventListener("input",()=>{this.a=h(parseInt(this.alphaSliderEl.value,10),0,100),this.alphaInput=String(this.a),this.panelAlphaInputEl&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput),this.updateVisuals(),this.onColorStateChanged()}),this.alphaSliderWrapEl.append(this.alphaSliderEl),a.append(d,this.alphaSliderWrapEl),o.append(this.eyedropperBtnEl,a);const u=document.createElement("div");u.className="designbase-wc-color-picker__value-display",this.formatSelectEl=document.createElement("db-select"),this.formatSelectEl.setAttribute("size","s"),this.formatSelectEl.setAttribute("position","top"),this.formatSelectEl.setAttribute("show-clear-button","false"),this.formatSelectEl.setAttribute("options",C),this.formatSelectEl.addEventListener("db-change",t=>{const e=t.detail,i=e?.value;i&&(this.format=i,this.updateVisuals())}),this.panelValueInputEl=document.createElement("db-input"),this.panelValueInputEl.classList.add("designbase-wc-color-picker__value-input"),this.panelValueInputEl.setAttribute("size","s"),this.panelValueInputEl.setAttribute("placeholder","#000000"),this.panelValueInputEl.addEventListener("db-focus",()=>{this.panelInputFocused=!0}),this.panelValueInputEl.addEventListener("db-blur",()=>{this.panelInputFocused=!1;const t="hex"===this.format?r(this.getUiFormatted()):this.getUiFormatted();this.getInputValue(this.panelValueInputEl)!==t&&(this.setInputValue(this.panelValueInputEl,t),this.colorInput=t)}),this.panelValueInputEl.addEventListener("db-input",t=>{const e=t.detail;void 0!==e?.value&&(this.colorInput=e.value)}),this.panelValueInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitColorInput("panel")}),this.panelAlphaWrapEl=document.createElement("div"),this.panelAlphaWrapEl.className="designbase-wc-color-picker__alpha-input-wrap",this.panelAlphaInputEl=document.createElement("db-input"),this.panelAlphaInputEl.classList.add("designbase-wc-color-picker__alpha-input"),this.panelAlphaInputEl.setAttribute("size","s"),this.panelAlphaInputEl.setAttribute("placeholder","100"),this.panelAlphaInputEl.setAttribute("aria-label","Alpha percent"),this.panelAlphaInputEl.addEventListener("db-focus",()=>{this.panelAlphaInputFocused=!0}),this.panelAlphaInputEl.addEventListener("db-blur",()=>{this.panelAlphaInputFocused=!1,this.commitAlphaInput()}),this.panelAlphaInputEl.addEventListener("db-input",t=>{const e=t.detail,i=(e?.value??"").replace(/[^\d]/g,"").slice(0,3);i!==e?.value&&this.setInputValue(this.panelAlphaInputEl,i),this.alphaInput=i}),this.panelAlphaInputEl.addEventListener("keydown",t=>{"Enter"===t.key&&this.commitAlphaInput()});const c=document.createElement("span");c.className="designbase-wc-color-picker__alpha-suffix",c.textContent="%",this.panelAlphaWrapEl.append(this.panelAlphaInputEl,c),this.panelCopyBtnEl=document.createElement("db-button"),this.panelCopyBtnEl.setAttribute("variant","tertiary"),this.panelCopyBtnEl.setAttribute("size","s"),this.panelCopyBtnEl.setAttribute("icon-only",""),this.panelCopyBtnEl.setAttribute("start-icon","copy"),this.panelCopyBtnEl.setAttribute("aria-label","Copy color value"),this.panelCopyBtnEl.addEventListener("click",()=>{this.onCopy()}),u.append(this.formatSelectEl,this.panelValueInputEl,this.panelAlphaWrapEl,this.panelCopyBtnEl),l.append(n,o,u),this.selectorEl=l,this.dropdownEl.append(l),this.modalEl=document.createElement("db-modal"),this.modalEl.setAttribute("size","s"),this.modalEl.setAttribute("title",t("색상 선택")),this.modalEl.setAttribute("close-on-escape","true"),this.modalEl.addEventListener("db-close",this.handleModalCancel),this.modalBodyEl=document.createElement("div"),this.modalBodyEl.className="designbase-wc-modal__body designbase-wc-color-picker__modal-body",this.modalBodyEl.setAttribute("data-modal-part","body");const g=document.createElement("div");g.className="designbase-wc-modal__footer",g.setAttribute("data-modal-part","footer");const m=document.createElement("db-button");m.setAttribute("variant","tertiary"),m.setAttribute("size","m"),m.textContent=t("취소"),m.addEventListener("click",this.handleModalCancel);const E=document.createElement("db-button");E.setAttribute("variant","primary"),E.setAttribute("size","m"),E.textContent=t("적용"),E.addEventListener("click",this.handleModalApply),g.append(m,E),this.modalEl.append(this.modalBodyEl,g),this.replaceChildren(this.triggerEl,this.dropdownEl,this.modalEl)}updateCopyButtons(){const t=this.getIconSize(),e=this.isCopied?"done":"copy";this.triggerCopyBtn&&this.triggerCopyBtn.replaceChildren(p(e,t)),this.panelCopyBtnEl&&this.panelCopyBtnEl.setAttribute("start-icon",e)}updateVisuals(){const t=this.getRgb(),e=this.getHex(),i=this.getBool("show-alpha",!0),s=this.getUiFormatted(),l="hex"===this.format?r(s):s;!this.triggerInputFocused&&this.triggerInputEl&&(this.triggerInputEl.value=l),!this.panelInputFocused&&this.panelValueInputEl&&this.setInputValue(this.panelValueInputEl,l),this.colorBoxEl&&(this.colorBoxEl.style.backgroundColor=i?`rgba(${t.r}, ${t.g}, ${t.b}, ${this.a/100})`:e),this.colorFieldEl&&(this.colorFieldEl.style.backgroundImage=`\n linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)),\n linear-gradient(to right, #ffffff, hsl(${this.h}, 100%, 50%))\n `),this.colorPointerEl&&(this.colorPointerEl.style.left=`${this.s}%`,this.colorPointerEl.style.top=100-this.v+"%",this.colorPointerEl.style.backgroundColor=`rgb(${t.r}, ${t.g}, ${t.b})`),this.hueSliderEl&&(this.hueSliderEl.value=String(this.h),this.hueSliderEl.style.background="linear-gradient(to right,\n hsl(0,100%,50%),\n hsl(60,100%,50%),\n hsl(120,100%,50%),\n hsl(180,100%,50%),\n hsl(240,100%,50%),\n hsl(300,100%,50%),\n hsl(360,100%,50%))"),this.alphaSliderEl&&(this.alphaSliderEl.value=String(this.a),this.alphaSliderEl.style.backgroundImage=`\n linear-gradient(45deg, var(--db-border-base) 25%, transparent 25%),\n linear-gradient(-45deg, var(--db-border-base) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--db-border-base) 75%),\n linear-gradient(-45deg, transparent 75%, var(--db-border-base) 75%),\n linear-gradient(to right, rgba(${t.r}, ${t.g}, ${t.b}, 0), rgba(${t.r}, ${t.g}, ${t.b}, 1))\n `,this.alphaSliderEl.style.backgroundSize="8px 8px,8px 8px,8px 8px,8px 8px,100% 100%",this.alphaSliderEl.style.backgroundPosition="0 0,0 4px,4px -4px,-4px 0,0 0",this.alphaSliderEl.style.backgroundColor="var(--db-surface-base)"),this.panelAlphaInputEl&&!this.panelAlphaInputFocused&&this.setInputValue(this.panelAlphaInputEl,this.alphaInput),this.formatSelectEl&&this.formatSelectEl.getAttribute("value")!==this.format&&this.formatSelectEl.setAttribute("value",this.format),this.colorInput=l}update(){if(!this.mounted)return;const t=this.getOption("size",E,"m"),e=this.getOption("type",b,"dropdown"),i=this.getOption("position",y,"bottom-start"),s=this.getBool("disabled"),l=this.getBool("readonly"),n=this.getBool("show-input",!0),o=this.getBool("show-alpha",!0),r=this.getBool("show-format-selector",!0),a=this.getBool("show-copy-button",!0),h=this.getBool("show-eyedropper",!0),d="undefined"!=typeof window&&"EyeDropper"in window,p=this.getOption("trigger",v,"input"),u="swatch"===p;if(this.className=["designbase-wc-color-picker",`designbase-wc-color-picker--${t}`,`designbase-wc-color-picker--${i}`,`designbase-wc-color-picker--trigger-${p}`,this.open&&"dropdown"===e&&"designbase-wc-color-picker--open",this.getBool("full-width")&&"designbase-wc-color-picker--full-width",this.getBool("error")&&"designbase-wc-color-picker--error",s&&"designbase-wc-color-picker--disabled",l&&"designbase-wc-color-picker--readonly",!n&&"designbase-wc-color-picker--no-input"].filter(Boolean).join(" "),this.triggerInputEl?.setAttribute("aria-invalid",String(this.getBool("error"))),this.triggerInputEl&&(this.triggerInputEl.hidden=u||!n,this.triggerInputEl.disabled=s,this.triggerInputEl.readOnly=l),this.triggerCopyBtn&&(this.triggerCopyBtn.hidden=u||!(n&&a),this.triggerCopyBtn.disabled=s),this.toggleBtn&&(this.toggleBtn.hidden=u,this.toggleBtn.disabled=s),this.dropdownEl&&(this.dropdownEl.className=`designbase-wc-color-picker__dropdown designbase-wc-color-picker__dropdown--${i}`,this.dropdownEl.hidden=!("dropdown"===e&&this.open)),this.alphaSliderWrapEl&&(this.alphaSliderWrapEl.hidden=!o),this.panelAlphaWrapEl&&(this.panelAlphaWrapEl.hidden=!o),this.formatSelectEl&&(this.formatSelectEl.hidden=!r),this.panelCopyBtnEl&&(this.panelCopyBtnEl.hidden=!a),this.eyedropperBtnEl&&(this.eyedropperBtnEl.hidden=!(h&&d)),this.selectorEl){const t="modal"===e?this.modalBodyEl:this.dropdownEl;t&&this.selectorEl.parentElement!==t&&t.append(this.selectorEl)}this.modalEl&&("modal"===e&&this.open?this.modalEl.setAttribute("open","true"):this.modalEl.removeAttribute("open")),this.updateCopyButtons(),this.updateVisuals()}}customElements.get("db-color-picker")||customElements.define("db-color-picker",DbColorPicker);export{DbColorPicker};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ConfettiMode, ConfettiTrigger, ConfettiShape } from '../ui-contracts/dist/components.d.js';
|
|
2
|
+
|
|
3
|
+
interface ConfettiParticleOptions {
|
|
4
|
+
particleCount?: number;
|
|
5
|
+
angle?: number;
|
|
6
|
+
spread?: number;
|
|
7
|
+
startVelocity?: number;
|
|
8
|
+
decay?: number;
|
|
9
|
+
gravity?: number;
|
|
10
|
+
drift?: number;
|
|
11
|
+
ticks?: number;
|
|
12
|
+
origin?: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
};
|
|
16
|
+
colors?: string[];
|
|
17
|
+
shapes?: ConfettiShape[];
|
|
18
|
+
scalar?: number;
|
|
19
|
+
zIndex?: number;
|
|
20
|
+
disableForReducedMotion?: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface ConfettiFireworkOptions {
|
|
23
|
+
duration?: number;
|
|
24
|
+
interval?: number;
|
|
25
|
+
particleCount?: number;
|
|
26
|
+
colors?: string[];
|
|
27
|
+
}
|
|
28
|
+
declare class DbConfetti extends HTMLElement {
|
|
29
|
+
static get observedAttributes(): string[];
|
|
30
|
+
private built;
|
|
31
|
+
private canvas?;
|
|
32
|
+
private ctx?;
|
|
33
|
+
private particles;
|
|
34
|
+
private animationId;
|
|
35
|
+
private fireworkTimer;
|
|
36
|
+
private resizeObserver?;
|
|
37
|
+
private clickHandler;
|
|
38
|
+
get mode(): ConfettiMode;
|
|
39
|
+
set mode(value: ConfettiMode);
|
|
40
|
+
get trigger(): ConfettiTrigger;
|
|
41
|
+
set trigger(value: ConfettiTrigger);
|
|
42
|
+
get particleCount(): number;
|
|
43
|
+
set particleCount(value: number);
|
|
44
|
+
get spread(): number;
|
|
45
|
+
set spread(value: number);
|
|
46
|
+
get originX(): number;
|
|
47
|
+
set originX(value: number);
|
|
48
|
+
get originY(): number;
|
|
49
|
+
set originY(value: number);
|
|
50
|
+
get colors(): string[];
|
|
51
|
+
set colors(value: string[]);
|
|
52
|
+
get shapes(): ConfettiShape[];
|
|
53
|
+
set shapes(value: ConfettiShape[]);
|
|
54
|
+
get duration(): number;
|
|
55
|
+
set duration(value: number);
|
|
56
|
+
get fullscreen(): boolean;
|
|
57
|
+
set fullscreen(value: boolean);
|
|
58
|
+
connectedCallback(): void;
|
|
59
|
+
disconnectedCallback(): void;
|
|
60
|
+
attributeChangedCallback(name: string, _oldVal: string | null, _newVal: string | null): void;
|
|
61
|
+
private build;
|
|
62
|
+
private updateCanvasSize;
|
|
63
|
+
private sync;
|
|
64
|
+
fire(opts?: ConfettiParticleOptions): void;
|
|
65
|
+
fireworks(options?: ConfettiFireworkOptions): void;
|
|
66
|
+
cannon(): void;
|
|
67
|
+
stars(): void;
|
|
68
|
+
pride(): void;
|
|
69
|
+
snow(): void;
|
|
70
|
+
fireByMode(): void;
|
|
71
|
+
private drawStar;
|
|
72
|
+
private loop;
|
|
73
|
+
}
|
|
74
|
+
declare global {
|
|
75
|
+
interface HTMLElementTagNameMap {
|
|
76
|
+
'db-confetti': DbConfetti;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
interface ConfettiFunction {
|
|
80
|
+
(options?: ConfettiParticleOptions): void;
|
|
81
|
+
fireworks(options?: ConfettiFireworkOptions): void;
|
|
82
|
+
cannon(options?: ConfettiParticleOptions): void;
|
|
83
|
+
stars(): void;
|
|
84
|
+
pride(): void;
|
|
85
|
+
snow(): void;
|
|
86
|
+
}
|
|
87
|
+
declare const confetti: ConfettiFunction;
|
|
88
|
+
|
|
89
|
+
export { DbConfetti, confetti };
|
|
90
|
+
export type { ConfettiFireworkOptions, ConfettiFunction, ConfettiParticleOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{coerceToContract as t}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{CONFETTI_MODES as e,CONFETTI_TRIGGERS as i,CONFETTI_SHAPES as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r="designbase-wc-confetti",n=["#26ccff","#a25afd","#ff5e7e","#88ff5a","#fcff42","#ffa62d","#ff36ff"];function randomRange(t,e){return Math.random()*(e-t)+t}function hexToRgb(t){let e=t.replace("#","");3===e.length&&(e=e.split("").map(t=>t+t).join(""));const i=parseInt(e,16);return{r:i>>16&255,g:i>>8&255,b:255&i}}class DbConfetti extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.particles=[],this.animationId=null,this.fireworkTimer=null,this.clickHandler=()=>this.fire(),this.loop=()=>{if(!this.ctx||!this.canvas)return void(this.animationId=null);const t=this.ctx,e=this.canvas.width/(window.devicePixelRatio||1),i=this.canvas.height/(window.devicePixelRatio||1);t.clearRect(0,0,e,i);const s=[],r=this.particles.length;for(let n=0;n<r;n++){const r=this.particles[n];r.tick++,r.vx*=r.drag,r.vy<r.maxFallSpeed?r.vy=r.vy*r.drag+r.gravity:(r.vy=.98*r.vy+.05*r.gravity,r.vy>r.maxFallSpeed&&(r.vy=r.maxFallSpeed)),r.wobble+=r.wobbleSpeed;const a=Math.sin(r.wobble)*r.flutterStrength;r.x+=r.vx+r.drift+a,r.y+=r.vy,r.angle+=r.rotationSpeed;const o=r.tick/r.totalTicks;if(r.opacity=o<.72?1:Math.max(0,1-(o-.72)/.28),r.opacity<=0||r.tick>=r.totalTicks||r.y>i+50||r.x<-80||r.x>e+80)continue;s.push(r),t.save(),t.translate(r.x,r.y),t.rotate(r.angle*Math.PI/180),t.scale(Math.cos(r.wobble),1);const l=hexToRgb(r.color);t.fillStyle=`rgba(${l.r}, ${l.g}, ${l.b}, ${r.opacity.toFixed(3)})`,"circle"===r.shape?(t.beginPath(),t.arc(0,0,r.w/2,0,2*Math.PI),t.fill()):"star"===r.shape?this.drawStar(t,0,0,5,r.w,r.w/2):t.fillRect(-r.w/2,-r.h/2,r.w,r.h),t.restore()}this.particles=s,this.particles.length>0?this.animationId=requestAnimationFrame(this.loop):this.animationId=null}}static get observedAttributes(){return["mode","trigger","particle-count","spread","origin-x","origin-y","colors","shapes","duration","fullscreen"]}get mode(){return t(this.getAttribute("mode"),e,"cannon")}set mode(t){this.setAttribute("mode",t)}get trigger(){return t(this.getAttribute("trigger"),i,"manual")}set trigger(t){this.setAttribute("trigger",t)}get particleCount(){const t=parseInt(this.getAttribute("particle-count")||"60",10);return Number.isFinite(t)&&t>0?t:60}set particleCount(t){this.setAttribute("particle-count",String(t))}get spread(){const t=parseFloat(this.getAttribute("spread")||"70");return Number.isFinite(t)?t:70}set spread(t){this.setAttribute("spread",String(t))}get originX(){const t=parseFloat(this.getAttribute("origin-x")||"0.5");return Number.isFinite(t)?t:.5}set originX(t){this.setAttribute("origin-x",String(t))}get originY(){const t=parseFloat(this.getAttribute("origin-y")||"0.7");return Number.isFinite(t)?t:.7}set originY(t){this.setAttribute("origin-y",String(t))}get colors(){const t=this.getAttribute("colors");if(!t)return n;const e=t.split(",").map(t=>t.trim()).filter(Boolean);return e.length?e:n}set colors(t){this.setAttribute("colors",t.join(","))}get shapes(){const t=this.getAttribute("shapes");if(!t)return["square","circle"];if("mix"===t)return["square","circle","star"];const e=t.split(",").map(t=>t.trim()).filter(t=>s.includes(t));return e.length?e:["square","circle"]}set shapes(t){this.setAttribute("shapes",t.join(","))}get duration(){const t=parseFloat(this.getAttribute("duration")||"3000");return Number.isFinite(t)&&t>0?t:3e3}set duration(t){this.setAttribute("duration",String(t))}get fullscreen(){return this.hasAttribute("fullscreen")}set fullscreen(t){t?this.setAttribute("fullscreen",""):this.removeAttribute("fullscreen")}connectedCallback(){this.setAttribute("aria-hidden","true"),this.built||(this.build(),this.built=!0),this.sync(),"auto"===this.trigger?requestAnimationFrame(()=>this.fireByMode()):"click"===this.trigger&&this.parentElement&&this.parentElement.addEventListener("click",this.clickHandler)}disconnectedCallback(){null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null),null!==this.fireworkTimer&&(clearInterval(this.fireworkTimer),this.fireworkTimer=null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.parentElement&&this.parentElement.removeEventListener("click",this.clickHandler)}attributeChangedCallback(t,e,i){this.built&&("trigger"===t&&this.parentElement&&(this.parentElement.removeEventListener("click",this.clickHandler),"click"===this.trigger&&this.parentElement.addEventListener("click",this.clickHandler)),this.sync())}build(){this.classList.add(r);const t=document.createElement("canvas");t.className=`${r}__canvas`,this.canvas=t,this.ctx=t.getContext("2d"),this.replaceChildren(t),this.updateCanvasSize(),"undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>this.updateCanvasSize()),this.resizeObserver.observe(this.fullscreen?document.body:this.parentElement||this))}updateCanvasSize(){if(!this.canvas)return;const t=window.devicePixelRatio||1;let e=this.clientWidth,i=this.clientHeight;if(this.fullscreen||0===e||0===i){const t=this.parentElement;t&&!this.fullscreen?(e=t.clientWidth,i=t.clientHeight):(e=window.innerWidth,i=window.innerHeight)}0===e&&(e=300),0===i&&(i=300),this.canvas.width=e*t,this.canvas.height=i*t,this.canvas.style.width=`${e}px`,this.canvas.style.height=`${i}px`,this.ctx&&(this.ctx.resetTransform?.(),this.ctx.scale(t,t))}sync(){this.fullscreen?this.classList.add(`${r}--fullscreen`):this.classList.remove(`${r}--fullscreen`),e.forEach(t=>this.classList.remove(`${r}--${t}`)),this.classList.add(`${r}--${this.mode}`)}fire(t){if(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches&&!1!==t?.disableForReducedMotion)return;this.updateCanvasSize();const e=this.canvas?this.canvas.width/(window.devicePixelRatio||1):400,i=this.canvas?this.canvas.height/(window.devicePixelRatio||1):400,s=this.fullscreen||this.parentElement===document.body||this.parentElement===document.documentElement||e>=window.innerWidth-30&&i>=window.innerHeight-30,r=s?1:Math.min(1,Math.max(.18,i/750)),n=s?1:Math.min(1,Math.max(.22,e/900)),a=Math.min(r,n),o=s?1:Math.max(.18,Math.min(1,.62*r+.04)),l=s?1:Math.max(.26,Math.min(1,.7*r+.15)),c=s?1:Math.max(.35,Math.min(1,.6*r+.25)),h=s?1:Math.max(.7,Math.min(1,.68+.32*a)),d=t?.particleCount??this.particleCount,u=t?.angle??90,f=t?.spread??this.spread,g=(t?.startVelocity??58)*o,m=t?.decay??(s?.925:Math.max(.91,.925-.015*(1-r))),p=(t?.gravity??.42)*l,b=(t?.drift??0)*(s?1:n),v=t?.ticks??380,w=s?v:Math.round(v*(.65+.35*a)),y=t?.colors??this.colors,k=t?.shapes??this.shapes,x=(t?.scalar??1)*h,M=(t?.origin?.x??this.originX)*e,C=(t?.origin?.y??this.originY)*i,R=u*Math.PI/180,A=f*Math.PI/180;for(let t=0;t<d;t++){const t=R+(Math.random()-.5)*A,e=g*randomRange(.65,1.35),i=y[Math.floor(Math.random()*y.length)],r=k[Math.floor(Math.random()*k.length)],n=m*randomRange(.99,1.01),o=p*randomRange(.85,1.15),l=randomRange(2.2,4.2)*x*c,h=randomRange(.8,2.2)*(s?1:Math.max(.5,1.2*a)),d=Math.floor(w*randomRange(.85,1.25));this.particles.push({x:M,y:C,vx:Math.cos(t)*e,vy:-Math.sin(t)*e,w:randomRange(7,12)*x,h:("circle"===r?randomRange(7,12):randomRange(10,18))*x,color:i,shape:r,angle:360*Math.random(),rotationSpeed:randomRange(-4.5,4.5),wobble:10*Math.random(),wobbleSpeed:randomRange(.06,.14),opacity:1,gravity:o,drag:n,drift:b,maxFallSpeed:l,flutterStrength:h,tick:0,totalTicks:d,scalar:x})}null===this.animationId&&this.loop()}fireworks(t){const e=t?.duration??(this.duration||3500),i=t?.interval??320,s=Date.now()+e;null!==this.fireworkTimer&&(clearInterval(this.fireworkTimer),this.fireworkTimer=null);const runBurst=()=>{if(Date.now()>s)return void(null!==this.fireworkTimer&&(clearInterval(this.fireworkTimer),this.fireworkTimer=null));const e=t?.particleCount??Math.floor(randomRange(30,50));this.fire({particleCount:e,startVelocity:42,spread:360,ticks:280,origin:{x:randomRange(.15,.85),y:randomRange(.12,.45)},colors:t?.colors??this.colors})};runBurst(),this.fireworkTimer=window.setInterval(runBurst,i)}cannon(){this.fire({particleCount:this.particleCount,angle:90,spread:75,startVelocity:58,ticks:380,origin:{x:.5,y:.85}})}stars(){this.fire({particleCount:45,spread:360,ticks:260,gravity:.25,decay:.94,startVelocity:38,shapes:["star"],colors:["#ffe600","#ffd700","#ffffff","#ffaa00","#f59e0b"],scalar:1.2})}pride(){const t=["#e40303","#ff8c00","#ffed00","#008026","#004dff","#750787"];this.fire({particleCount:48,angle:60,spread:60,startVelocity:56,ticks:380,origin:{x:0,y:.85},colors:t}),this.fire({particleCount:48,angle:120,spread:60,startVelocity:56,ticks:380,origin:{x:1,y:.85},colors:t})}snow(){this.fire({particleCount:50,angle:270,spread:180,startVelocity:14,gravity:.18,drift:randomRange(-.3,.3),ticks:450,shapes:["circle"],colors:["#ffffff","#e2e8f0","#93c5fd"],origin:{x:randomRange(.1,.9),y:-.05},scalar:.8})}fireByMode(){switch(this.mode){case"fireworks":this.fireworks();break;case"stars":this.stars();break;case"pride":this.pride();break;case"snow":this.snow();break;default:this.cannon()}}drawStar(t,e,i,s,r,n){let a=Math.PI/2*3,o=e,l=i;const c=Math.PI/s;t.beginPath(),t.moveTo(e,i-r);for(let h=0;h<s;h++)o=e+Math.cos(a)*r,l=i+Math.sin(a)*r,t.lineTo(o,l),a+=c,o=e+Math.cos(a)*n,l=i+Math.sin(a)*n,t.lineTo(o,l),a+=c;t.lineTo(e,i-r),t.closePath(),t.fill()}}customElements.get("db-confetti")||customElements.define("db-confetti",DbConfetti);let a=null;function getGlobalConfetti(){return a&&a.isConnected||(a=document.createElement("db-confetti"),a.setAttribute("fullscreen",""),document.body.appendChild(a)),a}const o=Object.assign(t=>{getGlobalConfetti().fire(t)},{fireworks:t=>{getGlobalConfetti().fireworks(t)},cannon:t=>{t?getGlobalConfetti().fire(t):getGlobalConfetti().cannon()},stars:()=>{getGlobalConfetti().stars()},pride:()=>{getGlobalConfetti().pride()},snow:()=>{getGlobalConfetti().snow()}});export{DbConfetti,o as confetti};
|