@designbasekorea/ui-wc 0.10.0 → 0.12.0
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 +19 -0
- package/LICENSE-WORLD-MAP.md +10 -0
- package/README.md +83 -0
- package/dist/esm/components/db-achievement-list.d.ts +2 -0
- package/dist/esm/components/db-achievement-list.js +1 -1
- package/dist/esm/components/db-activity-heatmap.d.ts +1 -0
- package/dist/esm/components/db-activity-heatmap.js +1 -1
- package/dist/esm/components/db-ad-banner.d.ts +2 -0
- package/dist/esm/components/db-ad-banner.js +1 -1
- package/dist/esm/components/db-ai-chat-list.d.ts +5 -1
- package/dist/esm/components/db-ai-chat-list.js +1 -1
- package/dist/esm/components/db-ai-chat.d.ts +2 -0
- package/dist/esm/components/db-ai-chat.js +1 -1
- package/dist/esm/components/db-ai-file-tree.js +1 -1
- package/dist/esm/components/db-alert.d.ts +1 -0
- package/dist/esm/components/db-alert.js +1 -1
- package/dist/esm/components/db-audio-player.d.ts +2 -0
- package/dist/esm/components/db-audio-player.js +1 -1
- package/dist/esm/components/db-avatar.d.ts +1 -0
- package/dist/esm/components/db-avatar.js +1 -1
- package/dist/esm/components/db-banner.d.ts +2 -0
- package/dist/esm/components/db-banner.js +1 -1
- package/dist/esm/components/db-button.d.ts +7 -1
- package/dist/esm/components/db-button.js +1 -1
- package/dist/esm/components/db-calendar.d.ts +5 -0
- package/dist/esm/components/db-calendar.js +1 -1
- package/dist/esm/components/db-card.d.ts +1 -0
- package/dist/esm/components/db-card.js +1 -1
- package/dist/esm/components/db-chat-composer.d.ts +3 -0
- package/dist/esm/components/db-chat-composer.js +1 -1
- package/dist/esm/components/db-chat-message.d.ts +2 -0
- package/dist/esm/components/db-chat-message.js +1 -1
- package/dist/esm/components/db-chat-room.d.ts +1 -0
- package/dist/esm/components/db-chat-room.js +1 -1
- package/dist/esm/components/db-chat-typing.d.ts +2 -0
- package/dist/esm/components/db-chat-typing.js +1 -1
- package/dist/esm/components/db-chat-widget.d.ts +9 -0
- package/dist/esm/components/db-chat-widget.js +1 -1
- package/dist/esm/components/db-chip.d.ts +2 -0
- package/dist/esm/components/db-chip.js +1 -1
- package/dist/esm/components/db-code-block.d.ts +1 -0
- package/dist/esm/components/db-code-block.js +1 -1
- package/dist/esm/components/db-color-picker.d.ts +4 -0
- package/dist/esm/components/db-color-picker.js +1 -1
- package/dist/esm/components/db-color-swatch.d.ts +3 -0
- package/dist/esm/components/db-color-swatch.js +1 -1
- package/dist/esm/components/db-cookie-banner.d.ts +3 -0
- package/dist/esm/components/db-cookie-banner.js +1 -1
- package/dist/esm/components/db-countdown.d.ts +2 -1
- package/dist/esm/components/db-countdown.js +1 -1
- package/dist/esm/components/db-date-picker.d.ts +3 -0
- package/dist/esm/components/db-date-picker.js +1 -1
- package/dist/esm/components/db-dropdown.d.ts +1 -0
- package/dist/esm/components/db-dropdown.js +1 -1
- package/dist/esm/components/db-dropzone.d.ts +2 -0
- package/dist/esm/components/db-dropzone.js +1 -1
- package/dist/esm/components/db-email-verify.d.ts +1 -0
- package/dist/esm/components/db-email-verify.js +1 -1
- package/dist/esm/components/db-file-uploader.d.ts +2 -0
- package/dist/esm/components/db-file-uploader.js +1 -1
- package/dist/esm/components/db-form.d.ts +2 -0
- package/dist/esm/components/db-form.js +1 -1
- package/dist/esm/components/db-gantt.d.ts +2 -0
- package/dist/esm/components/db-gantt.js +1 -1
- package/dist/esm/components/db-image-list.d.ts +3 -0
- package/dist/esm/components/db-image-list.js +1 -1
- package/dist/esm/components/db-image.d.ts +1 -0
- package/dist/esm/components/db-image.js +1 -1
- package/dist/esm/components/db-input-group.d.ts +30 -0
- package/dist/esm/components/db-input-group.js +1 -0
- package/dist/esm/components/db-input.d.ts +1 -0
- package/dist/esm/components/db-input.js +1 -1
- package/dist/esm/components/db-kanban.d.ts +3 -0
- package/dist/esm/components/db-kanban.js +1 -1
- package/dist/esm/components/db-lightbox.d.ts +2 -0
- package/dist/esm/components/db-lightbox.js +1 -1
- package/dist/esm/components/db-list.d.ts +2 -0
- package/dist/esm/components/db-list.js +1 -1
- package/dist/esm/components/db-markdown-editor.d.ts +2 -0
- package/dist/esm/components/db-markdown-editor.js +1 -1
- package/dist/esm/components/db-menu-item.js +1 -1
- package/dist/esm/components/db-navbar.d.ts +3 -0
- package/dist/esm/components/db-navbar.js +1 -1
- package/dist/esm/components/db-node.d.ts +1 -0
- package/dist/esm/components/db-node.js +1 -1
- package/dist/esm/components/db-onboarding-modal.d.ts +2 -0
- package/dist/esm/components/db-onboarding-modal.js +1 -1
- package/dist/esm/components/db-otp-input.d.ts +4 -1
- package/dist/esm/components/db-otp-input.js +1 -1
- package/dist/esm/components/db-page-header.js +1 -1
- package/dist/esm/components/db-popover.d.ts +1 -0
- package/dist/esm/components/db-popover.js +1 -1
- package/dist/esm/components/db-progress-step.d.ts +2 -0
- package/dist/esm/components/db-progress-step.js +1 -1
- package/dist/esm/components/db-range-slider.d.ts +1 -0
- package/dist/esm/components/db-range-slider.js +1 -1
- package/dist/esm/components/db-rating.d.ts +1 -0
- package/dist/esm/components/db-rating.js +1 -1
- package/dist/esm/components/db-search-bar.d.ts +2 -0
- package/dist/esm/components/db-search-bar.js +1 -1
- package/dist/esm/components/db-section-logo-cloud.js +1 -1
- package/dist/esm/components/db-selection-bar.d.ts +42 -0
- package/dist/esm/components/db-selection-bar.js +1 -0
- package/dist/esm/components/db-share.d.ts +5 -0
- package/dist/esm/components/db-share.js +1 -1
- package/dist/esm/components/db-sidebar.d.ts +3 -0
- package/dist/esm/components/db-sidebar.js +1 -1
- package/dist/esm/components/db-signature.d.ts +2 -0
- package/dist/esm/components/db-signature.js +1 -1
- package/dist/esm/components/db-spinner.d.ts +2 -0
- package/dist/esm/components/db-spinner.js +1 -1
- package/dist/esm/components/db-stepper.d.ts +2 -0
- package/dist/esm/components/db-stepper.js +1 -1
- package/dist/esm/components/db-table.d.ts +2 -0
- package/dist/esm/components/db-table.js +1 -1
- package/dist/esm/components/db-tabs.d.ts +1 -0
- package/dist/esm/components/db-tabs.js +1 -1
- package/dist/esm/components/db-ticket.js +1 -1
- package/dist/esm/components/db-time-picker.d.ts +2 -0
- package/dist/esm/components/db-time-picker.js +1 -1
- package/dist/esm/components/db-timeline.d.ts +4 -0
- package/dist/esm/components/db-timeline.js +1 -1
- package/dist/esm/components/db-toast.d.ts +2 -0
- package/dist/esm/components/db-toast.js +1 -1
- package/dist/esm/components/db-tool-layout.d.ts +92 -0
- package/dist/esm/components/db-tool-layout.js +1 -0
- package/dist/esm/components/db-tool-panel.d.ts +50 -0
- package/dist/esm/components/db-tool-panel.js +1 -0
- package/dist/esm/components/db-top-banner.d.ts +2 -0
- package/dist/esm/components/db-top-banner.js +1 -1
- package/dist/esm/components/db-video-player.d.ts +3 -0
- package/dist/esm/components/db-video-player.js +1 -1
- package/dist/esm/components/db-world-map.d.ts +60 -0
- package/dist/esm/components/db-world-map.js +1 -0
- package/dist/esm/components/db-youtube-player.d.ts +2 -0
- package/dist/esm/components/db-youtube-player.js +1 -1
- package/dist/esm/components/db-zoom-control.d.ts +52 -0
- package/dist/esm/components/db-zoom-control.js +1 -0
- package/dist/esm/components/input-validation.js +1 -1
- package/dist/esm/components/paper-card.d.ts +2 -0
- package/dist/esm/components/paper-card.js +1 -1
- package/dist/esm/core/i18n-copy.js +1 -1
- package/dist/esm/core/i18n-en.js +1 -1
- package/dist/esm/core/i18n.d.ts +34 -1
- package/dist/esm/core/i18n.js +1 -1
- package/dist/esm/helpers/world-countries.js +1 -0
- package/dist/esm/helpers/world-land.js +1 -0
- package/dist/esm/index.d.ts +7 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +484 -6
- package/dist/index.esm.js +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +871 -71
- package/dist/react/index.esm.js +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
import{labelFrom as t,dbText as e,dbFormat as s}from"../core/i18n.js";import{setHostClasses as i}from"../core/host-class.js";import{isDbIconName as a,createDbIcon as n}from"./db-icons.js";import"./db-button.js";import"./db-chip.js";import"./db-voice-control.js";const DEFAULT_ATTACH_OPTIONS=()=>[{id:"image",label:e("사진·이미지"),icon:"gallery",accept:"image/*",multiple:!0},{id:"video",label:e("동영상"),icon:"video",accept:"video/*",multiple:!0},{id:"file",label:e("문서·파일"),icon:"file-new",accept:"",multiple:!0},{id:"camera",label:e("카메라 촬영"),icon:"camera",accept:"image/*",capture:"environment"}];function kindFromMime(t){return t.startsWith("image/")?"image":t.startsWith("video/")?"video":"file"}const h={image:"gallery",video:"video",file:"file-new"};let c=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","stop-label","attach-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(e){if(this.built){if("value"===e){const t=this.getAttribute("value")||"";return this.field.value!==t&&(this.field.value=t,this.autoGrow()),void this.syncSendState()}"streaming"!==e&&"send-label"!==e&&"stop-label"!==e?"attach-label"!==e?this.sync():this.attachBtn.setAttribute("aria-label",t(this,"attach-label","chatComposer.attach")):this.renderSendButton()}}build(){this.built=!0,i(this,["designbase-wc-chat-composer"]),this.replaceChildren();const s=document.createElement("div");s.className="designbase-wc-chat-composer__surface",this.attachmentsEl=document.createElement("div"),this.attachmentsEl.className="designbase-wc-chat-composer__attachments",this.attachmentsEl.hidden=!0;const a=document.createElement("div");a.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),a.append(this.field);const n=document.createElement("div");n.className="designbase-wc-chat-composer__toolbar";const h=document.createElement("div");h.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,"attach-label","chatComposer.attach")),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",h.append(this.attachWrap,this.toolsEl);const c=document.createElement("div");c.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),c.append(this.micBtn,this.sendBtn),n.append(h,c),s.append(this.attachmentsEl,a,n),this.dropOverlay=document.createElement("div"),this.dropOverlay.className="designbase-wc-chat-composer__drop-overlay",this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true");const r=document.createElement("span");r.className="designbase-wc-chat-composer__drop-label",r.textContent=e("파일을 놓으면 첨부됩니다"),this.dropOverlay.append(r),this.append(s,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=t(this,"placeholder","chatComposer.placeholder"),this.field.disabled=this.disabled,this.field.readOnly=this.voiceActive;const e=this.getAttribute("max-length");e?this.field.maxLength=Number(e):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&&a(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 s=document.createElement("span");s.className="designbase-wc-chat-composer__attach-item-icon",t.icon&&a(t.icon)&&s.append(n(t.icon,18));const i=document.createElement("span");i.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,i.append(h),t.description){const e=document.createElement("span");e.className="designbase-wc-chat-composer__attach-item-desc",e.textContent=t.description,i.append(e)}e.append(s,i),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 e=document.createElement("div");e.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",s("{0} 제거",[t.name])),a.append(n("close",12)),a.addEventListener("click",()=>this.removeAttachment(t.id)),e.append(i,a),this.attachmentsEl.append(e);continue}const e=document.createElement("db-chip");e.setAttribute("label",t.name),e.setAttribute("size","s"),e.setAttribute("variant","neutral"),e.setAttribute("deletable",""),e.setAttribute("start-icon",h[t.kind]),e.addEventListener("db-delete",()=>this.removeAttachment(t.id)),this.attachmentsEl.append(e)}this.syncSendState()}renderSendButton(){const e=this.streaming;this.sendBtn.setAttribute("variant",e?"secondary":"primary"),this.sendBtn.setAttribute("start-icon",e?"stop":"send"),this.sendBtn.setAttribute("aria-label",e?t(this,"stop-label","chatComposer.stop"):t(this,"send-label","chatComposer.send")),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-${c+=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-${c+=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{subscribeDbLocale as t,labelFrom as e,dbText as s,dbFormat as i}from"../core/i18n.js";import{setHostClasses as a}from"../core/host-class.js";import{isDbIconName as n,createDbIcon as h}from"./db-icons.js";import"./db-button.js";import"./db-chip.js";import"./db-voice-control.js";const DEFAULT_ATTACH_OPTIONS=()=>[{id:"image",label:s("사진·이미지"),icon:"gallery",accept:"image/*",multiple:!0},{id:"video",label:s("동영상"),icon:"video",accept:"video/*",multiple:!0},{id:"file",label:s("문서·파일"),icon:"file-new",accept:"",multiple:!0},{id:"camera",label:s("카메라 촬영"),icon:"camera",accept:"image/*",capture:"environment"}];function kindFromMime(t){return t.startsWith("image/")?"image":t.startsWith("video/")?"video":"file"}const c={image:"gallery",video:"video",file:"file-new"};let r=0;class DbChatComposer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.defaultAttachOptions=!0,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","stop-label","attach-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.defaultAttachOptions=!Array.isArray(t)||0===t.length,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.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>this.syncLocale()),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(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,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))}syncLocale(){this.field.placeholder=e(this,"placeholder","chatComposer.placeholder"),this.attachBtn.setAttribute("aria-label",e(this,"attach-label","chatComposer.attach")),this.renderSendButton();const t=this.dropOverlay.querySelector(".designbase-wc-chat-composer__drop-label");t&&(t.textContent=s("파일을 놓으면 첨부됩니다")),this.defaultAttachOptions&&(this._attachOptions=DEFAULT_ATTACH_OPTIONS(),this.attachMenu.querySelectorAll(".designbase-wc-chat-composer__attach-item-label").forEach((t,e)=>{this._attachOptions[e]&&(t.textContent=this._attachOptions[e].label)})),this.attachmentsEl.querySelectorAll("[data-attachment-name]").forEach(t=>{t.setAttribute("aria-label",i("{0} 제거",[t.dataset.attachmentName||""]))})}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&&"send-label"!==t&&"stop-label"!==t?"attach-label"!==t?this.sync():this.attachBtn.setAttribute("aria-label",e(this,"attach-label","chatComposer.attach")):this.renderSendButton()}}build(){this.built=!0,a(this,["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 i=document.createElement("div");i.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),i.append(this.field);const n=document.createElement("div");n.className="designbase-wc-chat-composer__toolbar";const h=document.createElement("div");h.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",e(this,"attach-label","chatComposer.attach")),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",h.append(this.attachWrap,this.toolsEl);const c=document.createElement("div");c.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),c.append(this.micBtn,this.sendBtn),n.append(h,c),t.append(this.attachmentsEl,i,n),this.dropOverlay=document.createElement("div"),this.dropOverlay.className="designbase-wc-chat-composer__drop-overlay",this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true");const r=document.createElement("span");r.className="designbase-wc-chat-composer__drop-label",r.textContent=s("파일을 놓으면 첨부됩니다"),this.dropOverlay.append(r),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=e(this,"placeholder","chatComposer.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&&n(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 s=document.createElement("span");s.className="designbase-wc-chat-composer__attach-item-icon",t.icon&&n(t.icon)&&s.append(h(t.icon,18));const i=document.createElement("span");i.className="designbase-wc-chat-composer__attach-item-text";const a=document.createElement("span");if(a.className="designbase-wc-chat-composer__attach-item-label",a.textContent=t.label,i.append(a),t.description){const e=document.createElement("span");e.className="designbase-wc-chat-composer__attach-item-desc",e.textContent=t.description,i.append(e)}e.append(s,i),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 e=document.createElement("div");e.className="designbase-wc-chat-composer__thumb";const s=document.createElement("img");s.src=t.preview,s.alt=t.name;const a=document.createElement("button");a.type="button",a.dataset.attachmentName=t.name,a.className="designbase-wc-chat-composer__thumb-remove",a.setAttribute("aria-label",i("{0} 제거",[t.name])),a.append(h("close",12)),a.addEventListener("click",()=>this.removeAttachment(t.id)),e.append(s,a),this.attachmentsEl.append(e);continue}const e=document.createElement("db-chip");e.setAttribute("label",t.name),e.setAttribute("size","s"),e.setAttribute("variant","neutral"),e.setAttribute("deletable",""),e.setAttribute("start-icon",c[t.kind]),e.addEventListener("db-delete",()=>this.removeAttachment(t.id)),this.attachmentsEl.append(e)}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?e(this,"stop-label","chatComposer.stop"):e(this,"send-label","chatComposer.send")),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-${r+=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-${r+=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};
|
|
@@ -19,6 +19,7 @@ interface DbChatMessageAttachment {
|
|
|
19
19
|
* </db-chat-message>
|
|
20
20
|
*/
|
|
21
21
|
declare class DbChatMessage extends HTMLElement {
|
|
22
|
+
private unsubscribeLocale?;
|
|
22
23
|
static get observedAttributes(): string[];
|
|
23
24
|
private built;
|
|
24
25
|
private contentEl;
|
|
@@ -34,6 +35,7 @@ declare class DbChatMessage extends HTMLElement {
|
|
|
34
35
|
get attachments(): DbChatMessageAttachment[];
|
|
35
36
|
set attachments(list: DbChatMessageAttachment[]);
|
|
36
37
|
connectedCallback(): void;
|
|
38
|
+
disconnectedCallback(): void;
|
|
37
39
|
attributeChangedCallback(name: string): void;
|
|
38
40
|
private build;
|
|
39
41
|
private sync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createDbIcon as t,isDbIconName as e}from"./db-icons.js";import"./db-avatar.js";import{sanitizeHref as
|
|
1
|
+
import{createDbIcon as t,isDbIconName as e}from"./db-icons.js";import{subscribeDbLocale as s,dbText as a}from"../core/i18n.js";import"./db-avatar.js";import{sanitizeHref as i}from"../core/safe-url.js";import{CHAT_MESSAGE_ROLES as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const c={copy:"copy",regenerate:"refresh",like:"thumb-up",dislike:"thumb-down",edit:"edit"},r={copy:"복사",regenerate:"다시 생성",like:"좋아요",dislike:"싫어요",edit:"편집"};class DbChatMessage extends HTMLElement{constructor(){super(...arguments),this.built=!1,this._attachments=[]}static get observedAttributes(){return["role","name","avatar-src","avatar-initials","timestamp","status","actions","variant"]}get role(){return n.includes(this.getAttribute("role"))?this.getAttribute("role"):"assistant"}set role(t){this.setAttribute("role",t)}get attachments(){return this._attachments.map(t=>({...t}))}set attachments(t){this._attachments=Array.isArray(t)?t:[],this.built&&this.renderAttachments()}connectedCallback(){this.built||this.build(),this.unsubscribeLocale?.(),this.unsubscribeLocale=s(()=>{this.actionsEl.querySelectorAll("[data-chat-action]").forEach(t=>{const e=t.dataset.chatAction||"";t.setAttribute("aria-label",r[e]?a(r[e]):e)})}),this.sync()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(t){this.built&&("actions"===t&&this.renderActions(),this.sync())}build(){for(this.built=!0,this.contentEl=document.createElement("div"),this.contentEl.className="designbase-wc-chat-message__content";this.firstChild;)this.contentEl.append(this.firstChild);this.mediaEl=document.createElement("div"),this.mediaEl.className="designbase-wc-chat-message__media",this.mediaEl.hidden=!0,this.avatarWrap=document.createElement("div"),this.avatarWrap.className="designbase-wc-chat-message__avatar";const t=document.createElement("div");t.className="designbase-wc-chat-message__bubble",this.headerEl=document.createElement("div"),this.headerEl.className="designbase-wc-chat-message__header",this.nameEl=document.createElement("span"),this.nameEl.className="designbase-wc-chat-message__name",this.timeEl=document.createElement("span"),this.timeEl.className="designbase-wc-chat-message__time",this.headerEl.append(this.nameEl,this.timeEl),this.actionsEl=document.createElement("div"),this.actionsEl.className="designbase-wc-chat-message__actions",t.append(this.headerEl,this.mediaEl,this.contentEl,this.actionsEl),this.append(this.avatarWrap,t),this.renderAvatar(),this.renderActions(),this.renderAttachments()}sync(){this.className=["designbase-wc-chat-message",`designbase-wc-chat-message--${this.role}`,`designbase-wc-chat-message--${this.getAttribute("variant")||"bubble"}`,this.getAttribute("status")&&`designbase-wc-chat-message--${this.getAttribute("status")}`].filter(Boolean).join(" ");const t=this.getAttribute("name")||"";this.nameEl.textContent=t,this.nameEl.hidden=!t;const e=this.getAttribute("timestamp")||"";this.timeEl.textContent=e,this.timeEl.hidden=!e,this.headerEl.hidden=!t&&!e;const s=Boolean(this.contentEl.textContent?.trim()||this.contentEl.children.length);this.contentEl.hidden=!s&&this._attachments.length>0,this.renderAvatar()}renderAttachments(){if(!this.mediaEl)return;this.mediaEl.replaceChildren(),this.mediaEl.hidden=0===this._attachments.length;for(const e of this._attachments){const s=e.kind||(e.mime?.startsWith("image/")?"image":e.mime?.startsWith("video/")?"video":"file");if("image"===s&&e.url){const t=document.createElement("a");t.className="designbase-wc-chat-message__image",t.href=i(e.url,"#"),t.target="_blank",t.rel="noopener noreferrer";const s=document.createElement("img");s.src=e.url,s.alt=e.name,s.loading="lazy",t.append(s),this.mediaEl.append(t);continue}const a=document.createElement(e.url?"a":"div");a.className="designbase-wc-chat-message__file",e.url&&a instanceof HTMLAnchorElement&&(a.href=i(e.url,"#"),a.target="_blank",a.rel="noopener noreferrer",a.download=e.name);const n=document.createElement("span");n.textContent=e.name,a.append(t("video"===s?"video":"file-new",16),n),this.mediaEl.append(a)}const e=Boolean(this.contentEl.textContent?.trim()||this.contentEl.children.length);this.contentEl.hidden=!e&&this._attachments.length>0}renderAvatar(){const t=this.getAttribute("avatar-src"),e=this.getAttribute("avatar-initials"),s="assistant"===this.role||Boolean(t||e);if(this.avatarWrap.hidden=!s,!s)return void this.avatarWrap.replaceChildren();let a=this.avatarWrap.querySelector("db-avatar");a||(a=document.createElement("db-avatar"),this.avatarWrap.append(a)),a.setAttribute("size","s"),t?a.setAttribute("src",t):a.removeAttribute("src"),e?a.setAttribute("initials",e):a.removeAttribute("initials"),"assistant"!==this.role||t||e||a.setAttribute("variant","primary")}renderActions(){this.actionsEl.replaceChildren();const s=(this.getAttribute("actions")||"").split(",").map(t=>t.trim()).filter(Boolean);for(const i of s){const s=document.createElement("button");s.type="button",s.className="designbase-wc-chat-message__action",s.dataset.chatAction=i,s.setAttribute("aria-label",r[i]?a(r[i]):i);const n=c[i];s.append(t(n&&e(n)?n:"sparkle",16)),s.addEventListener("click",()=>this.handleAction(i,s)),this.actionsEl.append(s)}this.actionsEl.hidden=0===s.length}handleAction(t,e){if("copy"===t){const t=this.contentEl.textContent||"";navigator.clipboard?.writeText(t).catch(()=>{})}"like"!==t&&"dislike"!==t||e.classList.toggle("designbase-wc-chat-message__action--active"),this.dispatchEvent(new CustomEvent("db-action",{detail:{action:t,text:this.contentEl.textContent||""},bubbles:!0,composed:!0}))}}customElements.get("db-chat-message")||customElements.define("db-chat-message",DbChatMessage);export{DbChatMessage};
|
|
@@ -32,6 +32,7 @@ interface DbChatRoomMessage {
|
|
|
32
32
|
* room.addEventListener('db-send', (e) => room.addMessage({ mine:true, text:e.detail.value }));
|
|
33
33
|
*/
|
|
34
34
|
declare class DbChatRoom extends HTMLElement {
|
|
35
|
+
private unsubscribeLocale?;
|
|
35
36
|
static get observedAttributes(): string[];
|
|
36
37
|
private built;
|
|
37
38
|
private typing;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dbText as
|
|
1
|
+
import{subscribeDbLocale as t,dbText as e,getDbLocale as s}from"../core/i18n.js";import{createDbIcon as r}from"./db-icons.js";import{sanitizeHtmlFragment as a}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 i="designbase-wc-chat-room";let n=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${n+=1}`,...t})):[],this.built&&this.renderThread()}get messages(){return this._messages}connectedCallback(){this.built||this.build(),this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>{this.headerEl.querySelector(`.${i}__back`)?.setAttribute("aria-label",e("뒤로"));const t=this.headerEl.querySelector(`.${i}__name`);t&&(t.textContent=this.getAttribute("peer-name")||e("대화"));const s=this.headerEl.querySelector(`.${i}__status`);s&&(s.textContent=this.getAttribute("peer-status")||e("온라인"));const r=this.dropOverlay.querySelector(`.${i}__drop-label`);r&&(r.textContent=e("파일을 놓으면 첨부됩니다")),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||e("메시지 보내기…"))}),this.sync(),this.addEventListener("dragenter",this.handleDragEnter),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop)}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,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=i,this.replaceChildren(),this.headerEl=el("div",`${i}__header`),this.scrollEl=el("div",`${i}__scroll`),this.threadEl=el("div",`${i}__thread`),this.scrollEl.append(this.threadEl),this.dockEl=el("div",`${i}__dock`),this.dropOverlay=el("div",`${i}__drop-overlay`),this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true"),this.dropOverlay.append(el("span",`${i}__drop-label`,e("파일을 놓으면 첨부됩니다"))),this.composer=document.createElement("db-chat-composer"),this.composer.setAttribute("attach",""),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||e("메시지 보내기…")),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")||e("메시지 보내기…"))}renderHeader(){if(this.headerEl.replaceChildren(),this.hasAttribute("show-back")){const t=el("button",`${i}__back`);t.type="button",t.setAttribute("aria-label",e("뒤로")),t.append(r("chevron-left",20)),t.addEventListener("click",()=>this.dispatchEvent(new CustomEvent("db-back",{bubbles:!0,composed:!0}))),this.headerEl.append(t)}const t=document.createElement("db-avatar");t.className=`${i}__avatar`,t.setAttribute("size","m"),t.setAttribute("variant","primary");const s=this.getAttribute("peer-avatar"),a=this.getAttribute("peer-initials");s?t.setAttribute("src",s):a&&t.setAttribute("initials",a),this.hasAttribute("online")&&t.setAttribute("status","online");const n=el("div",`${i}__meta`);n.append(el("span",`${i}__name`,this.getAttribute("peer-name")||e("대화")));const h=this.getAttribute("peer-status")||(this.hasAttribute("online")?e("온라인"):"");h&&n.append(el("span",`${i}__status`,h)),this.headerEl.append(t,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");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);const s=t.avatarSrc||(t.mine?null:this.getAttribute("peer-avatar"));return s&&e.setAttribute("avatar-src",s),t.avatarInitials&&e.setAttribute("avatar-initials",t.avatarInitials),null!=t.html?e.replaceChildren(a(t.html)):e.textContent=t.text||"",t.attachments?.length&&(e.attachments=t.attachments),e}buildTyping(){const t=el("db-chat-typing");t.setAttribute("name",this.getAttribute("peer-name")||"");const e=this.getAttribute("peer-avatar");return e&&t.setAttribute("avatar-src",e),t.setAttribute("avatar-initials",this.getAttribute("peer-initials")||""),t}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}nowLabel(){return new Intl.DateTimeFormat("ko"===s()?"ko-KR":"en-US",{hour:"numeric",minute:"2-digit"}).format(new Date)}hasDragFiles(t){return Array.from(t.dataTransfer?.types||[]).includes("Files")}showDropOverlay(t){this.dropOverlay.hidden=!t,this.classList.toggle(`${i}--drop-active`,t)}addMessage(t){const e={id:`r${n+=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};
|
|
@@ -8,8 +8,10 @@ import './db-avatar.js';
|
|
|
8
8
|
* <db-chat-typing name="Orion" label="생각 중…"></db-chat-typing>
|
|
9
9
|
*/
|
|
10
10
|
declare class DbChatTyping extends HTMLElement {
|
|
11
|
+
private unsubscribeLocale?;
|
|
11
12
|
static get observedAttributes(): string[];
|
|
12
13
|
connectedCallback(): void;
|
|
14
|
+
disconnectedCallback(): void;
|
|
13
15
|
attributeChangedCallback(): void;
|
|
14
16
|
private render;
|
|
15
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./db-avatar.js";class DbChatTyping extends HTMLElement{static get observedAttributes(){return["name","avatar-src","avatar-initials","label"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}render(){this.className="designbase-wc-chat-typing",this.setAttribute("role","status"),this.setAttribute("aria-live","polite"),this.setAttribute("aria-label",this.getAttribute("label")||"AI가 응답을 생성하고 있습니다"),this.replaceChildren();const t=this.getAttribute("avatar-src"),
|
|
1
|
+
import"./db-avatar.js";import{subscribeDbLocale as t,dbText as e}from"../core/i18n.js";class DbChatTyping extends HTMLElement{static get observedAttributes(){return["name","avatar-src","avatar-initials","label"]}connectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>{this.setAttribute("aria-label",this.getAttribute("label")||e("AI가 응답을 생성하고 있습니다"))}),this.render()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(){this.render()}render(){this.className="designbase-wc-chat-typing",this.setAttribute("role","status"),this.setAttribute("aria-live","polite"),this.setAttribute("aria-label",this.getAttribute("label")||e("AI가 응답을 생성하고 있습니다")),this.replaceChildren();const t=this.getAttribute("avatar-src"),a=this.getAttribute("avatar-initials"),s=document.createElement("div");s.className="designbase-wc-chat-typing__avatar";const i=document.createElement("db-avatar");i.setAttribute("size","s"),i.setAttribute("variant","primary"),t&&i.setAttribute("src",t),a&&i.setAttribute("initials",a),s.append(i);const n=document.createElement("div");n.className="designbase-wc-chat-typing__bubble";const c=this.getAttribute("label");if(c){const t=document.createElement("span");t.className="designbase-wc-chat-typing__label",t.textContent=c,n.append(t)}const r=document.createElement("span");r.className="designbase-wc-chat-typing__dots",r.setAttribute("aria-hidden","true");for(let t=0;t<3;t+=1){const t=document.createElement("span");t.className="designbase-wc-chat-typing__dot",r.append(t)}n.append(r),this.append(s,n)}}customElements.get("db-chat-typing")||customElements.define("db-chat-typing",DbChatTyping);export{DbChatTyping};
|
|
@@ -86,6 +86,7 @@ interface DbChatWidgetArticle extends DbChatSuggestion {
|
|
|
86
86
|
declare class DbChatWidget extends HTMLElement {
|
|
87
87
|
static get observedAttributes(): string[];
|
|
88
88
|
private built;
|
|
89
|
+
private unsubscribeLocale?;
|
|
89
90
|
private typing;
|
|
90
91
|
private helpQuery;
|
|
91
92
|
private _activeArticle;
|
|
@@ -94,6 +95,12 @@ declare class DbChatWidget extends HTMLElement {
|
|
|
94
95
|
private _menuItems;
|
|
95
96
|
private _quickReplies;
|
|
96
97
|
private _articles;
|
|
98
|
+
private defaultMessages;
|
|
99
|
+
private defaultConversations;
|
|
100
|
+
private defaultMenu;
|
|
101
|
+
private defaultQuickReplies;
|
|
102
|
+
private defaultArticles;
|
|
103
|
+
private defaultSnapshots;
|
|
97
104
|
private launcher;
|
|
98
105
|
private launcherBadge;
|
|
99
106
|
private panel;
|
|
@@ -134,6 +141,8 @@ declare class DbChatWidget extends HTMLElement {
|
|
|
134
141
|
private handlePanelDragLeave;
|
|
135
142
|
private handlePanelDrop;
|
|
136
143
|
private sync;
|
|
144
|
+
private refreshDefaultData;
|
|
145
|
+
private refreshLocale;
|
|
137
146
|
private syncOpenState;
|
|
138
147
|
private renderBadge;
|
|
139
148
|
private renderHeader;
|
|
@@ -1 +1 @@
|
|
|
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 a}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const n=[{id:"c1",name:"디지털 상담원",avatarInitials:"상",preview:"무엇을 도와드릴까요? 24시간 대기중이에요.",time:"방금",unread:1,online:!0},{id:"c2",name:"결제·환불 지원",avatarInitials:"결",preview:"환불 요청이 정상 접수되었습니다.",time:"어제"},{id:"c3",name:"온보딩 도우미",avatarInitials:"온",preview:"설치가 완료되면 알려드릴게요.",time:"3일 전"}],r={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=r[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=n,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 a.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(){a.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")),a=el("div",`${o}__header-identity`),n=this.getAttribute("agent-avatar"),r=el(n?"db-avatar":"db-chat-orb");n?(r.setAttribute("src",n),r.setAttribute("alt",i),r.setAttribute("size","m")):(r.className=`${o}__header-orb`,r.setAttribute("size","xs"),r.setAttribute("variant",this.getAttribute("orb-variant")||"violet"));const l=el("div",`${o}__header-meta`);l.append(el("span",`${o}__header-name`,i),el("span",`${o}__header-status`,this.getAttribute("agent-status")||"보통 몇 분 내 응답")),a.append(r,l),s.append(t,a,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 a=el("div",`${o}__recent-body`);a.append(el("span",`${o}__recent-title`,"최근 대화 이어가기"),el("span",`${o}__recent-preview`,i.preview||"")),t.append(s,a,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 a=el("div",`${o}__conv-body`),n=el("div",`${o}__conv-top`);if(n.append(el("span",`${o}__conv-name`,t.name)),t.time&&n.append(el("span",`${o}__conv-time`,t.time)),a.append(n,el("span",`${o}__conv-preview`,t.preview||"")),i.append(s,a),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 a=el("input",`${o}__help-input`);a.type="search",a.placeholder=t("도움말 검색…"),a.value=this.helpQuery,a.addEventListener("input",()=>{this.helpQuery=a.value,this.renderHelpResults()}),i.append(s,a),this.bodyEl.append(i);const n=el("div",`${o}__help-results`);this.bodyEl.append(n),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 a=el("div",`${o}__article-body`);(t.body||t.description||"").split(/\n{2,}/).forEach(t=>{const e=el("p",void 0,t.trim());e.textContent&&a.append(e)}),s.append(a);const n=this.buildStartChatButton("이 내용으로 문의하기","secondary");s.append(n),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");i.setAttribute("role",e),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time);const s=t.avatarSrc||("assistant"===e?this.getAttribute("agent-avatar"):null);return s&&i.setAttribute("avatar-src",s),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`);t.setAttribute("name",this.getAttribute("agent-name")||"상담원");const e=this.getAttribute("agent-avatar");return e&&t.setAttribute("avatar-src",e),t.setAttribute("label","입력 중"),t}buildEmpty(t,e,i){const s=el("div",`${o}__empty`),a=el("db-chat-orb",`${o}__empty-orb`);return a.setAttribute("size","m"),a.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),s.append(a,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 a=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:a}),this.addMessage({role:"user",text:i,time:this.nowLabel(),attachments:a.length?a: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
|
+
import{subscribeDbLocale as t,dbText as e,getDbLocale as i}from"../core/i18n.js";import{createDbIcon as s,isDbIconName as a}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 r,CHAT_WIDGET_POSITIONS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const DEFAULT_MENU=()=>[{id:"products",title:e("제품·정책 알아보기"),description:e("AI가 정확하게 안내해 드려요"),icon:"sparkle",action:"help"},{id:"services",title:e("상담원과 대화하기"),description:e("지금 바로 대화를 시작하세요"),icon:"mail",action:"conversation"}],DEFAULT_QUICK_REPLIES=()=>[{id:"products",title:e("제품·정책 알아보기")},{id:"services",title:e("서비스 상담하기")},{id:"refund",title:e("환불·결제 문의")},{id:"account",title:e("계정·로그인 도움")}],DEFAULT_MESSAGES=()=>[{id:"m1",role:"assistant",name:e("상담원"),text:e("안녕하세요. 무엇을 도와드릴까요? 제품과 정책을 24시간 정확하게 안내해 드릴게요."),time:e("오후 12:31")},{id:"m2",role:"assistant",name:e("상담원"),text:e("아래에서 원하시는 도움을 선택하시거나, 자유롭게 메시지를 입력해 주세요."),time:e("오후 12:31"),actions:"like,dislike"}],DEFAULT_CONVERSATIONS=()=>[{id:"c1",name:e("디지털 상담원"),avatarInitials:"ko"===i()?"상":"A",preview:e("무엇을 도와드릴까요? 24시간 대기중이에요."),time:e("방금"),unread:1,online:!0},{id:"c2",name:e("결제·환불 지원"),avatarInitials:"ko"===i()?"결":"P",preview:e("환불 요청이 정상 접수되었습니다."),time:e("어제")},{id:"c3",name:e("온보딩 도우미"),avatarInitials:"ko"===i()?"온":"O",preview:e("설치가 완료되면 알려드릴게요."),time:e("3일 전")}],DEFAULT_ARTICLES=()=>[{id:"a1",title:e("시작하기 — 5분 만에 설정 완료"),description:e("계정 생성부터 첫 배포까지"),icon:"sparkle",body:e("계정을 만들고 첫 프로젝트를 배포하기까지 약 5분이면 충분합니다.\n\n1. 회원가입 후 이메일 인증\n2. 워크스페이스 생성\n3. 템플릿 선택 또는 빈 프로젝트 시작\n4. 배포 버튼을 눌러 미리보기 URL 확인")},{id:"a2",title:e("요금제와 결제 안내"),description:e("플랜 비교 · 청구 주기"),icon:"star",body:e("Free · Pro · Team 플랜을 제공합니다. 청구는 월간 또는 연간으로 선택할 수 있으며, 연간 결제는 할인율이 적용됩니다.\n\n결제 수단 변경과 영수증 다운로드는 설정 > 결제에서 가능합니다.")},{id:"a3",title:e("팀원 초대와 권한 관리"),description:e("역할별 접근 권한 설정"),icon:"user",body:e("Owner · Admin · Member · Viewer 역할을 지원합니다.\n\n설정 > 팀원에서 이메일로 초대하고, 역할은 언제든 변경할 수 있습니다.")},{id:"a4",title:e("데이터 내보내기·백업"),description:e("안전하게 보관하는 방법"),icon:"download",body:e("프로젝트 데이터는 JSON · CSV로 내보낼 수 있습니다. 자동 백업은 Pro 이상에서 매일 실행되며, 설정 > 데이터에서 수동 백업도 가능합니다.")}],o={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=o[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 l="designbase-wc-chat";class DbChatWidget extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.helpQuery="",this._activeArticle=null,this._messages=DEFAULT_MESSAGES(),this._conversations=DEFAULT_CONVERSATIONS(),this._menuItems=DEFAULT_MENU(),this._quickReplies=DEFAULT_QUICK_REPLIES(),this._articles=DEFAULT_ARTICLES(),this.defaultMessages=this._messages.slice(),this.defaultConversations=this._conversations.slice(),this.defaultMenu=this._menuItems.slice(),this.defaultQuickReplies=this._quickReplies.slice(),this.defaultArticles=this._articles.slice(),this.defaultSnapshots={messages:this.defaultMessages.map(t=>({...t})),conversations:this.defaultConversations.map(t=>({...t})),menu:this.defaultMenu.map(t=>({...t})),quick:this.defaultQuickReplies.map(t=>({...t})),articles:this.defaultArticles.map(t=>({...t}))},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(`${l}__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(`${l}__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(`${l}__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 r.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.refreshDefaultData(),this.sync(),this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>this.refreshLocale()),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,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(l),this.replaceChildren(),this.launcher=el("button",`${l}__launcher`),this.launcher.type="button",this.launcher.setAttribute("aria-haspopup","dialog");const t=el("span",`${l}__launcher-icon ${l}__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",`${l}__launcher-icon ${l}__launcher-icon--close`);i.append(widgetIcon("close-x",24)),this.launcherBadge=document.createElement("db-badge"),this.launcherBadge.className=`${l}__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",`${l}__panel`),this.panel.setAttribute("role","dialog"),this.panel.setAttribute("aria-modal","false"),this.headerEl=el("div",`${l}__header`),this.bodyEl=el("div",`${l}__body`),this.footerEl=el("div",`${l}__footer`),this.navEl=el("div",`${l}__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(`${l}--${t}`,t===this.position)),r.forEach(t=>this.classList.toggle(`${l}--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()}refreshDefaultData(){const update=(t,e,i,s,a)=>{e.forEach((e,r)=>{if(t.includes(e))for(const t of a)e[t]===i[r][t]&&(e[t]=s[r][t])})},t={messages:DEFAULT_MESSAGES(),conversations:DEFAULT_CONVERSATIONS(),menu:DEFAULT_MENU(),quick:DEFAULT_QUICK_REPLIES(),articles:DEFAULT_ARTICLES()};update(this._messages,this.defaultMessages,this.defaultSnapshots.messages,t.messages,["name","text","time"]),update(this._conversations,this.defaultConversations,this.defaultSnapshots.conversations,t.conversations,["name","avatarInitials","preview","time"]),update(this._menuItems,this.defaultMenu,this.defaultSnapshots.menu,t.menu,["title","description"]),update(this._quickReplies,this.defaultQuickReplies,this.defaultSnapshots.quick,t.quick,["title"]),update(this._articles,this.defaultArticles,this.defaultSnapshots.articles,t.articles,["title","description","body"]),this.defaultSnapshots=t}refreshLocale(){if(!this.built)return;const t=this.defaultSnapshots.messages;if(this.refreshDefaultData(),this.launcher.setAttribute("aria-label",this.getAttribute("launcher-label")||e(this.open?"상담창 닫기":"상담 시작하기")),this.renderHeader(),this.renderNav(),"home"===this.view||"messages"===this.view)this.renderBody();else if("help"===this.view){const t=this.bodyEl.querySelector(`.${l}__help-input`);t&&(t.placeholder=e("도움말 검색…")),this.renderHelpResults()}else if("article"===this.view){const t=this._activeArticle;if(t&&this.defaultArticles.includes(t)){const e=this.bodyEl.querySelector(`.${l}__article-title`),i=this.bodyEl.querySelector(`.${l}__article-desc`),s=this.bodyEl.querySelector(`.${l}__article-body`);e&&(e.textContent=t.title),i&&(i.textContent=t.description||""),s&&s.replaceChildren(...(t.body||t.description||"").split(/\n{2,}/).map(t=>el("p",void 0,t.trim())).filter(t=>t.textContent))}const i=this.bodyEl.querySelector(`.${l}__cta span`);i&&(i.textContent=e("이 내용으로 문의하기"))}else{const i=this.threadEl?.querySelectorAll("db-chat-message");this.defaultMessages.forEach((e,s)=>{const a=this._messages.indexOf(e),r=a>=0?i?.[a]:void 0;if(!r)return;const n=r.querySelector(".designbase-wc-chat-message__content");n?.textContent===t[s].text&&(n.textContent=e.text),r.getAttribute("name")===t[s].name&&r.setAttribute("name",e.name||""),r.getAttribute("timestamp")===t[s].time&&r.setAttribute("timestamp",e.time||"")});const s=this.bodyEl.querySelector('db-chat-suggestions[data-dbchat="quick"]');s&&(s.items=this._quickReplies),this.composer&&!this.hasAttribute("placeholder")&&this.composer.setAttribute("placeholder",e("메시지를 입력하세요…"));const a=this.threadEl?.querySelector("db-chat-typing");a&&(a.setAttribute("name",this.getAttribute("agent-name")||e("상담원")),a.setAttribute("label",e("입력 중")))}}syncOpenState(){const t=this.open;this.classList.toggle(`${l}--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")||e(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")||e("디지털 상담원"),a=el("div",`${l}__header-bar`);if("conversation"===t){const t=this.iconButton(s("chevron-left",20),e("뒤로"),()=>this.setView("messages")),r=el("div",`${l}__header-identity`),n=this.getAttribute("agent-avatar"),o=el(n?"db-avatar":"db-chat-orb");n?(o.setAttribute("src",n),o.setAttribute("alt",i),o.setAttribute("size","m")):(o.className=`${l}__header-orb`,o.setAttribute("size","xs"),o.setAttribute("variant",this.getAttribute("orb-variant")||"violet"));const h=el("div",`${l}__header-meta`);h.append(el("span",`${l}__header-name`,i),el("span",`${l}__header-status`,this.getAttribute("agent-status")||e("보통 몇 분 내 응답"))),r.append(o,h),a.append(t,r,this.iconButton(widgetIcon("close-x",20),e("닫기"),()=>this.close()))}else if("article"===t){const t=this.iconButton(s("chevron-left",20),e("뒤로"),()=>{this._activeArticle=null,this.setView("help")});a.append(t,el("span",`${l}__header-title`,this._activeArticle?.title||e("도움말")),this.iconButton(widgetIcon("close-x",20),e("닫기"),()=>this.close()))}else{const i=e("home"===t?"홈":"messages"===t?"메시지":"도움말");a.append(el("span",`${l}__header-title`,i),this.iconButton(widgetIcon("close-x",20),e("닫기"),()=>this.close()))}this.headerEl.append(a)}iconButton(t,e,i){const s=el("button",`${l}__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(`${l}__body--thread`,"conversation"===this.view),this.bodyEl.classList.toggle(`${l}__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",`${l}__welcome`);t.setAttribute("greeting",this.getAttribute("greeting")||e("안녕하세요")),t.setAttribute("title",this.getAttribute("title")||e("무엇을 도와드릴까요?")),t.setAttribute("highlight",this.getAttribute("highlight")||this.getAttribute("title")||e("도와드릴까요")),t.setAttribute("subtitle",this.getAttribute("subtitle")||e("제품·정책을 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",`${l}__recent`);t.type="button";const a=el("db-avatar",`${l}__recent-avatar`);a.setAttribute("size","s"),a.setAttribute("variant","primary"),i.avatarSrc?a.setAttribute("src",i.avatarSrc):a.setAttribute("initials",i.avatarInitials||i.name.charAt(0)||"상"),i.online&&a.setAttribute("status","online"),i.unread&&(a.setAttribute("badge",String(i.unread)),a.setAttribute("type","number"),a.setAttribute("badge-variant","primary"),a.setAttribute("badge-max-count","99"));const r=el("div",`${l}__recent-body`);r.append(el("span",`${l}__recent-title`,e("최근 대화 이어가기")),el("span",`${l}__recent-preview`,i.preview||"")),t.append(a,r,s("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(e("대화 시작하기"),"primary"));if(this._menuItems.length){const t=el("db-chat-suggestions",`${l}__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(e("아직 대화가 없어요"),e("궁금한 점이 있으면 새 대화를 시작해 보세요."),{ctaLabel:e("대화 시작하기"),onCta:()=>this.startConversation()}));const t=el("div",`${l}__conv-list`);this._conversations.forEach(e=>{const i=el("button",`${l}__conv`);i.type="button";const s=el("db-avatar",`${l}__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 a=el("div",`${l}__conv-body`),r=el("div",`${l}__conv-top`);if(r.append(el("span",`${l}__conv-name`,e.name)),e.time&&r.append(el("span",`${l}__conv-time`,e.time)),a.append(r,el("span",`${l}__conv-preview`,e.preview||"")),i.append(s,a),e.unread){const t=el("db-badge",`${l}__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",`${l}__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 t=el("div",`${l}__help-search`),i=el("span",`${l}__help-search-icon`);i.append(s("search",18));const a=el("input",`${l}__help-input`);a.type="search",a.placeholder=e("도움말 검색…"),a.value=this.helpQuery,a.addEventListener("input",()=>{this.helpQuery=a.value,this.renderHelpResults()}),t.append(i,a),this.bodyEl.append(t);const r=el("div",`${l}__help-results`);this.bodyEl.append(r),this.renderHelpResults()}renderHelpResults(){const t=this.bodyEl.querySelector(`.${l}__help-results`);if(!t)return;t.replaceChildren();const i=this.helpQuery.trim().toLowerCase(),s=i?this._articles.filter(t=>t.title.toLowerCase().includes(i)||(t.description||"").toLowerCase().includes(i)||(t.body||"").toLowerCase().includes(i)):this._articles;if(!s.length)return void t.append(this.buildEmpty(e(i?"검색 결과가 없어요":"문서가 없어요"),e(i?"다른 검색어로 다시 시도해 보세요.":"도움말 문서가 준비되면 여기에 표시됩니다.")));const a=el("db-chat-suggestions",`${l}__articles`);a.setAttribute("layout","card"),a.setAttribute("columns","1"),a.items=s,a.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,i=this._articles.find(t=>(t.id??t.title)===e.id)||s.find(t=>(t.id??t.title)===e.id);this.dispatch("db-article-select",{id:e.id,title:e.title}),i&&this.openArticle(i)}),t.append(a)}renderArticle(){const t=this._activeArticle;if(!t)return void this.setView("help");const i=el("article",`${l}__article`);if(t.icon&&a(t.icon)){const e=el("div",`${l}__article-icon`);e.append(s(t.icon,22)),i.append(e)}i.append(el("h2",`${l}__article-title`,t.title)),t.description&&i.append(el("p",`${l}__article-desc`,t.description));const r=el("div",`${l}__article-body`);(t.body||t.description||"").split(/\n{2,}/).forEach(t=>{const e=el("p",void 0,t.trim());e.textContent&&r.append(e)}),i.append(r);const n=this.buildStartChatButton(e("이 내용으로 문의하기"),"secondary");i.append(n),this.bodyEl.append(i)}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");i.setAttribute("role",e),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time);const s=t.avatarSrc||("assistant"===e?this.getAttribute("agent-avatar"):null);return s&&i.setAttribute("avatar-src",s),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",`${l}__typing`);t.setAttribute("name",this.getAttribute("agent-name")||e("상담원"));const i=this.getAttribute("agent-avatar");return i&&t.setAttribute("avatar-src",i),t.setAttribute("label",e("입력 중")),t}buildEmpty(t,e,i){const s=el("div",`${l}__empty`),a=el("db-chat-orb",`${l}__empty-orb`);return a.setAttribute("size","m"),a.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),s.append(a,el("p",`${l}__empty-title`,t),el("p",`${l}__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",`${l}__cta ${l}__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",`${l}__composer`);t.setAttribute("placeholder",this.getAttribute("placeholder")||e("메시지를 입력하세요…")),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 a=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:a}),this.addMessage({role:"user",text:i,time:this.nowLabel(),attachments:a.length?a: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:e("홈")},{view:"messages",icon:"chat",label:e("메시지")},{view:"help",icon:"help",label:e("도움말")}].forEach(t=>{const e=el("button",`${l}__tab`);e.type="button";const i=this.view===t.view;if(e.classList.toggle(`${l}__tab--active`,i),e.setAttribute("aria-current",String(i)),e.append(widgetIcon(t.icon,22),el("span",`${l}__tab-label`,t.label)),"messages"===t.view&&this.unread>0){const t=el("db-badge",`${l}__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(){return new Intl.DateTimeFormat("ko"===i()?"ko-KR":"en-US",{hour:"numeric",minute:"2-digit"}).format(new Date)}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{dbFormat as
|
|
1
|
+
import{subscribeDbLocale as e,dbFormat as t}from"../core/i18n.js";import{isDbIconName as s,createDbIcon as i}from"./db-icons.js";import{CHIP_SIZES as n,CHIP_VARIANTS as c}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(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>{this.querySelector(".designbase-wc-chip__delete-button")?.setAttribute("aria-label",t("{0} 삭제",[this.getAttribute("label")||""]))})}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(){this.render()}iconSize(e){return"s"===e?12:"l"===e?20:16}render(){const e=this.getAttribute("label")||this.textContent?.trim()||"",a=this.getAttribute("size")||"m",b=this.getAttribute("variant")||"default",d=this.hasAttribute("disabled"),l=this.hasAttribute("deletable"),r=this.hasAttribute("selected"),o=this.hasAttribute("full-width"),h=this.hasAttribute("clickable"),u=this.getAttribute("start-icon"),p=this.getAttribute("end-icon"),m=n.includes(a)?a:"m",g=c.includes(b)?b:"default",w=this.iconSize(m);if(this.className=["designbase-wc-chip",`designbase-wc-chip--${m}`,`designbase-wc-chip--${g}`,l&&"designbase-wc-chip--deletable",d&&"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&&!d?(this.setAttribute("role","button"),this.tabIndex=0,this.setAttribute("aria-pressed",String(r))):(this.removeAttribute("role"),this.removeAttribute("tabindex"),this.removeAttribute("aria-pressed")),u&&s(u)){const e=document.createElement("span");e.className="designbase-wc-chip__start-icon",e.append(i(u,w)),this.append(e)}const A=document.createElement("span");if(A.className="designbase-wc-chip__label",A.textContent=e,this.append(A),p&&s(p)&&!l){const e=document.createElement("span");e.className="designbase-wc-chip__end-icon",e.append(i(p,w)),this.append(e)}if(l){const s=document.createElement("button");s.type="button",s.className="designbase-wc-chip__delete-button",s.disabled=d,s.setAttribute("aria-label",t("{0} 삭제",[e])),s.append(i("close",w)),s.addEventListener("click",e=>{e.stopPropagation(),d||this.dispatchEvent(new CustomEvent("db-delete",{bubbles:!0,composed:!0}))}),this.append(s)}this.onclick=()=>{!d&&h&&this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0}))},this.onkeydown=e=>{"Enter"!==e.key&&" "!==e.key||d||!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{setHostClasses as e}from"../core/host-class.js";import{dbFormat as
|
|
1
|
+
import{setHostClasses as e}from"../core/host-class.js";import{subscribeDbLocale as t,dbText as i,dbFormat as s}from"../core/i18n.js";import{formatLanguageLabel as o,ensureHighlightReady as n,highlightCodeSync as c}from"./highlight-code.js";const a="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.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>{const e=this.querySelector(`.${a}__copy-button`);e&&(e.textContent=this.copied?this.getAttribute("copied-label")||i("복사됨"):this.getAttribute("copy-label")||i("복사"));const t=this.querySelector(`.${a}__textarea`),n=this.getActiveItem();t&&n&&t.setAttribute("aria-label",s("{0} 코드 편집",[o(n.language??this.getAttribute("language")??"tsx")]))}),this.style.display="block",this.built||(this.built=!0,this.internalActiveId=this.getAttribute("default-active-id")||this.getResolvedItems()[0]?.id||""),this.render(),n().then(()=>{this.isConnected&&this.built&&this.render()}),this.addEventListener("db-change",this.handleSegmentChange)}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,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(),r=d.length>1,l=this.getAttribute("language")||"tsx",h=n.language??l,u=this.getBool("show-language",!0),b=this.getBool("highlight",!0),m=this.getAttribute("theme")||"auto",g=this.getBool("show-copy",!0),p=this.getAttribute("copied-label")||i("복사됨"),v=this.getAttribute("copy-label")||i("복사"),A=this.getAttribute("overflow")||"scroll-x",y=this.getBool("editable",!1),f=y?this.editedCodes[n.id]??n.code:n.code,C=f.trim();let _=null;if(b&&f)try{_=c(f,h)}catch{_=null}if(t!==this.renderToken)return;const x=this.toCssSize(this.getAttribute("min-height")),E=this.toCssSize(this.getAttribute("max-height"));x?this.style.setProperty("--db-code-block-min-height",x):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"),e(this,[a,`${a}--overflow-${A}`,"auto"!==m&&`${a}--theme-${m}`,y&&`${a}--editable`]);const $=document.createElement("div");$.className=`${a}__toolbar`;const w=document.createElement("div");if(w.className=`${a}__toolbar-left`,r){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()),w.append(e)}if(u){const e=document.createElement("span");e.className=`${a}__language`,e.textContent=o(h),w.append(e)}if($.append(w),g){const e=document.createElement("db-button");e.className=`${a}__copy-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?p:v,e.addEventListener("db-click",()=>this.handleCopy()),$.append(e)}const I=document.createElement("div");if(I.className=`${a}__body`,r&&n.description){const e=document.createElement("p");e.className=`${a}__caption`,e.textContent=n.description,I.append(e)}if(y){void 0===this.editedCodes[n.id]&&(this.editedCodes[n.id]=n.code);const e=document.createElement("div");e.className=`${a}__editor`;const t=document.createElement("pre");t.className=`${a}__pre ${a}__pre--mirror`,t.setAttribute("aria-hidden","true");const i=document.createElement("code");i.className=`${a}__code language-${h}`,_?i.innerHTML=_:i.textContent=f,t.append(i);const d=document.createElement("textarea");d.className=`${a}__textarea`,d.value=f,d.spellcheck=!1,d.setAttribute("autocapitalize","off"),d.setAttribute("autocorrect","off"),d.setAttribute("aria-label",s("{0} 코드 편집",[o(h)])),d.addEventListener("input",()=>{this.editedCodes[n.id]=d.value;const e=b&&d.value?c(d.value,h):null;e?i.innerHTML=e:i.textContent=d.value,this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:d.value,itemId:n.id},bubbles:!0,composed:!0}))}),e.append(t,d),I.append(e)}else{const e=document.createElement("pre");e.className=`${a}__pre`;const t=document.createElement("code");t.className=`${a}__code language-${h}`,_?t.innerHTML=_:t.textContent=C,e.append(t),I.append(e)}this.replaceChildren($,I)}}customElements.get("db-code-block")||customElements.define("db-code-block",DbCodeBlock);export{DbCodeBlock};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
declare class DbColorPicker extends HTMLElement {
|
|
2
|
+
private unsubscribeLocale?;
|
|
3
|
+
private cancelButton?;
|
|
4
|
+
private applyButton?;
|
|
2
5
|
private h;
|
|
3
6
|
private s;
|
|
4
7
|
private v;
|
|
@@ -42,6 +45,7 @@ declare class DbColorPicker extends HTMLElement {
|
|
|
42
45
|
static get observedAttributes(): string[];
|
|
43
46
|
connectedCallback(): void;
|
|
44
47
|
disconnectedCallback(): void;
|
|
48
|
+
private updateLocale;
|
|
45
49
|
attributeChangedCallback(name: string): void;
|
|
46
50
|
private getBool;
|
|
47
51
|
private getOption;
|