@designbasekorea/ui-wc 0.9.4 → 0.11.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.
Files changed (182) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE-WORLD-MAP.md +10 -0
  3. package/README.md +60 -0
  4. package/dist/esm/components/db-accordion.d.ts +22 -10
  5. package/dist/esm/components/db-accordion.js +1 -1
  6. package/dist/esm/components/db-achievement-list.d.ts +2 -0
  7. package/dist/esm/components/db-achievement-list.js +1 -1
  8. package/dist/esm/components/db-activity-heatmap.d.ts +1 -0
  9. package/dist/esm/components/db-activity-heatmap.js +1 -1
  10. package/dist/esm/components/db-ad-banner.d.ts +2 -0
  11. package/dist/esm/components/db-ad-banner.js +1 -1
  12. package/dist/esm/components/db-ai-chat-list.d.ts +5 -1
  13. package/dist/esm/components/db-ai-chat-list.js +1 -1
  14. package/dist/esm/components/db-ai-chat.d.ts +2 -0
  15. package/dist/esm/components/db-ai-chat.js +1 -1
  16. package/dist/esm/components/db-ai-file-tree.js +1 -1
  17. package/dist/esm/components/db-alert.d.ts +1 -0
  18. package/dist/esm/components/db-alert.js +1 -1
  19. package/dist/esm/components/db-audio-player.d.ts +2 -0
  20. package/dist/esm/components/db-audio-player.js +1 -1
  21. package/dist/esm/components/db-avatar.d.ts +1 -0
  22. package/dist/esm/components/db-avatar.js +1 -1
  23. package/dist/esm/components/db-banner.d.ts +2 -0
  24. package/dist/esm/components/db-banner.js +1 -1
  25. package/dist/esm/components/db-button.d.ts +40 -2
  26. package/dist/esm/components/db-button.js +1 -1
  27. package/dist/esm/components/db-calendar.d.ts +5 -0
  28. package/dist/esm/components/db-calendar.js +1 -1
  29. package/dist/esm/components/db-card.d.ts +1 -0
  30. package/dist/esm/components/db-card.js +1 -1
  31. package/dist/esm/components/db-chat-composer.d.ts +3 -0
  32. package/dist/esm/components/db-chat-composer.js +1 -1
  33. package/dist/esm/components/db-chat-message.d.ts +2 -0
  34. package/dist/esm/components/db-chat-message.js +1 -1
  35. package/dist/esm/components/db-chat-orb.d.ts +13 -0
  36. package/dist/esm/components/db-chat-orb.js +1 -1
  37. package/dist/esm/components/db-chat-room.d.ts +1 -0
  38. package/dist/esm/components/db-chat-room.js +1 -1
  39. package/dist/esm/components/db-chat-typing.d.ts +2 -0
  40. package/dist/esm/components/db-chat-typing.js +1 -1
  41. package/dist/esm/components/db-chat-widget.d.ts +9 -0
  42. package/dist/esm/components/db-chat-widget.js +1 -1
  43. package/dist/esm/components/db-checkbox.d.ts +3 -0
  44. package/dist/esm/components/db-checkbox.js +1 -1
  45. package/dist/esm/components/db-chip.d.ts +2 -0
  46. package/dist/esm/components/db-chip.js +1 -1
  47. package/dist/esm/components/db-code-block.d.ts +1 -0
  48. package/dist/esm/components/db-code-block.js +1 -1
  49. package/dist/esm/components/db-color-picker.d.ts +4 -0
  50. package/dist/esm/components/db-color-picker.js +1 -1
  51. package/dist/esm/components/db-color-swatch.d.ts +3 -0
  52. package/dist/esm/components/db-color-swatch.js +1 -1
  53. package/dist/esm/components/db-confetti.d.ts +11 -5
  54. package/dist/esm/components/db-confetti.js +1 -1
  55. package/dist/esm/components/db-cookie-banner.d.ts +3 -0
  56. package/dist/esm/components/db-cookie-banner.js +1 -1
  57. package/dist/esm/components/db-countdown.d.ts +2 -1
  58. package/dist/esm/components/db-countdown.js +1 -1
  59. package/dist/esm/components/db-date-picker.d.ts +3 -0
  60. package/dist/esm/components/db-date-picker.js +1 -1
  61. package/dist/esm/components/db-dropdown.d.ts +1 -0
  62. package/dist/esm/components/db-dropdown.js +1 -1
  63. package/dist/esm/components/db-dropzone.d.ts +2 -0
  64. package/dist/esm/components/db-dropzone.js +1 -1
  65. package/dist/esm/components/db-email-verify.d.ts +37 -7
  66. package/dist/esm/components/db-email-verify.js +1 -1
  67. package/dist/esm/components/db-file-uploader.d.ts +2 -0
  68. package/dist/esm/components/db-file-uploader.js +1 -1
  69. package/dist/esm/components/db-floating-action-button.d.ts +1 -1
  70. package/dist/esm/components/db-floating-action-button.js +1 -1
  71. package/dist/esm/components/db-form.d.ts +2 -0
  72. package/dist/esm/components/db-form.js +1 -1
  73. package/dist/esm/components/db-gantt.d.ts +2 -0
  74. package/dist/esm/components/db-gantt.js +1 -1
  75. package/dist/esm/components/db-image-list.d.ts +3 -0
  76. package/dist/esm/components/db-image-list.js +1 -1
  77. package/dist/esm/components/db-image.d.ts +1 -0
  78. package/dist/esm/components/db-image.js +1 -1
  79. package/dist/esm/components/db-input-group.d.ts +30 -0
  80. package/dist/esm/components/db-input-group.js +1 -0
  81. package/dist/esm/components/db-input.d.ts +9 -1
  82. package/dist/esm/components/db-input.js +1 -1
  83. package/dist/esm/components/db-kanban.d.ts +3 -0
  84. package/dist/esm/components/db-kanban.js +1 -1
  85. package/dist/esm/components/db-lightbox.d.ts +2 -0
  86. package/dist/esm/components/db-lightbox.js +1 -1
  87. package/dist/esm/components/db-list.d.ts +2 -0
  88. package/dist/esm/components/db-list.js +1 -1
  89. package/dist/esm/components/db-markdown-editor.d.ts +2 -0
  90. package/dist/esm/components/db-markdown-editor.js +1 -1
  91. package/dist/esm/components/db-menu-item.js +1 -1
  92. package/dist/esm/components/db-navbar.d.ts +6 -0
  93. package/dist/esm/components/db-navbar.js +1 -1
  94. package/dist/esm/components/db-node.d.ts +1 -0
  95. package/dist/esm/components/db-node.js +1 -1
  96. package/dist/esm/components/db-onboarding-modal.d.ts +2 -0
  97. package/dist/esm/components/db-onboarding-modal.js +1 -1
  98. package/dist/esm/components/db-otp-input.d.ts +4 -1
  99. package/dist/esm/components/db-otp-input.js +1 -1
  100. package/dist/esm/components/db-page-header.d.ts +11 -0
  101. package/dist/esm/components/db-page-header.js +1 -1
  102. package/dist/esm/components/db-phone-verify.d.ts +33 -11
  103. package/dist/esm/components/db-phone-verify.js +1 -1
  104. package/dist/esm/components/db-popover.d.ts +1 -0
  105. package/dist/esm/components/db-popover.js +1 -1
  106. package/dist/esm/components/db-progress-step.d.ts +2 -0
  107. package/dist/esm/components/db-progress-step.js +1 -1
  108. package/dist/esm/components/db-radio.d.ts +3 -0
  109. package/dist/esm/components/db-radio.js +1 -1
  110. package/dist/esm/components/db-range-slider.d.ts +36 -7
  111. package/dist/esm/components/db-range-slider.js +1 -1
  112. package/dist/esm/components/db-rating.d.ts +1 -0
  113. package/dist/esm/components/db-rating.js +1 -1
  114. package/dist/esm/components/db-search-bar.d.ts +2 -0
  115. package/dist/esm/components/db-search-bar.js +1 -1
  116. package/dist/esm/components/db-section-logo-cloud.js +1 -1
  117. package/dist/esm/components/db-select.d.ts +35 -6
  118. package/dist/esm/components/db-select.js +1 -1
  119. package/dist/esm/components/db-share.d.ts +5 -0
  120. package/dist/esm/components/db-share.js +1 -1
  121. package/dist/esm/components/db-sidebar.d.ts +3 -0
  122. package/dist/esm/components/db-sidebar.js +1 -1
  123. package/dist/esm/components/db-signature.d.ts +2 -0
  124. package/dist/esm/components/db-signature.js +1 -1
  125. package/dist/esm/components/db-spinner.d.ts +2 -0
  126. package/dist/esm/components/db-spinner.js +1 -1
  127. package/dist/esm/components/db-stepper.d.ts +2 -0
  128. package/dist/esm/components/db-stepper.js +1 -1
  129. package/dist/esm/components/db-table.d.ts +2 -0
  130. package/dist/esm/components/db-table.js +1 -1
  131. package/dist/esm/components/db-tabs.d.ts +37 -13
  132. package/dist/esm/components/db-tabs.js +1 -1
  133. package/dist/esm/components/db-textarea.d.ts +13 -1
  134. package/dist/esm/components/db-textarea.js +1 -1
  135. package/dist/esm/components/db-ticket.js +1 -1
  136. package/dist/esm/components/db-time-picker.d.ts +2 -0
  137. package/dist/esm/components/db-time-picker.js +1 -1
  138. package/dist/esm/components/db-timeline.d.ts +4 -0
  139. package/dist/esm/components/db-timeline.js +1 -1
  140. package/dist/esm/components/db-toast.d.ts +2 -0
  141. package/dist/esm/components/db-toast.js +1 -1
  142. package/dist/esm/components/db-toggle.d.ts +33 -4
  143. package/dist/esm/components/db-toggle.js +1 -1
  144. package/dist/esm/components/db-tool-layout.d.ts +92 -0
  145. package/dist/esm/components/db-tool-layout.js +1 -0
  146. package/dist/esm/components/db-tool-panel.d.ts +50 -0
  147. package/dist/esm/components/db-tool-panel.js +1 -0
  148. package/dist/esm/components/db-top-banner.d.ts +2 -0
  149. package/dist/esm/components/db-top-banner.js +1 -1
  150. package/dist/esm/components/db-two-factor.d.ts +35 -12
  151. package/dist/esm/components/db-two-factor.js +1 -1
  152. package/dist/esm/components/db-video-player.d.ts +3 -0
  153. package/dist/esm/components/db-video-player.js +1 -1
  154. package/dist/esm/components/db-world-map.d.ts +60 -0
  155. package/dist/esm/components/db-world-map.js +1 -0
  156. package/dist/esm/components/db-youtube-player.d.ts +2 -0
  157. package/dist/esm/components/db-youtube-player.js +1 -1
  158. package/dist/esm/components/db-zoom-control.d.ts +52 -0
  159. package/dist/esm/components/db-zoom-control.js +1 -0
  160. package/dist/esm/components/input-validation.js +1 -1
  161. package/dist/esm/components/paper-card.d.ts +2 -0
  162. package/dist/esm/components/paper-card.js +1 -1
  163. package/dist/esm/core/control-label.js +1 -0
  164. package/dist/esm/core/i18n-copy.js +1 -1
  165. package/dist/esm/core/i18n-en.js +1 -1
  166. package/dist/esm/core/i18n.d.ts +31 -1
  167. package/dist/esm/core/i18n.js +1 -1
  168. package/dist/esm/core/range-values.js +1 -0
  169. package/dist/esm/core/text-field-state.js +1 -0
  170. package/dist/esm/helpers/world-countries.js +1 -0
  171. package/dist/esm/helpers/world-land.js +1 -0
  172. package/dist/esm/index.d.ts +6 -1
  173. package/dist/esm/index.js +1 -1
  174. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
  175. package/dist/index.cjs +1 -1
  176. package/dist/index.css +1 -1
  177. package/dist/index.d.ts +804 -83
  178. package/dist/index.esm.js +1 -1
  179. package/dist/react/index.cjs +1 -1
  180. package/dist/react/index.d.ts +6211 -648
  181. package/dist/react/index.esm.js +1 -1
  182. package/package.json +9 -4
@@ -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 s}from"../core/safe-url.js";import{CHAT_MESSAGE_ROLES as a}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const i={copy:"copy",regenerate:"refresh",like:"thumb-up",dislike:"thumb-down",edit:"edit"},n={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 a.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.sync()}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 a=e.kind||(e.mime?.startsWith("image/")?"image":e.mime?.startsWith("video/")?"video":"file");if("image"===a&&e.url){const t=document.createElement("a");t.className="designbase-wc-chat-message__image",t.href=s(e.url,"#"),t.target="_blank",t.rel="noopener noreferrer";const a=document.createElement("img");a.src=e.url,a.alt=e.name,a.loading="lazy",t.append(a),this.mediaEl.append(t);continue}const i=document.createElement(e.url?"a":"div");i.className="designbase-wc-chat-message__file",e.url&&i instanceof HTMLAnchorElement&&(i.href=s(e.url,"#"),i.target="_blank",i.rel="noopener noreferrer",i.download=e.name);const n=document.createElement("span");n.textContent=e.name,i.append(t("video"===a?"video":"file-new",16),n),this.mediaEl.append(i)}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 a of s){const s=document.createElement("button");s.type="button",s.className="designbase-wc-chat-message__action",s.setAttribute("aria-label",n[a]||a);const r=i[a];s.append(t(r&&e(r)?r:"sparkle",16)),s.addEventListener("click",()=>this.handleAction(a,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};
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};
@@ -40,6 +40,10 @@ declare class DbChatOrb extends HTMLElement {
40
40
  private lastVoiceRipple;
41
41
  private previousVoiceLevel;
42
42
  private displayedVoiceLevel;
43
+ private dotsMix;
44
+ private layer;
45
+ private customShapes;
46
+ private shapeBuffer;
43
47
  private inputStream;
44
48
  private audioContext;
45
49
  private audioSource;
@@ -53,6 +57,15 @@ declare class DbChatOrb extends HTMLElement {
53
57
  set state(v: OrbState);
54
58
  get theme(): OrbTheme;
55
59
  set theme(v: OrbTheme);
60
+ /** thinking 상태의 표현. `dots` 는 구체가 작아지며 점 3개가 순서대로 튀는 로딩 모션으로 바뀝니다. */
61
+ get thinkingStyle(): 'orb' | 'dots';
62
+ set thinkingStyle(v: 'orb' | 'dots');
63
+ /**
64
+ * morph 로 순환할 모양 목록. 프리셋 이름(circle · squircle · flower · star · triangle · blob)
65
+ * 또는 SVG path 의 d 값을 섞어서 넣을 수 있습니다. 속성 `shape="circle,flower,star"` 로도 지정합니다.
66
+ */
67
+ get shapes(): string[];
68
+ set shapes(v: string[] | null);
56
69
  /** 외부 오디오 분석기가 전달하는 정규화 음성 레벨(0–1). */
57
70
  get voiceLevel(): number;
58
71
  set voiceLevel(v: number);
@@ -1 +1 @@
1
- import{coerceToContract as t}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{CHAT_ORB_SIZES as e,CHAT_ORB_VARIANTS as i,CHAT_ORB_STATES as s,CHAT_ORB_THEMES as a}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r={aurora:{core:[22,20,52],lights:[[236,92,220],[70,140,250],[42,208,158]],rim:[199,210,254],glow:[120,110,240]},ocean:{core:[10,32,62],lights:[[56,189,248],[34,211,238],[96,165,250]],rim:[165,243,252],glow:[59,130,246]},emerald:{core:[6,42,38],lights:[[52,211,153],[74,222,128],[163,230,53]],rim:[187,247,208],glow:[34,197,94]},violet:{core:[40,12,82],lights:[[192,132,252],[168,85,247],[240,171,252]],rim:[245,208,254],glow:[139,92,246]},mono:{core:[30,34,44],lights:[[229,231,235],[156,163,175],[249,250,251]],rim:[255,255,255],glow:[156,163,175]}},n={aurora:{core:[240,240,250],lights:[[235,96,224],[74,144,246],[26,196,150]],rim:[150,130,210],glow:[138,128,244]},ocean:{core:[226,248,255],lights:[[14,165,233],[34,211,238],[96,165,250]],rim:[64,150,181],glow:[56,189,248]},emerald:{core:[228,250,240],lights:[[16,185,129],[74,222,128],[163,230,53]],rim:[57,145,111],glow:[52,211,153]},violet:{core:[246,233,255],lights:[[168,85,247],[217,70,239],[240,171,252]],rim:[143,91,183],glow:[192,132,252]},mono:{core:[243,246,250],lights:[[148,163,184],[203,213,225],[255,255,255]],rim:[100,116,139],glow:[148,163,184]}},o={idle:{speed:1.55,amp:.38,turb:.95,glow:1.15,breatheSpeed:1.9,breatheAmt:.055,lightAlpha:.95,autoRipple:0},thinking:{speed:3.2,amp:.5,turb:1.45,glow:1.85,breatheSpeed:4.2,breatheAmt:.075,lightAlpha:1.35,autoRipple:0},listening:{speed:1.45,amp:.32,turb:.9,glow:1.2,breatheSpeed:2.1,breatheAmt:.035,lightAlpha:.95,autoRipple:1.7}};function rgba(t,e){return`rgba(${t[0]},${t[1]},${t[2]},${e})`}function mix(t,e,i){return[t[0]+(e[0]-t[0])*i,t[1]+(e[1]-t[1])*i,t[2]+(e[2]-t[2])*i]}function clamp(t,e,i){return Math.min(i,Math.max(e,t))}function numberAttribute(t,e,i,s,a){const r=t.getAttribute(e);if(null===r||""===r.trim())return i;const n=Number(r);return Number.isFinite(n)?clamp(n,s,a):i}const h=2*Math.PI;function prefersReducedMotion(){return"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}const l=new Set;let c=0;function tick(t){l.forEach(e=>e.frame(t)),c=l.size?requestAnimationFrame(tick):0}class DbChatOrb extends HTMLElement{constructor(){super(...arguments),this.dpr=1,this.cssW=0,this.cssH=0,this.t0=0,this.lastFrame=0,this.resizeObs=null,this.io=null,this.themeObserver=null,this.themeMedia=null,this.visible=!1,this.hover={x:0,y:0,tx:0,ty:0},this.ripples=[],this.lastAutoRipple=0,this.lastVoiceRipple=0,this.previousVoiceLevel=0,this.displayedVoiceLevel=0,this.inputStream=null,this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null,this.onPointerMove=t=>{const e=this.getBoundingClientRect();e.width&&(this.hover.tx=Math.max(-1,Math.min(1,2*((t.clientX-e.left)/e.width-.5))),this.hover.ty=Math.max(-1,Math.min(1,2*((t.clientY-e.top)/e.height-.5))))},this.onPointerLeave=()=>{this.hover.tx=0,this.hover.ty=0},this.onPointerDown=()=>{this.addRipple(performance.now(),1)},this.onThemeChange=()=>{prefersReducedMotion()&&this.frame(performance.now())}}static get observedAttributes(){return["size","variant","state","theme","voice-level","min-scale","max-scale"]}get size(){return t(this.getAttribute("size"),e,"l")}set size(t){this.setAttribute("size",t)}get variant(){return t(this.getAttribute("variant"),i,"aurora")}set variant(t){this.setAttribute("variant",t)}get state(){return t(this.getAttribute("state"),s,"idle")}set state(t){this.setAttribute("state",t)}get theme(){return t(this.getAttribute("theme"),a,"auto")}set theme(t){this.setAttribute("theme",t)}get voiceLevel(){return numberAttribute(this,"voice-level",0,0,1)}set voiceLevel(t){const e=Number(t);this.setAttribute("voice-level",String(clamp(Number.isFinite(e)?e:0,0,1)))}get minScale(){return numberAttribute(this,"min-scale",.88,.5,1.5)}set minScale(t){const e=Number(t);this.setAttribute("min-scale",String(clamp(Number.isFinite(e)?e:.88,.5,1.5)))}get maxScale(){return Math.max(this.minScale,numberAttribute(this,"max-scale",1.18,.5,1.7))}set maxScale(t){const e=Number(t);this.setAttribute("max-scale",String(clamp(Number.isFinite(e)?e:1.18,.5,1.7)))}get audioStream(){return this.inputStream}set audioStream(t){(t!==this.inputStream||null!==t&&!this.analyser)&&(this.stopAudioAnalysis(),this.inputStream=t&&"function"==typeof t.getAudioTracks?t:null,this.isConnected&&this.inputStream&&this.startAudioAnalysis(this.inputStream))}connectedCallback(){this.syncClasses(),this.setAttribute("aria-hidden","true"),this.canvas||this.build(),this.t0=performance.now(),this.lastFrame=0,this.observeTheme(),this.inputStream&&this.startAudioAnalysis(this.inputStream),"undefined"!=typeof ResizeObserver&&(this.resizeObs=new ResizeObserver(()=>this.resize()),this.resizeObs.observe(this)),this.resize(),prefersReducedMotion()?this.frame(this.t0+1600):"undefined"!=typeof IntersectionObserver?(this.io=new IntersectionObserver(t=>{for(const e of t)this.setVisible(e.isIntersecting)},{threshold:.01}),this.io.observe(this)):this.setVisible(!0)}disconnectedCallback(){this.setVisible(!1),this.resizeObs?.disconnect(),this.resizeObs=null,this.io?.disconnect(),this.io=null,this.themeObserver?.disconnect(),this.themeObserver=null,this.themeMedia?.removeEventListener("change",this.onThemeChange),this.themeMedia=null,this.stopAudioAnalysis()}attributeChangedCallback(t){this.canvas&&("size"!==t&&"variant"!==t&&"state"!==t||this.syncClasses(),prefersReducedMotion()&&this.frame(performance.now()))}setVisible(t){t!==this.visible&&(this.visible=t,t?(l.add(this),function ensureTicking(){c||!l.size||prefersReducedMotion()||(c=requestAnimationFrame(tick))}()):l.delete(this))}syncClasses(){this.className=["designbase-wc-chat-orb",`designbase-wc-chat-orb--${this.size}`,`designbase-wc-chat-orb--${this.variant}`,`designbase-wc-chat-orb--${this.state}`].join(" ")}build(){this.replaceChildren(),this.canvas=document.createElement("canvas"),this.canvas.className="designbase-wc-chat-orb__canvas",this.ctx=this.canvas.getContext("2d"),this.append(this.canvas),this.addEventListener("pointermove",this.onPointerMove),this.addEventListener("pointerleave",this.onPointerLeave),this.addEventListener("pointerdown",this.onPointerDown)}addRipple(t,e){this.ripples.push({start:t,strength:clamp(e,.2,1)})}observeTheme(){"undefined"==typeof MutationObserver||this.themeObserver||(this.themeObserver=new MutationObserver(this.onThemeChange),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","data-color-mode","class"]})),"function"!=typeof window.matchMedia||this.themeMedia||(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),this.themeMedia.addEventListener("change",this.onThemeChange))}isDarkContext(){if("dark"===this.theme)return!0;if("light"===this.theme)return!1;const t=this.parentElement?.closest("[data-theme], [data-color-mode]"),e=t?.getAttribute("data-color-mode");if("dark"===e)return!0;if("light"===e)return!1;const i=t?.getAttribute("data-theme");if("dark"===i)return!0;if("light"===i)return!1;const s=document.documentElement,a=s.getAttribute("data-color-mode");if("dark"===a)return!0;if("light"===a)return!1;const r=s.getAttribute("data-theme");return"dark"===r||"light"!==r&&(this.themeMedia?.matches??!1)}startAudioAnalysis(t){if(this.analyser)return;const e=window.AudioContext??window.webkitAudioContext;if(e)try{const i=new e,s=i.createAnalyser();s.fftSize=512,s.smoothingTimeConstant=.58;const a=i.createMediaStreamSource(t);a.connect(s),this.audioContext=i,this.audioSource=a,this.analyser=s,this.analyserData=new Float32Array(s.fftSize),"suspended"===i.state&&i.resume().catch(()=>{})}catch{this.stopAudioAnalysis()}}stopAudioAnalysis(){try{this.audioSource?.disconnect(),this.analyser?.disconnect()}catch{}this.audioContext&&this.audioContext.close().catch(()=>{}),this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null}sampleVoiceLevel(t){if(this.analyser&&this.analyserData){this.analyser.getFloatTimeDomainData(this.analyserData);let t=0;for(const e of this.analyserData)t+=e*e;return{level:clamp((Math.sqrt(t/this.analyserData.length)-.01)/.11,0,1),hasInput:!0}}if(this.hasAttribute("voice-level"))return{level:this.voiceLevel,hasInput:!0};return{level:clamp(.16+.12*(Math.sin(2.05*t)+1)+.42*Math.pow(Math.max(0,Math.sin(.86*t+.7)),5),0,1),hasInput:!1}}resize(){this.canvas&&(this.dpr=Math.min(2,window.devicePixelRatio||1),this.cssW=this.clientWidth||72,this.cssH=this.clientHeight||this.cssW,this.canvas.width=Math.round(this.cssW*this.dpr),this.canvas.height=Math.round(this.cssH*this.dpr),prefersReducedMotion()&&this.frame(this.t0+1600))}frame(t){const e=this.ctx;if(!e||!this.cssW)return;const i=this.lastFrame?Math.min((t-this.lastFrame)/1e3,.1):1/60;this.lastFrame=t;const s=(t-this.t0)/1e3,a=this.cssW,l=this.cssH,c=a/2,d=l/2,m=Math.min(a,l)/2,u=this.isDarkContext(),p=u?r[this.variant]:n[this.variant],g=o[this.state];this.hover.x+=.08*(this.hover.tx-this.hover.x),this.hover.y+=.08*(this.hover.ty-this.hover.y);const b="listening"===this.state?this.sampleVoiceLevel(s):{level:0},v=b.level>this.displayedVoiceLevel?19:7;this.displayedVoiceLevel+=(b.level-this.displayedVoiceLevel)*(1-Math.exp(-v*i)),"listening"!==this.state&&this.displayedVoiceLevel<.001&&(this.displayedVoiceLevel=0);const f=Math.pow(clamp(this.displayedVoiceLevel,0,1),.72),y="listening"===this.state?this.minScale+(this.maxScale-this.minScale)*f:1,w=g.breatheAmt*("listening"===this.state?.55:1),M=1+Math.sin(s*g.breatheSpeed)*w,A="listening"===this.state?f:"thinking"===this.state?1:.55,S="thinking"===this.state?1.38:1,C=.68*m*M*y*("thinking"===this.state?1.1:1),x=.99*m;e.setTransform(this.dpr,0,0,this.dpr,0,0),e.clearRect(0,0,a,l),e.globalCompositeOperation="source-over",e.filter="none";const k="thinking"===this.state?u?.42:.22:u?.28:.5,O=u?mix(p.core,p.lights[0],k):"thinking"===this.state?mix(p.core,p.lights[0],.35):mix(p.core,[255,255,255],.5),R=mix(p.core,p.lights[1],u?"thinking"===this.state?.35:0:"thinking"===this.state?.28:.12),z=e.createRadialGradient(c,d-.12*C,.1*C,c,d,C);z.addColorStop(0,rgba(O,u?1:.96)),z.addColorStop(.72,rgba(R,u?1:.9)),z.addColorStop(.93,rgba(R,u?.72:.5)),z.addColorStop(1,rgba(R,0)),e.fillStyle=z,e.beginPath(),e.arc(c,d,C,0,h),e.fill(),e.globalCompositeOperation=u?"lighter":"source-over";const L=p.lights.length;for(let t=0;t<L;t+=1){const i=2.1*t,a=g.turb*(.85+.55*A),r=Math.sin(s*g.speed+i)*g.amp+Math.sin(s*g.speed*1.7+1.4*i)*g.amp*a*.75+Math.sin(s*g.speed*.45+.7*i)*g.amp*.35,n=Math.cos(s*g.speed*.9+1.3*i)*g.amp+Math.cos(s*g.speed*2+i)*g.amp*a*.75+Math.cos(s*g.speed*.55+.9*i)*g.amp*.35,o="thinking"===this.state?.42:.28,l=t/L*h-.5*Math.PI+s*g.speed*o,m=C*("thinking"===this.state?.52:.44),b=c+Math.cos(l)*m+r*C+this.hover.x*C*.12,v=d+Math.sin(l)*m+n*C+this.hover.y*C*.12,f=C*S*(.92+.28*A+.12*Math.sin(s*g.speed*1.3+i)),y="thinking"===this.state?mix(p.lights[t],u?[255,255,255]:p.core,u?.1:.18):p.lights[t],w=(u?.62:.78)*g.lightAlpha*(.85+.28*A)*("thinking"===this.state?1.22:1);e.save(),e.filter=`blur(${C*(u?.15:.18)*("thinking"===this.state?.92:1)}px)`;const M=e.createRadialGradient(b,v,0,b,v,f);M.addColorStop(0,rgba(y,Math.min(1,w))),M.addColorStop(.48,rgba(y,Math.min(1,.5*w))),M.addColorStop(1,rgba(y,0)),e.fillStyle=M,e.beginPath(),e.arc(b,v,f,0,h),e.fill(),e.restore()}e.filter="none",e.globalCompositeOperation="destination-in";const D=e.createRadialGradient(c,d,.6*C,c,d,C);D.addColorStop(0,"rgba(0,0,0,1)"),D.addColorStop(.86,"rgba(0,0,0,1)"),D.addColorStop(1,"rgba(0,0,0,0)"),e.fillStyle=D,e.beginPath(),e.arc(c,d,C,0,h),e.fill(),e.globalCompositeOperation="source-atop";const V=c-.22*C+this.hover.x*C*.1,F=d-.28*C+this.hover.y*C*.1,E=e.createRadialGradient(V,F,0,V,F,.95*C);E.addColorStop(0,`rgba(255,255,255,${u?.12:.2})`),E.addColorStop(1,"rgba(255,255,255,0)"),e.fillStyle=E,e.fillRect(c-C,d-C,2*C,2*C),e.globalCompositeOperation="destination-over";const P=e.createRadialGradient(c,d,.7*C,c,d,x),T=(u?.32:.14)*g.glow*(.85+.28*A);P.addColorStop(0,rgba(p.glow,T)),P.addColorStop(1,rgba(p.glow,0)),e.fillStyle=P,e.beginPath(),e.arc(c,d,x,0,h),e.fill(),e.globalCompositeOperation="source-over",e.filter="none"}}customElements.get("db-chat-orb")||customElements.define("db-chat-orb",DbChatOrb);export{DbChatOrb};
1
+ import{coerceToContract as t}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{CHAT_ORB_SIZES as e,CHAT_ORB_VARIANTS as i,CHAT_ORB_STATES as s,CHAT_ORB_THEMES as a}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r={aurora:{core:[22,20,52],lights:[[236,92,220],[70,140,250],[42,208,158]],rim:[199,210,254],glow:[120,110,240]},ocean:{core:[10,32,62],lights:[[56,189,248],[34,211,238],[96,165,250]],rim:[165,243,252],glow:[59,130,246]},emerald:{core:[6,42,38],lights:[[52,211,153],[74,222,128],[163,230,53]],rim:[187,247,208],glow:[34,197,94]},violet:{core:[40,12,82],lights:[[192,132,252],[168,85,247],[240,171,252]],rim:[245,208,254],glow:[139,92,246]},mono:{core:[30,34,44],lights:[[229,231,235],[156,163,175],[249,250,251]],rim:[255,255,255],glow:[156,163,175]}},o={aurora:{core:[240,240,250],lights:[[235,96,224],[74,144,246],[26,196,150]],rim:[150,130,210],glow:[138,128,244]},ocean:{core:[226,248,255],lights:[[14,165,233],[34,211,238],[96,165,250]],rim:[64,150,181],glow:[56,189,248]},emerald:{core:[228,250,240],lights:[[16,185,129],[74,222,128],[163,230,53]],rim:[57,145,111],glow:[52,211,153]},violet:{core:[246,233,255],lights:[[168,85,247],[217,70,239],[240,171,252]],rim:[143,91,183],glow:[192,132,252]},mono:{core:[243,246,250],lights:[[148,163,184],[203,213,225],[255,255,255]],rim:[100,116,139],glow:[148,163,184]}},n={idle:{speed:1.55,amp:.38,turb:.95,glow:1.15,breatheSpeed:1.9,breatheAmt:.055,lightAlpha:.95,autoRipple:0},thinking:{speed:3.2,amp:.5,turb:1.45,glow:1.85,breatheSpeed:4.2,breatheAmt:.075,lightAlpha:1.35,autoRipple:0},listening:{speed:1.45,amp:.32,turb:.9,glow:1.2,breatheSpeed:2.1,breatheAmt:.035,lightAlpha:.95,autoRipple:1.7}};function rgba(t,e){return`rgba(${t[0]},${t[1]},${t[2]},${e})`}function mix(t,e,i){return[t[0]+(e[0]-t[0])*i,t[1]+(e[1]-t[1])*i,t[2]+(e[2]-t[2])*i]}function clamp(t,e,i){return Math.min(i,Math.max(e,t))}function numberAttribute(t,e,i,s,a){const r=t.getAttribute(e);if(null===r||""===r.trim())return i;const o=Number(r);return Number.isFinite(o)?clamp(o,s,a):i}const h=2*Math.PI;function prefersReducedMotion(){return"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}const l=72,c={circle:()=>1,squircle:t=>1/Math.pow(Math.pow(Math.abs(Math.cos(t)),4)+Math.pow(Math.abs(Math.sin(t)),4),.25),flower:t=>1+.16*Math.cos(5*t),star:t=>.78+.22*Math.cos(5*t),triangle:t=>1+.3*Math.cos(3*(t+Math.PI/2)),blob:t=>1+.1*Math.sin(2*t+1)+.08*Math.sin(3*t)};function normalizeShape(t){let e=0;for(let i=0;i<t.length;i+=2)e=Math.max(e,Math.hypot(t[i],t[i+1]));const i=new Float32Array(t.length);for(let s=0;s<t.length;s+=1)i[s]=t[s]/(e||1);return i}function presetShape(t){const e=c[t];if(!e)return null;const i=[];for(let t=0;t<l;t+=1){const s=t/l*h,a=e(s);i.push(Math.cos(s)*a,Math.sin(s)*a)}return normalizeShape(i)}const d=new Map;function resolveShape(t){const e=t.trim(),i=d.get(e);if(i)return i;const s=(/^[Mm]/.test(e)?function svgPathShape(t){try{const e=document.createElementNS("http://www.w3.org/2000/svg","path");e.setAttribute("d",t);const i=e.getTotalLength();if(!(i>0))return null;const s=[];for(let t=0;t<l;t+=1){const a=e.getPointAtLength(i*t/l);s.push([a.x,a.y])}const a=s.map(t=>t[0]),r=s.map(t=>t[1]),o=(Math.min(...a)+Math.max(...a))/2,n=(Math.min(...r)+Math.max(...r))/2;let h=s.map(([t,e])=>[t-o,e-n]),c=0;for(let t=0;t<h.length;t+=1){const[e,i]=h[t],[s,a]=h[(t+1)%h.length];c+=e*a-s*i}c<0&&(h=h.reverse());let d=0,m=1/0;return h.forEach(([t,e],i)=>{const s=Math.abs(Math.atan2(e,t));s<m&&(m=s,d=i)}),h=[...h.slice(d),...h.slice(0,d)],normalizeShape(h.flat())}catch{return null}}(e):presetShape(e.toLowerCase()))??presetShape("circle");return d.set(e,s),s}function smoothstep(t,e,i){const s=clamp((i-t)/(e-t),0,1);return s*s*(3-2*s)}const m=new Set;let u=0;function tick(t){m.forEach(e=>e.frame(t)),u=m.size?requestAnimationFrame(tick):0}class DbChatOrb extends HTMLElement{constructor(){super(...arguments),this.dpr=1,this.cssW=0,this.cssH=0,this.t0=0,this.lastFrame=0,this.resizeObs=null,this.io=null,this.themeObserver=null,this.themeMedia=null,this.visible=!1,this.hover={x:0,y:0,tx:0,ty:0},this.ripples=[],this.lastAutoRipple=0,this.lastVoiceRipple=0,this.previousVoiceLevel=0,this.displayedVoiceLevel=0,this.dotsMix=0,this.layer=null,this.customShapes=null,this.shapeBuffer=new Float32Array(144),this.inputStream=null,this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null,this.onPointerMove=t=>{const e=this.getBoundingClientRect();e.width&&(this.hover.tx=Math.max(-1,Math.min(1,2*((t.clientX-e.left)/e.width-.5))),this.hover.ty=Math.max(-1,Math.min(1,2*((t.clientY-e.top)/e.height-.5))))},this.onPointerLeave=()=>{this.hover.tx=0,this.hover.ty=0},this.onPointerDown=()=>{this.addRipple(performance.now(),1)},this.onThemeChange=()=>{prefersReducedMotion()&&this.frame(performance.now())}}static get observedAttributes(){return["size","variant","state","theme","voice-level","min-scale","max-scale","thinking-style","shape","morph-interval"]}get size(){return t(this.getAttribute("size"),e,"l")}set size(t){this.setAttribute("size",t)}get variant(){return t(this.getAttribute("variant"),i,"aurora")}set variant(t){this.setAttribute("variant",t)}get state(){return t(this.getAttribute("state"),s,"idle")}set state(t){this.setAttribute("state",t)}get theme(){return t(this.getAttribute("theme"),a,"auto")}set theme(t){this.setAttribute("theme",t)}get thinkingStyle(){return"dots"===this.getAttribute("thinking-style")?"dots":"orb"}set thinkingStyle(t){this.setAttribute("thinking-style",t)}get shapes(){if(this.customShapes?.length)return this.customShapes;const t=(this.getAttribute("shape")??"").trim();return t?/^[Mm]/.test(t)?[t]:t.split(",").map(t=>t.trim()).filter(Boolean):["circle"]}set shapes(t){this.customShapes=Array.isArray(t)?t.filter(t=>"string"==typeof t&&t.trim()):null,prefersReducedMotion()&&this.canvas&&this.frame(performance.now())}get voiceLevel(){return numberAttribute(this,"voice-level",0,0,1)}set voiceLevel(t){const e=Number(t);this.setAttribute("voice-level",String(clamp(Number.isFinite(e)?e:0,0,1)))}get minScale(){return numberAttribute(this,"min-scale",.88,.5,1.5)}set minScale(t){const e=Number(t);this.setAttribute("min-scale",String(clamp(Number.isFinite(e)?e:.88,.5,1.5)))}get maxScale(){return Math.max(this.minScale,numberAttribute(this,"max-scale",1.18,.5,1.7))}set maxScale(t){const e=Number(t);this.setAttribute("max-scale",String(clamp(Number.isFinite(e)?e:1.18,.5,1.7)))}get audioStream(){return this.inputStream}set audioStream(t){(t!==this.inputStream||null!==t&&!this.analyser)&&(this.stopAudioAnalysis(),this.inputStream=t&&"function"==typeof t.getAudioTracks?t:null,this.isConnected&&this.inputStream&&this.startAudioAnalysis(this.inputStream))}connectedCallback(){this.syncClasses(),this.setAttribute("aria-hidden","true"),this.canvas||this.build(),this.t0=performance.now(),this.lastFrame=0,this.observeTheme(),this.inputStream&&this.startAudioAnalysis(this.inputStream),"undefined"!=typeof ResizeObserver&&(this.resizeObs=new ResizeObserver(()=>this.resize()),this.resizeObs.observe(this)),this.resize(),prefersReducedMotion()?this.frame(this.t0+1600):"undefined"!=typeof IntersectionObserver?(this.io=new IntersectionObserver(t=>{for(const e of t)this.setVisible(e.isIntersecting)},{threshold:.01}),this.io.observe(this)):this.setVisible(!0)}disconnectedCallback(){this.setVisible(!1),this.resizeObs?.disconnect(),this.resizeObs=null,this.io?.disconnect(),this.io=null,this.themeObserver?.disconnect(),this.themeObserver=null,this.themeMedia?.removeEventListener("change",this.onThemeChange),this.themeMedia=null,this.stopAudioAnalysis()}attributeChangedCallback(t){this.canvas&&("size"!==t&&"variant"!==t&&"state"!==t||this.syncClasses(),prefersReducedMotion()&&this.frame(performance.now()))}setVisible(t){t!==this.visible&&(this.visible=t,t?(m.add(this),function ensureTicking(){u||!m.size||prefersReducedMotion()||(u=requestAnimationFrame(tick))}()):m.delete(this))}syncClasses(){this.className=["designbase-wc-chat-orb",`designbase-wc-chat-orb--${this.size}`,`designbase-wc-chat-orb--${this.variant}`,`designbase-wc-chat-orb--${this.state}`].join(" ")}build(){this.replaceChildren(),this.canvas=document.createElement("canvas"),this.canvas.className="designbase-wc-chat-orb__canvas",this.ctx=this.canvas.getContext("2d"),this.append(this.canvas),this.addEventListener("pointermove",this.onPointerMove),this.addEventListener("pointerleave",this.onPointerLeave),this.addEventListener("pointerdown",this.onPointerDown)}addRipple(t,e){this.ripples.push({start:t,strength:clamp(e,.2,1)})}observeTheme(){"undefined"==typeof MutationObserver||this.themeObserver||(this.themeObserver=new MutationObserver(this.onThemeChange),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","data-color-mode","class"]})),"function"!=typeof window.matchMedia||this.themeMedia||(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),this.themeMedia.addEventListener("change",this.onThemeChange))}isDarkContext(){if("dark"===this.theme)return!0;if("light"===this.theme)return!1;const t=this.parentElement?.closest("[data-theme], [data-color-mode]"),e=t?.getAttribute("data-color-mode");if("dark"===e)return!0;if("light"===e)return!1;const i=t?.getAttribute("data-theme");if("dark"===i)return!0;if("light"===i)return!1;const s=document.documentElement,a=s.getAttribute("data-color-mode");if("dark"===a)return!0;if("light"===a)return!1;const r=s.getAttribute("data-theme");return"dark"===r||"light"!==r&&(this.themeMedia?.matches??!1)}startAudioAnalysis(t){if(this.analyser)return;const e=window.AudioContext??window.webkitAudioContext;if(e)try{const i=new e,s=i.createAnalyser();s.fftSize=512,s.smoothingTimeConstant=.58;const a=i.createMediaStreamSource(t);a.connect(s),this.audioContext=i,this.audioSource=a,this.analyser=s,this.analyserData=new Float32Array(s.fftSize),"suspended"===i.state&&i.resume().catch(()=>{})}catch{this.stopAudioAnalysis()}}stopAudioAnalysis(){try{this.audioSource?.disconnect(),this.analyser?.disconnect()}catch{}this.audioContext&&this.audioContext.close().catch(()=>{}),this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null}sampleVoiceLevel(t){if(this.analyser&&this.analyserData){this.analyser.getFloatTimeDomainData(this.analyserData);let t=0;for(const e of this.analyserData)t+=e*e;return{level:clamp((Math.sqrt(t/this.analyserData.length)-.01)/.11,0,1),hasInput:!0}}if(this.hasAttribute("voice-level"))return{level:this.voiceLevel,hasInput:!0};return{level:clamp(.16+.12*(Math.sin(2.05*t)+1)+.42*Math.pow(Math.max(0,Math.sin(.86*t+.7)),5),0,1),hasInput:!1}}resize(){this.canvas&&(this.dpr=Math.min(2,window.devicePixelRatio||1),this.cssW=this.clientWidth||72,this.cssH=this.clientHeight||this.cssW,this.canvas.width=Math.round(this.cssW*this.dpr),this.canvas.height=Math.round(this.cssH*this.dpr),prefersReducedMotion()&&this.frame(this.t0+1600))}frame(t){const e=this.ctx;if(!e||!this.cssW)return;let i=e;const s=this.lastFrame?Math.min((t-this.lastFrame)/1e3,.1):1/60;this.lastFrame=t;const a=(t-this.t0)/1e3,c=this.cssW,d=this.cssH,m=c/2,u=d/2,p=Math.min(c,d)/2,g=this.isDarkContext(),b=g?r[this.variant]:o[this.variant],f=n[this.state];this.hover.x+=.08*(this.hover.tx-this.hover.x),this.hover.y+=.08*(this.hover.ty-this.hover.y);const v="listening"===this.state?this.sampleVoiceLevel(a):{level:0},M=v.level>this.displayedVoiceLevel?19:7;this.displayedVoiceLevel+=(v.level-this.displayedVoiceLevel)*(1-Math.exp(-M*s)),"listening"!==this.state&&this.displayedVoiceLevel<.001&&(this.displayedVoiceLevel=0);const S=Math.pow(clamp(this.displayedVoiceLevel,0,1),.72),y="listening"===this.state?this.minScale+(this.maxScale-this.minScale)*S:1,w=f.breatheAmt*("listening"===this.state?.55:1),x=1+Math.sin(a*f.breatheSpeed)*w*(1-this.dotsMix),C="listening"===this.state?S:"thinking"===this.state?1:.55,A="thinking"===this.state?1.38:1,k=.68*p*x*y*("thinking"===this.state&&"dots"!==this.thinkingStyle?1.1:1),R=.99*p;i.setTransform(this.dpr,0,0,this.dpr,0,0),i.clearRect(0,0,c,d),i.globalCompositeOperation="source-over",i.filter="none";const O=this.shapes,L=numberAttribute(this,"morph-interval",2.6,.8,30),z=this.shapeBuffer;let P=0;if(O.length<=1||prefersReducedMotion())z.set(resolveShape(O[0]??"circle"));else{const t=Math.min(1.1,.55*L),e=Math.floor(a/L),i=smoothstep(0,1,(a-e*L-(L-t))/t),s=resolveShape(O[e%O.length]),r=resolveShape(O[(e+1)%O.length]);for(let t=0;t<z.length;t+=1)z[t]=s[t]+(r[t]-s[t])*i;P=(e+i)*h}const T=Math.cos(P),D=Math.sin(P),trace=(t,e,s)=>{const a=l,px=e=>t+(z[e%a*2]*T-z[e%a*2+1]*D)*s,py=t=>e+(z[t%a*2]*D+z[t%a*2+1]*T)*s;i.beginPath(),i.moveTo((px(0)+px(1))/2,(py(0)+py(1))/2);for(let t=1;t<=a;t+=1)i.quadraticCurveTo(px(t),py(t),(px(t)+px(t+1))/2,(py(t)+py(t+1))/2);i.closePath()},E="thinking"===this.state&&"dots"===this.thinkingStyle;prefersReducedMotion()?this.dotsMix=E?1:0:this.dotsMix+=((E?1:0)-this.dotsMix)*(1-Math.exp(-2.6*s)),Math.abs(this.dotsMix-(E?1:0))<.002&&(this.dotsMix=E?1:0);const F=smoothstep(0,1,this.dotsMix),V=.19*k,drawBody=t=>{const e=t;i.globalCompositeOperation="source-over";const s="thinking"===this.state?g?.42:.22:g?.28:.5,r=g?mix(b.core,b.lights[0],s):"thinking"===this.state?mix(b.core,b.lights[0],.35):mix(b.core,[255,255,255],.5),o=mix(b.core,b.lights[1],g?"thinking"===this.state?.35:0:"thinking"===this.state?.28:.12),n=i.createRadialGradient(m,u-.12*e,.1*e,m,u,e);n.addColorStop(0,rgba(r,g?1:.96)),n.addColorStop(.86,rgba(o,g?1:.9)),n.addColorStop(1,rgba(o,g?1:.9)),i.fillStyle=n,trace(m,u,e),i.fill(),i.globalCompositeOperation=g?"lighter":"source-over";const l=b.lights.length;for(let t=0;t<l;t+=1){const s=2.1*t,r=f.turb*(.85+.55*C),o=Math.sin(a*f.speed+s)*f.amp+Math.sin(a*f.speed*1.7+1.4*s)*f.amp*r*.75+Math.sin(a*f.speed*.45+.7*s)*f.amp*.35,n=Math.cos(a*f.speed*.9+1.3*s)*f.amp+Math.cos(a*f.speed*2+s)*f.amp*r*.75+Math.cos(a*f.speed*.55+.9*s)*f.amp*.35,c="thinking"===this.state?.42:.28,d=t/l*h-.5*Math.PI+a*f.speed*c,p=e*("thinking"===this.state?.52:.44),v=m+Math.cos(d)*p+o*e+this.hover.x*e*.12,M=u+Math.sin(d)*p+n*e+this.hover.y*e*.12,S=e*A*(.92+.28*C+.12*Math.sin(a*f.speed*1.3+s)),y="thinking"===this.state?mix(b.lights[t],g?[255,255,255]:b.core,g?.1:.18):b.lights[t],w=(g?.62:.78)*f.lightAlpha*(.85+.28*C)*("thinking"===this.state?1.22:1);i.save(),i.filter=`blur(${e*(g?.15:.18)*("thinking"===this.state?.92:1)}px)`;const x=i.createRadialGradient(v,M,0,v,M,S);x.addColorStop(0,rgba(y,Math.min(1,w))),x.addColorStop(.48,rgba(y,Math.min(1,.5*w))),x.addColorStop(1,rgba(y,0)),i.fillStyle=x,i.beginPath(),i.arc(v,M,S,0,h),i.fill(),i.restore()}i.filter="none",i.globalCompositeOperation="destination-in",i.fillStyle="#000",trace(m,u,e),i.fill(),i.globalCompositeOperation="source-atop";const c=m-.22*e+this.hover.x*e*.1,d=u-.28*e+this.hover.y*e*.1,p=i.createRadialGradient(c,d,0,c,d,.95*e);p.addColorStop(0,`rgba(255,255,255,${g?.14:.26})`),p.addColorStop(1,"rgba(255,255,255,0)"),i.fillStyle=p,i.fillRect(m-e,u-e,2*e,2*e);const v=i.createRadialGradient(m,u,.62*e,m,u,e),M=g?mix(b.rim,[255,255,255],.2):b.rim;v.addColorStop(0,rgba(M,0)),v.addColorStop(1,rgba(M,(g?.26:.2)*(.8+.4*C))),i.fillStyle=v,i.fillRect(m-e,u-e,2*e,2*e),i.globalCompositeOperation="source-over";const S=Math.max(1,.03*e)*("thinking"===this.state?1.2:1),y=b.lights.map(t=>g?mix(t,[255,255,255],.3):mix(t,b.rim,.45)),w=g?.55:.42,x=a*("thinking"===this.state?1.3:.42);let k;"function"==typeof i.createConicGradient?(k=i.createConicGradient(x,m,u),k.addColorStop(0,rgba(y[0],w)),k.addColorStop(.25,rgba(y[1],w)),k.addColorStop(.5,rgba(y[2%y.length],w)),k.addColorStop(.75,rgba(mix(y[1],[255,255,255],.55),w)),k.addColorStop(1,rgba(y[0],w))):(k=i.createLinearGradient(m-e,u-e,m+e,u+e),k.addColorStop(0,rgba(y[0],w)),k.addColorStop(1,rgba(y[1],w))),i.lineJoin="round",i.lineWidth=S,i.strokeStyle=k,trace(m,u,e-S/2),i.stroke();const O=i.createLinearGradient(m-.7*e,u-.9*e,m+.7*e,u+.9*e);O.addColorStop(0,"rgba(255,255,255,0.55)"),O.addColorStop(.35,"rgba(255,255,255,0)"),O.addColorStop(.75,"rgba(255,255,255,0)"),O.addColorStop(1,"rgba(255,255,255,0.22)"),i.lineWidth=Math.max(.7,.45*S),i.strokeStyle=O,trace(m,u,e-1.45*S),i.stroke(),i.globalCompositeOperation="destination-over";const L=i.createRadialGradient(m,u,.7*e,m,u,R),z=(g?.2:.095)*f.glow*(.85+.28*C);L.addColorStop(0,rgba(b.glow,z)),L.addColorStop(1,rgba(b.glow,0)),i.fillStyle=L,i.beginPath(),i.arc(m,u,R,0,h),i.fill(),i.globalCompositeOperation="source-over"},N=mix([(b.lights[0][0]+b.lights[1][0]+b.lights[2][0])/3,(b.lights[0][1]+b.lights[1][1]+b.lights[2][1])/3,(b.lights[0][2]+b.lights[1][2]+b.lights[2][2])/3],b.rim,g?.1:.35),drawDot=(t,e,s,a)=>{if(s<=.2||a<=.01)return;i.save(),i.globalAlpha=a;const r=i.createRadialGradient(t,e,.7*s,t,e,2*s);r.addColorStop(0,rgba(N,g?.18:.1)),r.addColorStop(1,rgba(N,0)),i.fillStyle=r,i.beginPath(),i.arc(t,e,2*s,0,h),i.fill(),i.fillStyle=rgba(N,.92),i.beginPath(),i.arc(t,e,s,0,h),i.fill(),i.lineWidth=Math.max(.8,.08*s),i.strokeStyle=rgba(mix(N,b.rim,.4),.55),i.beginPath(),i.arc(t,e,Math.max(.1,s-i.lineWidth/2),0,h),i.stroke(),i.restore()};if(F<.98){const t=k+(V-k)*smoothstep(0,.85,F);if(F<=.01)drawBody(t);else{const s=this.layer??(this.layer=document.createElement("canvas"));s.width===this.canvas.width&&s.height===this.canvas.height||(s.width=this.canvas.width,s.height=this.canvas.height);const a=s.getContext("2d");a&&(a.setTransform(this.dpr,0,0,this.dpr,0,0),a.clearRect(0,0,c,d),i=a,drawBody(t),i=e,i.save(),i.setTransform(1,0,0,1,0,0),i.globalAlpha=1-smoothstep(.55,.98,F),i.drawImage(s,0,0),i.restore())}}if(F>.01){const t=smoothstep(.3,1,F),e=.5*k*t;for(let i=0;i<3;i+=1){const s=5*a-.9*i,r=Math.pow(Math.max(0,Math.sin(s)),2);drawDot(m+(i-1)*e,u-r*V*1.3*smoothstep(.6,1,F),V*(1===i?1:t),(1===i?smoothstep(.5,.98,F):t)*(.75+.25*r))}}}}customElements.get("db-chat-orb")||customElements.define("db-chat-orb",DbChatOrb);export{DbChatOrb};
@@ -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 t}from"../core/i18n.js";import{createDbIcon as e}from"./db-icons.js";import{sanitizeHtmlFragment as s}from"../core/safe-html.js";import"./db-avatar.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-chat-composer.js";const r="designbase-wc-chat-room";let a=0;function el(t,e,s){const r=document.createElement(t);return e&&(r.className=e),null!=s&&(r.textContent=s),r}class DbChatRoom extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.dragDepth=0,this._messages=[],this.handleDragEnter=t=>{this.hasDragFiles(t)&&(t.preventDefault(),this.dragDepth+=1,this.showDropOverlay(!0))},this.handleDragOver=t=>{this.hasDragFiles(t)&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handleDragLeave=()=>{this.dragDepth=Math.max(0,this.dragDepth-1),0===this.dragDepth&&this.showDropOverlay(!1)},this.handleDrop=t=>{if(!this.hasDragFiles(t))return;t.preventDefault(),t.stopPropagation(),this.dragDepth=0,this.showDropOverlay(!1);const e=Array.from(t.dataTransfer?.files||[]);e.length&&this.composer&&(this.composer.addFiles(e,"drop"),this.composer.focusInput())}}static get observedAttributes(){return["peer-name","peer-avatar","peer-initials","peer-status","placeholder","online","show-back"]}set messages(t){this._messages=Array.isArray(t)?t.map(t=>({id:`r${a+=1}`,...t})):[],this.built&&this.renderThread()}get messages(){return this._messages}connectedCallback(){this.built||this.build(),this.sync(),this.addEventListener("dragenter",this.handleDragEnter),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop)}disconnectedCallback(){this.removeEventListener("dragenter",this.handleDragEnter),this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop)}attributeChangedCallback(){this.built&&this.sync()}build(){this.built=!0,this.className=r,this.replaceChildren(),this.headerEl=el("div",`${r}__header`),this.scrollEl=el("div",`${r}__scroll`),this.threadEl=el("div",`${r}__thread`),this.scrollEl.append(this.threadEl),this.dockEl=el("div",`${r}__dock`),this.dropOverlay=el("div",`${r}__drop-overlay`),this.dropOverlay.hidden=!0,this.dropOverlay.setAttribute("aria-hidden","true"),this.dropOverlay.append(el("span",`${r}__drop-label`,"파일을 놓으면 첨부됩니다")),this.composer=document.createElement("db-chat-composer"),this.composer.setAttribute("attach",""),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…"),this.composer.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail,s=e?.value?.trim()||"",r=e?.attachments||[];if(!s&&!r.length)return;const a=r.map(t=>({name:t.name,kind:t.kind,mime:t.mime,url:t.file?URL.createObjectURL(t.file):t.preview}));this.dispatchEvent(new CustomEvent("db-send",{detail:{value:s,attachments:a},bubbles:!0,composed:!0})),this.addMessage({mine:!0,text:s,time:this.nowLabel(),attachments:a.length?a:void 0})}),this.dockEl.append(this.composer),this.append(this.headerEl,this.scrollEl,this.dockEl,this.dropOverlay),this.renderThread()}sync(){this.renderHeader(),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…")}renderHeader(){if(this.headerEl.replaceChildren(),this.hasAttribute("show-back")){const s=el("button",`${r}__back`);s.type="button",s.setAttribute("aria-label",t("뒤로")),s.append(e("chevron-left",20)),s.addEventListener("click",()=>this.dispatchEvent(new CustomEvent("db-back",{bubbles:!0,composed:!0}))),this.headerEl.append(s)}const s=document.createElement("db-avatar");s.className=`${r}__avatar`,s.setAttribute("size","m"),s.setAttribute("variant","primary");const a=this.getAttribute("peer-avatar"),i=this.getAttribute("peer-initials");a?s.setAttribute("src",a):i&&s.setAttribute("initials",i),this.hasAttribute("online")&&s.setAttribute("status","online");const n=el("div",`${r}__meta`);n.append(el("span",`${r}__name`,this.getAttribute("peer-name")||"대화"));const h=this.getAttribute("peer-status")||(this.hasAttribute("online")?"온라인":"");h&&n.append(el("span",`${r}__status`,h)),this.headerEl.append(s,n)}renderThread(){this.threadEl.replaceChildren(),this._messages.forEach(t=>this.threadEl.append(this.buildMessage(t))),this.typing&&this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())}buildMessage(t){const e=el("db-chat-message");return e.dataset.id=t.id||"",e.setAttribute("role",t.mine?"user":"assistant"),!t.mine&&t.name&&e.setAttribute("name",t.name),t.time&&e.setAttribute("timestamp",t.time),!t.mine&&t.avatarSrc&&e.setAttribute("avatar-src",t.avatarSrc),!t.mine&&t.avatarInitials&&e.setAttribute("avatar-initials",t.avatarInitials),null!=t.html?e.replaceChildren(s(t.html)):e.textContent=t.text||"",t.attachments?.length&&(e.attachments=t.attachments),e}buildTyping(){const t=el("db-chat-typing");return t.setAttribute("name",this.getAttribute("peer-name")||""),t.setAttribute("avatar-initials",this.getAttribute("peer-initials")||""),t}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}nowLabel(){const t=new Date,e=t.getHours();return`${e<12?"오전":"오후"} ${e%12==0?12:e%12}:${t.getMinutes().toString().padStart(2,"0")}`}hasDragFiles(t){return Array.from(t.dataTransfer?.types||[]).includes("Files")}showDropOverlay(t){this.dropOverlay.hidden=!t,this.classList.toggle(`${r}--drop-active`,t)}addMessage(t){const e={id:`r${a+=1}`,...t};this._messages.push(e);const s=this.threadEl.querySelector("db-chat-typing"),r=this.buildMessage(e);return s?this.threadEl.insertBefore(r,s):this.threadEl.append(r),requestAnimationFrame(()=>this.scrollToEnd()),e.id}setMessages(t){this.messages=t}setTyping(t){if(this.typing=Boolean(t),!this.built)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())):!this.typing&&e&&e.remove()}focusInput(){this.composer?.focusInput()}getComposer(){return this.composer}}customElements.get("db-chat-room")||customElements.define("db-chat-room",DbChatRoom);export{DbChatRoom};
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"),e=this.getAttribute("avatar-initials"),a=document.createElement("div");a.className="designbase-wc-chat-typing__avatar";const s=document.createElement("db-avatar");s.setAttribute("size","s"),s.setAttribute("variant","primary"),t&&s.setAttribute("src",t),e&&s.setAttribute("initials",e),a.append(s);const i=document.createElement("div");i.className="designbase-wc-chat-typing__bubble";const n=this.getAttribute("label");if(n){const t=document.createElement("span");t.className="designbase-wc-chat-typing__label",t.textContent=n,i.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)}i.append(r),this.append(a,i)}}customElements.get("db-chat-typing")||customElements.define("db-chat-typing",DbChatTyping);export{DbChatTyping};
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 n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r=[{id:"c1",name:"디지털 상담원",avatarInitials:"상",preview:"무엇을 도와드릴까요? 24시간 대기중이에요.",time:"방금",unread:1,online:!0},{id:"c2",name:"결제·환불 지원",avatarInitials:"결",preview:"환불 요청이 정상 접수되었습니다.",time:"어제"},{id:"c3",name:"온보딩 도우미",avatarInitials:"온",preview:"설치가 완료되면 알려드릴게요.",time:"3일 전"}],a={chat:'<path d="M4 5.5A1.5 1.5 0 0 1 5.5 4h13A1.5 1.5 0 0 1 20 5.5v9a1.5 1.5 0 0 1-1.5 1.5H9l-4 3.5V16H5.5A1.5 1.5 0 0 1 4 14.5z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>',home:'<path d="M4 10.5 12 4l8 6.5M6 9.5V19a1 1 0 0 0 1 1h3.5v-4.5a1.5 1.5 0 0 1 3 0V20H17a1 1 0 0 0 1-1V9.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>',help:'<circle cx="12" cy="12" r="8.25" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M9.6 9.4a2.5 2.5 0 0 1 4.9.6c0 1.7-2.5 2-2.5 3.6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="12" cy="16.6" r="0.95" fill="currentColor"/>',"close-x":'<path d="m6.5 6.5 11 11m0-11-11 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>'};function widgetIcon(t,e=20){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox","0 0 24 24"),i.setAttribute("width",String(e)),i.setAttribute("height",String(e)),i.setAttribute("fill","none"),i.setAttribute("aria-hidden","true"),i.setAttribute("focusable","false"),i.innerHTML=a[t],i}function el(t,e,i){const s=document.createElement(t);return e&&(s.className=e),null!=i&&(s.textContent=i),s}function parseJson(t,e){if(!t)return e;try{const i=JSON.parse(t);return Array.isArray(i)?i:e}catch{return e}}const o="designbase-wc-chat";class DbChatWidget extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this.helpQuery="",this._activeArticle=null,this._messages=[{id:"m1",role:"assistant",name:"상담원",text:t("안녕하세요. 무엇을 도와드릴까요? 제품과 정책을 24시간 정확하게 안내해 드릴게요."),time:"오후 12:31"},{id:"m2",role:"assistant",name:"상담원",text:t("아래에서 원하시는 도움을 선택하시거나, 자유롭게 메시지를 입력해 주세요."),time:"오후 12:31",actions:"like,dislike"}],this._conversations=r,this._menuItems=[{id:"products",title:t("제품·정책 알아보기"),description:t("AI가 정확하게 안내해 드려요"),icon:"sparkle",action:"help"},{id:"services",title:t("상담원과 대화하기"),description:t("지금 바로 대화를 시작하세요"),icon:"mail",action:"conversation"}],this._quickReplies=[{id:"products",title:t("제품·정책 알아보기")},{id:"services",title:t("서비스 상담하기")},{id:"refund",title:t("환불·결제 문의")},{id:"account",title:t("계정·로그인 도움")}],this._articles=[{id:"a1",title:t("시작하기 — 5분 만에 설정 완료"),description:t("계정 생성부터 첫 배포까지"),icon:"sparkle",body:"계정을 만들고 첫 프로젝트를 배포하기까지 약 5분이면 충분합니다.\n\n1. 회원가입 후 이메일 인증\n2. 워크스페이스 생성\n3. 템플릿 선택 또는 빈 프로젝트 시작\n4. 배포 버튼을 눌러 미리보기 URL 확인"},{id:"a2",title:t("요금제와 결제 안내"),description:t("플랜 비교 · 청구 주기"),icon:"star",body:"Free · Pro · Team 플랜을 제공합니다. 청구는 월간 또는 연간으로 선택할 수 있으며, 연간 결제는 할인율이 적용됩니다.\n\n결제 수단 변경과 영수증 다운로드는 설정 > 결제에서 가능합니다."},{id:"a3",title:t("팀원 초대와 권한 관리"),description:t("역할별 접근 권한 설정"),icon:"user",body:"Owner · Admin · Member · Viewer 역할을 지원합니다.\n\n설정 > 팀원에서 이메일로 초대하고, 역할은 언제든 변경할 수 있습니다."},{id:"a4",title:t("데이터 내보내기·백업"),description:t("안전하게 보관하는 방법"),icon:"download",body:"프로젝트 데이터는 JSON · CSV로 내보낼 수 있습니다. 자동 백업은 Pro 이상에서 매일 실행되며, 설정 > 데이터에서 수동 백업도 가능합니다."}],this.composer=null,this.threadEl=null,this.handleDocumentKeyDown=t=>{"Escape"===t.key&&this.open&&(t.preventDefault(),this.close(),this.launcher?.focus())},this.panelDragDepth=0,this.handlePanelDragEnter=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),this.panelDragDepth+=1,this.bodyEl.classList.add(`${o}__body--drop-active`))},this.handlePanelDragOver=t=>{"conversation"===this.view&&this.composer&&Array.from(t.dataTransfer?.types||[]).includes("Files")&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"))},this.handlePanelDragLeave=()=>{this.panelDragDepth=Math.max(0,this.panelDragDepth-1),0===this.panelDragDepth&&this.bodyEl.classList.remove(`${o}__body--drop-active`)},this.handlePanelDrop=t=>{if("conversation"!==this.view||!this.composer)return;if(!Array.from(t.dataTransfer?.types||[]).includes("Files"))return;t.preventDefault(),t.stopPropagation(),this.panelDragDepth=0,this.bodyEl.classList.remove(`${o}__body--drop-active`);const e=Array.from(t.dataTransfer?.files||[]);e.length&&(this.composer.addFiles(e,"drop"),this.composer.focusInput())}}static get observedAttributes(){return["open","view","position","agent-name","agent-avatar","agent-status","greeting","title","subtitle","placeholder","unread","accent","orb-variant","launcher-label"]}get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",Boolean(t))}get view(){const t=this.getAttribute("view");return s.includes(t)?t:"home"}set view(t){this.setAttribute("view",t)}get position(){const t=this.getAttribute("position");return n.includes(t)?t:"bottom-right"}set position(t){this.setAttribute("position",t)}get unread(){return Number(this.getAttribute("unread")||0)}set unread(t){this.setAttribute("unread",String(t))}set messages(t){this._messages=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get messages(){return this._messages}set conversations(t){this._conversations=Array.isArray(t)?t:[],this.built&&this.renderBody()}get conversations(){return this._conversations}set menuItems(t){this._menuItems=Array.isArray(t)?t:[],this.built&&"home"===this.view&&this.renderBody()}get menuItems(){return this._menuItems}set quickReplies(t){this._quickReplies=Array.isArray(t)?t:[],this.built&&"conversation"===this.view&&this.renderBody()}get quickReplies(){return this._quickReplies}set articles(t){this._articles=Array.isArray(t)?t:[],!this.built||"help"!==this.view&&"article"!==this.view||this.renderBody()}get articles(){return this._articles}connectedCallback(){this.hydrateFromAttributes(),this.build(),this.sync(),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){document.removeEventListener("keydown",this.handleDocumentKeyDown)}attributeChangedCallback(t,e,i){if(this.built&&e!==i){if("open"!==t)return"view"===t?(this.sync(),void this.dispatch("db-view-change",{view:this.view})):void this.sync();this.syncOpenState()}}hydrateFromAttributes(){this._messages=parseJson(this.getAttribute("messages"),this._messages),this._conversations=parseJson(this.getAttribute("conversations"),this._conversations),this._menuItems=parseJson(this.getAttribute("menu-items"),this._menuItems),this._quickReplies=parseJson(this.getAttribute("quick-replies"),this._quickReplies),this._articles=parseJson(this.getAttribute("articles"),this._articles)}build(){if(this.built)return;this.built=!0,this.classList.add(o),this.replaceChildren(),this.launcher=el("button",`${o}__launcher`),this.launcher.type="button",this.launcher.setAttribute("aria-haspopup","dialog");const t=el("span",`${o}__launcher-icon ${o}__launcher-icon--open`),e=el("db-chat-orb");e.setAttribute("size","s"),e.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),e.setAttribute("state","idle"),t.append(e);const i=el("span",`${o}__launcher-icon ${o}__launcher-icon--close`);i.append(widgetIcon("close-x",24)),this.launcherBadge=document.createElement("db-badge"),this.launcherBadge.className=`${o}__launcher-badge`,this.launcherBadge.setAttribute("type","number"),this.launcherBadge.setAttribute("variant","danger"),this.launcherBadge.setAttribute("size","s"),this.launcherBadge.setAttribute("max-count","99"),this.launcher.append(t,i),this.launcher.addEventListener("click",()=>this.toggle()),this.panel=el("div",`${o}__panel`),this.panel.setAttribute("role","dialog"),this.panel.setAttribute("aria-modal","false"),this.headerEl=el("div",`${o}__header`),this.bodyEl=el("div",`${o}__body`),this.footerEl=el("div",`${o}__footer`),this.navEl=el("div",`${o}__nav`),this.panel.append(this.headerEl,this.bodyEl,this.footerEl,this.navEl),this.append(this.panel,this.launcher,this.launcherBadge),this.bodyEl.addEventListener("dragenter",this.handlePanelDragEnter),this.bodyEl.addEventListener("dragover",this.handlePanelDragOver),this.bodyEl.addEventListener("dragleave",this.handlePanelDragLeave),this.bodyEl.addEventListener("drop",this.handlePanelDrop)}sync(){n.forEach(t=>this.classList.toggle(`${o}--${t}`,t===this.position)),s.forEach(t=>this.classList.toggle(`${o}--view-${t}`,t===this.view));const t=this.getAttribute("accent");t?this.style.setProperty("--dbchat-accent",t):this.style.removeProperty("--dbchat-accent"),this.renderBadge(),this.renderHeader(),this.renderBody(),this.renderFooter(),this.renderNav(),this.syncOpenState()}syncOpenState(){const t=this.open;this.classList.toggle(`${o}--open`,t),this.launcher.setAttribute("aria-expanded",String(t)),this.panel.setAttribute("aria-hidden",String(!t)),t?this.panel.removeAttribute("inert"):this.panel.setAttribute("inert",""),this.launcher.setAttribute("aria-label",this.getAttribute("launcher-label")||(t?"상담창 닫기":"상담 시작하기")),t&&"conversation"===this.view&&requestAnimationFrame(()=>this.composer?.focusInput())}renderBadge(){const t=this.unread;t>0?this.launcherBadge.setAttribute("count",String(t)):this.launcherBadge.removeAttribute("count"),this.launcherBadge.hidden=t<=0}renderHeader(){this.headerEl.replaceChildren();const t=this.view,i=this.getAttribute("agent-name")||"디지털 상담원",s=el("div",`${o}__header-bar`);if("conversation"===t){const t=this.iconButton(e("chevron-left",20),"뒤로",()=>this.setView("messages")),n=el("div",`${o}__header-identity`),r=el("db-chat-orb");r.className=`${o}__header-orb`,r.setAttribute("size","xs"),r.setAttribute("variant",this.getAttribute("orb-variant")||"violet");const a=el("div",`${o}__header-meta`);a.append(el("span",`${o}__header-name`,i),el("span",`${o}__header-status`,this.getAttribute("agent-status")||"보통 몇 분 내 응답")),n.append(r,a),s.append(t,n,this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else if("article"===t){const t=this.iconButton(e("chevron-left",20),"뒤로",()=>{this._activeArticle=null,this.setView("help")});s.append(t,el("span",`${o}__header-title`,this._activeArticle?.title||"도움말"),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}else{const e="home"===t?"홈":"messages"===t?"메시지":"도움말";s.append(el("span",`${o}__header-title`,e),this.iconButton(widgetIcon("close-x",20),"닫기",()=>this.close()))}this.headerEl.append(s)}iconButton(t,e,i){const s=el("button",`${o}__icon-btn`);return s.type="button",s.setAttribute("aria-label",e),s.append(t),s.addEventListener("click",i),s}renderBody(){if(this.built)switch(this.threadEl=null,this.bodyEl.replaceChildren(),this.bodyEl.classList.toggle(`${o}__body--thread`,"conversation"===this.view),this.bodyEl.classList.toggle(`${o}__body--article`,"article"===this.view),this.view){case"home":this.renderHome();break;case"messages":this.renderMessages();break;case"conversation":this.renderConversation();break;case"help":this.renderHelp();break;case"article":this.renderArticle()}}renderHome(){const t=el("db-chat-welcome",`${o}__welcome`);t.setAttribute("greeting",this.getAttribute("greeting")||"안녕하세요"),t.setAttribute("title",this.getAttribute("title")||"무엇을 도와드릴까요?"),t.setAttribute("highlight",this.getAttribute("highlight")||this.getAttribute("title")||"도와드릴까요"),t.setAttribute("subtitle",this.getAttribute("subtitle")||"제품·정책을 24시간 정확하게 안내해 드릴게요."),t.setAttribute("orb-variant",this.getAttribute("orb-variant")||"violet"),t.setAttribute("align","left"),t.setAttribute("orb-size","m"),this.bodyEl.append(t);const i=this._conversations[0];if(i){const t=el("button",`${o}__recent`);t.type="button";const s=el("db-avatar",`${o}__recent-avatar`);s.setAttribute("size","s"),s.setAttribute("variant","primary"),i.avatarSrc?s.setAttribute("src",i.avatarSrc):s.setAttribute("initials",i.avatarInitials||i.name.charAt(0)||"상"),i.online&&s.setAttribute("status","online"),i.unread&&(s.setAttribute("badge",String(i.unread)),s.setAttribute("type","number"),s.setAttribute("badge-variant","primary"),s.setAttribute("badge-max-count","99"));const n=el("div",`${o}__recent-body`);n.append(el("span",`${o}__recent-title`,"최근 대화 이어가기"),el("span",`${o}__recent-preview`,i.preview||"")),t.append(s,n,e("chevron-right",18)),t.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:i.id}),this.setView("conversation")}),this.bodyEl.append(t)}else this.bodyEl.append(this.buildStartChatButton("대화 시작하기","primary"));if(this._menuItems.length){const t=el("db-chat-suggestions",`${o}__menu`);t.setAttribute("layout","card"),t.setAttribute("columns","1"),t.items=this._menuItems,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,i=this._menuItems.find(t=>(t.id??t.title)===e.id),s=i?.action??"none";this.dispatch("db-menu-select",{...e,action:s}),"conversation"===s?this.startConversation():"help"===s&&this.setView("help")}),this.bodyEl.append(t)}}renderMessages(){if(!this._conversations.length)return void this.bodyEl.append(this.buildEmpty("아직 대화가 없어요","궁금한 점이 있으면 새 대화를 시작해 보세요.",{ctaLabel:t("대화 시작하기"),onCta:()=>this.startConversation()}));const e=el("div",`${o}__conv-list`);this._conversations.forEach(t=>{const i=el("button",`${o}__conv`);i.type="button";const s=el("db-avatar",`${o}__conv-avatar`);s.setAttribute("size","m"),s.setAttribute("variant","primary"),t.avatarSrc?s.setAttribute("src",t.avatarSrc):s.setAttribute("initials",t.avatarInitials||t.name.charAt(0)||"?"),t.online&&s.setAttribute("status","online");const n=el("div",`${o}__conv-body`),r=el("div",`${o}__conv-top`);if(r.append(el("span",`${o}__conv-name`,t.name)),t.time&&r.append(el("span",`${o}__conv-time`,t.time)),n.append(r,el("span",`${o}__conv-preview`,t.preview||"")),i.append(s,n),t.unread){const e=el("db-badge",`${o}__conv-unread`);e.setAttribute("type","number"),e.setAttribute("variant","primary"),e.setAttribute("size","s"),e.setAttribute("max-count","99"),e.setAttribute("count",String(t.unread)),i.append(e)}i.addEventListener("click",()=>{this.dispatch("db-conversation-select",{id:t.id}),this.setView("conversation")}),e.append(i)}),this.bodyEl.append(e)}renderConversation(){const t=el("div",`${o}__thread`);if(this._messages.forEach(e=>t.append(this.buildMessage(e))),this.threadEl=t,this.bodyEl.append(t),this.typing&&t.append(this.buildTyping()),this._quickReplies.length){const t=el("db-chat-suggestions");t.setAttribute("data-dbchat","quick"),t.setAttribute("layout","chip"),t.items=this._quickReplies,t.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail;this.dispatch("db-quick-reply",e),this.addMessage({role:"user",text:e.title,time:this.nowLabel()})}),this.bodyEl.append(t)}requestAnimationFrame(()=>this.scrollThreadToEnd())}renderHelp(){const i=el("div",`${o}__help-search`),s=el("span",`${o}__help-search-icon`);s.append(e("search",18));const n=el("input",`${o}__help-input`);n.type="search",n.placeholder=t("도움말 검색…"),n.value=this.helpQuery,n.addEventListener("input",()=>{this.helpQuery=n.value,this.renderHelpResults()}),i.append(s,n),this.bodyEl.append(i);const r=el("div",`${o}__help-results`);this.bodyEl.append(r),this.renderHelpResults()}renderHelpResults(){const t=this.bodyEl.querySelector(`.${o}__help-results`);if(!t)return;t.replaceChildren();const e=this.helpQuery.trim().toLowerCase(),i=e?this._articles.filter(t=>t.title.toLowerCase().includes(e)||(t.description||"").toLowerCase().includes(e)||(t.body||"").toLowerCase().includes(e)):this._articles;if(!i.length)return void t.append(this.buildEmpty(e?"검색 결과가 없어요":"문서가 없어요",e?"다른 검색어로 다시 시도해 보세요.":"도움말 문서가 준비되면 여기에 표시됩니다."));const s=el("db-chat-suggestions",`${o}__articles`);s.setAttribute("layout","card"),s.setAttribute("columns","1"),s.items=i,s.addEventListener("db-select",t=>{t.stopPropagation();const e=t.detail,s=this._articles.find(t=>(t.id??t.title)===e.id)||i.find(t=>(t.id??t.title)===e.id);this.dispatch("db-article-select",{id:e.id,title:e.title}),s&&this.openArticle(s)}),t.append(s)}renderArticle(){const t=this._activeArticle;if(!t)return void this.setView("help");const s=el("article",`${o}__article`);if(t.icon&&i(t.icon)){const i=el("div",`${o}__article-icon`);i.append(e(t.icon,22)),s.append(i)}s.append(el("h2",`${o}__article-title`,t.title)),t.description&&s.append(el("p",`${o}__article-desc`,t.description));const n=el("div",`${o}__article-body`);(t.body||t.description||"").split(/\n{2,}/).forEach(t=>{const e=el("p",void 0,t.trim());e.textContent&&n.append(e)}),s.append(n);const r=this.buildStartChatButton("이 내용으로 문의하기","secondary");s.append(r),this.bodyEl.append(s)}openArticle(t){this._activeArticle=t,this.setView("article")}startConversation(){this.dispatch("db-start-chat"),this.setView("conversation")}buildMessage(t){const e="user"===t.role?"user":"assistant",i=el("db-chat-message");return i.setAttribute("role",e),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time),t.avatarSrc&&i.setAttribute("avatar-src",t.avatarSrc),t.avatarInitials&&i.setAttribute("avatar-initials",t.avatarInitials),t.actions&&i.setAttribute("actions",t.actions),i.textContent=t.text||"",t.attachments?.length&&(i.attachments=t.attachments),i.addEventListener("db-action",t=>{t.stopPropagation(),this.dispatch("db-message-action",t.detail)}),i}buildTyping(){const t=el("db-chat-typing",`${o}__typing`);return t.setAttribute("name",this.getAttribute("agent-name")||"상담원"),t.setAttribute("label","입력 중"),t}buildEmpty(t,e,i){const s=el("div",`${o}__empty`),n=el("db-chat-orb",`${o}__empty-orb`);return n.setAttribute("size","m"),n.setAttribute("variant",this.getAttribute("orb-variant")||"violet"),s.append(n,el("p",`${o}__empty-title`,t),el("p",`${o}__empty-desc`,e)),i?.ctaLabel&&i.onCta&&s.append(this.buildStartChatButton(i.ctaLabel,"primary",i.onCta)),s}buildStartChatButton(t,e="primary",i){const s=el("button",`${o}__cta ${o}__cta--${e}`);return s.type="button",s.append(widgetIcon("chat",18),el("span",void 0,t)),s.addEventListener("click",()=>i?i():this.startConversation()),s}renderFooter(){const t="conversation"===this.view;if(this.footerEl.hidden=!t,!t)return this.composer=null,void this.footerEl.replaceChildren();if(!this.composer){this.footerEl.replaceChildren();const t=el("db-chat-composer",`${o}__composer`);t.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지를 입력하세요…"),t.setAttribute("attach",""),t.setAttribute("voice",""),t.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail,i=e?.value?.trim()||"",s=e?.attachments||[];if(!i&&!s.length)return;const n=s.map(t=>({name:t.name,kind:t.kind,mime:t.mime,url:t.file?URL.createObjectURL(t.file):t.preview}));this.dispatch("db-send",{text:i,attachments:n}),this.addMessage({role:"user",text:i,time:this.nowLabel(),attachments:n.length?n:void 0})}),t.addEventListener("db-stop",t=>{t.stopPropagation(),this.dispatch("db-stop")}),t.addEventListener("db-attach",t=>{t.stopPropagation(),this.dispatch("db-attach")}),t.addEventListener("db-voice",t=>{t.stopPropagation(),this.dispatch("db-voice")}),this.composer=t,this.footerEl.append(t)}}renderNav(){this.navEl.replaceChildren();const e="conversation"!==this.view&&"article"!==this.view;if(this.navEl.hidden=!e,!e)return;[{view:"home",icon:"home",label:t("홈")},{view:"messages",icon:"chat",label:t("메시지")},{view:"help",icon:"help",label:t("도움말")}].forEach(t=>{const e=el("button",`${o}__tab`);e.type="button";const i=this.view===t.view;if(e.classList.toggle(`${o}__tab--active`,i),e.setAttribute("aria-current",String(i)),e.append(widgetIcon(t.icon,22),el("span",`${o}__tab-label`,t.label)),"messages"===t.view&&this.unread>0){const t=el("db-badge",`${o}__tab-badge`);t.setAttribute("type","dot"),t.setAttribute("variant","danger"),t.setAttribute("size","s"),e.append(t)}e.addEventListener("click",()=>{"help"===t.view&&(this.helpQuery=""),this.setView(t.view)}),this.navEl.append(e)})}scrollThreadToEnd(){this.bodyEl.scrollTop=this.bodyEl.scrollHeight}nowLabel(){const t=new Date,e=t.getHours();return`${e<12?"오전":"오후"} ${e%12==0?12:e%12}:${t.getMinutes().toString().padStart(2,"0")}`}dispatch(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}openWidget(){this.open||(this.open=!0,this.dispatch("db-open"),this.dispatch("db-toggle",{open:!0}))}close(){this.open&&(this.open=!1,this.dispatch("db-close"),this.dispatch("db-toggle",{open:!1}))}toggle(){this.open?this.close():this.openWidget()}setView(t){this.view!==t&&(this.view=t)}addMessage(t){if(this._messages=[...this._messages,{id:`m${Date.now()}`,...t}],"conversation"===this.view)if(this.built&&this.threadEl){const e=this.threadEl.querySelector("db-chat-typing");this.threadEl.insertBefore(this.buildMessage(t),e),requestAnimationFrame(()=>this.scrollThreadToEnd())}else this.built&&this.renderBody();else this.view="conversation"}setTyping(t){if(this.typing=Boolean(t),!this.built||"conversation"!==this.view||!this.threadEl)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollThreadToEnd())):!this.typing&&e&&e.remove()}setStreaming(t){this.composer?.setStreaming(Boolean(t))}setMessages(t){this.messages=t}}customElements.get("db-chat-widget")||customElements.define("db-chat-widget",DbChatWidget);export{DbChatWidget};
1
+ 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};
@@ -4,11 +4,14 @@ declare class DbCheckbox extends HTMLElement {
4
4
  private input?;
5
5
  private visual?;
6
6
  private labelEl?;
7
+ private readonly labelContent;
7
8
  private built;
8
9
  private userInteracted;
9
10
  private resetChecked?;
10
11
  private readonly cleanups;
11
12
  focus(options?: FocusOptions): void;
13
+ get label(): string;
14
+ set label(value: string);
12
15
  get name(): string;
13
16
  set name(value: string);
14
17
  get value(): string;
@@ -1 +1 @@
1
- import{createDbIcon as e}from"./db-icons.js";import{CleanupBag as t}from"../core/dom.js";import{coerceToContract as i}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{CHECKBOX_SIZES as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbCheckbox extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.userInteracted=!1,this.cleanups=new t,this.handleChange=()=>{if(!this.input||this.disabled||this.readOnly)return;this.userInteracted=!0;const e=this.input.checked;this.removeAttribute("selected"),this.toggleAttribute("checked",e),this.indeterminate&&(this.removeAttribute("indeterminate"),this.input.indeterminate=!1),this.syncVisual(),this.syncClasses(),this.syncValidity(),this.dispatchEvent(new CustomEvent("db-change",{detail:{checked:e,selected:e},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["checked","selected","default-checked","default-selected","indeterminate","size","disabled","readonly","required","name","value","has-label","aria-label","aria-labelledby","aria-describedby"]}focus(e){this.input?.focus(e)}get name(){return this.getAttribute("name")||""}set name(e){this.setAttribute("name",e)}get value(){return this.getAttribute("value")??"on"}set value(e){this.setAttribute("value",e)}get defaultChecked(){return this.hasAttribute("default-checked")||this.hasAttribute("default-selected")}set defaultChecked(e){this.removeAttribute("default-selected"),this.toggleAttribute("default-checked",e)}get defaultSelected(){return this.defaultChecked}set defaultSelected(e){this.defaultChecked=e}upgradeProperties(){for(const e of["checked","selected","defaultChecked","defaultSelected","name","value","disabled","readOnly","required","hasLabel","size","indeterminate"]){if(!Object.prototype.hasOwnProperty.call(this,e))continue;const t=this,i=t[e];delete t[e],t[e]=i}}get checked(){return this.input?.checked??(this.hasAttribute("checked")||this.hasAttribute("selected"))}set checked(e){this.userInteracted=!0,this.removeAttribute("selected"),this.toggleAttribute("checked",e),this.input&&(this.input.checked=e,this.syncVisual(),this.syncClasses())}get selected(){return this.checked}set selected(e){this.checked=e}get indeterminate(){return this.hasAttribute("indeterminate")}set indeterminate(e){this.toggleAttribute("indeterminate",e),this.input&&(this.input.indeterminate=e,this.syncVisual(),this.syncClasses())}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",e),this.input&&(this.input.disabled=e),this.syncClasses()}get readOnly(){return this.hasAttribute("readonly")}set readOnly(e){this.toggleAttribute("readonly",e),this.input&&(this.input.readOnly=e),this.syncClasses()}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",e),this.input&&(this.input.required=e)}get size(){return i(this.getAttribute("size"),s,"m")}set size(e){this.setAttribute("size",e)}connectedCallback(){this.upgradeProperties(),this.style.display="contents",this.built||this.build(),this.syncState();const e=this.input?.form;e&&this.cleanups.listen(e,"reset",()=>{window.setTimeout(()=>this.syncAfterReset(),0)})}disconnectedCallback(){this.cleanups.run()}syncAfterReset(){this.input&&(this.toggleAttribute("checked",this.input.checked),this.removeAttribute("selected"),this.syncVisual(),this.syncClasses())}attributeChangedCallback(e){if(this.built){if("default-checked"!==e&&"default-selected"!==e||(this.resetChecked=this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),"checked"===e||"selected"===e)return this.userInteracted=!0,this.input&&(this.input.checked=this.hasAttribute("checked")||this.hasAttribute("selected")),this.syncVisual(),void this.syncClasses();this.syncState()}}get hasLabel(){return!this.hasAttribute("has-label")||"false"!==this.getAttribute("has-label")}set hasLabel(e){this.setAttribute("has-label",String(e))}build(){for(this.root=document.createElement("label"),this.root.className="designbase-wc-checkbox",this.input=document.createElement("input"),this.input.type="checkbox",this.input.className="designbase-wc-checkbox__input",this.visual=document.createElement("span"),this.visual.className="designbase-wc-checkbox__visual",this.visual.setAttribute("aria-hidden","true"),this.labelEl=document.createElement("span"),this.labelEl.className="designbase-wc-checkbox__label";this.firstChild;)this.labelEl.appendChild(this.firstChild);this.root.append(this.input,this.visual,this.labelEl),this.appendChild(this.root),this.input.addEventListener("click",e=>{this.readOnly&&e.preventDefault()}),this.input.addEventListener("change",this.handleChange),this.built=!0}syncValidity(){if(!this.input)return;const e=!this.input.checkValidity();this.input.setAttribute("aria-invalid",String(e)),this.classList.toggle("designbase-wc-checkbox--invalid",e)}syncState(){if(!this.root||!this.input||!this.visual)return;const e=this.hasAttribute("checked")||this.hasAttribute("selected")||!this.userInteracted&&(this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),t=this.hasAttribute("indeterminate");this.input.checked=e,this.resetChecked??(this.resetChecked=e),this.input.defaultChecked=this.resetChecked,this.input.indeterminate=t,this.input.disabled=this.disabled,this.input.readOnly=this.readOnly,this.input.setAttribute("aria-readonly",String(this.readOnly)),this.input.required=this.required,this.input.name=this.getAttribute("name")||"",this.input.value=this.getAttribute("value")??"on";for(const e of["aria-label","aria-labelledby","aria-describedby"]){const t=this.getAttribute(e);t?this.input.setAttribute(e,t):this.input.removeAttribute(e)}this.hasLabel?this.labelEl?.removeAttribute("hidden"):this.labelEl?.setAttribute("hidden",""),this.syncVisual(),this.syncClasses()}syncVisual(){if(this.visual&&this.input){if(this.visual.replaceChildren(),this.input.indeterminate){const t=document.createElement("span");return t.className="designbase-wc-checkbox__indeterminate-icon",t.appendChild(e("minus",16)),void this.visual.appendChild(t)}if(this.input.checked){const t=document.createElement("span");t.className="designbase-wc-checkbox__check-icon",t.appendChild(e("done",16)),this.visual.appendChild(t)}}}syncClasses(){if(!this.root||!this.input)return;const e=s.includes(this.size)?this.size:"m",t=this.hasLabel&&Boolean(this.labelEl?.textContent?.trim());this.root.className=["designbase-wc-checkbox",`designbase-wc-checkbox--${e}`,this.input.checked&&"designbase-wc-checkbox--selected",this.input.indeterminate&&"designbase-wc-checkbox--indeterminate",this.disabled&&"designbase-wc-checkbox--disabled",this.readOnly&&"designbase-wc-checkbox--readonly",!t&&"designbase-wc-checkbox--no-label"].filter(Boolean).join(" ")}}customElements.get("db-checkbox")||customElements.define("db-checkbox",DbCheckbox);export{DbCheckbox};
1
+ import{ControlLabel as e}from"../core/control-label.js";import{createDbIcon as t}from"./db-icons.js";import{CleanupBag as s}from"../core/dom.js";import{coerceToContract as i}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{CHECKBOX_SIZES as h}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbCheckbox extends HTMLElement{constructor(){super(...arguments),this.labelContent=new e(this),this.built=!1,this.userInteracted=!1,this.cleanups=new s,this.handleChange=()=>{if(!this.input||this.disabled||this.readOnly)return;this.userInteracted=!0;const e=this.input.checked;this.removeAttribute("selected"),this.toggleAttribute("checked",e),this.indeterminate&&(this.removeAttribute("indeterminate"),this.input.indeterminate=!1),this.syncVisual(),this.syncClasses(),this.syncValidity(),this.dispatchEvent(new CustomEvent("db-change",{detail:{checked:e,selected:e},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["checked","selected","default-checked","default-selected","indeterminate","size","disabled","readonly","required","name","value","label","has-label","aria-label","aria-labelledby","aria-describedby"]}focus(e){this.input?.focus(e)}get label(){return this.labelContent.value}set label(e){this.setAttribute("label",e)}get name(){return this.getAttribute("name")||""}set name(e){this.setAttribute("name",e)}get value(){return this.getAttribute("value")??"on"}set value(e){this.setAttribute("value",e)}get defaultChecked(){return this.hasAttribute("default-checked")||this.hasAttribute("default-selected")}set defaultChecked(e){this.removeAttribute("default-selected"),this.toggleAttribute("default-checked",e)}get defaultSelected(){return this.defaultChecked}set defaultSelected(e){this.defaultChecked=e}upgradeProperties(){for(const e of["checked","selected","defaultChecked","defaultSelected","name","value","label","disabled","readOnly","required","hasLabel","size","indeterminate"]){if(!Object.prototype.hasOwnProperty.call(this,e))continue;const t=this,s=t[e];delete t[e],t[e]=s}}get checked(){return this.input?.checked??(this.hasAttribute("checked")||this.hasAttribute("selected"))}set checked(e){this.userInteracted=!0,this.removeAttribute("selected"),this.toggleAttribute("checked",e),this.input&&(this.input.checked=e,this.syncVisual(),this.syncClasses())}get selected(){return this.checked}set selected(e){this.checked=e}get indeterminate(){return this.hasAttribute("indeterminate")}set indeterminate(e){this.toggleAttribute("indeterminate",e),this.input&&(this.input.indeterminate=e,this.syncVisual(),this.syncClasses())}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",e),this.input&&(this.input.disabled=e),this.syncClasses()}get readOnly(){return this.hasAttribute("readonly")}set readOnly(e){this.toggleAttribute("readonly",e),this.input&&(this.input.readOnly=e),this.syncClasses()}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",e),this.input&&(this.input.required=e)}get size(){return i(this.getAttribute("size"),h,"m")}set size(e){this.setAttribute("size",e)}connectedCallback(){this.upgradeProperties(),this.style.display="contents",this.built||this.build(),this.labelContent.connect(this.labelEl,()=>this.syncClasses()),this.syncState();const e=this.input?.form;e&&this.cleanups.listen(e,"reset",()=>{window.setTimeout(()=>this.syncAfterReset(),0)})}disconnectedCallback(){this.cleanups.run(),this.labelContent.disconnect()}syncAfterReset(){this.input&&(this.toggleAttribute("checked",this.input.checked),this.removeAttribute("selected"),this.syncVisual(),this.syncClasses())}attributeChangedCallback(e){if(this.built){if("default-checked"!==e&&"default-selected"!==e||(this.resetChecked=this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),"checked"===e||"selected"===e)return this.userInteracted=!0,this.input&&(this.input.checked=this.hasAttribute("checked")||this.hasAttribute("selected")),this.syncVisual(),void this.syncClasses();this.syncState()}}get hasLabel(){return!this.hasAttribute("has-label")||"false"!==this.getAttribute("has-label")}set hasLabel(e){this.setAttribute("has-label",String(e))}build(){for(this.root=document.createElement("label"),this.root.className="designbase-wc-checkbox",this.input=document.createElement("input"),this.input.type="checkbox",this.input.className="designbase-wc-checkbox__input",this.visual=document.createElement("span"),this.visual.className="designbase-wc-checkbox__visual",this.visual.setAttribute("aria-hidden","true"),this.labelEl=document.createElement("span"),this.labelEl.className="designbase-wc-checkbox__label";this.firstChild;)this.labelEl.appendChild(this.firstChild);this.root.append(this.input,this.visual,this.labelEl),this.appendChild(this.root),this.input.addEventListener("click",e=>{this.readOnly&&e.preventDefault()}),this.input.addEventListener("change",this.handleChange),this.built=!0}syncValidity(){if(!this.input)return;const e=!this.input.checkValidity();this.input.setAttribute("aria-invalid",String(e)),this.classList.toggle("designbase-wc-checkbox--invalid",e)}syncState(){if(!this.root||!this.input||!this.visual)return;this.labelContent.sync();const e=this.hasAttribute("checked")||this.hasAttribute("selected")||!this.userInteracted&&(this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),t=this.hasAttribute("indeterminate");this.input.checked=e,this.resetChecked??(this.resetChecked=e),this.input.defaultChecked=this.resetChecked,this.input.indeterminate=t,this.input.disabled=this.disabled,this.input.readOnly=this.readOnly,this.input.setAttribute("aria-readonly",String(this.readOnly)),this.input.required=this.required,this.input.name=this.getAttribute("name")||"",this.input.value=this.getAttribute("value")??"on";for(const e of["aria-label","aria-labelledby","aria-describedby"]){const t=this.getAttribute(e);t?this.input.setAttribute(e,t):this.input.removeAttribute(e)}this.hasLabel?this.labelEl?.removeAttribute("hidden"):this.labelEl?.setAttribute("hidden",""),this.syncVisual(),this.syncClasses()}syncVisual(){if(this.visual&&this.input){if(this.visual.replaceChildren(),this.input.indeterminate){const e=document.createElement("span");return e.className="designbase-wc-checkbox__indeterminate-icon",e.appendChild(t("minus",16)),void this.visual.appendChild(e)}if(this.input.checked){const e=document.createElement("span");e.className="designbase-wc-checkbox__check-icon",e.appendChild(t("done",16)),this.visual.appendChild(e)}}}syncClasses(){if(!this.root||!this.input)return;const e=h.includes(this.size)?this.size:"m",t=this.hasLabel&&Boolean(this.label.trim());this.root.className=["designbase-wc-checkbox",`designbase-wc-checkbox--${e}`,this.input.checked&&"designbase-wc-checkbox--selected",this.input.indeterminate&&"designbase-wc-checkbox--indeterminate",this.disabled&&"designbase-wc-checkbox--disabled",this.readOnly&&"designbase-wc-checkbox--readonly",!t&&"designbase-wc-checkbox--no-label"].filter(Boolean).join(" ")}}customElements.get("db-checkbox")||customElements.define("db-checkbox",DbCheckbox);export{DbCheckbox};
@@ -1,6 +1,8 @@
1
1
  declare class DbChip extends HTMLElement {
2
+ private unsubscribeLocale?;
2
3
  static get observedAttributes(): string[];
3
4
  connectedCallback(): void;
5
+ disconnectedCallback(): void;
4
6
  attributeChangedCallback(): void;
5
7
  private iconSize;
6
8
  private render;
@@ -1 +1 @@
1
- import{dbFormat as e}from"../core/i18n.js";import{isDbIconName as t,createDbIcon as i}from"./db-icons.js";import{CHIP_SIZES as s,CHIP_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbChip extends HTMLElement{static get observedAttributes(){return["label","size","variant","deletable","disabled","selected","full-width","clickable","start-icon","end-icon"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}iconSize(e){return"s"===e?12:"l"===e?20:16}render(){const a=this.getAttribute("label")||this.textContent?.trim()||"",c=this.getAttribute("size")||"m",d=this.getAttribute("variant")||"default",l=this.hasAttribute("disabled"),b=this.hasAttribute("deletable"),r=this.hasAttribute("selected"),o=this.hasAttribute("full-width"),h=this.hasAttribute("clickable"),p=this.getAttribute("start-icon"),u=this.getAttribute("end-icon"),m=s.includes(c)?c:"m",g=n.includes(d)?d:"default",w=this.iconSize(m);if(this.className=["designbase-wc-chip",`designbase-wc-chip--${m}`,`designbase-wc-chip--${g}`,b&&"designbase-wc-chip--deletable",l&&"designbase-wc-chip--disabled",r&&"designbase-wc-chip--selected",h&&"designbase-wc-chip--clickable",o&&"designbase-wc-chip--full-width"].filter(Boolean).join(" "),this.replaceChildren(),h&&!l?(this.setAttribute("role","button"),this.tabIndex=0,this.setAttribute("aria-pressed",String(r))):(this.removeAttribute("role"),this.removeAttribute("tabindex"),this.removeAttribute("aria-pressed")),p&&t(p)){const e=document.createElement("span");e.className="designbase-wc-chip__start-icon",e.append(i(p,w)),this.append(e)}const A=document.createElement("span");if(A.className="designbase-wc-chip__label",A.textContent=a,this.append(A),u&&t(u)&&!b){const e=document.createElement("span");e.className="designbase-wc-chip__end-icon",e.append(i(u,w)),this.append(e)}if(b){const t=document.createElement("button");t.type="button",t.className="designbase-wc-chip__delete-button",t.disabled=l,t.setAttribute("aria-label",e("{0} 삭제",[a])),t.append(i("close",w)),t.addEventListener("click",e=>{e.stopPropagation(),l||this.dispatchEvent(new CustomEvent("db-delete",{bubbles:!0,composed:!0}))}),this.append(t)}this.onclick=()=>{!l&&h&&this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0}))},this.onkeydown=e=>{"Enter"!==e.key&&" "!==e.key||l||!h||(e.preventDefault(),this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0})))}}}customElements.get("db-chip")||customElements.define("db-chip",DbChip);export{DbChip};
1
+ 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};